Home > Software design >  Crcular Dependency in Cloud Formation
Crcular Dependency in Cloud Formation

Time:11-11

I'm stuck at circular dependency loop in Cfn (ECS Stack), This can be easily resolve by segregating resources in different stack, but challenge is to resolve it within same/single stack. Spent a night solving it, still not getting any close to resolve it. After a lot of debugging finally though of seeking some help, let me know if anyone can assist me in this, I'd really appreciate any leads or help. `

AWSTemplateFormatVersion: '2010-09-09'
Description: This stack will deploy following resources , May 
# Metadata:
Parameters:
  VPC:
    Description: Select One VPC available in your existing account
    Type: AWS::EC2::VPC::Id
  PubSubnets:
    Type: 'List<AWS::EC2::Subnet::Id>'
    Description: The list of PubSubnetIds in selected VPC)
  PvtSubnets:
    Type: 'List<AWS::EC2::Subnet::Id>'
    Description: The list of PvtSubnetIds in selected VPC)
  ClientName:
    Type: String
    Default: test

# Mappings: 

# Conditions: 

Resources:
  ELBTargetGroup:
   Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
  #  DependsOn:
  #   - ElasticLoadBalancer
   Properties:
     Name: "ELB-TG"
     HealthCheckIntervalSeconds: 6
     HealthCheckTimeoutSeconds: 5
     HealthyThresholdCount: 2
     Port: 80
     Protocol: HTTP
     UnhealthyThresholdCount: 2
     VpcId: !Ref VPC
     TargetType: instance
  
  ELBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: "ELBTraffic"
      GroupDescription: "Enable HTTP access on the inbound port for ELB"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: ELBSecurityGroup
  
  ElasticLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    # DependsOn:
    #   - ELBSecurityGroup
    Properties:
      Subnets: 
      - !Ref PubSubnets
      SecurityGroups: 
        - !Ref ELBSecurityGroup
  
  ElbListener:
   Type: 'AWS::ElasticLoadBalancingV2::Listener'
  #  DependsOn:
  #   - ElasticLoadBalancer
   Properties:
     DefaultActions:
       - Type: forward
         TargetGroupArn: !Ref ELBTargetGroup
     LoadBalancerArn: !Ref ElasticLoadBalancer
     Port: 80
     Protocol: HTTP
  
  AsgConfig:
    Type: AWS::EC2::LaunchTemplate
    DependsOn:
      - ELBSecurityGroup
    Properties:
      LaunchTemplateName: !Sub ${ClientName}-launch-template
      LaunchTemplateData:
        ImageId: ami-0171959e760b38d59
        InstanceType: t3.medium
        SecurityGroups:
        - !Ref ELBSecurityGroup
        #ImageId: "ami-0171959e760b38d59"
        UserData:
          Fn::Base64: !Sub |
            #!/bin/bash -xe
            echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
            sudo yum install -y python-pip pip
            yum install -y aws-cfn-bootstrap
            /opt/aws/apitools/cfn-init-2.0-6/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource AsGroup --region ${AWS::Region}
            /opt/aws/apitools/cfn-init-2.0-6/bin/cfn-init -v --stack ${AWS::StackName} --resource AsgConfig --region ${AWS::Region} -c default
  
  AsGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    DependsOn:
      - AsgConfig
    Properties:
      VPCZoneIdentifier: 
        - !Ref PvtSubnets
      LaunchTemplate:
        LaunchTemplateId: !Ref AsgConfig
        Version: !GetAtt AsgConfig.LatestVersionNumber
      # LaunchConfigurationName: !Ref AsgConfig
      MinSize: '1'
      DesiredCapacity: '2'
      MaxSize: '4'
      TargetGroupARNs:
        - !Ref ELBTargetGroup
  
  CapacityProvider:
    Type: AWS::ECS::CapacityProvider
    DependsOn:
      - AsGroup
    Properties:
      AutoScalingGroupProvider:
        AutoScalingGroupArn: !Ref AsGroup 
  
  CodeCommitRepository1:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryDescription: "HTML code"
      RepositoryName: "HTML_code"
  CodeCommitRepository2:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryDescription: "Python code"
      RepositoryName: "Python_code"
  CodeCommitRepository3:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryDescription: "Node code"
      RepositoryName: "Node_code"
  
  ECRrepo:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: "cfn_repo"
  
  ECSCluster:
    Type: AWS::ECS::Cluster
    DependsOn:
      - CapacityProvider
    Properties:
      CapacityProviders:
        - !Ref CapacityProvider
      ClusterName: !Ref ClientName
      Configuration:
       ExecuteCommandConfiguration:
          Logging: DEFAULT
      ClusterSettings:
        - Name: containerInsights
          Value: enabled
  
  ECSServiceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: "ecs-service-role"
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole
  
  ExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: "ecs-execution-role"
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
  
  ContainerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: "ContainerSecurityGroup"
      GroupDescription: "Security group for container"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
  Service:
    Type: AWS::ECS::Service
    DependsOn:
      - ECSCluster
      - ElasticLoadBalancer
      # - ECSServiceRole
      # - TaskDefinition
      - ELBTargetGroup
    Properties:
      Cluster: !Ref ECSCluster
      Role: !Ref ECSServiceRole
      DesiredCount: 1
      TaskDefinition: !Ref TaskDefinition
      LaunchType: EC2
      LoadBalancers:
        - ContainerName: "deployment-container"
          ContainerPort: 80
          TargetGroupArn: !Ref ELBTargetGroup
  
  AutoScalingRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: service-auto-scaling-role
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: [application-autoscaling.amazonaws.com]
            Action: ["sts:AssumeRole"]
      Policies:
        - PolicyName: service-auto-scaling-policy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ecs:DescribeServices
                  - ecs:UpdateService
                  - cloudwatch:PutMetricAlarm
                  - cloudwatch:DescribeAlarms
                  - cloudwatch:DeleteAlarms
                Resource:
                  - "*"

  ScalableTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    # DependsOn:
    #   - 
    Properties:
      RoleARN: !GetAtt AutoScalingRole.Arn
      ResourceId: !Sub service/${ClientName}/Service
      ServiceNamespace: ecs
      ScalableDimension: ecs:Service:DesiredCount
      MinCapacity: 1
      MaxCapacity: 5
  
  ScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    DependsOn:
      - ScalableTarget
    Properties:
      PolicyName: service-auto-scaling-policy
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageCPUUtilization
        TargetValue: 80.0

  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    # DependsOn:
    #   - ExecutionRole
      # - Service
    Properties:
      Family: deployment-task
      Cpu: "256"
      Memory: "512"
      NetworkMode: bridge
      ExecutionRoleArn: !Ref ExecutionRole
      ContainerDefinitions:
        - Name: deployment-container
          Image: cfn_repo/cfnrepo:latest
          PortMappings:
            - ContainerPort: 80      
      RequiresCompatibilities:
        - EC2
       
# Outputs:
#   outputELBTargetGroup:
#     Description: A reference to the created Target Group
#     Value: !Ref ELBTargetGroup
#   outputELBSecurityGroup:
#     Description: A reference to the created Security Group
#     Value: !Ref ELBSecurityGroup
#   outputElasticLoadBalancer:
#     Description: A reference to the created Elastic Load Balancer
#     Value: !Ref ElasticLoadBalancer
#   outputElasticListener:
#     Description: A reference to the created Elastic Load Balancer Listener
#     Value: !Ref ElbListener
#   outputAsgConfig: 
#     Description: Id for autoscaling launch configuration
#     Value: !Ref AsgConfig
#   outputAsgGroup: 
#     Description: Id for autoscaling group
#     Value: !Ref AsgGroup 
#   outputECSCluster: 
#     Description: Cluster name
#     Value: !Ref ECSCluster

`

Erorr on console

CodePudding user response:

You have a circular dependency, as follows:

  1. AsGroup depends on AsgConfig
  2. AsgConfig depends on ECSCluster because of echo ECS_CLUSTER=${ECSCluster} in user data
  3. ECSCluster depends on CapacityProvider
  4. CapacityProvider depends on AsGroup (which is #1 above)

I suggest that instead of configuring the ECSCluster with the CapacityProvider, you simply create the ECSCluster without a capacity provider (and without the DependsOn) and add a later AWS::ECS::ClusterCapacityProviderAssociations to associate the CapacityProvider with the ECSCluster.

For example (note that I have not tested this so some tweaks may be required):

CapacityProvider:
  Type: AWS::ECS::CapacityProvider
  DependsOn:
    - AsGroup
  Properties:
    AutoScalingGroupProvider:
      AutoScalingGroupArn: !Ref AsGroup 

ECSCluster:
  Type: AWS::ECS::Cluster
  Properties:
    ClusterName: !Ref ClientName
    Configuration:
      ExecuteCommandConfiguration:
        Logging: DEFAULT
    ClusterSettings:
      - Name: containerInsights
        Value: enabled

ClusterCapacityProviderAssociation:
  Type: AWS::ECS::ClusterCapacityProviderAssociations
  Properties: 
    CapacityProviders: 
      - !Ref CapacityProvider
    Cluster: ECSCluster
    DefaultCapacityProviderStrategy: 
      - Base: 0
        Weight: 1
        CapacityProvider: !Ref CapacityProvider
  • Related