Attach an IAM Role to an EC2 Instance with CloudFormation
Dear Reader, In my last post, I shared with you How to Launch an EC2 instance in an existing VPC.
In this post, you will learn to attach an IAM role to an EC2 instance with CloudFormation.
You will also see, how to attach an already created(existing) IAM role to an EC2 instance using CloudFormation.
You can also check my post on how to attach an IAM role to an EC2 instance using Terraform.
So, Let’s get started 🙂
Don’t want to miss any posts from us? join us on our Facebook group, and follow us on Facebook, Twitter, LinkedIn, and Instagram. You can also subscribe to our newsletter below to not miss any updates from us.
Why is this different?
Do you know that, you can’t directly attach an IAM role to an EC2 instance?
You might think why?
As AWS says, An application running on an EC2 instance is abstracted from AWS by the virtualized operating system. Because of this extra separation, an additional step is needed to assign an AWS role and its associated permissions to an EC2 instance.
What’s that extra step?
Well, that extra step is the creation of an instance profile that you can attach to an EC2 instance.
Let’s understand the instance profile below.
What is an instance profile?
Instance profile is nothing but the container for an IAM role.
- You can use an instance profile to pass an IAM role to an EC2 instance.
- An instance profile can only contain one IAM role. However, please note that a role can belong to multiple instance profiles.
You might say to me –
I didn’t do it while I launched my instance with an IAM role from the console
I agree with you.
Your point is so obvious.
Let me try to explain this.
Actually what happens is, when you create an IAM Role for EC2 using the IAM Console, it creates both an EC2 instance profile as well as an IAM role with the same name.
So ideally, when you launch an instance with an IAM role, you get your instance profile list in the drop-down and you choose one of them for you.
What was the magic here?
Well, the console tricks you that you are attaching an IAM role, but the truth is that you are attaching an instance profile which had the same name as your role.
That was the console story.
What about AWS CLI, SDKs or CloudFormation?
When you are using the AWS CLI, SDKs, or CloudFormation, you will need to define both of them explicitly.
- An IAM role with policies and permissions,
- An EC2 instance profile containing a role
P.S.: This time role name and instance profile name can be different so make sure that you use the instance profile name while attaching to an EC2 instance.
How exactly do you attach a role to an EC2 instance?
First, you create an instant profile using the role and then you attach the instance profile to an instance.
Create a Role/Take an existing Role –> Put it into an Instance Profile –> Attach instance Profile to EC2 instance.
Let’s understand how it is done using CloudFormation.
1: Create an AWS::IAM::InstanceProfile Resource
First, you create an AWS::IAM::InstanceProfile resource(Don’t worry, I will provide the complete template in YAML as well as JSON in the next section)
DemoInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: demo-ec2-instance-profile
Path: /
Roles:
- !Ref DemoEc2InstanceRole
Please pay attention to the Roles parameter.
!Ref DemoEc2InstanceRole can either be an IAM role resource created in the same template or a parameter which is passing an existing role to the instance profile. Thus allowing you to attach an existing role to an EC2 instance.
P.S.: Always make sure you are passing a role name and not the role arn to the Roles parameter.
2: Attach Instance Profile to EC2 Instance
Create an AWS::EC2::Instance and refer DemoInstanceProfile in the parameter IamInstanceProfile of AWS::EC2::Instance
DemoInstance:
Type: 'AWS::EC2::Instance'
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
AvailabilityZone: !Ref AvailabilityZone
KeyName: !Ref KeyName
SecurityGroupIds:
- !Ref DemoSecurityGroup
SubnetId: !Ref SubnetId
IamInstanceProfile: !Ref DemoInstanceProfile
I think our concept is clear now, let’s go and create the stack using the below steps.
Steps to Attach an IAM Role to an EC2 Instance with Cloud Formation
- Ensure you have permission
- Create a template
- Create a stack using the template and verify
Step 1: Permission
- You need to have permission to create a CloudFormation stack
- EC2 related permission
- PassRole permission to an instance
You can attach the below policy to cover all the above permission.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:RunInstances",
"ec2:AssociateIamInstanceProfile",
"ec2:ReplaceIamInstanceProfileAssociation"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"cloudformation:CreateStack"
],
"Resource": "*"
}
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "*"
}
]
}
Step 2: Create a template to Attach an IAM Role to EC2 Instance
Template example to Attach an IAM Role to an EC2 Instance with CloudFormation in YAML
AWSTemplateFormatVersion: '2010-09-09'
Description: Template to attach an IAM role to an EC2 instance
Parameters:
ImageId:
Type: String
Description: 'Linux 2 AMI for Ireland eu-west-1 Region'
Default: 'ami-0fc970315c2d38f01'
VpcId:
Type: String
Description: VPC id
Default: vpc-102b1234f012123f0
SubnetId:
Type: String
Description: Subnet in which to launch an EC2
Default: subnet-0345ec56fd9af123b
AvailabilityZone:
Type: String
Description: Availability Zone into which instance will launch
Default: eu-west-1c
InstanceType:
Type: String
Description: Choosing t2 micro because it is free
Default: t2.micro
KeyName:
Description: SSH Keypair to login to the instance
Type: AWS::EC2::KeyPair::KeyName
Default: demokeypair
Resources:
DemoInstance:
Type: 'AWS::EC2::Instance'
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
AvailabilityZone: !Ref AvailabilityZone
KeyName: !Ref KeyName
SecurityGroupIds:
- !Ref DemoSecurityGroup
SubnetId: !Ref SubnetId
IamInstanceProfile: !Ref DemoInstanceProfile
DemoSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
VpcId: !Ref VpcId
GroupDescription: SG to allow SSH access via port 22
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: '0.0.0.0/0'
Tags:
- Key: Name
Value: SSH-SG
DemoInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: demo-ec2-instance-profile
Path: /
Roles:
- !Ref DemoEc2InstanceRole
DemoEc2InstanceRole:
Type: AWS::IAM::Role
Properties:
RoleName: demo-ec2-instance-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
-
Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Path: /
DemoInstanceS3Policy:
Type: AWS::IAM::Policy
Properties:
PolicyName: DemoS3Policy
PolicyDocument:
Version: 2012-10-17
Statement:
-
Effect: Allow
Action:
- s3:*
Resource:
- arn:aws:s3:::demo-s3-bucket/*
- arn:aws:s3:::demo-s3-bucket
Roles:
-
!Ref DemoEc2InstanceRole
Outputs:
DemoInstanceId:
Description: Instance Id
Value: !Ref DemoInstance
Template Example to Attach an IAM Role to an EC2 Instance with CloudFormation in JSON
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Template to attach an IAM role to an Ec2 instance",
"Parameters": {
"ImageId": {
"Type": "String",
"Description": "Linux 2 AMI for Ireland eu-west1 Region",
"Default": "ami-0fc970315c2d38f01"
},
"VpcId": {
"Type": "String",
"Description": "VPC id",
"Default": "vpc-102b1234f012123f0"
},
"SubnetId": {
"Type": "String",
"Description": "Subnet in which to launch an EC2",
"Default": "subnet-0345ec56fd9af123b"
},
"AvailabilityZone": {
"Type": "String",
"Description": "Availability Zone into which instance will launch",
"Default": "eu-west-1c"
},
"InstanceType": {
"Type": "String",
"Description": "Choosing t2 micro because it is free",
"Default": "t2.micro"
},
"KeyName": {
"Description": "SSH Keypair to login to the instance",
"Type": "AWS::EC2::KeyPair::KeyName",
"Default": "demokeypair"
}
},
"Resources": {
"DemoInstance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": {
"Ref": "ImageId"
},
"InstanceType": {
"Ref": "InstanceType"
},
"AvailabilityZone": {
"Ref": "AvailabilityZone"
},
"KeyName": {
"Ref": "KeyName"
},
"SecurityGroupIds": [
{
"Ref": "DemoSecurityGroup"
}
],
"SubnetId": {
"Ref": "SubnetId"
},
"IamInstanceProfile": {
"Ref": "DemoInstanceProfile"
}
}
},
"DemoSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"VpcId": {
"Ref": "VpcId"
},
"GroupDescription": "SG to allow SSH access via port 22",
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "22",
"ToPort": "22",
"CidrIp": "0.0.0.0/0"
}
],
"Tags": [
{
"Key": "Name",
"Value": "SSH-SG"
}
]
}
},
"DemoInstanceProfile": {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"InstanceProfileName": "demo-ec2-instance-profile",
"Path": "/",
"Roles": [
{
"Ref": "DemoEc2InstanceRole"
}
]
}
},
"DemoEc2InstanceRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": "demo-ec2-instance-role",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ec2.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Path": "/"
}
},
"DemoInstanceS3Policy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "DemoS3Policy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::demo-s3-bucket/*",
"arn:aws:s3:::demo-s3-bucket"
]
}
]
},
"Roles": [
{
"Ref": "DemoEc2InstanceRole"
}
]
}
}
},
"Outputs": {
"DemoInstanceId": {
"Description": "Instance Id",
"Value": {
"Ref": "DemoInstance"
}
}
}
}
Create a stack using the template and verify
Grab the YAML or JSON template from above and change the parameter value such as SubnetId, VpcId etc. as per your requirement.
Save the template with .yml or .json as per the template and follow the below steps.
- Login to AWS Management Console, navigate to CloudFormation and click on Create stack
- Click on “Upload a template file”, upload ec2instance.yml or ec2instance.json and click Next
- Enter the stack name and click on Next. In the configuration, keep everything as default and click on Next.
- In the events tab of the stack, you can view the status.
- Once the stack is successfully created, go to the Resources tab(Right next to Event) and click on your instance resource.
- It will take you to your instance. You can verify all the details there.
If you prefer CLI, check how to deploy a CloudFormation template in AWS using CLI
Conclusion
Finally, let’s summarize what we did in this post.
- IAM role can’t be directly attached to an EC2 instance. The first instance profile needs to be created with the role and then the instance profile is attached to an instance.
- When using the IAM console to create an IAM role for EC2, an instance profile with the same name is created.
- Using CLI, SDKs and CloudFormation, needs roles and instance profiles to be created separately.
- After an instance profile is created with a role, It can be attached to an instance.
- An instance profile can only have one role at a time. However, one role can belong to multiple instance profiles.
I hope you found this post helpful.
Don’t forget to motivate me by-
- Adding a comment below on what you liked and what can be improved.
- Follow us on
- Subscribe to our newsletter to get notified each time we post new content
- Share this post with your friends
Suggested Read:
- Understand IAM PassRole to Secure your AWS Infrastructure
- How to Create S3 Bucket using CloudFomation
- Most Common types of Cyber Attacks in 2020
- Create DynamoDB table using CloudFormation
- How to Create S3 Bucket Policy using CloudFomation
- Provision ACM certificate using CloudFormation
- Create SSM Parameter using CloudFormation
- Create API Gateway Custom Domain using CloudFormation
One thought on “Attach an IAM Role to an EC2 Instance with CloudFormation”
Guys, this is a great content and really simple to understand, Loved it. Do you have something similar to lean about Yaml and JSON as well?