feat(smtp): write-only password support for SMTP credentials (#52)#76
feat(smtp): write-only password support for SMTP credentials (#52)#76dimoschi wants to merge 7 commits into
Conversation
- Extend SmtpCredentialModel with PasswordWO (types.String) and PasswordWOVersion (types.Int64) tfsdk fields alongside the existing Password field. - Add password_wo (WriteOnly, Optional, Sensitive) and password_wo_version (Optional) schema attributes with mutual conflict/require validators against the legacy password field. - Deprecate the password schema attribute in favour of password_wo; it remains Computed+Optional for backward compatibility. - TestSmtpCredentialResourceSchema_WriteOnlyPassword covers the new schema shape (red→green TDD cycle).
Two pure unexported helpers for write-only SMTP password support: - passwordForCreate: prefers password_wo, falls back to legacy password - writeOnlyRotationRequested: detects version bump as rotation signal 9 table-driven unit tests cover all paths (CRAP ≤3 for both functions).
Wire passwordForCreate (prefers password_wo over legacy password) into Create. Read password_wo from req.Config since write-only values are absent from plan/state. Pin plan.Password to null in state when the write-only path is used to avoid leaving it unknown. Add resolveUpdatePassword pure helper (100% unit-tested via TestResolveUpdatePassword) that encapsulates the Update rotation decision: write-only path rotates only on version bump, legacy null preserves imported state, legacy empty errors, legacy non-empty rotates. Note: Create and Update are framework lifecycle methods (I/O glue only); all decision logic extracted into pure helpers with 100% unit coverage.
|
|
||
| # Write-only password (recommended). Requires Terraform CLI >= 1.11. | ||
| # The secret is never written to Terraform state. Bump password_wo_version to rotate. | ||
| resource "random_password" "smtp" { |
There was a problem hiding this comment.
| resource "random_password" "smtp" { | |
| ephemeral "random_password" "smtp" { |
Since the only destination of this is a wo field, we should use an ephemeral resources for random_password as well.
There was a problem hiding this comment.
Good call. Switched the example to an ephemeral "random_password" and updated the reference to ephemeral.random_password.smtp.result so the generated secret never lands in state. Done in fc26208.
|
|
||
| # Write-only password (recommended). Requires Terraform CLI >= 1.11. | ||
| # The secret is never written to Terraform state. Bump password_wo_version to rotate. | ||
| resource "random_password" "smtp" { |
There was a problem hiding this comment.
| resource "random_password" "smtp" { | |
| ephemeral "random_password" "smtp" { |
There was a problem hiding this comment.
This block is generated from the example file, so it now shows the ephemeral resource after make generate (fc26208).
| - `password` (String, Sensitive) The password for SMTP authentication. This is write-only and cannot be read back from the API. Set this when creating a credential or when rotating the password of an imported credential. Leave it unset to keep the existing password of an imported credential. | ||
| > **NOTE**: [Write-only arguments](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments) are supported in Terraform 1.11 and later. | ||
|
|
||
| - `password` (String, Sensitive, Deprecated) The password for SMTP authentication. This is write-only and cannot be read back from the API. Set this when creating a credential or when rotating the password of an imported credential. Leave it unset to keep the existing password of an imported credential. |
There was a problem hiding this comment.
| - `password` (String, Sensitive, Deprecated) The password for SMTP authentication. This is write-only and cannot be read back from the API. Set this when creating a credential or when rotating the password of an imported credential. Leave it unset to keep the existing password of an imported credential. | |
| - `password` (String, Sensitive, Deprecated) The password for SMTP authentication. This is write-only and cannot be read back from the API. Set this when creating credentials or when rotating the password. Leave it unset to maintain the existing password during import. |
Some suggestion on the text, highly optional
There was a problem hiding this comment.
Applied (at the schema Description source, then regenerated). fc26208.
| > **NOTE**: [Write-only arguments](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments) are supported in Terraform 1.11 and later. | ||
|
|
||
| - `password` (String, Sensitive, Deprecated) The password for SMTP authentication. This is write-only and cannot be read back from the API. Set this when creating a credential or when rotating the password of an imported credential. Leave it unset to keep the existing password of an imported credential. | ||
| - `password_wo` (String, Sensitive, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) Write-only password for SMTP authentication. The value is never stored in Terraform state. Set it together with password_wo_version and increment the version to rotate the password. Requires Terraform CLI >= 1.11. |
There was a problem hiding this comment.
| - `password_wo` (String, Sensitive, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) Write-only password for SMTP authentication. The value is never stored in Terraform state. Set it together with password_wo_version and increment the version to rotate the password. Requires Terraform CLI >= 1.11. | |
| - `password_wo` (String, Sensitive, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) Write-only password for SMTP authentication. The value is never stored in Terraform state. Always set it together with password_wo_version. |
I think we can skip the password version specifics since we have a dedicated entry below
There was a problem hiding this comment.
Trimmed the version/CLI specifics from the password_wo description since password_wo_version has its own entry. fc26208.
|
|
||
| - `password` (String, Sensitive, Deprecated) The password for SMTP authentication. This is write-only and cannot be read back from the API. Set this when creating a credential or when rotating the password of an imported credential. Leave it unset to keep the existing password of an imported credential. | ||
| - `password_wo` (String, Sensitive, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) Write-only password for SMTP authentication. The value is never stored in Terraform state. Set it together with password_wo_version and increment the version to rotate the password. Requires Terraform CLI >= 1.11. | ||
| - `password_wo_version` (Number) Version counter for password_wo. Increment this value to rotate the write-only password. Required when password_wo is set. |
There was a problem hiding this comment.
| - `password_wo_version` (Number) Version counter for password_wo. Increment this value to rotate the write-only password. Required when password_wo is set. | |
| - `password_wo_version` (Number) Version counter for password_wo. Increment this value to rotate the write-only password. **Required** when password_wo is set. |
There was a problem hiding this comment.
Bolded Required on password_wo_version. fc26208.
Summary
Adds first-class write-only password support to
mailgun_smtp_credential, closing #52.password_wo(write-only, never stored in state) andpassword_wo_version(Int64) arguments; bumping the version rotates the password.passwordargument is deprecated but remains fully functional (backward-compatible minor release).passwordXOR (password_wo+password_wo_version).password_wo/password_wo_versionto enable rotation.Implementation notes
passwordForCreate,writeOnlyRotationRequested,resolveUpdatePassword) so the CRUD branching is testable without the Mailgun API client.passwordComputed attribute is pinned to null when the write-only path is used to avoid spurious diffs). It is not covered by a live acceptance test, and theConflictsWithvalidator is covered by schema unit assertions rather than an acceptance step, given the shared 1-domain CI account.Test Plan
go build ./...,go vet ./...,gofmt -lcleango test ./...— unit tests pass (helpers + schema); acceptance tests skip withoutMAILGUN_API_KEY/MAILGUN_TEST_DOMAINmake generateproduces no docs driftTF_ACC=1 MAILGUN_API_KEY=... MAILGUN_TEST_DOMAIN=... go test ./internal/provider/smtp_credentials/ -run TestAccSmtpCredentialResource_WriteOnly(create at v1 → no-op plan at v1 → rotate at v2) on Terraform CLI >= 1.11