- 公開日
- 最終更新日
【CloudFormation】Secrets Managerでパスワードを作成・管理する
この記事を共有する

はじめに
パーソル&サーバーワークス嶋田です。
AWSにはSecrets Managerと呼ばれるサービスがあり、パスワードなどの秘匿情報を管理する目的で使われます。
Secrets Managerで管理するパスワードは、自動・手動の両方で設定ができます。
本ブログでは、Secrets Managerを使ったパスワードを、以下のパターンで設定してみたいと思います。
- 特定の文字を含まないで自動生成パスワードを設定
- 一定の文字数量で自動生成パスワードを設定
- 手動でのパスワードを設定
なお、本ブログで紹介する自動生成パターンは1例であり、要件に応じて柔軟なカスタマイズができます。
本ブログで用いるテンプレートの基本形
自動設定と手動設定の基本形を簡単に説明します。
以下の2種類のテンプレートをベースに本ブログは進行します。
自動設定
AWSTemplateFormatVersion: '2010-09-09' Description: Secrets Manager Tutorial Resources: MySecret1: Type: AWS::SecretsManager::Secret Properties: Description: generate password GenerateSecretString: # パスワードと併せてSecrets Managerで保管する値をJSON形式で記述 SecretStringTemplate: | {} # 自動生成される文字列を格納するキー名 GenerateStringKey: "password" #### ここから↓に、以降のセクションでパスワード自動生成時に指定可能な条件などを書いていきます #### # 生成される文字列の長さ PasswordLength: 20 # 生成される文字列から除外する文字 ExcludeCharacters: abcdefghijklmnopqrstuvwz # 小文字を除外するか ExcludeLowercase: false # 数字を除外するか ExcludeNumbers: false # 【! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~】 を除外するか ExcludePunctuation: false # 大文字を除外するか ExcludeUppercase: false # スペースを生成文字列に含めるか IncludeSpace: false # 各文字種(大文字、小文字、数字、句読点など)を必ず1文字ずつ含むか RequireEachIncludedType: false
- SecretStringTemplate にSecrets Managerで管理する値を記述します
- GenerateSecretString の配下に、パスワードを自動生成するときの設定値を書きます
手動設定
AWSTemplateFormatVersion: '2010-09-09' Description: Secrets Manager Tutorial Resources: MySecret2: Type: AWS::SecretsManager::Secret Properties: Description: manually password SecretString: 'password'
- SecretString にSecrets Managerで管理する値を記述します
特定の文字を含まないで自動生成パスワードを設定
以下のテンプレートファイルを用意します。
AWSTemplateFormatVersion: '2010-09-09' Description: Secrets Manager Tutorial Resources: MySecret1: Type: AWS::SecretsManager::Secret Properties: Description: generate password GenerateSecretString: # パスワードと併せてSecrets Managerで保管する値をJSON形式で記述 SecretStringTemplate: | {} # 自動生成される文字列を格納するキー名 GenerateStringKey: "password" # 生成される文字列から除外する文字 ExcludeCharacters: opqrstuvwxyz # 小文字を除外するか ExcludeLowercase: false # 数字を除外するか ExcludeNumbers: true # 【! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~】 を除外するか ExcludePunctuation: true # 大文字を除外するか ExcludeUppercase: true # スペースを生成文字列に含めるか IncludeSpace: false # 各文字種(大文字、小文字、数字、句読点など)を必ず1文字ずつ含むか RequireEachIncludedType: false Outputs: SecretArn1: Value: !Ref MySecret1
本ブログでは、テンプレートファイル名をsecrets-manager.yamlとします。
準備ができたら、AWS CLIコマンドでデプロイします。
$ aws cloudformation deploy --stack-name SecretStack --template-file secrets-manager.yaml Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - SecretStack
デプロイが完了しているか確認します。
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text CREATE_COMPLETE
CREATE_FAILED になっている場合は、以下のコマンドでイベントを確認して、トラブルシューティングが必要です。
$ aws cloudformation describe-stack-events --stack-name SecretStack
※本ブログではトラブルシューティングの説明は割愛いたします
※イベント履歴の確認は、CloudFormationのマネジメントコンソールでも可能です
CREATE_COMPLETE と表示されていることを確認したら、以下のコマンドを実行します。
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[0].Outputs[?contains(OutputKey, 'SecretArn1')].OutputValue" --output text arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret1-STxw8hu524Q5-1bzKXq
SecretArn1 の値は、テンプレート内のOutputsセクションに記述した値に合わせます。
取得したARNを元手に、Secrets Managerサービスを確認してみましょう。
以下のコマンドを実行します。
$ aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret1-STxw8hu524Q5-1bzKXq --query "SecretString" --output text {"password":"fjmfjjfbfcnjdddkgncbbimidhdlnagk"}
パスワードの値が確認できました。
テンプレートファイルに記述した設定に則って自動生成されていますね。
念のため、どんな条件で自動生成される想定だったか、おさらいします。
- 特定の小文字(opqrstuvwxyz)を除いた文字列で生成
- 数字を除外
- 記号(! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~)を除外
- 大文字を除外
一定の文字数量で自動生成パスワードを設定
特定の文字を含まないで自動生成パスワードを設定 セクションで使ったテンプレートに、
PasswordLength を設定して、文字数を制御してみます。
以下のテンプレートは、設定反映後のコードです。
AWSTemplateFormatVersion: '2010-09-09' Description: Secrets Manager Tutorial Resources: MySecret1: Type: AWS::SecretsManager::Secret Properties: Description: generate password GenerateSecretString: # パスワードと併せてSecrets Managerで保管する値をJSON形式で記述 SecretStringTemplate: | {} # 自動生成される文字列を格納するキー名 GenerateStringKey: "password" # 生成される文字列の長さ PasswordLength: 8 # 生成される文字列から除外する文字 ExcludeCharacters: opqrstuvwxyz # 小文字を除外するか ExcludeLowercase: false # 数字を除外するか ExcludeNumbers: true # 【! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~】 を除外するか ExcludePunctuation: true # 大文字を除外するか ExcludeUppercase: true # スペースを生成文字列に含めるか IncludeSpace: false # 各文字種(大文字、小文字、数字、句読点など)を必ず1文字ずつ含むか RequireEachIncludedType: false Outputs: SecretArn1: Value: !Ref MySecret1
上記の通り編集を終えたら、改めてデプロイを実施します。
$ aws cloudformation deploy --stack-name SecretStack --template-file secrets-manager.yaml Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - SecretStack
デプロイが成功したようなので、スタックの状態を確認してみましょう。
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text UPDATE_COMPLETE
UPDATE_COMPLETE と表示されていれば成功です。
改めてSecrets Managerの値を確認してみましょう。
Secrets Managerの値確認には、get-secret-valueコマンドを使います。
$ aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret1-STxw8hu524Q5-1bzKXq --query "SecretString" --output text {"password":"ckmdbieh"}
8文字になっていますね。
ここまでまとめ
ここまでで、 GenerateSecretString の設定を使うことで、
自動生成されるパスワードが制御できることを体験しました。
一旦、作成したリソースを削除したいと思います。
以下のコマンドを実行します。
$ aws cloudformation delete-stack --stack-name SecretStack
リソースが消えているか念のため確認します。
$ aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret1-STxw8hu524Q5-1bzKXq --query "SecretString" --output text An error occurred (ResourceNotFoundException) when calling the GetSecretValue operation: Secrets Manager can't find the specified secret.
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text An error occurred (ValidationError) when calling the DescribeStacks operation: Stack with id SecretStack does not exist
上記のように、 ResourceNotFoundException / SecretStack does not exist と表示されていればOKです。
最後に、自動生成ではなく手動でパスワードを設定する方法について触れて締めたいと思います。
手動でのパスワードを設定
以下のテンプレートファイルを用意します。
※シークレットの情報をテンプレートに直書きするのは非推奨です
AWSTemplateFormatVersion: '2010-09-09' Description: Secrets Manager Tutorial Resources: MySecret2: Type: AWS::SecretsManager::Secret Properties: Description: manually password SecretString: 'password' Outputs: SecretArn2: Value: !Ref MySecret2
このままでは秘匿したい情報が平文で残るので、本ブログでは順を追って改善します。
一旦はこの書き方で動くことを確認したいので、このまま進めます。
それではデプロイしてみましょう。
※パスワード自動生成デモの時に使っていたファイル名を流用しています
$ aws cloudformation deploy --stack-name SecretStack --template-file secrets-manager.yaml Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - SecretStack
スタックの状態を確認します。
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text CREATE_COMPLETE
CREATE_COMPLETE となっていればスタックの作成はOKです。
ただし、この書き方には問題があります。
CloudFormationテンプレートにパスワードを直書きしているため、テンプレートを見られる人全員にパスワードが露呈します。
$ aws cloudformation get-template --stack-name SecretStack --query "TemplateBody" "AWSTemplateFormatVersion: '2010-09-09'\nDescription: Secrets Manager Tutorial\nResources:\n MySecret2:\n Type: AWS::SecretsManager::Secret\n Properties:\n Description: manually password\n SecretString: 'password'\nOutputs:\n SecretArn2:\n Value: !Ref MySecret2"
テンプレートファイルを表示するコマンドを実行してみると、 SecretString: 'password' と出ていますね。
つまり、テンプレートを閲覧できる人であれば誰でも見られる状態になっています。
ではどうするか?
Parametersを活用して、外部から値を注入しましょう。
テンプレートを以下の通り書き換えます。
AWSTemplateFormatVersion: '2010-09-09' Description: Secrets Manager Tutorial Parameters: Password: Type: String Description: SecretString Value Resources: MySecret2: Type: AWS::SecretsManager::Secret Properties: Description: manually password SecretString: !Ref Password Outputs: SecretArn2: Value: !Ref MySecret2
こうすることで、Parametersに定義した値を参照するようになりました。
この変更によって、パスワードがテンプレートに直書きされることは回避できます。
では、実際にデプロイします。
$ aws cloudformation deploy --stack-name SecretStack --template-file secrets-manager.yaml --parameter-overrides Password="ParametersPassword" Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - SecretStack
デプロイが完了したので、スタックの状態を確認します。
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text UPDATE_COMPLETE
UPDATE_COMPLETE となっているため、問題なさそうです。
テンプレートファイルを確認します。
$ aws cloudformation get-template --stack-name SecretStack --query "TemplateBody" "AWSTemplateFormatVersion: '2010-09-09'\nDescription: Secrets Manager Tutorial\nParameters:\n Password:\n Type: String\n Description: SecretString Value\nResources:\n MySecret2:\n Type: AWS::SecretsManager::Secret\n Properties:\n Description: manually password\n SecretString: !Ref Password\nOutputs:\n SecretArn2:\n Value: !Ref MySecret2"
SecretString: !Ref Password となっているので、一見問題なさそうです。
ですがまだ不完全です。以下のコマンドを実行します。
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].Parameters" --output table ---------------------------------------- | DescribeStacks | +---------------+----------------------+ | ParameterKey | ParameterValue | +---------------+----------------------+ | Password | ParametersPassword | +---------------+----------------------+
マネジメントコンソールのパラメータタブからも確認できますが、上記の通りパスワードが隠せていないですね。
そこで、NoEchoの登場です。以下のようにテンプレートを書き換えます。
AWSTemplateFormatVersion: '2010-09-09' Description: Secrets Manager Tutorial Parameters: Password: Type: String Description: SecretString Value NoEcho: true Resources: MySecret2: Type: AWS::SecretsManager::Secret Properties: Description: manually password SecretString: !Ref Password Outputs: SecretArn2: Value: !Ref MySecret2
改めてスタックに更新をかけます。パスワードも変更します。
$ aws cloudformation deploy --stack-name SecretStack --template-file secrets-manager.yaml --parameter-overrides Password="ParametersPasswordNoEcho" Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - SecretStack
改めてCloudFormationパラメーターをチェックしてみましょう。
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].Parameters" --output table ------------------------------------ | DescribeStacks | +---------------+------------------+ | ParameterKey | ParameterValue | +---------------+------------------+ | Password | **** | +---------------+------------------+
パスワードがマスクされています。
これで手動でパスワードを設定しつつ、テンプレートファイルやパラメータに平文で表示されるのを回避できました。
補足ですが、AWS公式としてはDynamic Referenceを活用したソリューションを推奨しているようです。
例えば、以下のような記述のことを指します。
CloudFormationで値が隠せるようになったので、Secrets Managerに値が生成されていることを確認します。
以下のコマンドを実行します。
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[0].Outputs[?contains(OutputKey, 'SecretArn2')].OutputValue" --output text arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret2-W95HxAf5gIAJ-K7qTRu
Secrets ManagerのARNを控えて、設定したパスワードが格納されているか確認します。
$ aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret2-W95HxAf5gIAJ-K7qTRu --query "SecretString" --output text ParametersPasswordNoEcho
Secrets Managerに格納されている値も想定通りですね。
後片付け
それでは、最後にリソースを削除して締めたいと思います。
以下のコマンドを実行します。
$ aws cloudformation delete-stack --stack-name SecretStac
リソースが残っていないか、以下のコマンドで確認をしましょう。
$ aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret2-W95HxAf5gIAJ-K7qTRu --query "SecretString" --output text An error occurred (ResourceNotFoundException) when calling the GetSecretValue operation: Secrets Manager can't find the specified secret.
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text An error occurred (ValidationError) when calling the DescribeStacks operation: Stack with id SecretStack does not exist
上記のように、 ResourceNotFoundException / SecretStack does not exist と表示されていればOKです。
お疲れさまでした。
この記事は私が書きました
嶋田 龍登
記事一覧インフラからアプリのことまでなんでもやりたい、フルスタックを目指すエンジニア
