Skip to content

Commit

Permalink
refactor(iam): cleanup of IAM library (#2823)
Browse files Browse the repository at this point in the history
Various changes.

* Move PolicyStatement to its own file.
* Remove `postProcess` from Token interface, move opting into it into `IResolvable.resolve()`, so it can be hidden from the `PolicyDocument` public interface.
* Introduce `PolicyStatement.fromAttributes({ ... })` as an alternative to the fluent interface chaining of old `PolicyStatement`.

Plus all the breaking changes below:

BREAKING CHANGE:

* **iam**: `PolicyStatement` no longer has a fluid API, and accepts a
  props object to be able to set the important fields.
* **iam**: rename `ImportedResourcePrincipal` to `UnknownPrincipal`.
* **iam**: `managedPolicyArns` renamed to `managedPolicies`, takes
  return value from `ManagedPolicy.fromAwsManagedPolicyName()`.
* **iam**: `PolicyDocument.postProcess()` is now removed.
* **iam**: `PolicyDocument.addStatement()` renamed to `addStatements`.
* **iam**: `PolicyStatement` is no longer `IResolvable`, call `.toStatementJson()`
  to retrieve the IAM policy statement JSON.
* **iam**:  `AwsPrincipal` has been removed, use `ArnPrincipal` instead.
  • Loading branch information
rix0rrr authored Jun 17, 2019
1 parent 210dd0f commit b735d1c
Show file tree
Hide file tree
Showing 106 changed files with 1,259 additions and 1,168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -222,17 +222,18 @@ export = nodeunit.testCase({
adminPermissions: false,
});
// we might need to add permissions
deployAction.addToDeploymentRolePolicy( new iam.PolicyStatement().
addActions(
deployAction.addToDeploymentRolePolicy(new iam.PolicyStatement({
actions: [
'ec2:AuthorizeSecurityGroupEgress',
'ec2:AuthorizeSecurityGroupIngress',
'ec2:DeleteSecurityGroup',
'ec2:DescribeSecurityGroups',
'ec2:CreateSecurityGroup',
'ec2:RevokeSecurityGroupEgress',
'ec2:RevokeSecurityGroupIngress'
).
addAllResources());
],
resources: ['*']
}));

// THEN //
// there should be 3 policies 1. CodePipeline, 2. Codebuild, 3.
Expand Down
11 changes: 6 additions & 5 deletions packages/@aws-cdk/assets-docker/lib/adopted-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,16 @@ export class AdoptedRepository extends ecr.RepositoryBase {
timeout: 300
});

fn.addToRolePolicy(new iam.PolicyStatement()
.addResource(ecr.Repository.arnForLocalRepository(props.repositoryName, this))
.addActions(
fn.addToRolePolicy(new iam.PolicyStatement({
resources: [ecr.Repository.arnForLocalRepository(props.repositoryName, this)],
actions: [
'ecr:GetRepositoryPolicy',
'ecr:SetRepositoryPolicy',
'ecr:DeleteRepository',
'ecr:ListImages',
'ecr:BatchDeleteImage'
));
],
}));

const adopter = new cfn.CustomResource(this, 'Resource', {
resourceType: 'Custom::ECRAdoptedRepository',
Expand Down Expand Up @@ -88,6 +89,6 @@ export class AdoptedRepository extends ecr.RepositoryBase {
* use the custom resource to modify the ECR resource policy if needed.
*/
public addToResourcePolicy(statement: iam.PolicyStatement) {
this.policyDocument.addStatement(statement);
this.policyDocument.addStatements(statement);
}
}
7 changes: 4 additions & 3 deletions packages/@aws-cdk/assets-docker/test/test.image-asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ export = {
});

// WHEN
asset.repository.addToResourcePolicy(new iam.PolicyStatement()
.addAction('BOOM')
.addPrincipal(new iam.ServicePrincipal('test.service')));
asset.repository.addToResourcePolicy(new iam.PolicyStatement({
actions: ['BOOM'],
principals: [new iam.ServicePrincipal('test.service')]
}));

// THEN
expect(stack).to(haveResource('Custom::ECRAdoptedRepository', {
Expand Down
9 changes: 1 addition & 8 deletions packages/@aws-cdk/aws-apigateway/lib/restapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,14 +343,7 @@ export class RestApi extends Resource implements IRestApi {
private configureCloudWatchRole(apiResource: CfnRestApi) {
const role = new iam.Role(this, 'CloudWatchRole', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'),
managedPolicyArns: [ Stack.of(this).formatArn({
service: 'iam',
region: '',
account: 'aws',
resource: 'policy',
sep: '/',
resourceName: 'service-role/AmazonAPIGatewayPushToCloudWatchLogs'
}) ]
managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonAPIGatewayPushToCloudWatchLogs')],
});

const resource = new CfnAccount(this, 'Account', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,10 @@ export = {
vpc
});

fleet.addToRolePolicy(new iam.PolicyStatement()
.addAction('test:SpecialName')
.addAllResources());
fleet.addToRolePolicy(new iam.PolicyStatement({
actions: ['test:SpecialName'],
resources: ['*']
}));

expect(stack).to(haveResource('AWS::IAM::Policy', {
PolicyDocument: {
Expand Down
7 changes: 4 additions & 3 deletions packages/@aws-cdk/aws-autoscaling/test/test.lifecyclehooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ export = {

class FakeNotificationTarget implements autoscaling.ILifecycleHookTarget {
public bind(_scope: Construct, lifecycleHook: autoscaling.ILifecycleHook): autoscaling.LifecycleHookTargetConfig {
lifecycleHook.role.addToPolicy(new iam.PolicyStatement()
.addAction('action:Work')
.addAllResources());
lifecycleHook.role.addToPolicy(new iam.PolicyStatement({
actions: ['action:Work'],
resources: ['*']
}));
return { notificationTargetArn: 'target:arn', };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,18 @@ export class DnsValidatedCertificate extends cdk.Resource implements ICertificat
runtime: lambda.Runtime.Nodejs810,
timeout: 15 * 60 // 15 minutes
});
requestorFunction.addToRolePolicy(
new iam.PolicyStatement()
.addActions('acm:RequestCertificate', 'acm:DescribeCertificate', 'acm:DeleteCertificate')
.addResource('*')
);
requestorFunction.addToRolePolicy(
new iam.PolicyStatement()
.addActions('route53:GetChange')
.addResource('*')
);
requestorFunction.addToRolePolicy(
new iam.PolicyStatement()
.addActions('route53:changeResourceRecordSets')
.addResource(`arn:aws:route53:::hostedzone/${this.hostedZoneId}`)
);
requestorFunction.addToRolePolicy(new iam.PolicyStatement({
actions: ['acm:RequestCertificate', 'acm:DescribeCertificate', 'acm:DeleteCertificate'],
resources: ['*'],
}));
requestorFunction.addToRolePolicy(new iam.PolicyStatement({
actions: ['route53:GetChange'],
resources: ['*'],
}));
requestorFunction.addToRolePolicy(new iam.PolicyStatement({
actions: ['route53:changeResourceRecordSets'],
resources: [`arn:aws:route53:::hostedzone/${this.hostedZoneId}`],
}));

const certificate = new cfn.CustomResource(this, 'CertificateRequestorResource', {
provider: cfn.CustomResourceProvider.lambda(requestorFunction),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,10 @@ export class AwsCustomResource extends cdk.Construct {
} else { // Derive statements from AWS SDK calls
for (const call of [props.onCreate, props.onUpdate, props.onDelete]) {
if (call) {
provider.addToRolePolicy(
new iam.PolicyStatement()
.addAction(awsSdkToIamAction(call.service, call.action))
.addAllResources()
);
provider.addToRolePolicy(new iam.PolicyStatement({
actions: [awsSdkToIamAction(call.service, call.action)],
resources: ['*']
}));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,10 @@ export = {
physicalResourceIdPath: 'ETag'
},
policyStatements: [
new iam.PolicyStatement()
.addAction('s3:PutObject')
.addResource('arn:aws:s3:::my-bucket/my-key')
new iam.PolicyStatement({
actions: ['s3:PutObject'],
resources: ['arn:aws:s3:::my-bucket/my-key']
})
]
});

Expand Down
10 changes: 5 additions & 5 deletions packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ const dist = new cloudfront.CloudFrontWebDistribution(stack, 'Distribution', {
},
}]
});
bucket.addToResourcePolicy(new iam.PolicyStatement()
.allow()
.addActions('s3:Get*', 's3:List*')
.addResources(bucket.bucketArn, bucket.arnForObjects('*'))
.addCanonicalUserPrincipal(oai.cloudFrontOriginAccessIdentityS3CanonicalUserId));
bucket.addToResourcePolicy(new iam.PolicyStatement({
actions: ['s3:Get*', 's3:List*'],
resources: [bucket.bucketArn, bucket.arnForObjects('*')],
principals: [new iam.CanonicalUserPrincipal(oai.cloudFrontOriginAccessIdentityS3CanonicalUserId)]
}));

new cdk.CfnOutput(stack, 'DistributionDomainName', { value: dist.domainName });
37 changes: 21 additions & 16 deletions packages/@aws-cdk/aws-cloudtrail/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,22 @@ export class Trail extends Resource {
super(scope, id);

const s3bucket = new s3.Bucket(this, 'S3', {encryption: s3.BucketEncryption.Unencrypted});
const cloudTrailPrincipal = "cloudtrail.amazonaws.com";

s3bucket.addToResourcePolicy(new iam.PolicyStatement()
.addResource(s3bucket.bucketArn)
.addActions('s3:GetBucketAcl')
.addServicePrincipal(cloudTrailPrincipal));

s3bucket.addToResourcePolicy(new iam.PolicyStatement()
.addResource(s3bucket.arnForObjects(`AWSLogs/${Stack.of(this).account}/*`))
.addActions("s3:PutObject")
.addServicePrincipal(cloudTrailPrincipal)
.setCondition("StringEquals", {'s3:x-amz-acl': "bucket-owner-full-control"}));
const cloudTrailPrincipal = new iam.ServicePrincipal("cloudtrail.amazonaws.com");

s3bucket.addToResourcePolicy(new iam.PolicyStatement({
resources: [s3bucket.bucketArn],
actions: ['s3:GetBucketAcl'],
principals: [cloudTrailPrincipal],
}));

s3bucket.addToResourcePolicy(new iam.PolicyStatement({
resources: [s3bucket.arnForObjects(`AWSLogs/${Stack.of(this).account}/*`)],
actions: ["s3:PutObject"],
principals: [cloudTrailPrincipal],
conditions: {
StringEquals: {'s3:x-amz-acl': "bucket-owner-full-control"}
}
}));

let logGroup: logs.CfnLogGroup | undefined;
let logsRole: iam.IRole | undefined;
Expand All @@ -148,11 +152,12 @@ export class Trail extends Resource {
retentionInDays: props.cloudWatchLogsRetentionTimeDays || logs.RetentionDays.OneYear
});

logsRole = new iam.Role(this, 'LogsRole', { assumedBy: new iam.ServicePrincipal(cloudTrailPrincipal) });
logsRole = new iam.Role(this, 'LogsRole', { assumedBy: cloudTrailPrincipal });

logsRole.addToPolicy(new iam.PolicyStatement()
.addActions("logs:PutLogEvents", "logs:CreateLogStream")
.addResource(logGroup.logGroupArn));
logsRole.addToPolicy(new iam.PolicyStatement({
actions: ["logs:PutLogEvents", "logs:CreateLogStream"],
resources: [logGroup.logGroupArn],
}));
}
if (props.managementEvents) {
const managementEvent = {
Expand Down
71 changes: 32 additions & 39 deletions packages/@aws-cdk/aws-codebuild/lib/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ export class Project extends ProjectBase {

constructor(s: Construct, i: string) {
super(s, i);
this.grantPrincipal = new iam.ImportedResourcePrincipal({ resource: this });
this.grantPrincipal = new iam.UnknownPrincipal({ resource: this });
}
}

Expand Down Expand Up @@ -585,7 +585,7 @@ export class Project extends ProjectBase {
resourceName: projectName,
});

this.grantPrincipal = new iam.ImportedResourcePrincipal({ resource: this });
this.grantPrincipal = new iam.UnknownPrincipal({ resource: this });
this.projectName = projectName;
}
}
Expand Down Expand Up @@ -808,15 +808,10 @@ export class Project extends ProjectBase {

const logGroupStarArn = `${logGroupArn}:*`;

const p = new iam.PolicyStatement();
p.allow();
p.addResource(logGroupArn);
p.addResource(logGroupStarArn);
p.addAction('logs:CreateLogGroup');
p.addAction('logs:CreateLogStream');
p.addAction('logs:PutLogEvents');

return p;
return new iam.PolicyStatement({
resources: [logGroupArn, logGroupStarArn],
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
});
}

private renderEnvironment(env: BuildEnvironment = {},
Expand Down Expand Up @@ -893,26 +888,26 @@ export class Project extends ProjectBase {
});
this._securityGroups = [securityGroup];
}
this.addToRoleInlinePolicy(new iam.PolicyStatement()
.addAllResources()
.addActions(
'ec2:CreateNetworkInterface',
'ec2:DescribeNetworkInterfaces',
'ec2:DeleteNetworkInterface',
'ec2:DescribeSubnets',
'ec2:DescribeSecurityGroups',
'ec2:DescribeDhcpOptions',
'ec2:DescribeVpcs'
));
this.addToRolePolicy(new iam.PolicyStatement()
.addResource(`arn:aws:ec2:${Aws.region}:${Aws.accountId}:network-interface/*`)
.addCondition('StringEquals', {
"ec2:Subnet": props.vpc
.selectSubnets(props.subnetSelection).subnetIds
.map(si => `arn:aws:ec2:${Aws.region}:${Aws.accountId}:subnet/${si}`),
"ec2:AuthorizedService": "codebuild.amazonaws.com"
})
.addAction('ec2:CreateNetworkInterfacePermission'));
this.addToRoleInlinePolicy(new iam.PolicyStatement({
resources: ['*'],
actions: [
'ec2:CreateNetworkInterface', 'ec2:DescribeNetworkInterfaces',
'ec2:DeleteNetworkInterface', 'ec2:DescribeSubnets',
'ec2:DescribeSecurityGroups', 'ec2:DescribeDhcpOptions',
'ec2:DescribeVpcs']
}));
this.addToRolePolicy(new iam.PolicyStatement({
resources: [`arn:aws:ec2:${Aws.region}:${Aws.accountId}:network-interface/*`],
actions: ['ec2:CreateNetworkInterfacePermission'],
conditions: {
StringEquals: {
"ec2:Subnet": props.vpc
.selectSubnets(props.subnetSelection).subnetIds
.map(si => `arn:aws:ec2:${Aws.region}:${Aws.accountId}:subnet/${si}`),
"ec2:AuthorizedService": "codebuild.amazonaws.com"
}
}
}));
return {
vpcId: props.vpc.vpcId,
subnets: props.vpc.selectSubnets(props.subnetSelection).subnetIds,
Expand Down Expand Up @@ -1267,12 +1262,10 @@ export enum BuildEnvironmentVariableType {
}

function ecrAccessForCodeBuildService(): iam.PolicyStatement {
return new iam.PolicyStatement()
.describe('CodeBuild')
.addServicePrincipal('codebuild.amazonaws.com')
.addActions(
'ecr:GetDownloadUrlForLayer',
'ecr:BatchGetImage',
'ecr:BatchCheckLayerAvailability'
);
const s = new iam.PolicyStatement({
principals: [new iam.ServicePrincipal('codebuild.amazonaws.com')],
actions: ['ecr:GetDownloadUrlForLayer', 'ecr:BatchGetImage', 'ecr:BatchCheckLayerAvailability'],
});
s.sid = 'CodeBuild';
return s;
}
7 changes: 4 additions & 3 deletions packages/@aws-cdk/aws-codebuild/lib/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,9 +466,10 @@ export class CodeCommitSource extends GitBuildSource {
*/
public _bind(project: Project) {
// https://docs.aws.amazon.com/codebuild/latest/userguide/setting-up.html
project.addToRolePolicy(new iam.PolicyStatement()
.addAction('codecommit:GitPull')
.addResource(this.repo.repositoryArn));
project.addToRolePolicy(new iam.PolicyStatement({
actions: ['codecommit:GitPull'],
resources: [this.repo.repositoryArn]
}));
}

protected toSourceProperty(): any {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export class LambdaDeploymentGroup extends cdk.Resource implements ILambdaDeploy
assumedBy: new iam.ServicePrincipal('codedeploy.amazonaws.com')
});

this.role.attachManagedPolicy('arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda');
this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSCodeDeployRoleForLambda'));

const resource = new CfnDeploymentGroup(this, 'Resource', {
applicationName: this.application.applicationName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase {

this.role = props.role || new iam.Role(this, 'Role', {
assumedBy: new iam.ServicePrincipal('codedeploy.amazonaws.com'),
managedPolicyArns: ['arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole'],
managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSCodeDeployRole')],
});

this._autoScalingGroups = props.autoScalingGroups || [];
Expand Down
Loading

0 comments on commit b735d1c

Please sign in to comment.