1

I have a pipeline stack with a stage. This stage contains multiple stacks. One of the stacks creates a Step Function. Now I would like to trigger that step function in the post of the stage (I created InvokeStepFunctionStep as a custom ICodePipelineActionFactory implementation for this).

This is from my pipeline stack code:

// TODO make this dynamic
const stepFunctionArn = "arn:aws:states:<FULL_ARN_OMITTED>";
pipeline.addStage(stage, {
          post: [ new InvokeStepFunctionStep('step-function-invoke', {
            stateMachine: sfn.StateMachine.fromStateMachineArn(this, 'StepFunctionfromArn',stepFunctionArn),
            stateMachineInput: StateMachineInput.literal(stepFunctionsInput)
          })]
        });

Obviously the hard coded ARN is bad. I tried getting the ARN of the step function as a variable from the stage's stack. However this fails with

dependency cannot cross stage boundaries

I also tried using a CfnOutput for the ARN but when I try to use it via Fn.ImportValue the UpdatePipelineStep fails in CloudFormation with

No export named EdgePackagingStateMachineArn found

What is the recommended way to pass this information dynamically?

Korgen
  • 5,191
  • 1
  • 29
  • 43

2 Answers2

1

Option 1: easy, not optimal.

Specify a name for your Step Function, and pass it to both the stack that creates it, and your invokation step. Build the ARN from the name.

This option isn't great because specifying physical names for CloudFormation resources has its disadvantages - mainly the inability to introduce any subsequent change that requires resource replacement, which may likely be necessary for a step function.

Option 2: more convoluted, but might be better long-term.

Create an SSM parameter with the step function's ARN from within the stack that creates the step function, then read the SSM parameter in your invokation step.

This will also require specifying a physical name for a resource - the SSM parameter, but you are not likely to require resource replacement for it, so it is less of an issue.

gshpychka
  • 8,523
  • 1
  • 11
  • 31
1

You could try using the CfnOutput.importValue property to reference CfnOutput value, which works for me. See below:

Service stack:

export class XxxStack extends Stack {
  public readonly s3BucketName: CfnOutput;
  constructor(scope: Construct, id: string, props?: StackProps) {
    ...
    this.s3BucketName = new CfnOutput(
      this,
      's3BucketName',
      {
        exportName: `${this.stackName}-s3BucketName`,
        value: s3Bucket.bucketName,
      }
    );
  }
}

Stage class:

import { CfnOutput, Construct, Stage, StageProps } from '@aws-cdk/core';

export class CdkPipelineStage extends Stage {
  public readonly s3BucketName: CfnOutput;

  constructor(scope: Construct, id: string, props?: StageProps) {
    super(scope, id, props);
    const service = new XxxStack(
      this,
      'xxx',
      {
        ...
      }
    );

    this.s3BucketName = service.s3BucketName;
  }
}

Pipeline stack:

    import { CdkPipeline, SimpleSynthAction } from '@aws-cdk/pipelines';
    
    const pipeline = new CdkPipeline(this, 'Pipeline', {...})

    const preprod = new CdkPipelineStage(this, 'Staging', {
      env: { account: PREPROD_ACCOUNT, region: PIPELINE_REGION },
    });

    // put validations for the stages
    const preprodStage = pipeline.addApplicationStage(preprod);

    preprodStage.addActions(
      new ShellScriptAction({
        actionName: 'TestService',
        additionalArtifacts: [sourceArtifact],
        rolePolicyStatements: [
          new PolicyStatement({
            effect: Effect.ALLOW,
            actions: ['s3:getObject'],
            resources: [
              `arn:aws:s3:::${preprod.s3BucketName.importValue}/*`,
              `arn:aws:s3:::${preprod.s3BucketName.importValue}`,
            ],
          }),
        ],
        useOutputs: {
          ENV_S3_BUCKET_NAME: pipeline.stackOutput(
            preprod.s3BucketName
          ),
        },
        ...
      }),
    );

Note: my CDK version is

$ cdk --version
1.121.0 (build 026cb8f)

And I can confirm that CfnOutput.importValue also works for CDK version 1.139.0, and CDK version 2.8.0

Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
Yuci
  • 27,235
  • 10
  • 114
  • 113
  • did you add any manual dependency? When I try your solution I receive an error from cloudformation "No export named xyz found. " – Korgen Jan 21 '22 at 16:21
  • No, I don't have manual dependencies. Be sure that your actual service stack has `public readonly s3BucketName: CfnOutput;` which needs to be popped to its stage class, and in turn the pipeline stack can use it. – Yuci Jan 21 '22 at 16:59
  • my bad, I missed your `useOutputs` – Korgen Jan 26 '22 at 16:57
  • Sweet. worked great for me. – Donato Azevedo Aug 19 '22 at 22:04