Cloudformation Export Outputs / Input parameters cross-account - aws-cloudformation

I have a parent cloudformation script that launches two child cloudformation scripts, each in separate accounts. Is there a way I can get outputs from one of the child stacks and use them as inputs in the other child stack, all done from this parent template? The user should be able to input different account numbers as a parameter as this script will be run across several different accounts.
Parent template sample code:
Resources:
ChildAccountA:
Type: Custom::StackA
Properties:
ServiceToken: example
TemplateURL: https://s3.amazonaws.com/exampleA
ChildAccountB:
Type: Custom::StackB
Properties:
ServiceToken: example
TemplateURL: https://s3.amazonaws.com/exampleB

If I correctly understand, then yes. Your custom resource lambda should return the outputs to the parent stack.
The custom lambda obviously needs to have correct permissions to be able to deploy stacks in other accounts and get their outputs to be returned to the parent stack.
Since you haven't written if there are any issues with development of custom resources, I will just put the link to AWS crhelper. The helper simplify development of custom resources and returning results to it.
Just for completeness, for deployment of stacks in different regions and accounts, StackSets.

Related

How to reuse CloudFormation CodeBlocks?

I wonder if there is a way for me to reuse codeblock in CloudFormation without copy and paste. For example: right now I am making a bunch of alarms for different Redis clusters, the only real difference is the CacheClusterId (cache-001 then cache-002, cache-003 in this case) itself. I have looked up the instruction but I couldn't find a good way to not Copy and Paste. Or is it possible to have array of values instead of single value
"CacheMemoryUsage001": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"MetricName": "DatabaseMemoryUsagePercentage"
...
}
"Dimensions": [{
"Name": "CacheClusterId",
"Value": "cache-001"
},
....
I would like to organize the template better, right now I have couple alarms type per cluster and they are getting messier to maintain and keep track of
There are general two ways for that:
Use nested stacks. In this case, definition of a common resource(s) would be put to a separate stack, and in the main stack you would use the nested stack to create multiple resources based on the nested stack.
Create CFN macro which would perform basic find-and-replace type of your template processing to create the copies of the resources.
Besides the already mentioned methods, you can use CodePipeline with CodeBuild.
Use the AWS CDK to dynamically (using for instanve standard python code) create the CF template in CodeBuild and then pass it to a CloudFormation deploy stage which deploys the previous generated template.
https://aws.amazon.com/cdk/
Secondly using StackSets is an option if you want to deploy one template in multiple regions, accouts or organization OU level from one central place!
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/what-is-cfnstacksets.html

What's the best way to consume Parameter Store value in AWS CDK

I am having problems using SSM valueForStringParameter method in CDK. It's working the first time I deploy the stack, but it is not picking up updates to the parameter value when I redeploy the stack because CloudFormation template hasn't changed and so CloudFormation thinks there were no updates, even if SSM parameter has changed.
For the context, I am deploying stack via CodePipeline, where I run cdk synth first, and then use CloudFormationCreateUpdateStackAction action to deploy template.
Anyone knows how to work around that? The only other option that I know will work is to switch to a custom resource lambda that calls SSM and returns value using aws-sdk, but that feels like a overly complicated option.
Update 1
I cannot use ValueFromLookupbecause value is only updated at runtime as part of cloudformation deployment by another stack (I deploy both stacks in CodePipeline, in 2 different regions), so synthesis time lookup would result in stale value.
All the valueOf* and from* methods work by adding a CloudFormation parameter. As you figured out already, changing the parameter value does not change the template and no change will be triggered.
What you probably want to use instead is the method valueFromLookup. Lookups are executed during synth and the result is put into the generated CFN template.
ssm.StringParameter.valueFromLookup(this, 'param-name');
But be aware, lookups are stored in the cdk.context.json. If you have commited that file to your repo, you need to erase that key via cdk context -e ... before synth/diff/deploy.
Since you cannot use lookup functions and the most common way to pass config to cdk is through context variables, I can only suggest dirty workarounds.
For example, you could create a dummy parameter in your stack to bump every time there's deployment.
var deploymentId = new CfnParameter(this, "deploymentId", new CfnParameterProps() { Type = "String", Description = "Deployment Id" });
SetParameterValue(deploymentId, this.Node.GetContext("deploymentId").ToString());
and when you synthesize the CF, you could generate an ID:
cdk synth -c deploymentId=$(uuidgen)
If you can avoid the "environment agnostic" syth and you really need an immutable artifact to deploy across multiple environments, you could use the built package from your cdk, for example, the npm package containing your cdk. Therefore, you could deploy it in each environment by overwriting the context parameters instead of using ssm parameters store.
See https://docs.aws.amazon.com/cdk/latest/guide/get_ssm_value.html, you can use method valueFromLookup which gets you parameter store value at synthesis time, when value is different from previous one, this shall trigger CF stack update.
However, I was under impression that valueForStringParameter should work on updated ssm parameter values as well, based on https://aws.amazon.com/blogs/mt/integrating-aws-cloudformation-with-aws-systems-manager-parameter-store/ Example 2:

How do I make Cloudformation reprocess a template using a macro when parameters change?

I have a Cloudformation template that uses a custom Macro to generate part of the template. The lambda for the Macro uses template parameters (via the templateParameterValues field in the incoming event) to generate the template fragment.
When I change the Cloudformation Stack's parameters, I get an error:
The submitted information didn't contain changes. Submit different information to create a change set.
If I use the CLI I get a similar error:
An error occurred (ValidationError) when calling the UpdateStack operation: No updates are to be performed.
The parameters I am changing are only used by the Macro, not the rest of the template.
How can I make Cloudformation reprocess the template with the macro when I update these parameters?
After working with AWS Support I learned that you must supply the template again in order for the macro to be re-processed.
Even if it is the same exact template it will cause the macros to be reprocessed.
You can do this via the Console UI (by uploading the template file again) or the CLI (by passing the template / template URL again).
I recently came across this when using the count macro to create instances.
I found i was able to modify just the parameters used just by the macro by moving that part of the template to a nested stack and passing the parameters through.
It does involve a bit more work to setup by having the separate stacks, but it did allow me to modify just the parameters of the parent stack how i wanted.

How to pass nested Stack outputs to another step in Octopus Deploy

In my Octopus project, the first step launches a bunch of nested stacks implemented with cloudformation.
I need to share the outputs of the master stack launched from Octopus, how can I do that?
Thanks.
The output variables from the CloudFormation template will be available to later steps the same as any other Octopus output variable, this is mentioned in the first paragraph of the documentation page.
Output variables can be accessed a number of different ways, depending on where you are accessing them, for example, in Powershell they can be accessed via the parameters dictionary $OctopusParameters["Octopus.Action[Step Name].Output.VariableName"].
You can also access them using the Variable Binding syntax, #{Octopus.Action[Step Name].Output.VariableName}
More information about output variables is available in the docs.

Why does GetAttr not work on cloudformation template parameters?

Have a collection of cloudformation templates in a parent-child relationship and want to pass an AWS::IAM::Role into the parameters of a child stack and use GetAttr to get the Arn.
This fails validation because can only call GetAttr on resources, not on parameters.
Anyone know/guess why this is designed in this way?
It's not a problem as it can be worked around by just passing the Arn into the stack, I'm just curious really
What Fn::GetAttr and Parameters are trying to do in AWS CloudFormation is fundamentally different. As per AWS docs:
The intrinsic function Fn::GetAtt returns the value of an attribute
from a resource in the template.
[1] http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
You can use the optional Parameters section to pass values into your
template when you create a stack. With parameters, you can create
templates that are customized each time you create a stack.
[2] http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
I believe your confusion is stemming from the fact that you're trying to think of this in terms of the object-oriented/some other programming paradigm, where Resources and Parameters are some kind of objects and Fn::GetAttr is a generic function which retrieves the value of a reference that's passed in as an argument.
In my case, I wanted to access the resource attributes like ARN and Name in the nested stack. If I pass resource string and use GetAtt to get these, I dont need to pass them as two parameters. With this limitation, I had to pass them as two parameters.