How do I resolve circular dependencies with AWS Serverless Application Model (SAM) templates in CloudFormation?

Last updated: 2021-08-31

If I deploy an AWS Serverless Application Model (SAM) template in AWS CloudFormation, I get an error similar to the following: "Circular dependency between resources: [Function, Bucket, FunctionRole, FunctionUploadPermission]."

Short description

You get an error in CloudFormation if you use the following example AWS SAM template:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Circular Dependency
Resources:
  Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "{AWS::StackName}-${AWS::Region}-${AWS::AccountId}"
  Function:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: s3://mybucket/function.zip
      Runtime: nodejs12.x
      Handler: index.handler
      Policies:
        - Version: 2012-10-17
          Statement:
            - Effect: Allow
              Action: s3:GetObject*
              Resource: !Sub "arn:aws:s3:::${Bucket}*"
      Events:
        Upload:
          Properties:
            Bucket:
              Ref: Bucket
            Events: s3:ObjectCreated:*
          Type: S3

The template produces an error because it creates the following circular dependency:

  1. The Bucket resource depends on the FunctionUploadPermission.
  2. The FunctionUploadPermission depends on the Function.
  3. The Function depends on the FunctionRole.
  4. FunctionRole depends on the Bucket resource, which creates a loop.

Note: In the error message, the FunctionUploadPermission resource is of type AWS::Lambda::Permission. This resource is generated by AWS SAM automatically when the Events property for AWS::Serverless::Function is specified. The FunctionRole resource is of type AWS::IAM::Role. This resource is generated by AWS SAM automatically when the Role property for AWS::Serverless::Function is not specified.

To resolve the circular dependency, you must break the loop by replacing the dynamic reference to the bucket resource.

Resolution

Replace the CloudFormation template that has a circular dependency with the following template:

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'
Transform: AWS::Serverless-2016-10-31
Description: Circular Dependency
Resources:
  Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "${AWS::StackName}-${AWS::Region}-${AWS::AccountId}"
  Function:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: s3://mybucket/function.zip
      Runtime: nodejs12.x
      Handler: index.handler
      Policies:
        - Version: 2012-10-17
          Statement:
            - Effect: Allow
              Action: s3:GetObject*
              Resource: !Sub "arn:aws:s3:::${AWS::StackName}-${AWS::Region}-${AWS::AccountId}*"
      Events:
        Upload:
          Properties:
            Bucket:
              Ref: Bucket
            Events: s3:ObjectCreated:*
          Type: S3

In the template, the bucket name in the policy statement's Resources section uses the same pseudo parameters used in the bucket resource’s BucketName property. Now, you can pass the bucket name without referencing that bucket directly. Consequently, the error is resolved because the circular dependency loop is broken. That is, the circular dependency loop that resulted from the FunctionRole depending on the Bucket resource.

Note: Replace s3://mybucket/function.zip with your file location.


Did this article help?


Do you need billing or technical support?