Comment utiliser des ressources avec des compartiments Amazon S3 sur CloudFormation ?

Lecture de 6 minute(s)
0

Je souhaite utiliser des ressources personnalisées avec les compartiments Amazon Simple Storage Service (Amazon S3) sur AWS CloudFormation.

Brève description

Utilisez le modèle CloudFormation dans la résolution suivante pour utiliser des ressources personnalisées avec un compartiment S3.

Considérez les éléments suivants :

  • Le modèle vous permet de créer des dossiers dans des compartiments S3. Amazon S3 possède une structure plate, mais prend en charge le concept de dossier comme moyen de regrouper les objets.
  • Le modèle vous permet d'effectuer des opérations après la création d'un compartiment S3, notamment la copie du contenu et son téléchargement, et la synchronisation de deux compartiments différents.
  • Vous pouvez modifier le modèle avec votre propre code.
  • Ce modèle utilise des ressources personnalisées AWS Lambda et suppose que vous connaissez les meilleures pratiques  Lambda et la résolution des problèmes.

Remarque : dans la résolution suivante, tout le contenu du compartiment S3 est supprimé lorsque la pile CloudFormation est supprimée. Vous pouvez modifier ce comportement en modifiant le code Lambda.

Résolution

Remarque : si vous recevez des erreurs lors de l'exécution de commandes depuis Interface de la ligne de commande AWS (AWS CLI), assurez-vous d'utiliser la version la plus récente d'AWS CLI.

Obtenez le modèle CloudFormation

Pour créer des dossiers dans un compartiment S3 à l'aide de CloudFormation, enregistrez le modèle JSON ou YAML suivant :

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

Déployer votre modèle CloudFormation

Vous pouvez déployer votre modèle CloudFormation à l'aide de la console CloudFormation ou de l'AWS CLI.

Console CloudFormation :

1.    Ouvrez la console CloudFormation.

2.    Choisissez Créer une pile, puis choisissez Avec de nouvelles ressources (standard).

3.    Dans la section Spécifier un modèle, choisissez Charger un fichier de modèle.

4.    Choisissez Choisir un fichier, sélectionnez le modèle que vous avez téléchargé à l'étape 1, puis choisissez Suivant.

5.    Dans la section Paramètres, pour S3BucketName, choisissez votre compartiment S3.

6.    Pour DirsToCreate, entrez une liste des dossiers et sous-dossiers que vous voulez créer en les séparant par des virgules.

Remarque : Par exemple, vous pouvez entrer dir_1,dir_2/sub_dir_2,dir_3 sous la forme d’une liste.

7.    Effectuez le reste des étapes de l'assistant de configuration, puis choisissez Create stack.

AWS CLI :

1.    Donnez le nom suivant à votre modèle téléchargé : custom-resource-lmabda-s3.template

2.    Ouvrez une ligne de commande dans votre système d'exploitation, puis accédez au dossier où se trouve le modèle.

3.    Exécutez la commande suivante :

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"

Remarque : Mettez à jour les paramètres avec vos valeurs.


AWS OFFICIEL
AWS OFFICIELA mis à jour il y a 2 ans