Closed kianris closed 2 years ago
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 helped 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
)])
I've tried the code supplied by @skinny85 (thanks) and I've tried both suggestions (#1 and #2) to add permissions to the role but I get this error in the deployment action:
Action execution failed Deployment completed, but with errors: During an aborted deployment, some instances may have deployed the new application version. To ensure all instances are running the same version, re-deploy the appropriate application version. Failed to deploy application. Unsuccessful command execution on instance id(s) '...'. Aborting the operation. [Instance: ... ConfigSet: Infra-WriteRuntimeConfig, Infra-EmbeddedPreBuild, Hook-PreAppDeploy, Infra-EmbeddedPostBuild, Hook-EnactAppDeploy, Hook-PostAppDeploy] Command failed on instance. Return code: 1 Output: null. Error occurred during build: Command hooks failed Deployment Failed: Unexpected Exception
can anyone help with that?
Right now I'm just adding any roles I can think of:
resources: ['*'],
actions: [
'elasticbeanstalk:*',
'autoscaling:*',
'elasticloadbalancing:*',
'rds:*',
's3:*',
'cloudwatch:*',
'cloudformation:*',
'codedeploy:GetApplicationRevision',
'codedeploy:RegisterApplicationRevision'
],
}));
@thinkOfaNumber
think only the AWSElasticBeanstalkFullAccess
policy is required to deploy to ELB
deployActionRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('AWSElasticBeanstalkFullAccess'));
this is my deploy action
export interface ElasticBeanstalkDeployActionProps {
id: string;
ebsApplicationName: string;
ebsEnvironmentName: string;
input: Artifact;
role?: IRole;
}
export class ElasticBeanstalkDeployAction implements IAction {
readonly actionProperties: ActionProperties;
private readonly props: ElasticBeanstalkDeployActionProps;
constructor(props: ElasticBeanstalkDeployActionProps) {
this.actionProperties = {
...props,
category: ActionCategory.DEPLOY,
actionName: `${props.id}-elasticbeanstalk-deploy-action`,
owner: 'AWS',
provider: 'ElasticBeanstalk',
artifactBounds: {
minInputs: 1,
maxInputs: 1,
minOutputs: 0,
maxOutputs: 0,
},
inputs: [props.input],
};
this.props = props;
}
bind(scope: Construct, stage: IStage, options: ActionBindOptions): ActionConfig {
options.bucket.grantRead(options.role);
return {
configuration: {
ApplicationName: this.props.ebsApplicationName,
EnvironmentName: this.props.ebsEnvironmentName,
},
};
}
onStateChange(name: string, target?: IRuleTarget, options?: RuleProps): Rule {
throw new Error('not supported');
}
}
here is how I use it
new ElasticBeanstalkDeployAction({
id: 'my-elb-app',
ebsEnvironmentName: config.environment.environmentName!!,
ebsApplicationName: config.application.applicationName!!,
input: build_output_artifact,
role: deployActionRole,
}),
hope it helps
thanks @cudba I had tried that, but turns out the error was due to something completely different...
I forgot to include the aws-windows-deployment-manifest.json
which is required for an IIS app.
For future readers, I placed this inside my artifact at the root level:
{
"manifestVersion": 1,
"deployments": {
"aspNetCoreWeb": [
{
"name": "... your app name here ...",
"parameters": {
"appBundle": ".",
"iisPath": "/",
"iisWebSite": "Default Web Site"
}
}
]
}
}
more info on that here.
@skinny85 Do you maybe have an idea why the proposed approach for Typescript doesn't work for C# .net? I have the same issue as @FernandoCaletti: Unhandled exception. Amazon.JSII.Runtime.JsiiException: Validation failed with the following errors: [Pipeline] Stage 'Deploy' must have at least one action
@Nemanjalj66 can you show your code? That doesn't seem like an error related to the custom Action implementation...
public class ElasticBeanStalkDeployActionProps : CommonAwsActionProps
{
public string ApplicationName;
public string EnvironmentName;
public Artifact_ Input;
}
public class ElasticBeanStalkDeployAction : IAction
{
private readonly ElasticBeanStalkDeployActionProps props;
public ElasticBeanStalkDeployAction(ElasticBeanStalkDeployActionProps props)
{
this.props = props ?? throw new ArgumentNullException(nameof(props));
}
public IActionProperties ActionProperties => new ActionProperties()
{
Provider = "ElasticBeanstalk",
Category = ActionCategory.DEPLOY,
ArtifactBounds = new ActionArtifactBounds() { MaxInputs = 1, MinInputs = 1, MinOutputs = 0, MaxOutputs = 0 },
Inputs = new Artifact_[] { props.Input },
Role = props.Role,
ActionName = props.ActionName,
RunOrder = props.RunOrder,
VariablesNamespace = props.VariablesNamespace,
Owner = "Custom"
};
public IActionConfig Bind(Construct scope, IStage stage, IActionBindOptions options)
{
options.Bucket.GrantRead(options.Role);
return new ActionConfig() {
Configuration = new
{
ApplicationName = props.ApplicationName,
EnvironmentName = props.EnvironmentName,
}
};
}
public Rule OnStateChange(string name, IRuleTarget target = null, IRuleProps options = null)
{
throw new Exception("Unsupported");
}
}
Pipeline:
public PipelineStack(Construct scope, string id, string applicationName, string environmentName, IStackProps props = null) :
base(scope, id, props)
{
//aBuildOutput is defined as an output of a build stage
new Pipeline(this, "Pipeline", new PipelineProps
{
Stages = new[]
{
new Amazon.CDK.AWS.CodePipeline.StageProps
{
StageName = "Deploy_Application",
Actions = new []
{
new ElasticBeanStalkDeployAction(new ElasticBeanStalkDeployActionProps() {
ActionName = "Deploy",
ApplicationName = applicationName,
EnvironmentName = environmentName,
Input = aBuildOutput
})
}
}
}
});
}
Hmm, you're right @Nemanjalj66 . I tried this:
using Amazon.CDK;
using Amazon.CDK.AWS.Cognito;
using Amazon.CDK.AWS.IoT;
using Amazon.CDK.AWS.Route53;
using Amazon.CDK.AWS.CodeBuild;
using Amazon.CDK.AWS.CodeCommit;
using Amazon.CDK.AWS.CodePipeline;
using Amazon.CDK.AWS.CodePipeline.Actions;
namespace CsharpCognitoL1
{
public class ElasticBeanStalkDeployActionProps : CommonAwsActionProps
{
public string ApplicationName;
public string EnvironmentName;
public Artifact_ Input;
}
public class ElasticBeanStalkDeployAction : Amazon.CDK.AWS.CodePipeline.Actions.Action
{
private readonly ElasticBeanStalkDeployActionProps props;
public ElasticBeanStalkDeployAction(ElasticBeanStalkDeployActionProps props) : base(new ActionProperties
{
Provider = "ElasticBeanstalk",
Category = ActionCategory.DEPLOY,
ArtifactBounds = new ActionArtifactBounds { MaxInputs = 1, MinInputs = 1, MinOutputs = 0, MaxOutputs = 0 },
Inputs = new Artifact_[] { props.Input },
Role = props.Role,
ActionName = props.ActionName,
RunOrder = props.RunOrder,
VariablesNamespace = props.VariablesNamespace,
Owner = "Custom",
})
{
this.props = props;
}
protected override IActionConfig Bound(Construct scope, IStage stage, IActionBindOptions options)
{
options.Bucket.GrantRead(options.Role);
return new ActionConfig {
Configuration = new
{
ApplicationName = this.props.ApplicationName,
EnvironmentName = this.props.EnvironmentName,
},
};
}
}
public class CsharpCognitoL1Stack : Stack
{
internal CsharpCognitoL1Stack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
{
var sourceOutput = new Artifact_();
var buildOutput = new Artifact_();
new Pipeline(this, "Pipeline", new PipelineProps
{
Stages = new Amazon.CDK.AWS.CodePipeline.StageProps[]
{
new Amazon.CDK.AWS.CodePipeline.StageProps
{
StageName = "Source",
Actions = new IAction[]
{
new CodeCommitSourceAction(new CodeCommitSourceActionProps
{
ActionName = "Source",
Output = sourceOutput,
Repository = Repository.FromRepositoryName(this, "Repo", "my-repo"),
}),
},
},
new Amazon.CDK.AWS.CodePipeline.StageProps
{
StageName = "Build",
Actions = new IAction[]
{
new CodeBuildAction(new CodeBuildActionProps
{
ActionName = "Build",
Input = sourceOutput,
Outputs = new Artifact_[] { buildOutput },
Project = Project.FromProjectName(this, "Project", "my-build"),
}),
},
},
new Amazon.CDK.AWS.CodePipeline.StageProps
{
StageName = "Deploy_Application",
Actions = new IAction[]
{
new ElasticBeanStalkDeployAction(new ElasticBeanStalkDeployActionProps
{
ActionName = "Deploy",
ApplicationName = "applicationName",
EnvironmentName = "environmentName",
Input = buildOutput,
}),
},
},
},
});
}
}
}
But it fails with the error:
Unhandled exception. Amazon.JSII.Runtime.JsiiException: Amazon.JSII.Runtime.JsiiException: System.ArgumentException: Could not infer JSII type for .NET type '<>f__AnonymousType0`2' (Parameter 'type')
at Amazon.JSII.Runtime.Services.Converters.FrameworkToJsiiConverter.InferType(IReferenceMap referenceMap, Type type)
at Amazon.JSII.Runtime.Services.Converters.FrameworkToJsiiConverter.InferType(IReferenceMap referenceMap, Object value)
at Amazon.JSII.Runtime.Services.Converters.ValueConverter.ConvertAny(Type type, IReferenceMap referenceMap, Object value)
at Amazon.JSII.Runtime.Services.Converters.ValueConverter.TryConvertPrimitive(Type type, IReferenceMap referenceMap, Object value, Boolean isOptional, PrimitiveType primitiveType, Object& result)
at Amazon.JSII.Runtime.Services.Converters.ValueConverter.TryConvert(IOptionalValue optionalValue, Type type, IReferenceMap referenceMap, Object value, Object& result)
at Amazon.JSII.Runtime.Services.Converters.FrameworkToJsiiConverter.TryConvertClass(Type type, IReferenceMap referenceMap, Object value, Object& result)
at Amazon.JSII.Runtime.Services.Converters.ValueConverter.TryConvertCustomType(Type type, IReferenceMap referenceMap, Object value, Boolean isOptional, String fullyQualifiedName, Object& result)
at Amazon.JSII.Runtime.Services.Converters.ValueConverter.TryConvert(IOptionalValue optionalValue, Type type, IReferenceMap referenceMap, Object value, Object& result)
at Amazon.JSII.Runtime.Services.Converters.FrameworkToJsiiConverter.TryConvert(IOptionalValue optionalValue, IReferenceMap referenceMap, Object value, Object& result)
at Amazon.JSII.Runtime.CallbackExtensions.InvokeCallback(Callback callback, IReferenceMap referenceMap, IFrameworkToJsiiConverter converter, String& error)
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.Send[TRequest,TResponse](TRequest requestObject)
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.Send[TRequest,TResponse](TRequest requestObject)
at Amazon.JSII.Runtime.Services.Client.Invoke(InvokeRequest request)
at Amazon.JSII.Runtime.Services.Client.Invoke(ObjectReference objectReference, String method, Object[] arguments)
at Amazon.JSII.Runtime.Deputy.DeputyBase.<>c__DisplayClass17_0`1.<InvokeInstanceMethod>b__1(IClient client, Object[] args)
at Amazon.JSII.Runtime.Deputy.DeputyBase.<InvokeMethodCore>g__GetResult|18_0[T](<>c__DisplayClass18_0`1& )
at Amazon.JSII.Runtime.Deputy.DeputyBase.InvokeMethodCore[T](JsiiMethodAttribute methodAttribute, Object[] arguments, Func`3 beginFunc, Func`3 invokeFunc)
at Amazon.JSII.Runtime.Deputy.DeputyBase.InvokeInstanceMethod[T](Type[] parameterTypes, Object[] arguments, String methodName)
at Amazon.CDK.AWS.CodePipeline.Action.Bind(Construct scope, IStage stage, IActionBindOptions options)
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.Send[TRequest,TResponse](TRequest requestObject)
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.Send[TRequest,TResponse](TRequest requestObject)
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.Send[TRequest,TResponse](TRequest requestObject)
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.Send[TRequest,TResponse](TRequest requestObject)
at Amazon.JSII.Runtime.Services.Client.Create(CreateRequest request)
at Amazon.JSII.Runtime.Services.Client.Create(String fullyQualifiedName, Object[] arguments, Override[] overrides, String[] interfaces)
at Amazon.JSII.Runtime.Deputy.DeputyBase..ctor(DeputyProps props)
at Constructs.Construct..ctor(DeputyProps props)
at Amazon.CDK.Construct..ctor(DeputyProps props)
at Amazon.CDK.Resource..ctor(DeputyProps props)
at Amazon.CDK.AWS.CodePipeline.Pipeline..ctor(Construct scope, String id, IPipelineProps props)
at CsharpCognitoL1.CsharpCognitoL1Stack..ctor(Construct scope, String id, IStackProps props) in /Users/adamruka/workplace/cdk/on-call/csharp-cognito-l1/src/CsharpCognitoL1/CsharpCognitoL1Stack.cs:line 59
at CsharpCognitoL1.Program.Main(String[] args) in /Users/adamruka/workplace/cdk/on-call/csharp-cognito-l1/src/CsharpCognitoL1/Program.cs:line 13
Subprocess exited with error 134
I've raised an error to the JSII project: https://github.com/aws/jsii/issues/2870, let's see what they say.
Thank you for your answer @skinny85 ! The reason why you got a different error is that your ElasticBeanStalkDeployAction is derived from the abstract class Action. In the code I posted, you can see that ElasticBeanStalkDeployAction actually implements the IAction interface. I have also tried the approach you did and got the same error, but I've been hoping that implementing the IAction interface would help me overcome the issue. Do you have an idea of what might be a problem with the approach of implementing the IAction interface and do you have any ideas for alternatives?
Yeah, you're right @Nemanjalj66, I was able to reproduce that error... this looks like another JSII bug, unfortunately 😕.
Apparently, you need to extend the Amazon.JSII.Runtime.Deputy.DeputyBase
class to implement an interface in JSII: https://github.com/aws/jsii/issues/1029
However, I have tried it, and I still didn't work:
using System;
using Amazon.JSII.Runtime.Deputy;
using Amazon.CDK;
using Amazon.CDK.AWS.Cognito;
using Amazon.CDK.AWS.IoT;
using Amazon.CDK.AWS.Route53;
using Amazon.CDK.AWS.CodeBuild;
using Amazon.CDK.AWS.CodeCommit;
using Amazon.CDK.AWS.CodePipeline;
using Amazon.CDK.AWS.CodePipeline.Actions;
using Amazon.CDK.AWS.Events;
namespace CsharpCognitoL1
{
public class ElasticBeanStalkDeployActionProps : CommonAwsActionProps
{
public string ApplicationName;
public string EnvironmentName;
public Artifact_ Input;
}
public class ElasticBeanStalkDeployAction : DeputyBase, IAction
{
private readonly ElasticBeanStalkDeployActionProps props;
public ElasticBeanStalkDeployAction(ElasticBeanStalkDeployActionProps props)
{
this.props = props;
}
public IActionProperties ActionProperties => new ActionProperties()
{
Provider = "ElasticBeanstalk",
Category = ActionCategory.DEPLOY,
ArtifactBounds = new ActionArtifactBounds() { MaxInputs = 1, MinInputs = 1, MinOutputs = 0, MaxOutputs = 0 },
Inputs = new Artifact_[] { props.Input },
Role = props.Role,
ActionName = props.ActionName,
RunOrder = props.RunOrder,
VariablesNamespace = props.VariablesNamespace,
Owner = "Custom"
};
public IActionConfig Bind(Construct scope, IStage stage, IActionBindOptions options)
{
options.Bucket.GrantRead(options.Role);
return new ActionConfig() {
Configuration = new
{
ApplicationName = props.ApplicationName,
EnvironmentName = props.EnvironmentName,
}
};
}
public Rule OnStateChange(string name, IRuleTarget target = null, IRuleProps options = null)
{
throw new Exception("Unsupported");
}
}
public class CsharpCognitoL1Stack : Stack
{
internal CsharpCognitoL1Stack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
{
var sourceOutput = new Artifact_();
var buildOutput = new Artifact_();
new Pipeline(this, "Pipeline", new PipelineProps
{
Stages = new Amazon.CDK.AWS.CodePipeline.StageProps[]
{
new Amazon.CDK.AWS.CodePipeline.StageProps
{
StageName = "Source",
Actions = new IAction[]
{
new CodeCommitSourceAction(new CodeCommitSourceActionProps
{
ActionName = "Source",
Output = sourceOutput,
Repository = Repository.FromRepositoryName(this, "Repo", "my-repo"),
}),
},
},
new Amazon.CDK.AWS.CodePipeline.StageProps
{
StageName = "Build",
Actions = new IAction[]
{
new CodeBuildAction(new CodeBuildActionProps
{
ActionName = "Build",
Input = sourceOutput,
Outputs = new Artifact_[] { buildOutput },
Project = Project.FromProjectName(this, "Project", "my-build"),
}),
},
},
new Amazon.CDK.AWS.CodePipeline.StageProps
{
StageName = "Deploy_Application",
Actions = new IAction[]
{
new ElasticBeanStalkDeployAction(new ElasticBeanStalkDeployActionProps
{
ActionName = "Deploy",
ApplicationName = "applicationName",
EnvironmentName = "environmentName",
Input = buildOutput,
}),
},
},
},
});
}
}
}
Error:
$ npx cdk synth
Unhandled exception. Amazon.JSII.Runtime.JsiiException: System.ArgumentException: Could not infer JSII type for .NET type '<>f__AnonymousType0`2' (Parameter 'type')
at Amazon.JSII.Runtime.Services.Converters.FrameworkToJsiiConverter.InferType(IReferenceMap referenceMap, Type type)
at Amazon.JSII.Runtime.Services.Converters.FrameworkToJsiiConverter.InferType(IReferenceMap referenceMap, Object value)
at Amazon.JSII.Runtime.Services.Converters.ValueConverter.ConvertAny(Type type, IReferenceMap referenceMap, Object value)
at Amazon.JSII.Runtime.Services.Converters.ValueConverter.TryConvertPrimitive(Type type, IReferenceMap referenceMap, Object value, Boolean isOptional, PrimitiveType primitiveType, Object& result)
at Amazon.JSII.Runtime.Services.Converters.ValueConverter.TryConvert(IOptionalValue optionalValue, Type type, IReferenceMap referenceMap, Object value, Object& result)
at Amazon.JSII.Runtime.Services.Converters.FrameworkToJsiiConverter.TryConvertClass(Type type, IReferenceMap referenceMap, Object value, Object& result)
at Amazon.JSII.Runtime.Services.Converters.ValueConverter.TryConvertCustomType(Type type, IReferenceMap referenceMap, Object value, Boolean isOptional, String fullyQualifiedName, Object& result)
at Amazon.JSII.Runtime.Services.Converters.ValueConverter.TryConvert(IOptionalValue optionalValue, Type type, IReferenceMap referenceMap, Object value, Object& result)
at Amazon.JSII.Runtime.Services.Converters.FrameworkToJsiiConverter.TryConvert(IOptionalValue optionalValue, IReferenceMap referenceMap, Object value, Object& result)
at Amazon.JSII.Runtime.CallbackExtensions.InvokeCallback(Callback callback, IReferenceMap referenceMap, IFrameworkToJsiiConverter converter, String& error)
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
at Amazon.JSII.Runtime.Services.Client.Create(CreateRequest request)
at Amazon.JSII.Runtime.Services.Client.Create(String fullyQualifiedName, Object[] arguments, Override[] overrides, String[] interfaces)
at Amazon.JSII.Runtime.Deputy.DeputyBase..ctor(DeputyProps props)
at Constructs.Construct..ctor(DeputyProps props)
at Amazon.CDK.Construct..ctor(DeputyProps props)
at Amazon.CDK.Resource..ctor(DeputyProps props)
at Amazon.CDK.AWS.CodePipeline.Pipeline..ctor(Construct scope, String id, IPipelineProps props)
at CsharpCognitoL1.CsharpCognitoL1Stack..ctor(Construct scope, String id, IStackProps props) in /Users/adamruka/workplace/cdk/on-call/csharp-cognito-l1/src/CsharpCognitoL1/CsharpCognitoL1Stack.cs:line 142
at CsharpCognitoL1.Program.Main(String[] args) in /Users/adamruka/workplace/cdk/on-call/csharp-cognito-l1/src/CsharpCognitoL1/Program.cs:line 13
Subprocess exited with error 134
Thank you again for the answer! I have tried it in my implementation and it also fails with the same exception as you mentioned.
@Nemanjalj66
As a workaround for your .NET issue, you can fallback to raw overrides on your pipeline like this
CfnPipeline cfnPipeline = pipeline.Node.DefaultChild as CfnPipeline;
cfnPipeline.AddPropertyOverride("Stages.2", new Dictionary<string, object>
{
{ "Name", "Deploy" },
{ "Actions", new object[]
{
new Dictionary<string, object>
{
{ "Name", "Deploy" },
{ "ActionTypeId", new Dictionary<string, object>
{
{ "Category", "Deploy" },
{ "Owner", "AWS" },
{ "Provider", "ElasticBeanstalk" },
{ "Version", "1" }
}
},
{ "Configuration", new Dictionary<string, string>()
{
{"ApplicationName", "<YOUR_BEANSTALK_APPLICATION_NAME>" },
{"EnvironmentName", "<YOUR_BEANSTALK_ENVIRONMENT_NAME>" }
}
},
{ "InputArtifacts", new object[]
{
new Dictionary<string, string>{
{ "Name", "<YOUR_INPUT_ARTIFACT_NAME>" }
}
}
},
{ "Namespace", "DeployVariables" }
}
}
}
});
The culprit is likely with this type of syntax:
public IActionConfig Bind(Construct scope, IStage stage, IActionBindOptions options)
{
options.Bucket.GrantRead(options.Role);
return new ActionConfig() {
Configuration = new // <-- HERE
{
ApplicationName = props.ApplicationName,
EnvironmentName = props.EnvironmentName,
}
};
}
Returning an anonymous object here (new { ... }
) is probably what causes the problem. This particular value is typed any
in TypeScript, and hence I suspect it could be solved by returning a Dictionary<string, object>
or a JObject
instead.
This is brilliant @RomainMuller! I can confirm switching to dictionary works, like in this code:
using System.Collections.Generic;
using Amazon.CDK;
using Amazon.CDK.AWS.CodeBuild;
using Amazon.CDK.AWS.CodeCommit;
using Amazon.CDK.AWS.CodePipeline;
using Amazon.CDK.AWS.CodePipeline.Actions;
public class ElasticBeanStalkDeployActionProps : CommonAwsActionProps
{
public string ApplicationName;
public string EnvironmentName;
public Artifact_ Input;
}
public class ElasticBeanStalkDeployAction : Amazon.CDK.AWS.CodePipeline.Actions.Action
{
private readonly ElasticBeanStalkDeployActionProps props;
public ElasticBeanStalkDeployAction(ElasticBeanStalkDeployActionProps props) : base(new ActionProperties
{
Provider = "ElasticBeanstalk",
Category = ActionCategory.DEPLOY,
ArtifactBounds = new ActionArtifactBounds { MaxInputs = 1, MinInputs = 1, MinOutputs = 0, MaxOutputs = 0 },
Inputs = new Artifact_[] { props.Input },
Role = props.Role,
ActionName = props.ActionName,
RunOrder = props.RunOrder,
VariablesNamespace = props.VariablesNamespace,
Owner = "Custom",
})
{
this.props = props;
}
protected override IActionConfig Bound(Construct scope, IStage stage, IActionBindOptions options)
{
options.Bucket.GrantRead(options.Role);
return new ActionConfig {
Configuration = new Dictionary<string, object>
{
{ "ApplicationName", this.props.ApplicationName },
{ "EnvironmentName", this.props.EnvironmentName },
},
};
}
}
public class MyStack : Stack
{
internal MyStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
{
var sourceOutput = new Artifact_();
var buildOutput = new Artifact_();
new Pipeline(this, "Pipeline", new PipelineProps
{
Stages = new Amazon.CDK.AWS.CodePipeline.StageProps[]
{
new Amazon.CDK.AWS.CodePipeline.StageProps
{
StageName = "Source",
Actions = new IAction[]
{
new CodeCommitSourceAction(new CodeCommitSourceActionProps
{
ActionName = "Source",
Output = sourceOutput,
Repository = Repository.FromRepositoryName(this, "Repo", "my-repo"),
}),
},
},
new Amazon.CDK.AWS.CodePipeline.StageProps
{
StageName = "Build",
Actions = new IAction[]
{
new CodeBuildAction(new CodeBuildActionProps
{
ActionName = "Build",
Input = sourceOutput,
Outputs = new Artifact_[] { buildOutput },
Project = Project.FromProjectName(this, "Project", "my-build"),
}),
},
},
new Amazon.CDK.AWS.CodePipeline.StageProps
{
StageName = "Deploy_Application",
Actions = new IAction[]
{
new ElasticBeanStalkDeployAction(new ElasticBeanStalkDeployActionProps
{
ActionName = "Deploy",
ApplicationName = "applicationName",
EnvironmentName = "environmentName",
Input = buildOutput,
}),
},
},
},
});
}
}
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?
have you implemented now ?
+1 for this feature.
Hitting a roadblock trying to add a deployment stage to my pipeline.
Is there anyway to do this in vanilla JS?
const deployAction = {
actionName: 'DeployAction',
provider: 'ElasticBeanstalk',
category: ActionCategory.DEPLOY,
artifactBounds: { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 },
applicationName: environment.environmentName,
environmentName: environment.applicationName
};
deployStage.addAction(deployAction);
Returns error:
/Users/*REDACTED*/Documents/*REDACTED*/TradingBotCdk/node_modules/@aws-cdk/aws-codepipeline/lib/private/rich-action.js:25
return this.action.bind(scope, stage, options);
^
TypeError: this.action.bind is not a function
@A-ndy-git you're missing the bind()
method of the IAction
interface.
Also... use TS, seriously 😜. It will make your life so much easier.
At least use it until you make this work, and then convert to JS after. But I have a feeling, after you see how much better is CDK in TS than in JS, you won't want to go back 😉.
@skinny85 thanks for your quick reply :) - as much as I'd love to use TS, this would involve migrating my entire CDK project which I don't want to do (at this stage).
EDIT - got there in the end, heres how its done:
import { ActionBindOptions, ActionCategory, ActionConfig, ActionProperties, Artifact, IAction, IStage } from '@aws-cdk/aws-codepipeline';
import { IRuleTarget, Rule, RuleProps } from '@aws-cdk/aws-events';
import { IRole } from '@aws-cdk/aws-iam';
import { Construct } from '@aws-cdk/core';
export interface ElasticBeanstalkDeployActionProps {
id: string;
ebsApplicationName: string;
ebsEnvironmentName: string;
input: Artifact;
role?: IRole;
}
export class ElasticBeanstalkDeployAction implements IAction {
readonly actionProperties: ActionProperties;
private readonly props: ElasticBeanstalkDeployActionProps;
constructor(props: ElasticBeanstalkDeployActionProps) {
this.actionProperties = {
...props,
category: ActionCategory.DEPLOY,
actionName: `${props.id}-elasticbeanstalk-deploy-action`,
owner: 'AWS',
provider: 'ElasticBeanstalk',
artifactBounds: {
minInputs: 1,
maxInputs: 1,
minOutputs: 0,
maxOutputs: 0,
},
inputs: [props.input],
};
this.props = props;
}
bind(scope: Construct, stage: IStage, options: ActionBindOptions): ActionConfig {
options.bucket.grantRead(options.role);
return {
configuration: {
ApplicationName: this.props.ebsApplicationName,
EnvironmentName: this.props.ebsEnvironmentName,
},
};
}
onStateChange(name: string, target?: IRuleTarget, options?: RuleProps): Rule {
throw new Error('not supported');
}
}
And use it via:
const deployAction = new ElasticBeanstalkDeployAction({
id: 'your-app-name',
ebsEnvironmentName: environment.environmentName,
ebsApplicationName: application.applicationName,
input: sourceOutput,
role: pipeline.role,
});
deployStage.addAction(deployAction);
This issue has received a significant amount of attention so we are automatically upgrading its priority. A member of the community will see the re-prioritization and provide an update on the issue.
Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.
A new software.amazon.awscdk.services.codepipeline.Action for deploys to Elastic Beanstalk. Requires: