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 committed 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.
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
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
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
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
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
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 @@
}
}
}
}
}
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();

0 comments on commit 6daaca8

Please sign in to comment.