Attach an IAM Role to an EC2 Instance with CloudFormation

attach an iam role to an ec2 instance using cloudformation

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.

attach an iam role to an ec2 instance using cloudformation

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

  1. Ensure you have permission
  2. Create a template
  3. Create a stack using the template and verify

Step 1: Permission

  1. You need to have permission to create a CloudFormation stack
  2. EC2 related permission
  3. 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.
launch an ec2 instance in an existing vpc

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:

One thought on “Attach an IAM Role to an EC2 Instance with CloudFormation

  1. 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?

Leave a Reply

Your email address will not be published. Required fields are marked *