Identity and Access Management (IAM) is a core component of AWS, enabling security by granting authenticated entities granular permissions to AWS services and resources.
One of the ways IAM achieves this is through the concept of “principals”, which are entities that can make requests to AWS services.
A principal can be an IAM user, an AWS service, or even an anonymous internet user.
In this blog post, we will walk you through creating an IAM role that utilizes multiple principals using the AWS CDK.
Table of Contents
Understanding Role Session Principals
A principal in AWS IAM is an entity that can perform actions on AWS resources. There are various types of principals, but we’ll focus on two primary ones:
The IAM user principal and the AWS service principal.
- The IAM user principal represents the person or application that uses the IAM user to interact with AWS. When an IAM user accesses a resource, the user principal is the IAM user.
- The AWS service principal represents an AWS service that needs to perform actions on your behalf. The service principal is defined by the service, and it’s the security identity that the service uses to carry out actions on AWS resources.
1. Prerequisites
Before we start building the AWS Lambda and IAM Role construct, you’re required to have done the following prerequisites before you can run AWS CDK code in TypeScript.
- Install AWS CDK and TypeScript NPM packages
- Install the AWS CLI and configure an AWS profile
- Create an AWS CDK TypeScript project
If you’ve already done this, you can proceed with step 2.
1.1 Install AWS CDK
Use the NPM package manager in your terminal to install AWS CDK and TypeScript globally on your system:
➜ npm install -g aws-cdk typescript
added 180 packages, and audited 181 packages in 7s
found 0 vulnerabilities
~ took 7s
Once you’ve installed AWS CDK you can validate that you’re running on the latest version by running the following command in the terminal:
➜ cdk version
2.23.0 (build 50444aa)
1.2 Install AWS CLI and configure an AWS profile
The AWS CLI is a command line tool that allows you to interact with AWS services in your terminal. Depending on if you’re running Linux, macOS, or Windows the installation goes like this:
# macOS install method:
brew install awscli
# Windows install method:
wget https://awscli.amazonaws.com/AWSCLIV2.msi
msiexec.exe /i https://awscli.amazonaws.com/AWSCLIV2.msi
# Linux (Ubuntu) install method:
sudo apt install awscli
In order to access your AWS account with the AWS CLI, you first need to configure an AWS Profile. There are 2 ways of configuring a profile:
- Access and secret key credentials from an IAM user
- AWS Single Sign-on (SSO) user
In this article, I’ll briefly explain how to configure the first method so that you can proceed more quickly to set up the Amazon S3 Bucket in AWS CDK.
If you wish to set up the AWS profile more securely, then I’d suggest you read and apply the steps described in setting up AWS CLI with AWS Single Sign-On (SSO).
In order to configure the AWS CLI with your IAM user’s access and secret key credentials, you need to login to the AWS Console. Go to IAM > Users, select your IAM user and click on the Security credentials tab to create an access and secret key.
Then configure the AWS profile on the AWS CLI as follows:
➜ aws configure
AWS Access Key ID [None]: <insert_access_key>
AWS Secret Access Key [None]: <insert_secret_key>
Default region name [None]: <insert_aws_region>
Default output format [json]: json
Your was credentials are stored in ~/.aws/credentials and you can validate that your AWS profile is working by running the command:
➜ aws sts get-caller-identity
{
"UserId": "AIDA5BRFSNF24CDMD7FNY",
"Account": "012345678901",
"Arn": "arn:aws:iam::012345678901:user/test-user"
}
1.3 Create a new AWS CDK TypeScript Project
Now that we’ve configured our profile and installed the packages, it’s time to create an AWS CDK TypeScript project where you’re going to build the Amazon S3 Bucket construct.
You can generate a new AWS CDK TypeScript project by running the following command in an empty directory:
➜ cdk init sample-app --language=typescript
Applying project template sample-app for typescript
# Welcome to your CDK TypeScript project!
You should explore the contents of this project. It demonstrates a CDK app with an instance of a stack (`CdkProjectStack`)
which contains an Amazon SQS queue that is subscribed to an Amazon SNS topic.
The `cdk.json` file tells the CDK Toolkit how to execute your app.
## Useful commands
* `npm run build` compile typescript to js
* `npm run watch` watch for changes and compile
* `npm run test` perform the jest unit tests
* `cdk deploy` deploy this stack to your default AWS account/region
* `cdk diff` compare deployed stack with current state
* `cdk synth` emits the synthesized CloudFormation template
Initializing a new git repository...
Executing npm install...
✅ All done!
2. How to create an IAM role that can be assumed by multiple principals
Now that we’ve configured our workspace and cdk app, we can proceed with the creation of the constructs.
There are two ways of adding multiple principals to a single IAM role.
- Using the
addPrincipals
method to add principals to an existing IAM role - Using
CompositePrincipal
to add multiple principals to a new IAM role
We’ll go over both methods.
2.1 Use the addPrincipals method to add principals to an existing IAM role
First step is to create an IAM role.
const role = new iam.Role(this, 'MyRole', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
});
The assumedBy
prop is required in order to create an IAM role. However as the example shows, it only contains a single AWS Service Principal.
The next step is to add more principals using the addPrincipals method:
role.assumeRolePolicy?.addPrincipals(new iam.ServicePrincipal('dynamodb.amazonaws.com'));
In this example, we’ve added DynamoDB to the list of services that can assume the role.
2.2 Use CompositePrincipal to Add Multiple Service Principals to your IAM role
In certain scenarios, you might want your IAM role to be assumable by multiple different services. This is where CompositePrincipal
comes into play.
CompositePrincipal
is a construct provided by CDK which lets you add multiple service principals to a role.
Here’s an example:
const role = new iam.Role(this, 'MyRole', {
assumedBy: new iam.CompositePrincipal(
new iam.ServicePrincipal('ec2.amazonaws.com'),
new iam.ServicePrincipal('dynamodb.amazonaws.com')
),
});
In this example, we’ve defined an IAM role that can be assumed by both EC2 and DynamoDB services.
2.3 What the result looks like in CloudFormation
When you synthesise the stack you’ll see what the result looks like for the IAM role and policy document.
{
"Resources": {
"MyRoleF48FFE04": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
}
},
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "dynamodb.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
}
}
}
}
Conclusion
IAM roles and principals are crucial components of AWS security, and the AWS CDK makes it easy to define and manage these entities in your infrastructure code.
By using constructs like CompositePrincipal
and the addPrincipals
method, you can create roles that can be assumed by multiple different principals, providing you with the flexibility to grant access to AWS resources according to the specific needs of your applications and services.