Skip to content

Commit

Permalink
Add credential_type and credential_config to static roles for DBs
Browse files Browse the repository at this point in the history
  • Loading branch information
catouc committed Dec 20, 2024
1 parent 78f2eda commit 739a781
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ FEATURES:
* Update `vault_pki_secret_backend_role` to support the `cn_validations` role field ([#1820](https://github.com/hashicorp/terraform-provider-vault/pull/1820)).
* Add new resource `vault_pki_secret_backend_acme_eab` to manage PKI ACME external account binding tokens. Requires Vault 1.14+. ([#2367](https://github.com/hashicorp/terraform-provider-vault/pull/2367))
* Add new data source and resource `vault_pki_secret_backend_config_cmpv2`. Requires Vault 1.18+. *Available only for Vault Enterprise* ([#2330](https://github.com/hashicorp/terraform-provider-vault/pull/2330))
* 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

IMPROVEMENTS:

Expand Down
24 changes: 24 additions & 0 deletions vault/resource_database_secret_backend_static_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ var staticRoleFields = []string{
consts.FieldRotationPeriod,
consts.FieldRotationStatements,
consts.FieldDBName,
consts.FieldCredentialType,
consts.FieldCredentialConfig,
}

func databaseSecretBackendStaticRoleResource() *schema.Resource {
Expand Down Expand Up @@ -99,6 +101,20 @@ func databaseSecretBackendStaticRoleResource() *schema.Resource {
Description: "The password corresponding to the username in the database. " +
"Required when using the Rootless Password Rotation workflow for static roles.",
},
consts.FieldCredentialType: {
Type: schema.TypeString,
Optional: true,
Default: "password",
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\".",
},
},
}
}
Expand Down Expand Up @@ -138,6 +154,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
Expand Down
186 changes: 186 additions & 0 deletions vault/resource_database_secret_backend_static_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,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]

Expand Down Expand Up @@ -237,6 +311,7 @@ func createTestUser(connURL, username string) error {
if err != nil {
return err
}

tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
Expand All @@ -257,6 +332,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 = <<EOT
length = 20
rule "charset" {
charset = "0123456789"
}
EOT
}
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"
credential_config = { "password_policy" = "numeric" }
rotation_period = 1800
rotation_statements = ["ALTER USER '{{username}}'@'localhost' IDENTIFIED BY '{{password}}';"]
}
`, path, db, connURL, name, username)
}

func testAccDatabaseSecretBackendStaticRoleConfig_updatedCredentialConfig(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 = "alphanumeric"
policy = <<EOT
length = 20
rule "charset" {
charset = "abcdefghijklmnopqrstuvwxyz0123456789"
}
EOT
}
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"
credential_config = { "password_policy" = "alphanumeric" }
rotation_period = 1800
rotation_statements = ["ALTER USER '{{username}}'@'localhost' IDENTIFIED BY '{{password}}';"]
}
`, path, db, connURL, name, username)
}

func testAccDatabaseSecretBackendStaticRoleConfig_rotationSchedule(name, username, db, path, connURL string) string {
return fmt.Sprintf(`
resource "vault_mount" "db" {
Expand Down

0 comments on commit 739a781

Please sign in to comment.