前提
以下の前提があるとします。
- できる限り aws secrets manager でシークレットを一元管理したいとします。
- tfstate にシークレットが書き込まれたらインシデントとします。
場合分け
(A) シークレット埋め込みたいフィールドが write-only arg である場合
今回は、Resource: aws_rds_cluster の master_password_woフィールド にシークレットを埋め込みたいとします。
まず、以下の二つのリソースで、aws secrets manager の箱だけ作っておきます。
そして、手動で(aws cli or aws console)、具体的なパスワードをセットします。
シークレット取得は、Data Source: aws_secretsmanager_secret_version を使います。ただし、data を ephemeral に置き換えてください。data ブロックを使うと、tfstate にシークレットが書き込まれてしまいます。
ちなみにですが、パスワードを更新する場合は、master_password_wo_version の値を同時に引き上げる必要があります。
またまた、ちなみにですが、Aurora の場合、manage_master_user_password フィールド を使って、aws secrets manager を使うこともできます。個人的には、terraform 全体で一貫して ephemeral 使った方が分かりやすと思います。
(B) シークレット埋め込みたいフィールドが write-only arg でない場合
Resource: aws_cognito_identity_provider の provider_details.client_secret にシークレットをセットしたいとします。
write-only arg でないので、ephemeral で取得した値はセットできません。まあ、仮に ephemeral が使えたとしても、セット先のフィールドが tfstate に書き込まれるので意味がありませんけどね。data で取得してセットすることはできますが、tfstate にシークレットが書き込まれてしまいます。
このフィールドは、aws secrets manager を使うことはできないので、他の方法で回避します。
まず、リソースを作成するときは、"dummy" などの適当な文字列をセットすると同時に、ignore_changes を使います。その後、手動で、secret をセットします。ignore_changes を使うことで、そのフィールドを apply しなくなるので、せっかく手動で secret をセットしたのに、apply したら "dummy" に戻ってしまったという事態を防げます。
この方法は、AWS Secres Manger で一元管理ができないですが、今のところはこれしか方法はないと思います。このフィールドに write-only arg が登場するのを待つしかないですね。
(c) シークレットではないフィールドにシークレットを埋め込みたい場合
例えば、EC2 のインスタンスタイプをシークレットとして扱いたいとします。
結論から言うと、これは不可能です。
(B)で紹介された方法を使えばできると思うかもしれませんが、ignore_changes の特性的に無理なのです。
以下の記事がめちゃくちゃ分かりやすいですが、ignore_changes は 「apply による .tf ファイルを元にしたインフラ変更 & tfstate 変更」を無効化することはできますが、「apply 時の refresh 」を無効化するかどうかは管轄外なのです。知らない人もいるかもしれないので説明しておくと、terraform apply は実は、「.tf ファイルを元にインフラと tfstate を変更」する前に、「refresh (インフラ状態を tfstateに反映する)」を行っているのです。
この refresh 操作によって実際のインフラの値が tfstate に反映されるかどうかは、フィールドによって異なります。(B) のようにシークレットとして考えられているフィールドは、refresh 時に実際のインフラから tfstate に同期されませんが、今回のようにシークレットとして考えられてないフィールドは、refresh 時に実際のインフラから tfstate に同期が起きます。