简体   繁体   中英

AWS Cloudformation template EC2 Role/Policy circular dependency

I am writing a Cloudformation template with a single EC2 instance and an EBS volume. I attach the volume later on at some point when the machine is created using Powershell script. It works when I put wildcard '*' in policy statement resource however I want to limit the access to one instance and one ebs volume. With EBS volume it's easy I can just refer it in the template and it is created before the role but with instance the problem is that instance requires the role to be created first but also to be able to create the instance we need to create the role first. What's a good way of resolving this kind of circular dependency?

Here is my template:

Resources:
  InstanceRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: InstanceRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      Policies:
        - PolicyName: AttachVolume
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'ec2:AttachVolume'
                Resource:
                  - !Join 
                    - ''
                    - - 'arn:aws:ec2:'
                      - !Ref 'AWS::Region'
                      - ':'
                      - !Ref 'AWS::AccountId'
                      - ':instance/*'
                  - !Join 
                    - ''
                    - - 'arn:aws:ec2:'
                      - !Ref 'AWS::Region'
                      - ':'
                      - !Ref 'AWS::AccountId'
                      - ':volume/'
                      - !Ref DataVolume             
  InstanceProfile:
    Type: 'AWS::IAM::InstanceProfile'
    Properties:
      Roles:
        - !Ref InstanceRole
      InstanceProfileName: InstanceProfile
  Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      ImageId: !Ref AMI
      InstanceType: !Ref InstanceType
      IamInstanceProfile: !Ref InstanceProfile
      KeyName: ec2key
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeType: gp2
            DeleteOnTermination: 'true'
            VolumeSize: '30'
      Tags:
        - Key: Name
          Value: MyInstance
      SubnetId: !Ref SubnetId
      SecurityGroupIds:
        - !Ref SGId
      UserData: !Base64 
        'Fn::Join':
          - ''
          - - |
              <script>
            - 'cfn-init.exe -v -c config -s '
            - !Ref 'AWS::StackId'
            - ' -r Instance'
            - ' --region '
            - !Ref 'AWS::Region'
            - |+

            - |
              </script>
  DataVolume:
    Type: "AWS::EC2::Volume"
    Properties:  
      AvailabilityZone: !GetAtt 
        - Instance
        - AvailabilityZone
      Size: "100"
      Tags:
        - Key: Name
          Value: InstanceExtraVolume

In your particular example, you have the following dependency chain: InstanceRole -> DataVolume -> Instance -> InstanceProfile -> InstanceRole

In general, when your Role depends on your Resources and your Resources depend on your Role , this is where the AWS::IAM::Policy resource type is useful. This basically decouples the particular policy on the IAM Role from being resolved at the same time as the IAM Policy itself.

To do this, you would take your InstanceRole and split it into an InstanceRole and an InstanceRolePolicy

Resources:
  InstanceRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: InstanceRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
  InstanceRolePolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      Roles:
        - !Ref InstanceRole
      PolicyName: AttachVolume
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - 'ec2:AttachVolume'
            Resource:
              - !Join 
                - ''
                - - 'arn:aws:ec2:'
                  - !Ref 'AWS::Region'
                  - ':'
                  - !Ref 'AWS::AccountId'
                  - ':instance/*'
              - !Join 
                - ''
                - - 'arn:aws:ec2:'
                  - !Ref 'AWS::Region'
                  - ':'
                  - !Ref 'AWS::AccountId'
                  - ':volume/'
                  - !Ref DataVolume

With that, the InstanceRolePolicy depends on the InstanceRole and DataVolume , but the InstanceRole doesn't depend on anything, so the DataVolume -> Instance -> InstanceProfile -> InstanceRole chain can resolve.

One common solution to circular dependencies is to do this is multiple steps: create the stack with minimal resources, then modify the template and update the stack.

So, v1 of your template creates just the basic dependent resource and in v2, you modify the template to add the depending resource and modify the original dependent resource at the same time. Then do a stack update.

Also, see more ideas .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM