Access to output data from stack

4.6k Views Asked by At

I am creating a REST API using CloudFormation. In an other CloudFormation stack I would like to have access to values that are in the ouput section (the invoke URL) of that CloudFormation script.

Is this possible, and if so how?

1

There are 1 best solutions below

0
On

You can export your outputs. Exporting makes them accessible to other stacks.

From the AWS Docs:

To export a stack's output value, use the Export field in the Output section of the stack's template. To import those values, use the Fn::ImportValue function in the template for the other stacks

The following exports an API Gateway Id.

Description: API for interacting with API resources

Parameters:
  TargetEnvironment:
    Description: 'Examples can be dev, test or prod'
    Type: 'String'
    
  ProductName:
    Description: 'Represents the name of the product you want to call the deployment'
    Type: 'String'

Resources:
  MyApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: !Sub '${ProductName}-${TargetEnvironment}-apigw-primaryapi'

Outputs:
  MyApiId:
    Description: 'Id of the API'
    Value: !Ref MyApi
    Export:
      Name: !Sub '${ProductName}-${TargetEnvironment}-apigw-primaryapi'
  MyApiRootResourceId:
    Description: 'Id of the root resource on the API'
    Value: !GetAtt MyApi.RootResourceId
    Export:
      Name: !Sub '${ProductName}-${TargetEnvironment}-apigw-primaryapirootresource'

The Export piece of the Output is the important part here. If you provide the Export then other Stacks can consume from it.

Now, in another file I can import that MyApiId value by using the Fn::Import intrinsic function, importing the exported name. I can also import it's root resource and consume both of these values when creating a child API resource.

From the AWS Docs:

The intrinsic function Fn::ImportValue returns the value of an output exported by another stack. You typically use this function to create cross-stack references.

Description: Resource endpoints for interacting with the API

Parameters:
  TargetEnvironment:
    Description: 'Examples can be dev, test or prod'
    Type: 'String'
    
  ProductName:
    Description: 'Represents the name of the product you want to call the deployment'
    Type: 'String'

Resources:
  MyResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      ParentId: {'Fn::ImportValue': !Sub '${ProductName}-${TargetEnvironment}-apigw-primaryapirootresource' }
      PathPart: foobar
      RestApiId: {'Fn::ImportValue': !Sub '${ProductName}-${TargetEnvironment}-apigw-primaryapi' }

These are two completely different .yaml files and can be deployed as two independent stacks but now they depend on each other. If you try to delete the MyApi API Gateway stack before deleting the MyResource stack the CloudFormation delete operation will fail. You must delete the dependencies first.

One thing to keep in mind is that in some cases you might want to have the flexability to delete the root resource without worrying about dependencies. The delete operation could in some cases be done without any side-effects. For instance, deleting an SNS topic won't break a Lambda - it's prevents it from running. There's no reason to delete the Lambda just to re-deploy a new SNS topic. In that scenario I utilize naming conventions and tie things together that way instead of using exports. For example - the above AWS::ApiGateway::Resource can be tied to an environment specific API Gateway based on the naming convention.

Parameters:
  TargetEnvironment:
    Description: 'Examples can be dev, test or prod'
    Type: 'String'
    
  ProductName:
    Description: 'Represents the name of the product you want to call the deployment'
    Type: 'String'

Resources:
  MyResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      ParentId: {'Fn::ImportValue': !Sub '${ProductName}-${TargetEnvironment}-apigw-primaryapirootresource' }
      PathPart: foobar
      RestApiId: !Sub '${ProductName}-${TargetEnvironment}-apigw-primaryapi'

With this there's no need to worry about the export/import as long as the last half of the resource is named the same across all environments. The environment can change via the TargetEnvironment parameter so this can be re-used across dev, test and prod.

One caveat to this approach is that naming conventions only work for when you want to access something that can be referenced by name. If you need a property, such as the RootResource in this example, or EC2 size, EBS Volume size, etc then you can't just use a naming convention. You'll need to export the value and import it. In the example above I could replace the RestApiId import usage with a naming convention but I could not replace the ParentId with a convention - I had to perform an import.

I use a mix of both in my templates - you'll find when it makes sense to use one approach over the other as you build experience.