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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With