-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(iam): customize IAM role creation behavior (#22856)
Introduces a new static method `Role.customizeRoles` which allows customization of role creation behavior. By default this will prevent all IAM roles and policies from being created (they will not be synthesized in the template) and will generate a policy report (both human and machine readable versions) in the cloud assembly (i.e. cdk.out) with information on the roles that need to be created and their associated IAM policies. If `customizeRoles.preventSynthesis=true` then this will - prevent IAM roles (AWS::IAM::Role) from being created - prevent managed policies (AWS::IAM::ManagedPolicy) from being created - prevent IAM policies (AWS::IAM::Policy) from being created - Managed policies that are created and _not_ attached to a role will not be synthesized _and_ will not be added to the report. Errors are added using `Annotations.addError` so that the app will still synthesize to the `cdk.out` directory and the policy report will be generated. The policy report can be generated _without_ preventing resource synthesis by setting `customizeRoles.preventSynthesis=false`. I added an integration test `aws-lambda/test/integ.lambda-customize-roles.ts` that will only synthesize the snapshot. You can view the generated reports in the snapshot. refactored: - Moved the `Import` class that was created under the `fromRoleArn` to a separate private class (imported-role.ts). The implementation is the same (just moved). This also adds support for the role created as part of custom resources in `core`. closes #22749, closes #22862 ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
- Loading branch information
Showing
25 changed files
with
2,305 additions
and
463 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { Resource, Token, TokenComparison, Annotations } from '@aws-cdk/core'; | ||
import { Construct } from 'constructs'; | ||
import { Grant } from '../grant'; | ||
import { IManagedPolicy } from '../managed-policy'; | ||
import { Policy } from '../policy'; | ||
import { PolicyStatement } from '../policy-statement'; | ||
import { IComparablePrincipal, IPrincipal, ArnPrincipal, AddToPrincipalPolicyResult, PrincipalPolicyFragment } from '../principals'; | ||
import { IRole, FromRoleArnOptions } from '../role'; | ||
import { AttachedPolicies } from '../util'; | ||
|
||
export interface ImportedRoleProps extends FromRoleArnOptions { | ||
readonly roleArn: string; | ||
readonly roleName: string; | ||
readonly account?: string; | ||
} | ||
|
||
export class ImportedRole extends Resource implements IRole, IComparablePrincipal { | ||
public readonly grantPrincipal: IPrincipal = this; | ||
public readonly principalAccount?: string; | ||
public readonly assumeRoleAction: string = 'sts:AssumeRole'; | ||
public readonly policyFragment: PrincipalPolicyFragment; | ||
public readonly roleArn: string; | ||
public readonly roleName: string; | ||
private readonly attachedPolicies = new AttachedPolicies(); | ||
private readonly defaultPolicyName?: string; | ||
private defaultPolicy?: Policy; | ||
|
||
constructor(scope: Construct, id: string, props: ImportedRoleProps) { | ||
super(scope, id, { | ||
account: props.account, | ||
}); | ||
|
||
this.roleArn = props.roleArn; | ||
this.roleName = props.roleName; | ||
this.policyFragment = new ArnPrincipal(this.roleArn).policyFragment; | ||
this.defaultPolicyName = props.defaultPolicyName; | ||
this.principalAccount = props.account; | ||
} | ||
|
||
public addToPolicy(statement: PolicyStatement): boolean { | ||
return this.addToPrincipalPolicy(statement).statementAdded; | ||
} | ||
|
||
public addToPrincipalPolicy(statement: PolicyStatement): AddToPrincipalPolicyResult { | ||
if (!this.defaultPolicy) { | ||
this.defaultPolicy = new Policy(this, this.defaultPolicyName ?? 'Policy'); | ||
this.attachInlinePolicy(this.defaultPolicy); | ||
} | ||
this.defaultPolicy.addStatements(statement); | ||
return { statementAdded: true, policyDependable: this.defaultPolicy }; | ||
} | ||
|
||
public attachInlinePolicy(policy: Policy): void { | ||
const thisAndPolicyAccountComparison = Token.compareStrings(this.env.account, policy.env.account); | ||
const equalOrAnyUnresolved = thisAndPolicyAccountComparison === TokenComparison.SAME || | ||
thisAndPolicyAccountComparison === TokenComparison.BOTH_UNRESOLVED || | ||
thisAndPolicyAccountComparison === TokenComparison.ONE_UNRESOLVED; | ||
if (equalOrAnyUnresolved) { | ||
this.attachedPolicies.attach(policy); | ||
policy.attachToRole(this); | ||
} | ||
} | ||
|
||
public addManagedPolicy(policy: IManagedPolicy): void { | ||
Annotations.of(this).addWarning(`Not adding managed policy: ${policy.managedPolicyArn} to imported role: ${this.roleName}`); | ||
} | ||
|
||
public grantPassRole(identity: IPrincipal): Grant { | ||
return this.grant(identity, 'iam:PassRole'); | ||
} | ||
|
||
public grantAssumeRole(identity: IPrincipal): Grant { | ||
return this.grant(identity, 'sts:AssumeRole'); | ||
} | ||
|
||
public grant(grantee: IPrincipal, ...actions: string[]): Grant { | ||
return Grant.addToPrincipal({ | ||
grantee, | ||
actions, | ||
resourceArns: [this.roleArn], | ||
scope: this, | ||
}); | ||
} | ||
|
||
public dedupeString(): string | undefined { | ||
return `ImportedRole:${this.roleArn}`; | ||
} | ||
} |
Oops, something went wrong.