【AWS Organizations】アカウントが 特定OUに所属したタイミングで Lambda を実行させる
はじめに
AWS Organizations の 特定OU(Organizational Unit) にアカウントが所属したタイミングで、 そのアカウント内で特定 Lamdbaを実行する構成を作ってみます。
どうやって作るか?
AWS CloudFormation(CFn)の 2つの機能を使います。
- CFnの Lambda-backed カスタムリソース
- CFnスタックセットの Organizations 連携
CFnの Lambda-backed カスタムリソース
Lambda-backed カスタムリソース は CFnテンプレートで作ることができるリソースの1つです。 実際にリソースが作られるわけではなく、バックエンドで Lambda関数を実行することで リソース作成(or 更新) を完了とします。
詳しい説明や使う際のポイントは以下ブログが良くまとまっているので、ぜひ参照ください。
CFnスタックセットの Organizations 連携
CFnスタックセットは AWS Organizations 連携が可能です。 この連携により以下のような機能が使えるようになります。
- OUをターゲットでスタックをデプロイ
- スタックの自動デプロイ/削除
特に今回は 後者の『自動デプロイ/削除機能』を意識します。 これは OUに所属したアカウントへ自動でスタックを展開、 または OUから離脱したアカウントから自動でスタックを削除する機能です。
詳しい使い方は以下ブログを参照ください。
つまり
- Lambda-backed カスタムリソースを含めた CFnテンプレートを作成
- この CFnテンプレートを使って Organizations 連携の スタックセットを作成 (自動デプロイを ONにする)
これでアカウントが特定OUに所属したタイミングで 特定Lambda が実行されるようになります。
作ってみる
今回は例題として 『アカウントが 特定OUに所属したタイミングで Lambda(アカウントレベルの S3パブリックアクセスブロックを有効化) を実行させる』 仕組みを作ってみます。
CFnテンプレートの準備
以下リソースが必要です。
リソース名 | タイプ | 備考 |
---|---|---|
EnableS3PublicAccessBlock | カスタムリソース | LambdaFunction を呼び出す |
LambdaExecutionRole | IAMロール | LambdaFunction の実行ロール |
LambdaFunction | Lambda関数 | Create時にアカウントレベルの S3ブロックパブリックアクセスアクセスを有効化する |
CFnテンプレートを作成してみました。
AWSTemplateFormatVersion: "2010-09-09" Parameters: RoleName: Type: String Default: InitialSetupRole-EnableS3PublicAccessBlock Resources: EnableS3PublicAccessBlock: Type: Custom::EnableS3PublicAccessBlock Properties: ServiceToken: !GetAtt "LambdaFunction.Arn" LambdaFunction: Type: AWS::Lambda::Function Properties: Role: !GetAtt "LambdaExecutionRole.Arn" Runtime: "python3.8" Handler: index.lambda_handler Timeout: 60 Code: ZipFile: | import boto3 import cfnresponse from logging import getLogger, INFO logger = getLogger() logger.setLevel(INFO) def enable_s3_public_access_block(event, context): logger.info('[START] enable_s3_public_access_block') logger.info("boto3 version:{}".format(boto3.__version__)) try: account_id = boto3.client('sts').get_caller_identity()['Account'] response = boto3.client('s3control').put_public_access_block( PublicAccessBlockConfiguration={ 'BlockPublicAcls': True, 'IgnorePublicAcls': True, 'BlockPublicPolicy': True, 'RestrictPublicBuckets': True }, AccountId=account_id ) if response['ResponseMetadata']['HTTPStatusCode'] == 200: logger.info(" enable success") else: logger.info(" enable failed") logger.info('[END] enable_s3_public_access_block') except Exception as e: logger.error(e) cfnresponse.send(event, context, cfnresponse.FAILED, {'Response': 'Failure'}) exit() def lambda_handler(event, context): if event['RequestType'] == 'Create': # Create時に実行したい処理 --> S3パブリックアクセスブロック有効化 enable_s3_public_access_block(event, context) cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Response': 'Success'}) if event['RequestType'] == 'Delete': # Delete時に実行したい処理 --> 無し cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Response': 'Success'}) if event['RequestType'] == 'Update': # Update時に実行したい処理 --> 無し cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Response': 'Success'}) LambdaExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Ref RoleName AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: "lambda.amazonaws.com" Action: "sts:AssumeRole" ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" Policies: - PolicyName: S3PutAccountPublicAccessBlock PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "sts:GetCallerIdentity" - "s3:PutAccountPublicAccessBlock" Resource: "*"
スタックセットの作成
適当に作成した SecureOU
にスタックセットを仕込みます。
STACKSET_NAME="InitialSetup-EnableS3PublicAccessBlock" OU_ID="ou-jk11-42ets6of" # スタックセット作成 aws cloudformation create-stack-set \ --stack-set-name "${STACKSET_NAME}" \ --template-body file://${テンプレートのパス} \ --permission-model SERVICE_MANAGED \ --auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false \ --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM # スタックインスタンス作成 aws cloudformation create-stack-instances \ --stack-set-name "${STACKSET_NAME}" \ --regions ap-northeast-1 \ --deployment-targets OrganizationalUnitIds=${OU_ID}
以下のようなスタックセットができました( SecureOU にはまだアカウントを所属させていないのでスタックインスタンスは無い)。
確認してみる
事前にメンバーアカウントの パブリックアクセスブロック を全て無効化しておきます。
メンバーアカウントを SecureOU へ移動させます。
移動後にスタックセットを確認すると CREATE オペレーションが開始されていました。 スタックインスタンスが作成されました。
メンバーアカウントを確認します。 パブリックアクセスブロック が有効になっていました。
CloudWatch ロググループへ配信しているログからも、実行完了を確認できました。
おわりに
CFnスタックセットのOrganizations 連携、および Lambda-backed カスタムリソースを組み合わせた活用例でした。
アカウントの初期セットアップの自動化に この自動デプロイ機能は活用できると思います。 CFnリソースに対応していないものは今回のように Lambda-backed カスタムリソースを使うことで対応できます。
他にも色々と活用例があると思います。有用そうなものがあればまたブログにします。