I am trying to use AWS secrets manager to declare RDS admin credentials.
variable "RdsAminCred" {
default = {
username = "dbadmin"
password = "dbadmin#02avia"
}
type = map(string)
}
resource "aws_secretsmanager_secret" "RdsAminCred" {
name = "RdsAminCred"
}
resource "aws_secretsmanager_secret_version" "RdsAminCred" {
secret_id = aws_secretsmanager_secret.RdsAminCred.id
secret_string = jsonencode(var.RdsAminCred)
}
resource "aws_db_instance" "default" {
identifier = "testdb"
allocated_storage = 20
storage_type = "gp2"
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t2.medium"
name = "mydb"
username = "dbadmin"
password = "dbadmin#01avia"
Any help is appreciated..
I would have a TF config that sets up your secret and stores it in AWS Secrets Manager, like this.
resource "random_password" "master"{
length = 16
special = true
override_special = "_!%^"
}
resource "aws_secretsmanager_secret" "password" {
name = "test-db-password"
}
resource "aws_secretsmanager_secret_version" "password" {
secret_id = aws_secretsmanager_secret.password.id
secret_string = random_password.master.result
}
And then in a separate TF config for your database, you can use the secret from AWS Secrets Manager.
data "aws_secretsmanager_secret" "password" {
name = "test-db-password"
}
data "aws_secretsmanager_secret_version" "password" {
secret_id = data.aws_secretsmanager_secret.password
}
resource "aws_db_instance" "default" {
identifier = "testdb"
allocated_storage = 20
storage_type = "gp2"
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t2.medium"
name = "mydb"
username = "dbadmin"
password = data.aws_secretsmanager_secret_version.password
In the comments above, Asri Badlah suggested that the password be entered manually in the console. And I guess you can do that. However, that approach does start to get away from the fundamental tenet of IaC - put everything in source control. Of course you don't want to check passwords, private keys or the like into source control. But here, you can see we're not doing that. We populate a secret with one config and consume it with another. This ensures the code can be checked into source control, but the password is not.
In terms of state, it's true that the password will be stored and decipherable in TF state. But if you use proper state management, this shouldn't be an issue. Ideally, you would want to be using remote state, encrypted and with restricted access.
As a final point, I would not use just random_password as Evan Closson suggested. That approach would mean that your database password is 100% managed by Terraform. By using Secrets Manager, your password is managed by a service, which means that you can do other stuff like rotate the password (not shown) and retrieve the password without having to rely on Terraform (e.g. terraform output or cracking open the state file).
I'd recommend using the random_password
resource instead. Then you can reference that in the cluster configuration and secrets manager.
Example:
resource "random_password" "master_password" {
length = 16
special = false
}
resource "aws_rds_cluster" "default" {
cluster_identifier = "my-cluster"
master_username = "admin"
master_password = random_password.default_master_password.result
# other configurations
# .
# .
# .
}
resource "aws_secretsmanager_secret" "rds_credentials" {
name = "credentials"
}
resource "aws_secretsmanager_secret_version" "rds_credentials" {
secret_id = aws_secretsmanager_secret.rds_credentials.id
secret_string = <<EOF
{
"username": "${aws_rds_cluster.default.master_username}",
"password": "${random_password.master_password.result}",
"engine": "mysql",
"host": "${aws_rds_cluster.default.endpoint}",
"port": ${aws_rds_cluster.default.port},
"dbClusterIdentifier": "${aws_rds_cluster.default.cluster_identifier}"
}
EOF
}
In your Terraform code, you can use the aws_secretsmanager_secret_version data source to read this secret:
data "aws_secretsmanager_secret_version" "creds" {
# write your secret name here
secret_id = "your_secret"
}
parse the secret from JSON, using jsondecode :
locals {
your_secret = jsondecode(
data.aws_secretsmanager_secret_version.creds.secret_string
)
}
Now pass the secret to RDS:
resource "aws_db_instance" "example" {
engine = "engine"
engine_version = "version"
instance_class = "instance"
name = "example"
# Set the secrets from AWS Secrets Manager
username = local.your_secret.username
password = local.your_secret.password
}
variable "RdsAdminCred" {
default = {
username = "dbadmin"
password = "dbadmin#02avia"
}
type = map(string)
}
resource "aws_secretsmanager_secret" "RdsAdminCred" {
name = "RdsAdminCred"
}
resource "aws_secretsmanager_secret_version" "RdsAdminCred" {
secret_id = aws_secretsmanager_secret.RdsAdminCred.id
secret_string = jsonencode(var.RdsAdminCred)
}
after you have created a secret, you need to take data from there
data "aws_secretsmanager_secret" "env_secrets" {
name = "RdsAdminCred"
depends_on = [
aws_secretsmanager_secret.RdsAdminCred
]
}
data "aws_secretsmanager_secret_version" "current_secrets" {
secret_id = data.aws_secretsmanager_secret.env_secrets.id
}
resource "aws_db_instance" "default" {
identifier = "testdb"
allocated_storage = 20
storage_type = "gp2"
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t2.medium"
name = "mydb"
username = jsondecode(data.aws_secretsmanager_secret_version.current_secrets.secret_string)["username"]
password = jsondecode(data.aws_secretsmanager_secret_version.current_secrets.secret_string)["password"]
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With