🎃

terraformでRDSのパスワードをSystem Managerから参照して使う

2022/04/12に公開

背景

terraformでRDSを作るときにユーザー名,パスワードが必須となります.しかし直書きはしたくありません.ググってみると,.tfvarsを使ったりの方法があったのですが,何が何でもローカルファイルにパスワードを書きたくねぇ..
仮のパスワードを設定して作成後に変更という方法もあったのですが,仮パスワードも書きたくねぇ..(もはや意地)

そう考えて,System Managerから参照する方法について考えてみました.
手順としては下記になります.

①System Managerのパラメータストアに機密データを登録
②System Managerのパラメータストアから機密データ取得して
③terraform applyの引数で②で取得できた情報を渡す

環境

terraform version 1.1.8
terraform aws provider version 4.9.0

今回試したソースコード

https://github.com/katsuya-n/pub_rds_tf

RDSの機密データをSystem Managerのパラメータストアに登録

タイプは安全な文字列に指定してデータベース名,ユーザー名,パスワードの3つの機密データを登録しておきます.

/dev/db/database_name
/dev/db/username
/dev/db/password

登録したパラメータをCLIで取得できるか確認します.
※レスポンスのXXXXXの部分はマスクしています.

$ aws ssm get-parameters --name "/dev/db/database_name" --region=us-west-1
{
    "Parameters": [
        {
            "Name": "/dev/db/database_name",
            "Type": "SecureString",
            "Value": "XXXXX",
            "Version": 1,
            "LastModifiedDate": XXXXX,
            "ARN": "arn:aws:ssm:us-west-1:XXXXX",
            "DataType": "text"
        }
    ],
    "InvalidParameters": []
}

これができたら,パラメータストアからの取得は問題なしです.
あとは"Value"を使いたいのですが,上記のままだと暗号化されてしまっているので--with-decryptionオプションをつけて,
レスポンスの"Value"のところだけほしいので| jq -r ".Parameters[].Value"として必要なものだけ取り出します.

$ aws ssm get-parameters --name "/dev/db/database_name" --region=us-west-1 --with-decryption | jq  -r ".Parameters[].Value"
[登録したデータベース名]

これでデータベース名をパラメータストアから取得できました.ユーザー名とパスワードも取得できることを確認しておきます.

$ aws ssm get-parameters --name "/dev/db/username" --region=us-west-1 --with-decryption | jq  -r ".Parameters[].Value"
[登録したユーザー名]

$ aws ssm get-parameters --name "/dev/db/password" --region=us-west-1 --with-decryption | jq  -r ".Parameters[].Value"
[登録したパスワード]

terraformを書いていく

構成は次のとおりです.本当はサブネットやVPCなども作成したほうが良いのですが,今回は必要最低限のものだけにしました.

.
├── app
│   ├── locals.tf
│   └── main.tf
└── modules
    └── rds
        ├── main.tf
        └── varriable.tf
app/main.tf
# コマンドライン引数から取得
variable "database_name" {}
variable "db_master_username" {}
variable "db_master_password" {}

provider "aws" {
  region = "us-west-1"

  default_tags {
    tags = {
      System = local.name_prefix
      Owner  = "lightkun"
    }
  }
}

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.9.0"
    }
  }
  required_version = "1.1.8"
  backend "s3" {
    bucket = "[S3バケット名]"
    key    = "dev.tfstate"
    region = "us-west-1"
  }
}

module "rds" {
  source             = "../modules/rds"
  name_prefix        = local.name_prefix
  db_az              = [local.az_1a, local.az_1b]
  database_name      = var.database_name
  db_master_username = var.db_master_username
  db_master_password = var.db_master_password
  db_num             = 2
  instance_class     = "db.t3.small"
}
app/locals.tf
locals {
  name_prefix        = "rds_tf"
  env                = "dev"
  az_1a              = "us-west-1a"
  az_1b              = "us-west-1b"
}
modules/rds/main.tf
resource "aws_rds_cluster" "cluster" {
  cluster_identifier      = "aurora-cluster"
  engine                  = "aurora-mysql"
  engine_version          = "5.7.mysql_aurora.2.03.2"
  availability_zones      = var.db_az
  database_name           = var.database_name
  master_username         = var.db_master_username
  master_password         = var.db_master_password
  preferred_backup_window = "07:00-09:00"
  skip_final_snapshot     = true
}

resource "aws_rds_cluster_instance" "cluster_instances" {
  count              = var.db_num
  identifier         = "aurora-cluster-${count.index + 1}"
  cluster_identifier = aws_rds_cluster.cluster.id
  instance_class     = var.instance_class
  engine             = aws_rds_cluster.cluster.engine
  engine_version     = aws_rds_cluster.cluster.engine_version
}
modules/rds/varriable.tf
variable "name_prefix" {}
variable "db_az" {}
variable "database_name" {}
variable "db_master_username" {}
variable "db_master_password" {}
variable "db_num" {}
variable "instance_class" {}

実行

terraformを実行してrdsを作成していきます.引数ではパラメータストアの値を渡します.

$ cd app/
$ terraform init
$ terraform apply \
-var=database_name=`aws ssm get-parameters --name "/dev/db/database_name" --region=us-west-1 --with-decryption | jq  -r ".Parameters[].Value"` \
-var=db_master_username=`aws ssm get-parameters --name "/dev/db/username" --region=us-west-1 --with-decryption | jq  -r ".Parameters[].Value"` \
-var=db_master_password=`aws ssm get-parameters --name "/dev/db/password" --region=us-west-1 --with-decryption | jq  -r ".Parameters[].Value"` \
--parallelism=30

成功!

ちなみに,僕愛用のfish shellだと上記の書き方のterraform applyがエラーになってしまうので,bashで動かしました.

terraform apply実行時の差分に正しくデータベース名とユーザー名を設定できているか確認できます.パスワードは (sensitive value) となっていて表示されません.

...
      + database_name                   = "[登録したデータベース名]"
...
      + master_username                 = "[登録したユーザー名]"
...

作成後,(terraformで管理してないですが)Cloud9を起動して,セキュリティグループを作成・アタッチしてRDSにアクセスできることを確認しました.

デメリット

ローカルでは機密データの情報を記述しませんが,S3の.tfstateには平文で保存されてしまいます.これはなんともできないか...

参考資料

https://docs.aws.amazon.com/cli/latest/reference/ssm/get-parameters.html

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/param-create-cli.html

https://dev.classmethod.jp/articles/run-command-parameter-store-secure-string/

https://www.wakuwakubank.com/posts/676-linux-jq/

株式会社ゆめみ

Discussion