Attach an IAM Role to an EC2 Instance with CloudFormation

attach an iam role to an ec2 instance using cloudformation
Sharing is Caring:

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 existing IAM role to an EC2 instance using CloudFormation.

So, Let’s get started 🙂

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 instance profile that you can attach to an EC2 instance.

Let’s understand instance profile below.

What is instance profile?

Instance profile is nothing but the container for an IAM role.

  • You can use 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 profile.

You might say to me –

I didn’t do it while I launched my instance with an IAM role from 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 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 instance profile name while attaching to an EC2 instance.

How exactly 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.

Using CloudFormation

Step 1: First you create an AWS::IAM::InstanceProfile resource(Don’t worry, I will provide complete template in YAML as well as JSON in 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 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.

Step 2: 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 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 template and verify

Permission

  1. You need to have permission to create a CloudFormation stack
  2. EC2 related permission
  3. PassRole permission to instance

You can attach 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": "*"
    }
  ]
}

Template to Attach an IAM Role to an EC2 Instance with CloudFormation: 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 to Attach an IAM Role to an EC2 Instance with CloudFormation: 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 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 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 configuration, keep everything as default and click on Next.
  • In the events tab of stack, you can view the status.
  • Once stack is successfully created, go to 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

Conclusion

Finally, let’s summarize what we did in this post.

  • IAM role can’t be directly attached to an EC2 instance. First instance profile needs to be created with the role and then instance profile is attached to an instance.
  • When using IAM console to create an IAM role for EC2, an instance profile with same name is created.
  • Using CLI, SDKs and CloudFormation, needs role and instance profile to be created separately.
  • After 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:

Sharing is Caring:

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 *