diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eeeaa7b..6ab3b413 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ Canonical reference for changes, improvements, and bugfixes for the Boundary Terraform provider. +## Next + +### New and Improved + +* Add support for setting mapping overrides for vault credential libraries + ([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/287)). + ## 1.1.1 (October 12, 2022) ### Bug Fixes diff --git a/docs/resources/credential_library_vault.md b/docs/resources/credential_library_vault.md index 2e4ed122..29e15ebd 100644 --- a/docs/resources/credential_library_vault.md +++ b/docs/resources/credential_library_vault.md @@ -56,6 +56,33 @@ resource "boundary_credential_library_vault" "bar" { } EOT } + +resource "boundary_credential_library_vault" "baz" { + name = "baz" + description = "vault username password credential with mapping overrides" + credential_store_id = boundary_credential_store_vault.foo.id + path = "my/secret/baz" # change to Vault backend path + http_method = "GET" + credential_type = "username_password" + credential_mapping_overrides = { + password_attribute = "alternative_password_label" + username_attribute = "alternative_username_label" + } +} + +resource "boundary_credential_library_vault" "quz" { + name = "quz" + description = "vault ssh private key credential with mapping overrides" + credential_store_id = boundary_credential_store_vault.foo.id + path = "my/secret/quz" # change to Vault backend path + http_method = "GET" + credential_type = "ssh_private_key" + credential_mapping_overrides = { + private_key_attribute = "alternative_key_label" + private_key_passphrase_attribute = "alternative_passphrase_label" + username_attribute = "alternative_username_label" + } +} ``` @@ -68,6 +95,7 @@ EOT ### Optional +- `credential_mapping_overrides` (Map of String) The credential mapping override. - `credential_type` (String) The type of credential the library generates. - `description` (String) The Vault credential library description. - `http_method` (String) The HTTP method the library uses when requesting credentials from Vault. Defaults to 'GET' diff --git a/examples/resources/boundary_credential_library_vault/resource.tf b/examples/resources/boundary_credential_library_vault/resource.tf index 5a344aac..0d5af81f 100644 --- a/examples/resources/boundary_credential_library_vault/resource.tf +++ b/examples/resources/boundary_credential_library_vault/resource.tf @@ -41,3 +41,30 @@ resource "boundary_credential_library_vault" "bar" { } EOT } + +resource "boundary_credential_library_vault" "baz" { + name = "baz" + description = "vault username password credential with mapping overrides" + credential_store_id = boundary_credential_store_vault.foo.id + path = "my/secret/baz" # change to Vault backend path + http_method = "GET" + credential_type = "username_password" + credential_mapping_overrides = { + password_attribute = "alternative_password_label" + username_attribute = "alternative_username_label" + } +} + +resource "boundary_credential_library_vault" "quz" { + name = "quz" + description = "vault ssh private key credential with mapping overrides" + credential_store_id = boundary_credential_store_vault.foo.id + path = "my/secret/quz" # change to Vault backend path + http_method = "GET" + credential_type = "ssh_private_key" + credential_mapping_overrides = { + private_key_attribute = "alternative_key_label" + private_key_passphrase_attribute = "alternative_passphrase_label" + username_attribute = "alternative_username_label" + } +} diff --git a/internal/provider/resource_credential_library_vault.go b/internal/provider/resource_credential_library_vault.go index 2dbff4f8..12886f35 100644 --- a/internal/provider/resource_credential_library_vault.go +++ b/internal/provider/resource_credential_library_vault.go @@ -11,11 +11,12 @@ import ( ) const ( - credentialStoreIdKey = "credential_store_id" - credentialLibraryVaultHttpMethodKey = "http_method" - credentialLibraryVaultHttpRequestBodyKey = "http_request_body" - credentialLibraryVaultPathKey = "path" - credentialLibraryCredentialTypeKey = "credential_type" + credentialStoreIdKey = "credential_store_id" + credentialLibraryVaultHttpMethodKey = "http_method" + credentialLibraryVaultHttpRequestBodyKey = "http_request_body" + credentialLibraryVaultPathKey = "path" + credentialLibraryCredentialTypeKey = "credential_type" + credentialLibraryCredentialMappingOverridesKey = "credential_mapping_overrides" ) var libraryVaultAttrs = []string{ @@ -78,6 +79,11 @@ func resourceCredentialLibraryVault() *schema.Resource { Type: schema.TypeString, Optional: true, }, + credentialLibraryCredentialMappingOverridesKey: { + Description: "The credential mapping override.", + Type: schema.TypeMap, + Optional: true, + }, }, } } @@ -93,6 +99,10 @@ func setFromVaultCredentialLibraryResponseMap(d *schema.ResourceData, raw map[st return err } + if err := d.Set(credentialLibraryCredentialMappingOverridesKey, raw[credentialLibraryCredentialMappingOverridesKey]); err != nil { + return err + } + if attrsVal, ok := raw["attributes"]; ok { attrs := attrsVal.(map[string]interface{}) for _, v := range libraryVaultAttrs { @@ -129,6 +139,10 @@ func resourceCredentialLibraryCreateVault(ctx context.Context, d *schema.Resourc if v, ok := d.GetOk(credentialLibraryCredentialTypeKey); ok { opts = append(opts, credentiallibraries.WithCredentialType(v.(string))) } + if v, ok := d.GetOk(credentialLibraryCredentialMappingOverridesKey); ok { + mappingOverrides := v.(map[string]interface{}) + opts = append(opts, credentiallibraries.WithCredentialMappingOverrides(mappingOverrides)) + } var credentialStoreId string cid, ok := d.GetOk(credentialStoreIdKey) @@ -216,6 +230,40 @@ func resourceCredentialLibraryUpdateVault(ctx context.Context, d *schema.Resourc opts = append(opts, credentiallibraries.WithVaultCredentialLibraryPath(v.(string))) } } + if d.HasChange(credentialLibraryCredentialMappingOverridesKey) { + var oldMapping, newMapping map[string]interface{} + old, new := d.GetChange(credentialLibraryCredentialMappingOverridesKey) + + if old == nil { + old = map[string]interface{}{} + } + oldMapping = old.(map[string]interface{}) + + if new == nil { + new = map[string]interface{}{} + } + newMapping = new.(map[string]interface{}) + + newAttrs := []string{} + for k := range newMapping { + newAttrs = append(newAttrs, k) + } + + for oldAttr := range oldMapping { + isRemoved := true + for _, newAttr := range newAttrs { + if oldAttr == newAttr { + isRemoved = false + break + } + } + if isRemoved { + newMapping[oldAttr] = nil + } + } + + opts = append(opts, credentiallibraries.WithCredentialMappingOverrides(newMapping)) + } if len(opts) > 0 { opts = append(opts, credentiallibraries.WithAutomaticVersioning(true)) diff --git a/internal/provider/resource_credential_library_vault_test.go b/internal/provider/resource_credential_library_vault_test.go index c53c8ae1..879655b6 100644 --- a/internal/provider/resource_credential_library_vault_test.go +++ b/internal/provider/resource_credential_library_vault_test.go @@ -16,24 +16,26 @@ import ( ) const ( - vaultCredResc = "boundary_credential_library_vault.example" - vaultCredTypedResc = "boundary_credential_library_vault.typed_example" - vaultCredLibName = "foo" - vaultCredLibDesc = "the foo" - vaultCredLibPath = "/foo/bar" - vaultCredLibMethodGet = "GET" - vaultCredLibMethodPost = "POST" - vaultCredLibRequestBody = "foobar" - vaultCredLibStringUpdate = "_random" + vaultCredResc = "boundary_credential_library_vault.example" + vaultCredTypedResc = "boundary_credential_library_vault.typed_example" + vaultCredUsernamePasswordResc = "boundary_credential_library_vault.username_password_mapping_override" + vaultCredSshPrivateKeyResc = "boundary_credential_library_vault.ssh_private_key_mapping_override" + vaultCredLibName = "foo" + vaultCredLibDesc = "the foo" + vaultCredLibPath = "/foo/bar" + vaultCredLibMethodGet = "GET" + vaultCredLibMethodPost = "POST" + vaultCredLibRequestBody = "foobar" + vaultCredLibStringUpdate = "_random" ) var vaultCredLibResource = fmt.Sprintf(` resource "boundary_credential_library_vault" "example" { - name = "%s" - description = "%s" + name = "%s" + description = "%s" credential_store_id = boundary_credential_store_vault.example.id - path = "%s" - http_method = "%s" + path = "%s" + http_method = "%s" }`, vaultCredLibName, vaultCredLibDesc, vaultCredLibPath, @@ -41,12 +43,12 @@ resource "boundary_credential_library_vault" "example" { var vaultCredLibResourceUpdate = fmt.Sprintf(` resource "boundary_credential_library_vault" "example" { - name = "%s" - description = "%s" + name = "%s" + description = "%s" credential_store_id = boundary_credential_store_vault.example.id - path = "%s" - http_method = "%s" - http_request_body = "%s" + path = "%s" + http_method = "%s" + http_request_body = "%s" }`, vaultCredLibName+vaultCredLibStringUpdate, vaultCredLibDesc+vaultCredLibStringUpdate, vaultCredLibPath+vaultCredLibStringUpdate, @@ -66,6 +68,71 @@ resource "boundary_credential_library_vault" "typed_example" { vaultCredLibPath, vaultCredLibMethodGet) +var vaultUsernamePasswordMappingOverrideCredLibResource = fmt.Sprintf(` +resource "boundary_credential_library_vault" "username_password_mapping_override" { + name = "%s" + description = "%s" + credential_store_id = boundary_credential_store_vault.example.id + path = "%s" + http_method = "%s" + credential_type = "username_password" + credential_mapping_overrides = { + password_attribute = "alternative_password_label" + username_attribute = "alternative_username_label" + } +}`, vaultCredLibName, + vaultCredLibDesc, + vaultCredLibPath, + vaultCredLibMethodGet) + +var vaultUsernamePasswordMappingOverrideCredLibResourceUpdate = fmt.Sprintf(` + resource "boundary_credential_library_vault" "username_password_mapping_override" { + name = "%s" + description = "%s" + credential_store_id = boundary_credential_store_vault.example.id + path = "%s" + http_method = "%s" + credential_type = "username_password" + credential_mapping_overrides = { + password_attribute = "updated_password_label" + username_attribute = "updated_username_label" + } + }`, vaultCredLibName, + vaultCredLibDesc, + vaultCredLibPath, + vaultCredLibMethodGet) + +var vaultUsernamePasswordMappingOverrideCredLibResourceRemove = fmt.Sprintf(` + resource "boundary_credential_library_vault" "username_password_mapping_override" { + name = "%s" + description = "%s" + credential_store_id = boundary_credential_store_vault.example.id + path = "%s" + http_method = "%s" + credential_type = "username_password" + }`, vaultCredLibName, + vaultCredLibDesc, + vaultCredLibPath, + vaultCredLibMethodGet) + +var vaultSshPrivateKeyMappingOverrideCredLibResource = fmt.Sprintf(` +resource "boundary_credential_library_vault" "ssh_private_key_mapping_override" { + name = "%s" + description = "%s" + credential_store_id = boundary_credential_store_vault.example.id + path = "%s" + http_method = "%s" + credential_type = "ssh_private_key" + credential_mapping_overrides = { + private_key_attribute = "alternative_key_label" + private_key_passphrase_attribute = "alternative_passphrase_label" + username_attribute = "alternative_username_label" + } +}`, vaultCredLibName, + vaultCredLibDesc, + vaultCredLibPath, + vaultCredLibMethodGet) + func TestAccCredentialLibraryVault(t *testing.T) { tc := controller.NewTestController(t, tcConfig...) defer tc.Shutdown() @@ -132,6 +199,74 @@ func TestAccCredentialLibraryVault(t *testing.T) { ), }, importStep(vaultCredResc), + + { + Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResourceUpdate, vaultUsernamePasswordMappingOverrideCredLibResource), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, NameKey, vaultCredLibName), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, DescriptionKey, vaultCredLibDesc), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultPathKey, vaultCredLibPath), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodGet), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpRequestBodyKey, ""), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryCredentialTypeKey, "username_password"), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, "credential_mapping_overrides.password_attribute", "alternative_password_label"), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, "credential_mapping_overrides.username_attribute", "alternative_username_label"), + + testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredUsernamePasswordResc), + ), + }, + importStep(vaultCredResc), + + { + Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResourceUpdate, vaultUsernamePasswordMappingOverrideCredLibResourceUpdate), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, NameKey, vaultCredLibName), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, DescriptionKey, vaultCredLibDesc), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultPathKey, vaultCredLibPath), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodGet), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpRequestBodyKey, ""), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryCredentialTypeKey, "username_password"), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, "credential_mapping_overrides.password_attribute", "updated_password_label"), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, "credential_mapping_overrides.username_attribute", "updated_username_label"), + + testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredUsernamePasswordResc), + ), + }, + importStep(vaultCredResc), + + { + Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResourceUpdate, vaultUsernamePasswordMappingOverrideCredLibResourceRemove), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, NameKey, vaultCredLibName), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, DescriptionKey, vaultCredLibDesc), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultPathKey, vaultCredLibPath), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodGet), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpRequestBodyKey, ""), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryCredentialTypeKey, "username_password"), + resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryCredentialMappingOverridesKey+".%", "0"), + + testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredUsernamePasswordResc), + ), + }, + importStep(vaultCredResc), + + { + Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResourceUpdate, vaultSshPrivateKeyMappingOverrideCredLibResource), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, NameKey, vaultCredLibName), + resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, DescriptionKey, vaultCredLibDesc), + resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, credentialLibraryVaultPathKey, vaultCredLibPath), + resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodGet), + resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, credentialLibraryVaultHttpRequestBodyKey, ""), + resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, credentialLibraryCredentialTypeKey, "ssh_private_key"), + resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, "credential_mapping_overrides.private_key_attribute", "alternative_key_label"), + resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, "credential_mapping_overrides.private_key_passphrase_attribute", "alternative_passphrase_label"), + resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, "credential_mapping_overrides.username_attribute", "alternative_username_label"), + + testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredSshPrivateKeyResc), + ), + }, + importStep(vaultCredResc), }, }) }