Aws-cdk: Add Elastic Beanstalk deploy action for CodePipeline

Created on 10 May 2019  路  19Comments  路  Source: aws/aws-cdk

A new software.amazon.awscdk.services.codepipeline.Action for deploys to Elastic Beanstalk. Requires:

  • Input Artifact
  • Application Name
  • Environment Name
@aws-cdaws-codepipeline efformedium feature-request p2

All 19 comments

Thanks for bringing this to our attention @kianris . We're not planning to work on Elastic BeanStalk in the very near future, but it's good to have this on our backlog.

Thanks,
Adam

+1 for this feature. Is there any workaround? How to achieve Elastic Beanstalk as deployment provider using CDK?

To unblock yourself, you can create your own implementation of IAction in the meantime:

import codepipeline = require('@aws-cdk/aws-codepipeline');
import events = require('@aws-cdk/aws-events');
import { Construct } from '@aws-cdk/core';

export interface ElasticBeanStalkDeployActionProps extends codepipeline.CommonAwsActionProps {
    applicationName: string;

    environmentName: string;
}

export class ElasticBeanStalkDeployAction implements codepipeline.IAction {
    public readonly actionProperties: codepipeline.ActionProperties;
    private readonly props: ElasticBeanStalkDeployActionProps;

    constructor(props: ElasticBeanStalkDeployActionProps) {
        this.actionProperties = {
            ...props,
            provider: 'ElasticBeanstalk',
            category: codepipeline.ActionCategory.DEPLOY,
            artifactBounds: { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 },
        };
        this.props = props;
    }

    public bind(_scope: Construct, _stage: codepipeline.IStage, _options: codepipeline.ActionBindOptions):
            codepipeline.ActionConfig {
        return {
            configuration: {
                ApplicationName: this.props.applicationName,
                EnvironmentName: this.props.environmentName,
            },
        };
    }

    public onStateChange(_name: string, _target?: events.IRuleTarget, _options?: events.RuleProps): events.Rule {
        throw new Error('unsupported');
    }
}

EDIT: actually, since the Action has an input, this needs to be something like:

import codepipeline = require('@aws-cdk/aws-codepipeline');
import events = require('@aws-cdk/aws-events');
import { Construct } from '@aws-cdk/core';

export interface ElasticBeanStalkDeployActionProps extends codepipeline.CommonAwsActionProps {
    applicationName: string;

    environmentName: string;

    input: codepipeline.Artifact;
}

export class ElasticBeanStalkDeployAction implements codepipeline.IAction {
    public readonly actionProperties: codepipeline.ActionProperties;
    private readonly props: ElasticBeanStalkDeployActionProps;

    constructor(props: ElasticBeanStalkDeployActionProps) {
        this.actionProperties = {
            ...props,
            provider: 'ElasticBeanstalk',
            category: codepipeline.ActionCategory.DEPLOY,
            artifactBounds: { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 },
            inputs: [props.input],
        };
        this.props = props;
    }

    public bind(_scope: Construct, _stage: codepipeline.IStage, options: codepipeline.ActionBindOptions):
            codepipeline.ActionConfig {
        options.bucket.grantRead(options.role);

        return {
            configuration: {
                ApplicationName: this.props.applicationName,
                EnvironmentName: this.props.environmentName,
            },
        };
    }

    public onStateChange(_name: string, _target?: events.IRuleTarget, _options?: events.RuleProps): events.Rule {
        throw new Error('unsupported');
    }
}

I don't see an option to vote, so +1 for this feature.

after adding own implementation of IAction. I am getting below error in deploy step of my pipeline

Insufficient permissions
The provided role does not have the elasticbeanstalk:CreateApplicationVersion permission

I am trying to add new role with assume role but my aws account user is not authorized to assume role.
Is there any other way I can achieve this?

after adding own implementation of IAction. I am getting below error in deploy step of my pipeline

Insufficient permissions
The provided role does not have the elasticbeanstalk:CreateApplicationVersion permission

I am trying to add new role with assume role but my aws account user is not authorized to assume role.
Is there any other way I can achieve this?

What you're probably missing is, in your bind method, some code like:

  public bind(scope: Construct, stage: codepipeline.IStage, options: codepipeline.ActionBindOptions):
      codepipeline.ActionConfig {
    options.role.addToPolicy(new iam.PolicyStatement({
      resources: ['*'],
      actions: ['elasticbeanstalk:CreateApplicationVersion'],
    }));
  }

, similarly to what happens in different actions, example: https://github.com/aws/aws-cdk/blob/b90905d8da3c6fa6fd369f84de4fb2645b948c87/packages/%40aws-cdk/aws-codepipeline-actions/lib/codedeploy/server-deploy-action.ts#L44-L47

Thanks @skinny85 this helps me resolve access issue.

Yeah. Adding permission solved my problem as well. I needed to add these premission as codepipeline deploy stage needed it to deploy to Elastic Beanstalk.

This worked for me.

options.role.addToPolicy(new iam.PolicyStatement({
            resources: ['*'],
            actions: [
                'elasticbeanstalk:*',
                'autoscaling:*',
                'elasticloadbalancing:*',
                'rds:*',
                's3:*',
                'cloudwatch:*',
                'cloudformation:*'
            ],
          }));

I tried implement in .net, but doesn't work.

Unhandled exception. Amazon.JSII.Runtime.JsiiException: Validation failed with the following errors:
  [CdkCicdStack/cdkcicdstackPipeline] Stage 'Deploy' must have at least one action
using Amazon.CDK;
using Amazon.CDK.AWS.CodePipeline;
using Amazon.CDK.AWS.Events;
using Amazon.CDK.AWS.IAM;

namespace CdkCicd
{
    public class ElasticBeanStalkDeployAction : IAction
    {
        public IActionProperties ActionProperties { get; set; }
        private readonly ElasticBeanStalkDeployActionProps Props;

        public ElasticBeanStalkDeployAction(ElasticBeanStalkDeployActionProps props)
        {
            this.ActionProperties = new ActionProperties()
            {
                Provider = "ElasticBeanstalk",
                Category = ActionCategory.DEPLOY,
                ArtifactBounds = new ActionArtifactBounds()
                {
                    MinInputs = 1,
                    MaxInputs = 1,
                    MinOutputs = 0,
                    MaxOutputs = 0
                },
                Inputs = new[] { props.Input }
            };

            this.Props = props;
        }

        public IActionConfig Bind(Construct scope, IStage stage, IActionBindOptions options)
        {
            options.Bucket.GrantRead(options.Role);

            options.Role.AddToPrincipalPolicy(new PolicyStatement(new PolicyStatementProps() 
            {
                Resources = new [] { "*" },
                Actions = new[] 
                { 
                    "elasticbeanstalk:*",
                    "autoscaling:*",
                    "elasticloadbalancing:*",
                    "rds:*",
                    "s3:*",
                    "cloudwatch:*",
                    "cloudformation:*"
                },
            }));

            return new ActionConfig()
            {
                Configuration = new
                {
                    ApplicationName = this.Props.ApplicationName,
                    EnvironmentName = this.Props.EnvironmentName,
                }
            };
        }

        public Rule OnStateChange(string name, IRuleTarget target = null, IRuleProps options = null)
        {
            throw new System.NotImplementedException();
        }
    }
}


using Amazon.CDK.AWS.CodePipeline;

namespace CdkCicd
{
    public class ElasticBeanStalkDeployActionProps : CommonAwsActionProps
    {
        public string ApplicationName { get; set; }
        public string EnvironmentName { get; set; }
        public Artifact_ Input { get; set; }
    }
}



var pipeline = new Pipeline(this, "cdkcicdstackPipeline", new PipelineProps()
            {
                PipelineName = "cdkcicdstack",
                Stages = new[]
                {
                    new Amazon.CDK.AWS.CodePipeline.StageProps()
                    {
                        StageName = "Source",
                        Actions = new []
                        {
                            new S3SourceAction(new S3SourceActionProps() 
                            {
                                Bucket = bucket,
                                BucketKey = "Teste.zip",
                                Output = new Artifact_("SourceArtifact"),
                                ActionName = "Source"
                            })
                        }
                    },
                    new Amazon.CDK.AWS.CodePipeline.StageProps()
                    {
                        StageName = "Deploy",
                        Actions = new []
                        {
                            new ElasticBeanStalkDeployAction(new ElasticBeanStalkDeployActionProps()
                            {
                                Input = new Artifact_("SourceArtifact"),
                                ActionName = "Deploy",
                                ApplicationName = "AuthService",
                                EnvironmentName = "AuthService-Testing2"
                            })
                        }
                    },
                },
            });


Also would like to see a .net implementation of this workaround if anyone can translate the typescript example to C#

@nsquires413 I try translate, but doesn't work

Yeah I get the same

require this feature +1

export class ElasticBeanStalkDeployAction implements codepipeline.IAction {
    public readonly actionProperties: codepipeline.ActionProperties;
    private readonly props:

i added also the role prop to actionProperties,

export interface ElasticBeanstalkDeployActionProps {
    ebsApplicationName: string;
    ebsEnvironmentName: string;
    input: Artifact;
    role?: IRole;
}

then you can pass in the role from the pipeline itself

pipeline.addStage({
            stageName: 'Deploy',
            actions: [
                new ElasticBeanstalkDeployAction({
                    ebsEnvironmentName: elasticBeanstalk.environment.environmentName!!,
                    ebsApplicationName: elasticBeanstalk.application.applicationName!!,
                    input: buildOutput,
                    role: pipeline.role,
                }),
            ],
        });

while adding the AWSElasticBeanstalkFullAccess role to the pipeline

pipeline.role.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('AWSElasticBeanstalkFullAccess'));

So you dont need to do this @Jaimin-Patel30

options.role.addToPolicy(new iam.PolicyStatement({
            resources: ['*'],
            actions: [
                'elasticbeanstalk:*',
                'autoscaling:*',
                'elasticloadbalancing:*',
                'rds:*',
                's3:*',
                'cloudwatch:*',
                'cloudformation:*'
            ],
          }));

and it will not create another policy in IAM.

nothing too big but might help someone, i initially thought the pipline role would be injected in the bind function, but this is not the case. if you dont provide a role in the actionProperties it will create a new one, so you have to add the policy again in the bind function

I also was trying to implement this in python but didn't have luck. Do you guys have an estimate for when the Elastic Beanstalk Deploy Action might be available?

Not really @rangat , sorry.

What was the problem that you encountered?

Thanks for bringing this to our attention @kianris . We're not planning to work on Elastic BeanStalk in the very near future, but it's good to have this on our backlog.

Thanks,
Adam
@skinny85 what is the reason not to provide the implementation? It was more than one year this question is open, people are interested in it and we have AWS examples integrating codepipeline with beanstalk.

@kafka399 the main problem is that we don't have an L2 Construct Library for BeanStalk, which makes it challenging to support a good CodePipeline Action abstraction for it.

I'd be glad to help someone with guidance on the matter, if they wanted to submit us a PR adding this feature.

Here is a working implementation in Python, thanks @RomainMuller for the support!

from aws_cdk import (
    aws_codepipeline as codepipeline,
    core
)
from  aws_cdk.aws_codepipeline import IAction
import jsii

class ElasticBeanStalkDeployActionProps(codepipeline.CommonAwsActionProps):

    @property
    def environment_name(self):
        return self._environment_name

    @property
    def application_name(self):
        return self._application_name

    @property
    def role(self):
        return self._role

    def __init__(self,  *, environment_name, application_name, role) -> None:
        self._environment_name=environment_name
        self._application_name=application_name
        self._role=role


@jsii.implements(IAction)
class ElasticBeanStalkDeployAction():

    @property
    def props(self) -> ElasticBeanStalkDeployActionProps:
        return self._props

    @property
    def action_properties(self) -> codepipeline.ActionProperties:
        return self._action_properties

    @action_properties.setter
    def action_properties(self, value):
        print("setter of x called")
        self._action_properties = value

    def __init__(self, *args, **kwargs) -> None:
        super().__init__()
        print(kwargs)

        self._props = ElasticBeanStalkDeployActionProps(
            application_name=kwargs['application_name'],
            environment_name=kwargs['environment_name'],
            role=kwargs['role']
        )
        self._action_properties = codepipeline.ActionProperties(
            provider="ElasticBeanstalk",
            category=codepipeline.ActionCategory.DEPLOY,
            inputs=[kwargs['input']],
            action_name=kwargs['action_name'],
            role=kwargs['role'],
            artifact_bounds=codepipeline.ActionArtifactBounds(
                max_inputs=1,
                max_outputs=0,
                min_inputs=1,
                min_outputs=0
            )
        )

    def bind(self, scope: core.Construct, stage: codepipeline.IStage, options: codepipeline.ActionBindOptions) -> codepipeline.ActionConfig:

        return codepipeline.ActionConfig(
            configuration={
                'ApplicationName': self.props.application_name,
                'EnvironmentName': self.props.environment_name

            }
        )

    def on_state_change(self, name, target=None, *, description=None, enabled=None, event_bus=None, event_pattern=None,
                        rule_name=None, schedule=None, targets=None):
        print('on_state_change')

And then you call add this call to your pipeline:

codepipeline.StageProps(stage_name="DeployBean",
                  actions=[
                      ElasticBeanStalkDeployAction(

                          action_name='Deploy',
                          role=pipeline_role,
                          application_name=APP_NAME,
                          run_order=1,
                          input=source_output,
                          environment_name=ENV_NAME


                      )])
Was this page helpful?
0 / 5 - 0 ratings