CloudFormationで作るリソースの名前にAWSアカウント名を使いたいことがありました。
擬似パラメータには該当するものがなかったため、カスタムリソースを使ってCloudFormation内でAWSアカウント名を取得してみました。
カスタムリソースとは
カスタムリソースは、CloudFormationがネイティブにサポートしていないカスタム処理を可能にする機能です。
カスタム処理をLambdaで作成して、CloudFormation実行時にLambdaが実行されます。
カスタムリソースについては、以下を確認するとイメージがつくかと思います。
- カスタムリソース - AWS CloudFormation
- Lambda-backed カスタムリソースを理解するためにチュートリアルをやってみた | DevelopersIO
- CloudFormationで提供されていない処理をカスタムリソースで作ってみた。 | DevelopersIO
AWSアカウント名
AWS アカウント名は、AWS マネジメントコンソールのアカウント ID の横に表示される名前です。この名前は最初にアカウントを作成するときに選択するものですが、 AWS 請求管理コンソールから更新することができます。
AWSアカウントのエイリアスとは別物のため、注意してください。
アカウントエイリアスはIAMの画面で設定変更可能で、サインインURLになります。
AWSアカウント名はサインインURLとして使用されません。また、マネジメントコンソールから確認する際は、ルートユーザーでのログインが必要です。
今回は、AWSアカウント名の取得を行います。
やってみた
カスタムリソースのLambda上でAWS SDKを実行して、AWSアカウント名を取得します。
注意点
この方法では、AWS SDKでAWS OrganizationsのAPIを利用します。
AWS Organizationsが有効になっていない場合スタック作成に失敗するため、ご注意ください。
CloudFormationテンプレート
テンプレートは以下のとおりです。
Resources:
GetAccountName:
Type: Custom::GetAccountName
Properties:
ServiceToken: !GetAtt GetAccountNameLambda.Arn
GetAccountNameLambda:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import boto3
import cfnresponse
def lambda_handler(event, context):
try:
# Get AWS account ID
sts = boto3.client('sts')
account_id = sts.get_caller_identity().get('Account')
# Get AWS account name
organizations = boto3.client('organizations')
response_value = organizations.describe_account(AccountId=account_id)
account_name = response_value['Account']['Name']
responseData = {
"AccountName": account_name,
}
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
except Exception as e:
cfnresponse.send(event, context, cfnresponse.FAILED, {})
Handler: index.lambda_handler
Role: !GetAtt GetAccountNameLambdaRole.Arn
Runtime: python3.11
GetAccountNameLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: GetAccountName
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sts:GetCallerIdentity
- organizations:DescribeAccount
Resource: '*'
- PolicyName: Logs
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
Outputs:
AccountName:
Value: !GetAtt GetAccountName.AccountName
GetAccountName
というカスタムリソースを定義しています。
カスタムリソースで利用するLambda関数やLambda関数用のIAMロールを定義しています。
Lambda関数の中身はシンプルで、以下の処理を行います。
- AWS SDKを使ってAWSアカウントIDからAWSアカウント名を取得
- cfn-response モジュール を使ってAWSアカウント名をカスタムリソースに送信
カスタムリソースに送信されたAWSアカウント名は、今回のコード上では!GetAtt GetAccountName.AccountName
で取得することができます。
動作確認
まずは現在のAWSアカウント名を確認します。
$ aws organizations describe-account --account-id 893022333407 --query Account.Name
<AWSアカウント名>
準備したテンプレートをデプロイしてみましょう。
aws cloudformation create-stack --stack-name GetAwsAccountTest --template-body file://templates.yaml \
--capabilities CAPABILITY_NAMED_IAM
デプロイが完了したら以下のコマンドでOutputを確認します。
AWSアカウント名の箇所が先程コマンドで確認したものと同じであることが確認できます。
$ aws cloudformation describe-stacks --stack-name GetAwsAccountTest --query 'Stacks[].Outputs'
[
[
{
"OutputKey": "AccountName",
"OutputValue": "<AWSアカウント名>"
}
]
]
おわりに
カスタムリソースを使って、AWSアカウント名を取得する方法でした。
今回はCloudFormaitonのどのステータスで実行されても問題ないため、リクエストタイプごとの処理を設定していませんが、必要に応じてリクエストタイプごとの処理も実装したほうがよさそうです。
以上、AWS事業本部の佐藤(@chari7311)でした。