DEV Community

Marco Aguzzi
Marco Aguzzi

Posted on • Originally published at marcoaguzzi.it on

Domesticate AWS nested stacks in Java: doing the chores Cloudformation doesn't do (w/ code samples)

In this article we’ll navigate through the creation of a Nested Stack in Cloudformation using the Java SDK. The child stack will be a lambda function, and the code will be uploaded with a zip archive.

What’s Cloudformation, and what’s a nested stack?

Cloudformation is the AWS offering of infrastructure as code. Instead of navigating the web UI adding and configuring resources, Cloudformation offers the capability of reading a user - supplied file (either JSON or YAML) containing the list of resources and their relationships and create them as the code states.

These resources must be grouped in Stacks, which is the parentmost object that Cloudformation can process.

Things get interesting when stacks reference other stacks, of course :-)

A nested stack example

Doing this process with SDK, there are a couple of things that are not done automatically (or easier) than using the AWS CLI. Let’s get through them

Parent stack

Below there is the JSON for the parent stack. There is no specification that this is a stack (it’s written in the Description, but it’s arbitrary), because the command that will create the stack will ask first for a stack name to create (or update).

On the contrary, the child stack is explicitly declared as one of the resources with an AWS::CloudFormation::Stack type (line 7). Within the ChildStack element, the most important property is the TemplateURL (line 9), that points to the file (YAML in this case). The path is relative to the root template location.

Here’s the code:

Parent stack

{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Root stack",
 ...
"Resources": {
"ChildStack":{
"Type" : "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL":"child-folder/child-template.yaml",
 ...
}
},
"OtherResource": {
"Type": "ResourceType",
 ...
}
}
}

Enter fullscreen mode Exit fullscreen mode

Let’s view the child stack:

Child stack

Also in this case there’s no particular reference to the fact that this is a child stack. The parent stack can pass parameters to the child stack via the parameter section, but that’s it.

Child stack

AWSTemplateFormatVersion: "2010-09-09"
Description: child stack
Parameters:
...
Resources:
LambdaEdge:
Properties:
Runtime: nodejs20.x
Handler: index.handler
Code:
S3Bucket:
...
S3Key: "example value"
Role: !GetAtt LambdaRoleForCF.Arn
Type: "AWS::Lambda::Function"
Outputs:
...

Enter fullscreen mode Exit fullscreen mode

The lambda reference

On line 13, the template file references a S3 location there the zip file with the code must be found. So the zip file with the code must be uploaded in S3 before the creation of the parent stack is issued, otherwise the references won’t work. As it’s noted in the next section, the AWS CLI package command can resolve references like this, but it’s not present in the SDK.

Once that the zip file with the code is uploaded onto S3, the creation of the stack can proceed.

Creating the stack

Before creating the stack, the template file must be present on S3 in order to be pointed at by the create-stack command.

This is fine, but the template contains the child stack file path that references the local machine, which won’t never work from s3.

The solution for Cloudformation is packaging the stack.

Packaging the stack

Using the CLI

Via the CLI is quite easy: there’s a package command that

  • detects the substack reference
  • searches for the referenced file
  • uploads it on s3
  • changes the reference in the parent file

Java SDK

Unfortunately, the aws-cli package command is not available in Java SDK, so a manual approach must be taken.

Here’s the code from the S3 static website project (around line 44)

The code does the same stuff that the cli do:

  • templateSrcPath (line 3) contains the path to the local parent template
  • s3PathToReplace (line 9) contains the path to the child stack template already uploaded to S3
  • lines 4 to 11 cycle throught the parent template,
    • searches for a resource with type AWS::CloudFormation::Stack
    • replaces the TemplateURL attribute with the path of the template uploaded onto S3
  • Then it persist the file with the substitution onto a temporary folder. Using the SDK the template can be referenced from within the disk.

Template packaging

App.screenMessage("PACKAGE TEMPLATE START");
ObjectMapper objectMapper = new ObjectMapper();
JsonNode root = objectMapper.readTree(Utils.readFileContent(templateSrcPath)); 
Iterator<JsonNode> elements = root.get("Resources").elements();
while(elements.hasNext()) {
 JsonNode node = elements.next(); 
 if ("AWS::CloudFormation::Stack".equals(node.get("Type").asText())) {
JsonNode properties = node.get("Properties");
 ((ObjectNode)properties).put("TemplateURL", s3PathToReplace);
 }
};
File file = File.createTempFile(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()),"\_compiled\_template.json");
objectMapper.writeValue(file, root);
App.screenMessage("PACKAGE TEMPLATE END");

Enter fullscreen mode Exit fullscreen mode

Once that the template on the disk has all of its references ready, the CreateStack api can be called and the stack will be created along with all of its resources.

Improvements and what’s next

The actual CLI package command does not only resolve nested stack references, but also lambda code (as it has been shown) and a bunch of other stuff (as stated here).

There are two major improvements to the code for the package section

  • also discover other type of references (like lambda code)
  • automatically upload the referenced path to s3 without actually asking for it in the input parameters

If you’d like to browse to the full code here, please give it a go!

Top comments (0)