La création de ma pile AWS CloudFormation échoue si je déploie un modèle avec les ressources suivantes :

  1. Une ressource de fonction AWS Lambda.
  2. Une ressource de compartiment Amazon S3 avec une propriété NotificationConfiguration qui fait référence à la fonction Lambda.
  3. Une ressource d'autorisation Lambda avec les propriétés FunctionName et SourceArn qui correspondent à la fonction Lambda et au compartiment S3.
    Remarque : une bonne pratique consiste à ajouter la propriété SourceAccount à la ressource d'autorisation Lambda pour les sources d'événements S3, car un Amazon Resource Name (ARN) S3 n'inclut pas d'ID de compte. Bien que la propriété SourceArn convienne à la plupart des autres sources d'événements, pensez à ajouter la propriété SourceAccount pour les sources d'événements S3 afin de vous protéger contre un scénario dans lequel vous supprimez le compartiment et découvrez que quelqu'un d'autre l'a recréé, le nouveau propriétaire du compartiment disposant alors des autorisations pour appeler votre fonction Lambda.

Si la pile échoue, une erreur semblable à ce qui suit se produit :

Unable to validate the following destination configurations

Lors de la création du compartiment, S3 doit valider la configuration de la notification en vérifiant si le compartiment a l'autorisation de pousser des événements vers la fonction Lambda. La ressource d'autorisation (qui doit exister pour que ce contrôle réussisse) a besoin du nom du compartiment. Par conséquent, la ressource d'autorisation dépend du compartiment, et le compartiment dépend de la ressource d'autorisation.

Remarque : si vous tentez de résoudre ce problème en implémentant un attribut de ressource DependsOn similaire à ce qui suit, vous recevez une erreur :

"MyS3BucketPermission": {
  "Properties": {
    "Action": "lambda:InvokeFunction",
    ...
    ...
    "SourceArn": {
      "Ref": "MyS3Bucket"
    }
  },
  "Type": "AWS::Lambda::Permission"
},
"Resources": {
  "MyS3Bucket": {
    "DependsOn" : "MyS3BucketPermission",

Erreur d'attribut de ressource DependsOn :

Circular dependency between resources

Dans cet exemple, il y a une dépendance circulaire entre la ressource de compartiment S3 et la propriété SourceArn de la ressource d'autorisation Lambda, car aucune des deux n'existe et l'une ne peut pas être créée sans l'autre.

Si le problème se produit, vous pouvez éviter la dépendance circulaire en utilisant la fonction intrinsèque Fn::Join avec les paramètres de pile.

Examinez cet exemple de modèle où le nom du compartiment « BucketPrefix » est fourni en tant que paramètre à « AWS::S3::Bucket » et aux ressources « Lambda::Permission ».

Remarque : cet exemple suppose que le nom de compartiment n'a pas été utilisé précédemment avec vos comptes AWS. Si vous souhaitez réutiliser un modèle avec cet extrait de code, vous devez fournir à chaque fois un préfixe de compartiment différent.

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    "BucketPrefix": {
      "Type": "String",
      "Default": "test-bucket-name"
    }
  },
  "Resources": {
    "EncryptionServiceBucket": {
      "DependsOn": "LambdaInvokePermission",
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {
          "Fn::Sub": "${BucketPrefix}-encryption-service"
        },
        "NotificationConfiguration": {
          "LambdaConfigurations": [
            {
              "Function": {
                "Fn::GetAtt": [
                  "AppendItemToListFunction",
                  "Arn"
                ]
              },
              "Event": "s3:ObjectCreated:*",
              "Filter": {
                "S3Key": {
                  "Rules": [
                    {
                      "Name": "suffix",
                      "Value": "zip"
                    }
                  ]
                }
              }
            }
          ]
        }
      }
    },
    "LambdaInvokePermission": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "AppendItemToListFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": "s3.amazonaws.com",
        "SourceAccount": {
          "Ref": "AWS::AccountId"
        },
        "SourceArn": {
          "Fn::Join": [
            "",
            [
              "arn:aws:s3:::",
              {
                "Fn::Sub": "${BucketPrefix}-encryption-service"
              }
            ]
          ]
        }
      }
    },
    "AppendItemToListFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Handler": "index.handler",
        "Role": {
          "Fn::GetAtt": [
            "LambdaExecutionRole",
            "Arn"
          ]
        },
        "Code": {
          "ZipFile": {
            "Fn::Join": [
              "",
              [
                "var response = require('cfn-response');",
                "exports.handler = function(event, context) {",
                " var responseData = {Value: event.ResourceProperties.List};",
                " responseData.Value.push(event.ResourceProperties.AppendedItem);",
                " response.send(event, context, response.SUCCESS, responseData);",
                "};"
              ]
            ]
          }
        },
        "Runtime": "nodejs4.3"
      }
    },
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/",
        "Policies": [
          {
            "PolicyName": "root",
            "PolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": [
                    "logs:*"
                  ],
                  "Resource": "arn:aws:logs:*:*:*"
                }
              ]
            }
          }
        ]
      }
    }
  }
}

Cela permet d'éviter la dépendance circulaire, car les ressources sont créées dans cet ordre :

  1. Rôle IAM
  2. Fonction Lambda
  3. Autorisation Lambda
  4. Compartiment S3

Cette approche permet à S3 de vérifier sa configuration de notification et de créer le compartiment sans générer d'erreur.

Autres solutions de contournement :

  • Créez le compartiment sans configuration de notification, puis ajoutez-la dans la mise à jour de pile suivante.
  • Créez une autorisation Lambda moins restrictive. Par exemple, autorisez les appels pour un compte AWS spécifique en omettant la propriété SourceArn.
  • Créez une ressource personnalisée à exécuter à la fin du flux de travail de la pile. Cette ressource ajoute la configuration de notification au compartiment lorsque toutes les autres ressources sont créées.

Cette page vous a-t-elle été utile ? Oui | Non

Retour au Centre de connaissances AWS Support

Vous avez besoin d'aide ? Consultez le site du Centre AWS Support

Date de publication : 03/10/2017

Date de mise à jour : 06/09/2018