Last updated: Jan 26, 2024
Reading timeยท5 min
A principal is an IAM entity that can assume a role and take on its associated permissions.
Principals can be:
Let's look at concrete examples, starting with service principals.
A service principal is an IAM principal that represents an AWS service.
In order to create a service principal in AWS CDK, we have to instantiate the ServicePrincipal class.
import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ๐ Create a role with a Service Principal const role1 = new iam.Role(this, 'role-1', { assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), }); const policy1 = new iam.PolicyStatement({ resources: ['arn:aws:logs:*:*:log-group:/aws/lambda/*'], actions: ['logs:FilterLogEvents'], }); // ๐ add a service principal to the policy policy1.addServicePrincipal('ec2.amazonaws.com'); } }
Let's go over what we did in the code sample:
lambda
service as the principal, which can
assume the role.ec2
service as the principal,
which can assume the role.Let's deploy our app:
npx aws-cdk deploy
After a successful deployment, we can look at the trust relationship of the IAM
role and see that the lambda
service is the only trusted entity:
In order to specify an account principal in AWS CDK, we have to instantiate the AccountPrincipal class and pass it an account ID.
import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ...rest // ๐ create a role with an AWS Account principal const role2 = new iam.Role(this, 'role-2', { assumedBy: new iam.AccountPrincipal(cdk.Stack.of(this).account), }); } }
We instantiated the AccountPrincipal
class and passed it an
account id. We used the account ID that was
used to deploy the CDK stack, however, you can simply pass in any account ID you
desire, i.e. 123456789
.
After running the deploy
command, we can see that the account number is set as
the trusted entity.
The root account principal specifies the account, into which a stack is deployed as the principal entity.
In order to create a root account principal in AWS CDK, we have to instantiate the AccountRootPrincipal class.
import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ...rest // ๐ create a role with an Account Root Principal const role3 = new iam.Role(this, 'role-3', { assumedBy: new iam.AccountRootPrincipal(), }); } }
We instantiated the AccountRootPrincipal
class to set the account into which
the stack is deployed as the principal entity.
In order to specify a principal by the Amazon Resource Name (ARN), we have to instantiate the ArnPrincipal class.
ARN principals can be:
Let's look at an example where we set a user principal by the ARN:
// ๐ create a role with an ARN Principal const role4 = new iam.Role(this, 'role-4', { assumedBy: new iam.ArnPrincipal( `arn:aws:iam::${cdk.Stack.of(this).account}:user/YOUR_USER_NAME`, ), });
We created a role that sets an IAM user, by the ARN, as the trusted entity.
The any
principal represents all identities in all accounts.
In order to specify any
principal in AWS CDK, we have to instantiate the
AnyPrincipal
class.
import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ...rest // ๐ create a policy with Any Principal const policy2 = new iam.PolicyStatement({ resources: ['*'], actions: ['s3:*'], effect: iam.Effect.DENY, principals: [new iam.AnyPrincipal()], }); } }
We created a policy with any
principal. This policy applies to all identities
in all accounts.
A principal with conditions is an IAM principal, where conditions we've set specify when the policy is in effect.
In order to create a principal with conditions in AWS CDK, we have to instantiate the PrincipalWithConditions class.
import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ...rest // ๐ create a role with PrincipalWithConditions const role4 = new iam.Role(this, 'role-4', { assumedBy: new iam.PrincipalWithConditions( new iam.AccountRootPrincipal(), { Bool: { 'aws:MultiFactorAuthPresent': true, 'aws:SecureTransport': true, }, NumericLessThan: { 'aws:MultiFactorAuthAge': 300, }, }, ), }); } }
We instantiated the PrincipalWithConditions
class. The constructor takes 2
parameters:
principal
- the principalconditions
- a map of conditions, under which the policy takes effect. In
our case, the conditions verify that the user authenticated via MFA within the
past 300 seconds.Let's execute a deployment:
npx aws-cdk deploy
After a successful deployment, we can see that the conditions have been applied in the trust policy of the role:
A web identity principal represents a federated identity provider as Web Identity, i.e. Cognito, Facebook, Google, etc.
In order to create a web identity principal in CDK, we have to instantiate the WebIdentityPrincipal class.
// ๐ create a role with WebIdentityPrincipal const role5 = new iam.Role(this, 'role-5', { assumedBy: new iam.WebIdentityPrincipal( 'cognito-identity.amazonaws.com', { StringEquals: { 'cognito-identity.amazonaws.com:aud': identityPool.ref, }, 'ForAnyValue:StringLike': { 'cognito-identity.amazonaws.com:amr': 'authenticated', }, }, ), });
The WebIdentityPrincipal
constructor takes the following parameters:
identityProvider
- the name of the identity provider - in our case -
Cognito.conditions
- conditions, under which the policy takes effect.A federated principal represents a federated identity provider, i.e. Cognito, that can be used to provide temporary security credentials to authenticated users.
In order to create a federated principal in CDK, we have to instantiate the FederatedPrincipal class.
// ๐ create a role with FederatedPrincipal const role6 = new iam.Role(this, 'role-6', { assumedBy: new iam.FederatedPrincipal( 'cognito-identity.amazonaws.com', { StringEquals: { 'cognito-identity.amazonaws.com:aud': identityPool.ref, }, 'ForAnyValue:StringLike': { 'cognito-identity.amazonaws.com:amr': 'authenticated', }, }, 'sts:AssumeRoleWithWebIdentity', ), });
The FederatedPrincipal
constructor takes the following parameters:
federatedIdentityProvider
- the federated identity provider.conditions
- conditions, under which the policy takes effect.assumeRoleAction
- the action to use when the principal is used in an
AssumeRole
policy.An organization principal represents an AWS organization.
In order to specify an organization as a principal, we have to instantiate the OrganizationPrincipal class.
import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ...rest // ๐ create a role with an OrganizationPrincipal const role7 = new iam.Role(this, 'role-7', { assumedBy: new iam.OrganizationPrincipal('o-123asdf'), }); } }
The only parameter the OrganizationPrincipal
constructor takes is the
unique identifier of the organization.
To delete the resources we've provisioned, run the destroy
command.
npx aws-cdk destroy
You can learn more about the related topics by checking out the following tutorials: