1
votes

My team is using Azure Devops pipelines to deploy Cloudformation stacks to AWS. We have common Cloudformation templates that we are reusing (e.g. a single Lambda.yml Cloudformation template that is reused to deploy multiple Lambda functions).

Along with this I am setting up a step function to orchestrate logical flows. The step function steps need to reference the ARNs of the various parts of the infrastructure (for this example, focus on the lambda functions).

Since the Lambda.yml is reused, the output variables are overwritten each time a task reuses the template to deploy a new lambda. I'd like to capture the output after each task in a variable or parameter in Azure Devops. I've looked at numerous examples and the documentation on variables, and I have been unable to get it to work. The relevant parts of the Cloudformation template and the deployment script are listed here (YAML format).

Lambda.yml (Cloudformation template)

AWSTemplateFormatVersion: '2010-09-09'
Description: Template for Lambda function.
Parameters:
  FunctionName: 
    Description: The name of the Lambda function
    Type: String

Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Ref FunctionName
.....

Outputs:
  LambdaFunctionName:
    Description: Lambda function name
    Value: !Ref LambdaFunction
  LambdaFunctionARN:
    Description: Lambda ARN
    Value: 
      Fn::GetAtt:
        - LambdaFunction
        - Arn

Deploy.yml (Azure Devops pipeline deployment)

# Deploy.yml is a series of deployment tasks called from a job/stage defined in another.yml file
# LambdaFunctionNumber1
- task: AmazonWebServices.aws-vsts-tools.CloudFormationCreateOrUpdateStack.CloudFormationCreateOrUpdateStack@1
  displayName: 'Stack: LambdaFunctionNumber1'
  inputs:
    templateSource: s3
    s3BucketName: ${{ parameters.azdoS3ArtifactBucket }}
    s3ObjectKey: '${{ parameters.azdoS3ArtifactPrefix }}/cfn/Lambda.yml'
    templateParametersSource: inline
    templateParameters: |
     -
         ParameterKey: FunctionName
         ParameterValue: LambdaFunctionNumber1
    ....
    captureStackOutputs: asVariables

# I would like to capture Cloudformation output variables here for use in other tasks
# variables lambdaNumberOneFunctionName and lambdaNumberOneFunctionArn are defined as variables by the job triggering these deployment tasks, and initialized to empty strings
- bash:
    echo "##vso[task.setvariable variable=lambdaNumbertOneFunctionName;isOutput=true]$LAMBDAFUNCTIONNAME"
    echo "##vso[task.setvariable variable=lambdaNumberOneFunctionArn;isOutput=true]$LAMBDAFUNCTIONARN"

# LambdaFunctionNumber2
- task: AmazonWebServices.aws-vsts-tools.CloudFormationCreateOrUpdateStack.CloudFormationCreateOrUpdateStack@1
  displayName: 'Stack: LambdaFunctionNumber2'
  inputs:
    templateSource: s3
    s3BucketName: ${{ parameters.azdoS3ArtifactBucket }}
    s3ObjectKey: '${{ parameters.azdoS3ArtifactPrefix }}/cfn/Lambda.yml'
    templateParametersSource: inline
    templateParameters: |
     -
         ParameterKey: FunctionName
         ParameterValue: LambdaFunctionNumber2
    ....
    captureStackOutputs: asVariables

# At this point the $(LambdaFunctionARN) output variable is set from Cloudformation of LambdaFunctionNumber2. 
# The ARN from LambdaFunctionNumber1 is lost unless I capture it somehow.
# I need multiple ARNs to provide as input to the Step Function that gets deployed here
- task: AmazonWebServices.aws-vsts-tools.CloudFormationCreateOrUpdateStack.CloudFormationCreateOrUpdateStack@1
  displayName: 'Stack: Step Function'
  inputs:
    stackName:step-function
    templateSource: s3
    s3BucketName: ${{ parameters.azdoS3ArtifactBucket }}
    s3ObjectKey: '${{ parameters.azdoS3ArtifactPrefix }}/cfn/StepFunction.yml'
    ....

    templateParametersSource: inline
    templateParameters: |
     -
         ParameterKey: StateMachineName
         ParameterValue: orchestration-step-function
     -
         # This doesn't work
         ParameterKey: State1LambdaArn
         ParameterValue: $[lambdaNumberOneFunctionArn]
     -
         # This works but only allows capturing the most recent output variable
         ParameterKey: State2LambdaArn
         ParameterValue: $(LambdaFunctionARN)
1
Not get your latest information, is Krzysztof Madej's answer helpful for you? Or if you have any concern, feel free to share it here.Hugh Lin

1 Answers

1
votes

You may try to name step where you set a variable and then use name of step as part of variable name. You can even define variable name as template parameter so each time you will get an unique name. In code it may look like this:

template.yaml

parameters:
- name: name
  type: string
  default: 'lambdaNumbertOneFunctionName'
- name: arn
  type: string
  default: 'lambdaNumberOneFunctionArn'
- name: stepName
  type: string
  default: 'StepOne'
- name: value
  type: string
  default: 'testValue'

steps:
- bash: |
    echo "##vso[task.setvariable variable=${{ parameters.name }};isOutput=true]${{ parameters.value }}"
    echo "##vso[task.setvariable variable=${{ parameters.arn }};isOutput=true]${{ parameters.value }}"
    echo "##vso[task.setvariable variable=Test;isOutput=true]${{ parameters.value }}"
    echo "##vso[task.setvariable variable=Test2;isOutput=true]Yesy2"
  name: ${{ parameters.stepName }}

- script: |
    echo $(${{ parameters.stepName }}.${{ parameters.name }})
    echo $(${{ parameters.stepName }}.${{ parameters.arn }})
    echo $(${{ parameters.stepName }}.Test)
    echo $(${{ parameters.stepName }}.Test2)

and main file

jobs:
- job: myJob
  timeoutInMinutes: 10
  pool:
    vmImage: 'ubuntu-16.04'
  steps:
  - template: template.yaml
    parameters:
      name: 'lambdaNumbertOneFunctionName'
      arn: 'lambdaNumberOneFunctionArn'
      value: 'value-1'
      stepName: 'StepOne'
  - template: template.yaml
    parameters:
      name: 'lambdaNumberTwoFunctionName'
      arn: 'lambdaNumberTwoFunctionArn'
      value: 'value-2'
      stepName: 'StepTwo'
  - script: |
      echo 'lambdaNumbertOneFunctionName - $(StepOne.lambdaNumbertOneFunctionName)'
      echo 'lambdaNumberOneFunctionArn - $(StepOne.lambdaNumberOneFunctionArn)'
      echo 'lambdaNumberTwoFunctionName - $(StepTwo.lambdaNumberTwoFunctionName)'
      echo 'lambdaNumberTwoFunctionArn - $(StepTwo.lambdaNumberTwoFunctionArn)'

For that I got:

lambdaNumbertOneFunctionName - value-1
lambdaNumberOneFunctionArn - value-1
lambdaNumberTwoFunctionName - value-2
lambdaNumberTwoFunctionArn - value-2

I hope I understood you and your issue and it solves it :)