CloudFormation の Amazon S3 バケットでカスタムリソースを使用する方法を教えてください。
最終更新日: 2021 年 2 月 5 日
AWS CloudFormation の Amazon Simple Storage Service (Amazon S3) バケットでカスタムリソースを使用したいと考えています。
簡単な説明
次の解決方法では、CloudFormation テンプレートを使用して、S3 バケットでカスタムリソースを使用できます。
以下の点を考慮してください。
- テンプレートを使用すれば、S3 バケットにフォルダを作成できます。Amazon S3 はフラットな構造になっていますが、オブジェクトをグループ化するための手段としてフォルダの概念をサポートしています。
- テンプレートを使用して、S3 バケットを作成した後、コンテンツのコピー、コンテンツのアップロード、2 つの異なるバケットの同期などの操作を実行できます。
- テンプレートは、独自のコードで変更できます。
- このテンプレートは、AWS Lambda ベースのカスタムリソースを使用し、Lambda のベストプラクティスと問題のトラブルシューティングに精通していることを前提としています。
注意: 次の解決方法では、CloudFormation スタックが削除されると、すべての S3 バケットコンテンツが削除されます。この動作は、Lambda コードを変更することで変更できます。
解決方法
注: AWS コマンドラインインターフェイス (AWS CLI) コマンドの実行中にエラーが発生した場合は、最新の AWS CLI バージョンを使用していることを確認してください。
CloudFormation テンプレートを取得する
CloudFormation を使用して 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.6",
"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.6
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 CLI を使用してデプロイできます。
CloudFormation コンソール:
1. CloudFormation コンソールを開きます。
2. [Create stack (スタックの作成)] を選択し、[With new resources (standard) (新しいリソース (標準))] を選択します。
3. [Specify template (テンプレートの指定)] セクションで、[Upload a template file (テンプレートファイルのアップロード)] を選択します。
4. [Choose file (ファイルの選択)] をクリックし、手順 1 でダウンロードしたテンプレートを選択して、[Next (次へ)] を選択します。
5. [Parameters (パラメータ)] セクションの [S3BucketName] で、S3 バケットを選択します。
6. [DirsToCreate] に、作成するフォルダと下位フォルダのコンマ区切りリストを入力します。
注意: たとえば、dir_1,dir_2/sub_dir_2,dir_3 をリストとして入力できます。
7. セットアップウィザードの残りのステップを完了し、[Create stack] (スタックの作成) をクリックします。
AWS CLI:
1. ダウンロードしたテンプレートに次の名前を付けます: custom-resource-lmabda-s3.template
2. オペレーティングシステムでコマンドラインを開き、テンプレートがあるフォルダに移動します。
3. 次のコマンドを実行します。
aws cloudformation create-stack \
--timeout-in-minutes 10 \
--disable-rollback \
--stack-name testing-custom-resource-s3 \
--template-body file://custom-resource-lmabda-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"
注意: パラメータをご使用の値に更新してください。