Como uso recursos personalizados com buckets do Amazon S3 no CloudFormation?

6 minuto de leitura
0

Quero usar recursos personalizados com buckets do Amazon Simple Storage Service (Amazon S3) no AWS CloudFormation.

Breve descrição

Use o modelo do CloudFormation na resolução a seguir para usar recursos personalizados com um bucket do S3.

Considere o seguinte:

  • O modelo permite criar pastas em buckets do S3. O Amazon S3 tem uma estrutura plana, mas suporta o conceito de pasta como meio de agrupar objetos.
  • O modelo permite que você realize operações após a criação de um bucket do S3, incluindo cópia de conteúdo, upload de conteúdo e sincronização de dois buckets diferentes.
  • Você pode modificar o modelo com seu próprio código.
  • Esse modelo usa recursos personalizados apoiados pelo AWS Lambda e pressupõe que você esteja familiarizado com as melhores práticas e com a solução de problemas do Lambda.

Observação: na resolução a seguir, todo o conteúdo do bucket do S3 é excluído quando a pilha do CloudFormation é excluída. Você pode modificar esse comportamento modificando o código Lambda.

Resolução

Observação: se você receber erros ao executar comandos da AWS Command Line Interface (AWS CLI), certifique-se de estar usando a versão mais recente da AWS CLI.

Obtenha o modelo do CloudFormation

Para criar pastas em um bucket do S3 usando o CloudFormation, salve o seguinte modelo JSON ou 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

Implante seu modelo do CloudFormation

Você pode implantar seu modelo do CloudFormation usando o console do CloudFormation ou o AWS CLI.

Console do CloudFormation:

1.Abra o console do CloudFormation.

2.Escolha Criar pilha e, em seguida, escolha Com novos recursos (padrão).

3.Na seção Especificar modelo, escolha Carregar um arquivo de modelo.

4.Selecione Escolher arquivo, selecione o modelo que você baixou na etapa 1 e, em seguida, selecione Avançar.

5.Na seção Parâmetros, para S3BucketName, escolha seu bucket do S3.

6.Em DirsToCreate, insira uma lista delimitada por vírgula de pastas e subpastas que você deseja criar.

**Observação:**Por exemplo, você pode inserir dir_1,dir_2/sub_dir_2,dir_3 como uma lista.

7.Conclua o restante das etapas no assistente de configuração e, em seguida, selecione Criar pilha.

AWS CLI:

1.Dê ao seu modelo baixado o seguinte nome: custom-resource-lmabda-s3.template

2.Abra uma linha de comando em seu sistema operacional e, em seguida, vá para a pasta em que o modelo está localizado.

3.Execute o comando a seguir:

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"

Observação: atualize os parâmetros com seus valores.


AWS OFICIAL
AWS OFICIALAtualizada há 2 anos