diff --git a/CHANGELOG.md b/CHANGELOG.md index 7195aff62..630fd7d0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ FEATURES: * Add support for signature_bits field to `vault_pki_secret_backend_role`, `vault_pki_secret_backend_root_cert`, `vault_pki_secret_backend_root_sign_intermediate` and `vault_pki_secret_backend_intermediate_cert_request` ([#2401])(https://github.com/hashicorp/terraform-provider-vault/pull/2401) * Add support for key_usage and serial_number to `vault_pki_secret_backend_intermediate_cert_request` ([#2404])(https://github.com/hashicorp/terraform-provider-vault/pull/2404) * Add support for `skip_import_rotation` in `vault_database_secret_backend_static_role`. Requires Vault Enterprise 1.18.5+ ([#2386](https://github.com/hashicorp/terraform-provider-vault/pull/2386)). +* Add `credential_type` and `credential_config` to `database_secret_backend_static_role` to support features like rsa keys for Snowflake DB engines with static roles BUGS: diff --git a/vault/resource_database_secret_backend_static_role.go b/vault/resource_database_secret_backend_static_role.go index d332916f8..cf7458287 100644 --- a/vault/resource_database_secret_backend_static_role.go +++ b/vault/resource_database_secret_backend_static_role.go @@ -27,6 +27,8 @@ var staticRoleFields = []string{ consts.FieldRotationPeriod, consts.FieldRotationStatements, consts.FieldDBName, + consts.FieldCredentialType, + consts.FieldCredentialConfig, } func databaseSecretBackendStaticRoleResource() *schema.Resource { @@ -105,6 +107,20 @@ func databaseSecretBackendStaticRoleResource() *schema.Resource { Optional: true, Description: "Skip rotation of the password on import.", }, + consts.FieldCredentialType: { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "The credential type for the user, can be one of \"password\", \"rsa_private_key\" or \"client_certificate\"." + + "The configuration can be done in `credential_config`.", + }, + consts.FieldCredentialConfig: { + Type: schema.TypeMap, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Description: "The configuration for the credential type." + + "Full documentation for the allowed values can be found under \"https://developer.hashicorp.com/vault/api-docs/secret/databases#credential_config\".", + }, }, } } @@ -144,6 +160,14 @@ func databaseSecretBackendStaticRoleWrite(ctx context.Context, d *schema.Resourc data[consts.FieldRotationPeriod] = v } + if v, ok := d.GetOk(consts.FieldCredentialType); ok && v != "" { + data[consts.FieldCredentialType] = v + } + + if v, ok := d.GetOk(consts.FieldCredentialConfig); ok && v != "" { + data[consts.FieldCredentialConfig] = v + } + if provider.IsAPISupported(meta, provider.VaultVersion118) && provider.IsEnterpriseSupported(meta) { if v, ok := d.GetOk(consts.FieldSelfManagedPassword); ok && v != "" { data[consts.FieldSelfManagedPassword] = v diff --git a/vault/resource_database_secret_backend_static_role_test.go b/vault/resource_database_secret_backend_static_role_test.go index d8759d1e3..5b54b95e2 100644 --- a/vault/resource_database_secret_backend_static_role_test.go +++ b/vault/resource_database_secret_backend_static_role_test.go @@ -59,6 +59,80 @@ func TestAccDatabaseSecretBackendStaticRole_import(t *testing.T) { }) } +func TestAccDatabaseSecretBackendStaticRole_credentialType(t *testing.T) { + connURL := testutil.SkipTestEnvUnset(t, "MYSQL_URL")[0] + + backend := acctest.RandomWithPrefix("tf-test-db") + username := acctest.RandomWithPrefix("user") + dbName := acctest.RandomWithPrefix("db") + name := acctest.RandomWithPrefix("staticrole") + resourceName := "vault_database_secret_backend_static_role.test" + + if err := createTestUser(connURL, username); err != nil { + t.Fatal(err) + } + + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories, + PreCheck: func() { testutil.TestAccPreCheck(t) }, + CheckDestroy: testAccDatabaseSecretBackendStaticRoleCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDatabaseSecretBackendStaticRoleConfig_credentialType(name, username, dbName, backend, connURL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "backend", backend), + resource.TestCheckResourceAttr(resourceName, "username", username), + resource.TestCheckResourceAttr(resourceName, "db_name", dbName), + resource.TestCheckResourceAttr(resourceName, "credential_type", "password"), + ), + }, + }, + }) +} + +func TestAccDatabaseSecretBackendStaticRole_credentialConfig(t *testing.T) { + connURL := testutil.SkipTestEnvUnset(t, "MYSQL_URL")[0] + + backend := acctest.RandomWithPrefix("tf-test-db") + username := acctest.RandomWithPrefix("user") + dbName := acctest.RandomWithPrefix("db") + name := acctest.RandomWithPrefix("staticrole") + resourceName := "vault_database_secret_backend_static_role.test" + + if err := createTestUser(connURL, username); err != nil { + t.Fatal(err) + } + + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories, + PreCheck: func() { testutil.TestAccPreCheck(t) }, + CheckDestroy: testAccDatabaseSecretBackendStaticRoleCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDatabaseSecretBackendStaticRoleConfig_credentialConfig(name, username, dbName, backend, connURL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "backend", backend), + resource.TestCheckResourceAttr(resourceName, "username", username), + resource.TestCheckResourceAttr(resourceName, "db_name", dbName), + resource.TestCheckResourceAttr(resourceName, "credential_config.password_policy", "numeric"), + ), + }, + { + Config: testAccDatabaseSecretBackendStaticRoleConfig_updatedCredentialConfig(name, username, dbName, backend, connURL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "backend", backend), + resource.TestCheckResourceAttr(resourceName, "username", username), + resource.TestCheckResourceAttr(resourceName, "db_name", dbName), + resource.TestCheckResourceAttr(resourceName, "credential_config.password_policy", "alphanumeric"), + ), + }, + }, + }) +} + func TestAccDatabaseSecretBackendStaticRole_rotationPeriod(t *testing.T) { connURL := testutil.SkipTestEnvUnset(t, "MYSQL_URL")[0] @@ -284,6 +358,7 @@ func createTestUser(connURL, username string) error { if err != nil { return err } + tx, err := db.BeginTx(ctx, nil) if err != nil { return err @@ -304,6 +379,117 @@ func createTestUser(connURL, username string) error { return nil } +func testAccDatabaseSecretBackendStaticRoleConfig_credentialType(name, username, db, path, connURL string) string { + return fmt.Sprintf(` +resource "vault_mount" "db" { + path = "%s" + type = "database" +} + +resource "vault_database_secret_backend_connection" "test" { + backend = vault_mount.db.path + name = "%s" + allowed_roles = ["*"] + + mysql { + connection_url = "%s" + } +} + +resource "vault_database_secret_backend_static_role" "test" { + backend = vault_mount.db.path + db_name = vault_database_secret_backend_connection.test.name + name = "%s" + username = "%s" + credential_type = "password" + rotation_period = 1800 + rotation_statements = ["ALTER USER '{{username}}'@'localhost' IDENTIFIED BY '{{password}}';"] +} +`, path, db, connURL, name, username) +} + +func testAccDatabaseSecretBackendStaticRoleConfig_credentialConfig(name, username, db, path, connURL string) string { + return fmt.Sprintf(` +resource "vault_mount" "db" { + path = "%s" + type = "database" +} + +resource "vault_database_secret_backend_connection" "test" { + backend = vault_mount.db.path + name = "%s" + allowed_roles = ["*"] + + mysql { + connection_url = "%s" + } +} + +resource "vault_password_policy" "test" { + name = "numeric" + + policy = <