AWS Developer Tools Blog

IAM Roles for Amazon EC2 instances (Access Key Management for .NET Applications – Part 4)

In this post, we’ll see how to use Identity and Access Management(IAM) roles for Amazon EC2 instances. Using IAM roles for EC2 instances, you don’t need to manage or distribute credentials that your application needs. Instead, credentials are automatically distributed to EC2 instances and picked up by the AWS SDK for .NET. Here are the advantages of using this approach.

  • No need to distribute and manage credentials for your application
  • Credentials are periodically auto rotated and distributed to EC2 instances
  • The credentials are transparently available to your application through the SDK

Before we go further and look at code snippets, let’s talk about IAM roles and related concepts in a little more detail. A role lets you define a set of permissions to access resources that your application needs. This is specified using an access policy. A role also contains information about who can assume the role. This is specified using a trust policy. To use roles with EC2 instances, we need an instance profile. An instance profile is a container for roles and is used to pass role information to EC2 instances when they are launched. When you launch an EC2 instance with an instance profile, your application can make requests to AWS resources using the role credentials for the role associated with the instance profile.

In the rest of this post, we will perform the steps required to use IAM roles using the AWS SDK for .NET. Please note that all of these steps can be performed using the AWS Management Console as well.

Create an IAM Role

We start by creating an IAM role. As I mentioned before, you need to provide two pieces of information here: the access policy that will contain the permissions your application needs, and the trust policy that will specify that EC2 can assume this role. The trust policy is required so that EC2 can assume the role and fetch the temporary role credentials.

This is the trust policy that allows EC2 to assume the role.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal":{"Service":["ec2.amazonaws.com"]},
    "Action": "sts:AssumeRole"
  }]
}

This is a sample access policy that gives restricted access to a bucket by allowing the ListBucket, PutObject and GetObject actions.

{
  "Version" : "2012-10-17",
  "Statement" : [
    {
      "Effect":"Allow",
      "Action":[
        "s3:ListBucket"
      ],
      "Resource":"arn:aws:s3:::MyApplicationBucket"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::MyApplicationBucket/*"
    }
  ]
}

The following code creates a role with the given trust and access policy.

var roleName = "S3Access";
var profileName = "S3Access";
var iamClient = new AmazonIdentityManagementServiceClient();

// Create a role with the trust policy
var role = iamClient.CreateRole(new CreateRoleRequest
{
   RoleName = roleName,
   AssumeRolePolicyDocument = trustPolicy
});

// Add the access policy to the role
iamClient.PutRolePolicy(new PutRolePolicyRequest
{
    RoleName = roleName,
    PolicyName = "S3Policy",
    PolicyDocument = accessPolicy                
});

Create an instance profile

Now we create an instance profile for the role.

// Create an instance profile
iamClient.CreateInstanceProfile(new CreateInstanceProfileRequest
{
    InstanceProfileName = profileName                
});

// Add the role to the instance profile
iamClient.AddRoleToInstanceProfile(new AddRoleToInstanceProfileRequest
{
    InstanceProfileName = profileName,
    RoleName = roleName
});

Launch EC2 instance(s) with the instance profile

We can now launch EC2 instances with the instance profile that we created. Notice that we use the Amazon.EC2.Util.ImageUtilities helper class to retrieve the image identifier.

var ec2Client = new AmazonEC2Client();
            
// Find an image using ImageUtilities helper class
var image = Amazon.EC2.Util.ImageUtilities.FindImage(
    ec2Client,
    Amazon.EC2.Util.ImageUtilities.WINDOWS_2012_BASE);

//Launch an EC2 instance with the instance profile
var instance = ec2Client.RunInstances(new RunInstancesRequest
{
    ImageId = image.ImageId,
    IamInstanceProfile = new IamInstanceProfileSpecification
    {
        Name = profileName
    },
    MinCount=1, MaxCount =1,
});

Access AWS Resources from your application code deployed on EC2

You don’t need to make any changes to your application code to use IAM roles. Your application code should construct service clients without specifying any explicit credentials like the code below (without having any credentials in the application configuration file). Behind the scenes, the Amazon.Runtime.InstanceProfileAWSCredentials class fetches the credentials from EC2 Instance metadata service and automatically refreshes them when a new set of credentials is available.

// Create an S3 client with the default constructor,
// this will use the role credentials to access resources.
var s3Client = new AmazonS3Client();
var s3Objects = s3Client.ListObjects(new ListObjectsRequest 
{
    BucketName = "MyApplicationBucket" 
}).S3Objects;

In this post, we saw how IAM roles can greatly simplify and secure access key management for applications on Amazon EC2. We highly recommend that you use this approach for all applications that are run on Amazon EC2.