A new software.amazon.awscdk.services.codepipeline.Action for deploys to Elastic Beanstalk. Requires:
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 permissionI 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
)])