AWS re:Postを使用することにより、以下に同意したことになります AWS re:Post 利用規約

CloudFormation の Amazon S3 バケットでカスタムリソースを使用するにはどうすればよいですか?

所要時間5分
0

AWS CloudFormation の Amazon Simple Storage Service (Amazon S3) バケットでカスタムリソースを使用したいと考えています。

解決策

CloudFormation テンプレートは AWS Lambda ベースのカスタムリソースを使用します。S3 バケットでのカスタムリソースに CloudFormation テンプレートを使用する場合は、次のアクションを実行できます。

  • テンプレートを使用して S3 バケットにフォルダを作成します。
  • S3 バケットの作成後に、テンプレートを使用して 2 つのバケット間でコンテンツをコピー、アップロード、または同期します。
  • 独自のコードでテンプレートを変更します。

注: 次の解決策では、CloudFormation スタックを削除すると、Amazon S3 によってすべての S3 バケットコンテンツが削除されます。この動作を変更するには、Lambda コードを変更する必要があります。

CloudFormation テンプレートを取得する

Amazon S3 バケットでカスタムリソースを使用するには、次の JSON または YAML テンプレートをファイルとして保存します。

JSON:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Working with custom resources and S3",
  "Parameters": {
    "S3BucketName": {
      "Type": "String",
      "Description": "S3 bucket to create.",
      "AllowedPattern": "[a-zA-Z][a-zA-Z0-9_-]*"
    },
    "DirsToCreate": {
      "Description": "Comma delimited list of directories to create.",
      "Type": "CommaDelimitedList"
    }
  },
  "Resources": {
    "SampleS3Bucket": {
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {"Ref":"S3BucketName"}
      }
    },
    "S3CustomResource": {
      "Type": "Custom::S3CustomResource",
      "DependsOn":"AWSLambdaExecutionRole",
      "Properties": {
        "ServiceToken": {"Fn::GetAtt": ["AWSLambdaFunction","Arn"]},
        "the_bucket": {"Ref":"SampleS3Bucket"},
        "dirs_to_create": {"Ref":"DirsToCreate"}
      }
    },
    "AWSLambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Description": "Work with S3 Buckets!",
        "FunctionName": {"Fn::Sub":"${AWS::StackName}-${AWS::Region}-lambda"},
        "Handler": "index.handler",
        "Role": {"Fn::GetAtt": ["AWSLambdaExecutionRole","Arn"]},
        "Timeout": 360,
        "Runtime": "python3.9",
        "Code": {
          "ZipFile": "import boto3\r\nimport cfnresponse\r\ndef handler(event, context):\r\n    # Init ...\r\n    the_event = event['RequestType']\r\n    print(\"The event is: \", str(the_event))\r\n    response_data = {}\r\n    s_3 = boto3.client('s3')\r\n    # Retrieve parameters\r\n    the_bucket = event['ResourceProperties']['the_bucket']\r\n    dirs_to_create = event['ResourceProperties']['dirs_to_create']\r\n    try:\r\n        if the_event in ('Create', 'Update'):\r\n            print(\"Requested folders: \", str(dirs_to_create))\r\n            for dir_name in dirs_to_create:\r\n                print(\"Creating: \", str(dir_name))\r\n                s_3.put_object(Bucket=the_bucket,\r\n                                Key=(dir_name\r\n                                    + '\/'))\r\n        elif the_event == 'Delete':\r\n            print(\"Deleting S3 content...\")\r\n            b_operator = boto3.resource('s3')\r\n            b_operator.Bucket(str(the_bucket)).objects.all().delete()\r\n        # Everything OK... send the signal back\r\n        print(\"Operation successful!\")\r\n        cfnresponse.send(event,\r\n                        context,\r\n                        cfnresponse.SUCCESS,\r\n                        response_data)\r\n    except Exception as e:\r\n        print(\"Operation failed...\")\r\n        print(str(e))\r\n        response_data['Data'] = str(e)\r\n        cfnresponse.send(event,\r\n                        context,\r\n                        cfnresponse.FAILED,\r\n                        response_data)"
        }
      }
    },
    "AWSLambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [
            {
              "Action": [
                "sts:AssumeRole"
              ],
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              }
            }
          ],
          "Version": "2012-10-17"
        },
        "Path": "/",
        "Policies": [
          {
            "PolicyDocument": {
              "Statement": [
                {
                  "Action": [
                    "logs:CreateLogGroup",
                    "logs:CreateLogStream",
                    "logs:PutLogEvents"
                  ],
                  "Effect": "Allow",
                  "Resource": "arn:aws:logs:*:*:*"
                }
              ],
              "Version": "2012-10-17"
            },
            "PolicyName": {"Fn::Sub": "${AWS::StackName}-${AWS::Region}-AWSLambda-CW"}
          },
          {
            "PolicyDocument": {
              "Statement": [
                {
                  "Action": [
                    "s3:PutObject",
                    "s3:DeleteObject",
                    "s3:List*"
                  ],
                  "Effect": "Allow",
                  "Resource": [
                    {"Fn::Sub": "arn:aws:s3:::${SampleS3Bucket}/*"},
                    {"Fn::Sub": "arn:aws:s3:::${SampleS3Bucket}"}
                  ]
                }
              ],
              "Version": "2012-10-17"
            },
            "PolicyName": {"Fn::Sub":"${AWS::StackName}-${AWS::Region}-AWSLambda-S3"}
          }
        ],
        "RoleName": {"Fn::Sub":"${AWS::StackName}-${AWS::Region}-AWSLambdaExecutionRole"}
      }
    }
  }
}

YAML:

Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

AWSTemplateFormatVersion: 2010-09-09
Description: Working with custom resources and S3
Parameters:
  S3BucketName:
    Type: String
    Description: "S3 bucket to create."
    AllowedPattern: "[a-zA-Z][a-zA-Z0-9_-]*"
  DirsToCreate:
    Description: "Comma delimited list of directories to create."
    Type: CommaDelimitedList
Resources:
  SampleS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref S3BucketName
  S3CustomResource:
    Type: Custom::S3CustomResource
    Properties:
      ServiceToken: !GetAtt AWSLambdaFunction.Arn
      the_bucket: !Ref SampleS3Bucket
      dirs_to_create: !Ref DirsToCreate
  AWSLambdaFunction:
     Type: "AWS::Lambda::Function"
     Properties:
       Description: "Work with S3 Buckets!"
       FunctionName: !Sub '${AWS::StackName}-${AWS::Region}-lambda'
       Handler: index.handler
       Role: !GetAtt AWSLambdaExecutionRole.Arn
       Timeout: 360
       Runtime: python3.9
       Code:
         ZipFile: |
          import boto3
          import cfnresponse
          def handler(event, context):
              # Init ...
              the_event = event['RequestType']
              print("The event is: ", str(the_event))
              response_data = {}
              s_3 = boto3.client('s3')
              # Retrieve parameters
              the_bucket = event['ResourceProperties']['the_bucket']
              dirs_to_create = event['ResourceProperties']['dirs_to_create']
              try:
                  if the_event in ('Create', 'Update'):
                      print("Requested folders: ", str(dirs_to_create))
                      for dir_name in dirs_to_create:
                          print("Creating: ", str(dir_name))
                          s_3.put_object(Bucket=the_bucket,
                                         Key=(dir_name
                                              + '/'))
                  elif the_event == 'Delete':
                      print("Deleting S3 content...")
                      b_operator = boto3.resource('s3')
                      b_operator.Bucket(str(the_bucket)).objects.all().delete()
                  # Everything OK... send the signal back
                  print("Operation successful!")
                  cfnresponse.send(event,
                                   context,
                                   cfnresponse.SUCCESS,
                                   response_data)
              except Exception as e:
                  print("Operation failed...")
                  print(str(e))
                  response_data['Data'] = str(e)
                  cfnresponse.send(event,
                                   context,
                                   cfnresponse.FAILED,
                                   response_data)
  AWSLambdaExecutionRole:
     Type: AWS::IAM::Role
     Properties:
       AssumeRolePolicyDocument:
         Statement:
         - Action:
           - sts:AssumeRole
           Effect: Allow
           Principal:
             Service:
             - lambda.amazonaws.com
         Version: '2012-10-17'
       Path: "/"
       Policies:
       - PolicyDocument:
           Statement:
           - Action:
             - logs:CreateLogGroup
             - logs:CreateLogStream
             - logs:PutLogEvents
             Effect: Allow
             Resource: arn:aws:logs:*:*:*
           Version: '2012-10-17'
         PolicyName: !Sub ${AWS::StackName}-${AWS::Region}-AWSLambda-CW
       - PolicyDocument:
           Statement:
           - Action:
             - s3:PutObject
             - s3:DeleteObject
             - s3:List*
             Effect: Allow
             Resource:
             - !Sub arn:aws:s3:::${SampleS3Bucket}/*
             - !Sub arn:aws:s3:::${SampleS3Bucket}
           Version: '2012-10-17'
         PolicyName: !Sub ${AWS::StackName}-${AWS::Region}-AWSLambda-S3
       RoleName: !Sub ${AWS::StackName}-${AWS::Region}-AWSLambdaExecutionRole

CloudFormation テンプレートをデプロイする

CloudFormation テンプレートをデプロイするには、CloudFormation コンソールまたは AWS コマンドラインインターフェイス (AWS CLI) を使用します。

**注:**AWS CLI のコマンドの実行時にエラーが発生する場合は、「AWS CLI エラーのトラブルシューティング」を参照してください。また、使用している AWS CLI が最新バージョンであることを確認してください。

CloudFormation コンソールを使用するには、次の手順を実行します。

  1. CloudFormation コンソールを開きます。
  2. [スタックを作成] を選択し、[新しいリソースを使用 (標準)] を選択します。
  3. [テンプレートの指定] で、[テンプレートファイルのアップロード] をクリックします。
  4. [ファイルの選択] を選択し、ダウンロードしたテンプレートを選択して、[次へ] を選択します。
  5. [パラメータ][S3BucketName] に、新しいバケットの S3 バケット名を指定します。
  6. [DirsToCreate] に、作成するフォルダとサブフォルダのカンマ区切りリストを入力します。
    注: 例えば、リストとして dir_1,dir_2/sub_dir_2,dir_3 のように入力します。
  7. セットアップウィザードの残りのステップを完了し、[スタックを作成] を選択します。

AWS CLIを使用して、次の手順を実行します。

  1. ダウンロードしたテンプレートの名前を custom-resource-lambda-s3.template に変更します。
  2. オペレーティングシステム (OS) でコマンドラインを開き、テンプレートを保存したフォルダに移動します。
  3. テンプレートをデプロイするには、create-stack コマンドを実行します。
    aws cloudformation create-stack \                   
                       --timeout-in-minutes 10 \
                       --disable-rollback \
                       --stack-name testing-custom-resource-s3 \
                       --template-body file://custom-resource-lambda-s3.template \
                       --capabilities CAPABILITY_NAMED_IAM \
                       --parameters \
                       ParameterKey=DirsToCreate,ParameterValue="dir_1\,dir_2/sub_dir_2\,dir_3" \
                       ParameterKey=S3BucketName,ParameterValue="test-bucket-custom-resource"
    注: custom-resource-lambda-s3.template は自分のテンプレートに、test-bucket-custom-resource は S3 バケット名に、dir_1\,dir_2/sub_dir_2\,dir_3 は作成したいフォルダとサブフォルダのリストにそれぞれ置き換えてください。

関連情報

CloudFormation で Lambda ベースのカスタムリソースを実装するためのベストプラクティスにはどのようなものがありますか?

CloudFormation で DELETE_FAILED ステータスまたは DELETE_IN_PROGRESS ステータスのままになっている Lambda-backed カスタムリソースを削除するにはどうすればよいですか?

AWS公式
AWS公式更新しました 2ヶ月前
コメントはありません

関連するコンテンツ