Skip to content

Commit

Permalink
feat: formalize the concept of physical names, and use them for cross…
Browse files Browse the repository at this point in the history
…-environment CodePipelines. (#1924)

Introduces a new type, `PhysicalName`, that represents the physical ID of resources, and adds it to `IResource`.

Adds a `ResourceIdentifiers` class that is supposed to be used inside the Construct Library.

Introduces a concept of a late-bound name, that is automatically generated by the framework if a given resource is used in a cross-environment fashion. Implements it in IAM's Role, CodeBuild's Project, and S3's Bucket.

Adds logic to the CodePipeline construct that automatically generates a Role with a physical name if an Action backed by a resource from a different account is added to it.

BREAKING CHANGE:
* iam: `roleName` in `RoleProps` is now of type `PhysicalName`
* s3: `bucketName` in `BucketProps` is now of type `PhysicalName`
* codebuild: `roleName` in `RoleProps` is now of type `PhysicalName`
  • Loading branch information
skinny85 authored Jun 11, 2019
1 parent 523807c commit 6daaca8
Show file tree
Hide file tree
Showing 35 changed files with 1,266 additions and 278 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ export interface DnsValidatedCertificateProps extends CertificateProps {
/**
* A certificate managed by AWS Certificate Manager. Will be automatically
* validated using DNS validation against the specified Route 53 hosted zone.
*
* @resource AWS::CertificateManager::Certificate
*/
export class DnsValidatedCertificate extends cdk.Construct implements ICertificate {
export class DnsValidatedCertificate extends cdk.Resource implements ICertificate {
public readonly certificateArn: string;
private normalizedZoneName: string;
private hostedZoneId: string;
Expand Down
24 changes: 18 additions & 6 deletions packages/@aws-cdk/aws-codebuild/lib/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ecr = require('@aws-cdk/aws-ecr');
import events = require('@aws-cdk/aws-events');
import iam = require('@aws-cdk/aws-iam');
import kms = require('@aws-cdk/aws-kms');
import { Aws, Construct, IResource, Resource, Stack, Token } from '@aws-cdk/cdk';
import { Aws, Construct, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack, Token } from '@aws-cdk/cdk';
import { BuildArtifacts, CodePipelineBuildArtifacts, NoBuildArtifacts } from './artifacts';
import { Cache } from './cache';
import { CfnProject } from './codebuild.generated';
Expand Down Expand Up @@ -451,7 +451,7 @@ export interface CommonProjectProps {
*
* @default - Name is automatically generated.
*/
readonly projectName?: string;
readonly projectName?: PhysicalName;

/**
* VPC network to place codebuild network interfaces
Expand Down Expand Up @@ -616,13 +616,16 @@ export class Project extends ProjectBase {
private _securityGroups: ec2.ISecurityGroup[] = [];

constructor(scope: Construct, id: string, props: ProjectProps) {
super(scope, id);
super(scope, id, {
physicalName: props.projectName,
});

if (props.buildScriptAssetEntrypoint && !props.buildScriptAsset) {
throw new Error('To use buildScriptAssetEntrypoint, supply buildScriptAsset as well.');
}

this.role = props.role || new iam.Role(this, 'Role', {
roleName: PhysicalName.auto({ crossEnvironment: true }),
assumedBy: new iam.ServicePrincipal('codebuild.amazonaws.com')
});
this.grantPrincipal = this.role;
Expand Down Expand Up @@ -700,16 +703,25 @@ export class Project extends ProjectBase {
encryptionKey: props.encryptionKey && props.encryptionKey.keyArn,
badgeEnabled: props.badge,
cache: cache._toCloudFormation(),
name: props.projectName,
name: this.physicalName.value,
timeoutInMinutes: props.timeout,
secondarySources: new Token(() => this.renderSecondarySources()),
secondaryArtifacts: new Token(() => this.renderSecondaryArtifacts()),
triggers: this.source._buildTriggers(),
vpcConfig: this.configureVpc(props),
});

this.projectArn = resource.projectArn;
this.projectName = resource.projectName;
const resourceIdentifiers = new ResourceIdentifiers(this, {
arn: resource.projectArn,
name: resource.projectName,
arnComponents: {
service: 'codebuild',
resource: 'project',
resourceName: this.physicalName.value,
},
});
this.projectArn = resourceIdentifiers.arn;
this.projectName = resourceIdentifiers.name;

this.addToRolePolicy(this.createLoggingPermission());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export interface LambdaDeploymentGroupAttributes {
readonly deploymentGroupName: string;
}

class ImportedLambdaDeploymentGroup extends cdk.Construct implements ILambdaDeploymentGroup {
class ImportedLambdaDeploymentGroup extends cdk.Resource implements ILambdaDeploymentGroup {
public readonly application: ILambdaApplication;
public readonly deploymentGroupName: string;
public readonly deploymentGroupArn: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class CodeBuildAction extends codepipeline.Action {
artifactBounds: { minInputs: 1, maxInputs: 5, minOutputs: 0, maxOutputs: 5 },
inputs: [props.input, ...props.extraInputs || []],
outputs: getOutputs(props),
resource: props.project,
configuration: {
ProjectName: props.project.projectName,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ function _stackArn(stackName: string, scope: cdk.IConstruct): string {
});
}

class PipelineDouble extends cdk.Construct implements codepipeline.IPipeline {
class PipelineDouble extends cdk.Resource implements codepipeline.IPipeline {
public readonly pipelineName: string;
public readonly pipelineArn: string;
public readonly role: iam.Role;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,6 @@
}
}
},
"ActionRole60B0EDF7": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"AWS": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":iam::",
{
"Ref": "AWS::AccountId"
},
":root"
]
]
}
}
}
],
"Version": "2012-10-17"
}
}
},
"ActionRoleDefaultPolicyCA33BE56": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "sqs:*",
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"PolicyName": "ActionRoleDefaultPolicyCA33BE56",
"Roles": [
{
"Ref": "ActionRole60B0EDF7"
}
]
}
},
"MyPipelineRoleC0D47CA4": {
"Type": "AWS::IAM::Role",
"Properties": {
Expand Down Expand Up @@ -157,54 +104,14 @@
]
},
{
"Action": "iam:PassRole",
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"MyPipelineCFNCFNDeployRole9CC99B3F",
"ActionRole60B0EDF7",
"Arn"
]
}
},
{
"Action": [
"cloudformation:CreateStack",
"cloudformation:DescribeStack*",
"cloudformation:GetStackPolicy",
"cloudformation:GetTemplate*",
"cloudformation:SetStackPolicy",
"cloudformation:UpdateStack",
"cloudformation:ValidateTemplate"
],
"Effect": "Allow",
"Resource": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":cloudformation:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":stack/aws-cdk-codepipeline-cross-region-deploy-stack/*"
]
]
}
},
{
"Action": [
"sts:AssumeRole",
"iam:PassRole"
],
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
Expand Down Expand Up @@ -305,6 +212,101 @@
"MyPipelineRoleC0D47CA4"
]
},
"ActionRole60B0EDF7": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"AWS": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":iam::",
{
"Ref": "AWS::AccountId"
},
":root"
]
]
}
}
}
],
"Version": "2012-10-17"
}
}
},
"ActionRoleDefaultPolicyCA33BE56": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "sqs:*",
"Effect": "Allow",
"Resource": "*"
},
{
"Action": "iam:PassRole",
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"MyPipelineCFNCFNDeployRole9CC99B3F",
"Arn"
]
}
},
{
"Action": [
"cloudformation:CreateStack",
"cloudformation:DescribeStack*",
"cloudformation:GetStackPolicy",
"cloudformation:GetTemplate*",
"cloudformation:SetStackPolicy",
"cloudformation:UpdateStack",
"cloudformation:ValidateTemplate"
],
"Effect": "Allow",
"Resource": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":cloudformation:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":stack/aws-cdk-codepipeline-cross-region-deploy-stack/*"
]
]
}
}
],
"Version": "2012-10-17"
},
"PolicyName": "ActionRoleDefaultPolicyCA33BE56",
"Roles": [
{
"Ref": "ActionRole60B0EDF7"
}
]
}
},
"MyPipelineCFNCFNDeployRole9CC99B3F": {
"Type": "AWS::IAM::Role",
"Properties": {
Expand Down Expand Up @@ -333,4 +335,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,12 @@ const cfnStage = {
],
};

const pipeline = new codepipeline.Pipeline(stack, 'MyPipeline', {
new codepipeline.Pipeline(stack, 'MyPipeline', {
artifactBucket: bucket,
stages: [
sourceStage,
cfnStage,
],
});
pipeline.addToRolePolicy(new iam.PolicyStatement()
.addActions("sts:AssumeRole", "iam:PassRole")
.addAllResources()
);

app.synth();
Loading

0 comments on commit 6daaca8

Please sign in to comment.