アカウントレベルのS3ブロックパブリックアクセスをLambda(Python3)から有効化する

2022.03.07

LambdaからアカウントレベルのS3ブロックパブリックアクセスを有効化したので、サンプルとしてコードを置いておきます。ご自由にお使いください。

S3ブロックパブリックアクセスの詳細については以下ブログをご参照ください。

サンプルコード

先にLambdaで利用するコードを載せておきます。LambdaのランタイムはPython3.8で動作確認をしています。

ログの出力や有効後のリソースチェックはしていますが、あくまでサンプルとしてご利用ください。

アカウントレベルのS3ブロックパブリックアクセスを有効化する

import logging
import boto3
from time import sleep

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def put_public_access_block(account_id: str) -> None:
    try:
        s3 = boto3.client("s3control")
        s3.put_public_access_block(
            PublicAccessBlockConfiguration={
                'BlockPublicAcls': True,
                'IgnorePublicAcls': True,
                'BlockPublicPolicy': True,
                'RestrictPublicBuckets': True
            },
            AccountId = account_id
        )
        logger.info(f'Succeeded to put s3 bucket public_access_block: {account_id}')
    except Exception as e:
        logger.info(f'Failed to put s3 bucket public_access_block: {account_id}')
        logger.error(e)

def is_public_access_block(account_id: str) -> bool:
    try:
        s3 = boto3.client("s3control")
        res = s3.get_public_access_block(
            AccountId = account_id,
        )
        return all(res["PublicAccessBlockConfiguration"].values())
    except Exception as e:
        logger.error(e)
        return False

def lambda_handler(event, context):

    #アカウントID取得
    account_id = boto3.client("sts").get_caller_identity()["Account"]

    #アカウントレベルのS3のBPA有効化
    put_public_access_block(account_id)

    # 有効化直後だとgetできない場合があるため5秒待機
    sleep(5)

    is_resources = {
        "s3_account_level_bpa_enabled": False,
    }
    is_resources["s3_account_level_bpa_enabled"] = is_public_access_block(account_id)

    # is_resourcesにFalseがないか確認
    if not all(is_resources.values()):
        logger.error("Failed to enable s3 account level public_access_block")
        logger.error(
            f"is_resources: {is_resources}"
        )
        raise Exception('Fail to check resource')

    logger.info("Completed to enable s3 account level public_access_block")
    logger.info(
        f"is_resources: {is_resources}"
    )
    return {
        "is_resources": is_resources
    }

数回に一度有効化からリソースチェックまでが早すぎて失敗するケースがあったので、有効化後にsleepを5秒入れてます。特に根拠はないですが、余裕を持たせて5秒を設定しています。

アカウントレベルのS3ブロックパブリックアクセスを無効化する

実際に利用する際にはテストのために無効化したいこともあると思うので、無効化できるコードも置いておきます。こちらはリソースの状態チェックまではしてません。

import boto3
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def delete_public_access_block(account_id: str) -> None:
    try:
        s3 = boto3.client("s3control")
        s3.delete_public_access_block(
            AccountId = account_id
        )
        logger.info(f'Succeeded in disable s3 bucket public_access_block: {account_id}')
    except Exception as e:
        logger.info(f'Failed in disable s3 bucket public_access_block: {account_id}')
        logger.error(e)
        
def lambda_handler(event, context):
    #アカウントID取得
    account_id = boto3.client("sts").get_caller_identity()["Account"]
    #アカウントレベルのS3のBPA有効化
    delete_public_access_block(account_id)

やってみる

有効化

デフォルトでは以下のようにアカウントレベルのS3ブロックパブリックアクセスが無効になっている状態です。

先ほどのコードを使ってこれらの設定を全て有効にしてみます。

有効化用のコードを貼り付けてDeployした状態です。(LambdaのIAMロールや実行時間は適宜調整してください。)テストイベントはデフォルトのまま実行します。

実行が完了すると、以下のようにレスポンスや実行ログが出力されます。

Responseにはリソースの状態が出力されます。成功していればs3_account_level_bpa_enabledの値にtrueが入っているはずです。

Function Log には有効化が成功したことを示すログを出力しています。黒でマスキングしている部分はアカウントIDが出力されます。

改めてコンソールからアカウントレベルのS3ブロックパブリックアクセス設定を確認すると、有効化されていることが確認できました。

無効化

有効化したアカウントレベルのS3ブロックパブリックアクセスを無効化します。先ほど紹介した無効化用のコードを同じようにLambdaで実行します。

実行するとレスポンスはなく、アカウントレベルのS3ブロックパブリックアクセスの無効化に成功したメッセージが出力されています。

コンソールから確認してみると、ちゃんと無効化されていることが確認できました。

おわりに

LambdaからアカウントレベルのS3ブロックパブリックアクセスを有効化(無効化)してみました。ログのメッセージ等は自由にカスタマイズしてご利用頂ければ幸いです。

Control Tower環境であれば以下の仕組みと組み合わせることで、アカウント発行時の自動化フローを作成できます。活用方法の1つとしてご検討ください。