Skip to content

Commit

Permalink
feat(codepipeline-actions): add elastic beanstalk deploy action
Browse files Browse the repository at this point in the history
closes #2516
  • Loading branch information
TheRealAmazonKendra committed Sep 20, 2022
1 parent 205e493 commit f8a34b1
Show file tree
Hide file tree
Showing 13 changed files with 1,723 additions and 0 deletions.
22 changes: 22 additions & 0 deletions packages/@aws-cdk/aws-codepipeline-actions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,28 @@ new codepipeline.Pipeline(this, 'Pipeline', {
});
```

### Elastic Beanstalk Deployment

To deploy an Elastic Beanstalk Application in CodePipeline:

```ts
const sourceOutput = new codepipeline.Artifact();
const targetBucket = new s3.Bucket(this, 'MyBucket');

const pipeline = new codepipeline.Pipeline(this, 'MyPipeline');
const deployAction = mew codepipeline_actions.ElasticBeanstalkDeployAction({
actionName: 'ElasticBeanstalkDeploy',
input: sourceOutput,
environmentName: 'envName',
applicationName: 'appName',
});

const deployStage = pipeline.addStage({
stageName: 'Deploy',
actions: [deployAction],
});
```

### Alexa Skill

You can deploy to Alexa using CodePipeline with the following Action:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import { PolicyStatement } from '@aws-cdk/aws-iam';
import { Arn, Stack } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { Action } from '../action';
import { deployArtifactBounds } from '../common';

/**
* Construction properties of the {@link ElasticBeanstalkDeployAction Elastic Beanstalk deploy CodePipeline Action}.
*/
export interface ElasticBeanstalkDeployActionProps extends codepipeline.CommonAwsActionProps {
/**
* The source to use as input for deployment.
*/
readonly input: codepipeline.Artifact;

/**
* The name of the AWS Elastic Beanstalk application to deploy.
*/
readonly applicationName: string;

/**
* The name of the AWS Elastic Beanstalk environment to deploy to.
*/
readonly environmentName: string;
}

/**
* something here
*/
export class ElasticBeanstalkDeployAction extends Action {
private readonly applicationName: string;
private readonly environmentName: string;

constructor(props: ElasticBeanstalkDeployActionProps) {
super({
...props,
category: codepipeline.ActionCategory.DEPLOY,
provider: 'ElasticBeanstalk',
artifactBounds: deployArtifactBounds(),
inputs: [props.input],
});

this.applicationName = props.applicationName;
this.environmentName = props.environmentName;
}

protected bound(
scope: Construct,
_stage: codepipeline.IStage,
options: codepipeline.ActionBindOptions,
): codepipeline.ActionConfig {
const getArn = (resource: string, resourceName?: string): string => {
const fullResourceName = resourceName ? `${this.applicationName.toLowerCase()}/${resourceName}` : `${this.applicationName.toLowerCase()}`;
return Arn.format({
service: 'elasticbeanstalk',
resource,
resourceName: fullResourceName,
}, Stack.of(scope));
};

options.role.addToPrincipalPolicy(new PolicyStatement({
resources: [getArn('application')],
actions: [
'elasticbeanstalk:CreateApplicationVersion',
'elasticbeanstalk:DescribeEvents',
],
}));

options.role.addToPrincipalPolicy(new PolicyStatement({
resources: [getArn('applicationversion', 'code-pipeline-*')],
actions: [
'elasticbeanstalk:CreateApplicationVersion',
'elasticbeanstalk:DescribeApplicationVersions',
'elasticbeanstalk:DescribeEnvironments',
'elasticbeanstalk:DescribeEvents',
'elasticbeanstalk:UpdateEnvironment',
],
}));

options.role.addToPrincipalPolicy(new PolicyStatement({
resources: [getArn('configurationtemplate', '*')],
actions: [
'elasticbeanstalk:DescribeEvents',
'elasticbeanstalk:UpdateEnvironment',
],
}));

options.role.addToPrincipalPolicy(new PolicyStatement({
resources: [getArn('environment', this.environmentName.toLowerCase())],
actions: [
'elasticbeanstalk:DescribeEnvironments',
'elasticbeanstalk:DescribeEvents',
'elasticbeanstalk:UpdateEnvironment',
],
}));

// the Action's Role needs to read from the Bucket to get artifacts
options.bucket.grantRead(options.role);

return {
configuration: {
ApplicationName: this.applicationName,
EnvironmentName: this.environmentName,
},
};
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-codepipeline-actions/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './codedeploy/ecs-deploy-action';
export * from './codedeploy/server-deploy-action';
export * from './ecr/source-action';
export * from './ecs/deploy-action';
export * from './elastic-beanstalk/deploy-action';
export * from './github/source-action';
export * from './jenkins/jenkins-action';
export * from './jenkins/jenkins-provider';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import { Bucket } from '@aws-cdk/aws-s3';
import { App, Stack } from '@aws-cdk/core';
import { ElasticBeanstalkDeployAction, S3SourceAction, S3Trigger } from '../../lib';

describe('elastic beanstalk deploy action tests', () => {
test('region and account are action region and account when set', () => {
const stack = buildPipelineStack();
// eslint-disable-next-line no-console
console.log(stack);
});
});

function buildPipelineStack(): Stack {
const app = new App();
const stack = new Stack(app);
const sourceOutput = new codepipeline.Artifact();
const pipeline = new codepipeline.Pipeline(stack, 'MyPipeline');
pipeline.addStage({
stageName: 'Source',
actions: [
new S3SourceAction({
actionName: 'Source',
bucket: new Bucket(stack, 'MyBucket'),
bucketKey: 'some/path/to',
output: sourceOutput,
trigger: S3Trigger.POLL,
}),
],
});

pipeline.addStage({
stageName: 'Deploy',
actions: [
new ElasticBeanstalkDeployAction({
actionName: 'Deploy',
applicationName: 'testApplication',
environmentName: 'env-testApplication',
input: sourceOutput,
}),
],
});

return stack;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import * as s3 from '@aws-cdk/aws-s3';
import { App, RemovalPolicy, Stack } from '@aws-cdk/core';
import * as integ from '@aws-cdk/integ-tests';
import * as cpactions from '../lib';

const app = new App();

const stack = new Stack(app, 'aws-cdk-codepipeline-elastic-beanstalk-deploy');

const bucket = new s3.Bucket(stack, 'PipelineBucket', {
versioned: true,
removalPolicy: RemovalPolicy.DESTROY,
});

const pipeline = new codepipeline.Pipeline(stack, 'Pipeline', {
artifactBucket: bucket,
});

const sourceOutput = new codepipeline.Artifact('SourceArtifact');
const sourceAction = new cpactions.S3SourceAction({
actionName: 'Source',
output: sourceOutput,
bucket,
bucketKey: 'key',
});

pipeline.addStage({
stageName: 'Source',
actions: [
sourceAction,
],
});


const deployAction = new cpactions.ElasticBeanstalkDeployAction({
actionName: 'Deploy',
input: sourceOutput,
environmentName: 'envName',
applicationName: 'appName',
});

pipeline.addStage({
stageName: 'Deploy',
actions: [
deployAction,
],
});

new integ.IntegTest(app, 'codepipeline-elastic-beanstalk-deploy', {
testCases: [stack],
});

app.synth();
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "21.0.0",
"files": {
"c3b822f0f6f9953e08e4b5b21695b697a6098de14055e74c9c4ade297e2f1b8d": {
"source": {
"path": "aws-cdk-codepipeline-elastic-beanstalk-deploy.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "c3b822f0f6f9953e08e4b5b21695b697a6098de14055e74c9c4ade297e2f1b8d.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Loading

0 comments on commit f8a34b1

Please sign in to comment.