AWS: CloudFormation – using lists in Parameters
Tags: aws, devops, automation, tutorial
Published: 2020-05-08 05:28:01

---
title: AWS: CloudFormation – using lists in Parameters
published: true
date: 2020-05-08 05:03:09 UTC
tags: AWS,DevOps,automation,tutorial
canonical_url: https://rtfm.co.ua/en/aws-cloudformation-using-lists-in-parameters/
---

![](https://rtfm.co.ua/wp-content/uploads/2014/11/aws-logo-square-02-e1417012834176-1.png)In addition to the [AWS Elastic Kubernetes Service: a cluster creation automation, part 1 – CloudFormation](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/) and [AWS Elastic Kubernetes Service: a cluster creation automation, part 2 – Ansible, eksctl](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-2-ansible-eksctl/ "Permalink to AWS Elastic Kubernetes Service: a cluster creation automation, part 2 – Ansible, eksctl") posts – now I’d like to pass a Parameter as a List with multiply values to a CloudForamtion stack.

The idea is to get all AvailabilityZones of a region in Ansible, and then use this list for the `eksctl`, which will create WorkerNodes groups in dedicated AvailabilityZones, and use the same list for CloudFormation to create child stacks in right AvailabilityZone.

With Jenkins and Ansible it will work in a next way:

1. from a Jenkins, job Parameters take a `$REGION` with value _eu-west-3_
2. a Jenkins job will spin up a Docker container with Ansible passing the `$REGION` variable as an environment variable
3. Ansible using its `{{ region }}` variable will get a list of the AvailabilityZones of the region specified from Jenkins
4. and will pass this list to the _cloudformation_ role to create two nested stacks in two AvailabilityZones
5. and will pass this list to the _eksctl_ role to create NodeGroups in two AvailabilityZones

At this moment in my CloudFormation AvailabilityZones are taken via the `Fn::GetAZs - { "Fn::Select": ["0", { "Fn::GetAZs": "" }] }`:

```
...
    "AZNetworkStackA": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "eks-azs-networking.json",
        "Parameters": {
          "VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] },
          "AZ": { "Fn::Select": [ "0", { "Fn::GetAZs": "" } ] },
...
```

So, instead of using  `{ "Fn::Select": ["0", { "Fn::GetAZs": "" }] }` – let’s add a new task in Ansible to obtain all AvailabilityZones of a region where a CloudFormation stack will be created and then both its roles – _eksctl_ и _cloudformation_ – will use the same list with the same values.

The initial example was googled long time ago here – [How do I use multiple values for individual parameters in an AWS CloudFormation template](https://aws.amazon.com/ru/premiumsupport/knowledge-center/multiple-values-list-parameter-cli/), and now I got a chance to use it in a real example.

### Ansible

The most difficult task was to create the list in such a form, that CloudForamtion can use it in a correct way, so let’s start with this.

Also, as this list will be used by two separated roles in Ansible – worth to move it in a dedicated task and call it from the roles directly, to avoid duplicating the code.

So, create a _common_ role:

```
$ mkdir -p roles/common/tasks
```

Create a new file `roles/common/tasks/main.yml` and create a task here to obtain all AvailabilityZones and save the result to a `cluster_azs` variable:

```
- name: "Getting AvailabilityZones list"
  command: "aws ec2 describe-availability-zones --region {{ region }} --query 'AvailabilityZones[*].ZoneName' --output text"
  register: cluster_azs
```

The `cluster_azs` will return us a structure like the next:

```
...
ok: [localhost] => {
"msg": {
"changed": true,
"cmd": [
"aws",
"ec2",
"describe-availability-zones",
"--region",
"eu-west-2",
"--query",
"AvailabilityZones[*].ZoneName",
"--output",
"text"
],
"delta": "0:00:00.993216",
"end": "2020-04-08 15:55:56.357234",
"failed": false,
"rc": 0,
"start": "2020-04-08 15:55:55.364018",
"stderr": "",
"stderr_lines": [],
"stdout": "eu-west-2a\teu-west-2b\teu-west-2c",
"stdout_lines": [
"eu-west-2a\teu-west-2b\teu-west-2c"
]
}
}
...
```

Now, from the `stdout` string with the “_eu-west-2a\teu-west-2b\teu-west-2c_” value need to create a comma-separated list.

The elements here are divided with the TAB, so we can use the `split()` function to separate them, and then pass the data via a pipe to the `join()` which will concatenate them to a new list – but with the comma as a separator:

```
...
- set_fact:
    cluster_azs_names: "{{ cluster_azs.stdout.split('\t') | join(',') }}"
```

And add a debug at the end to check the result:

```
...
- debug:
    msg: "cluster_azs_names: {{ cluster_azs_names }}"
```

Check:

```
...
TASK [cloudformation : debug] ****
ok: [localhost] => {
"msg": "cluster_azs_names: eu-west-2a,eu-west-2b,eu-west-2c"
}
...
```

Cool.

### CloudFormation Parameter

#### `List<AWS::EC2::AvailabilityZone::Name`

Go to your CloudFormation template and add a new parameter with the “`List<AWS::EC2::AvailabilityZone::Name>`” type. See the full list in the [AWS-Specific Parameter Types](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#aws-specific-parameter-types):

```
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "AWS CloudFormation stack for Kubernetes cluster",
  "Parameters": {
    "VPCCIDRBlock": {
      "Description": "VPC CidrBlock",
      "Type": "String",
      "Default": "10.0.0.0/16"
    },
    "AvailabilityZones": {
      "Type": "List<AWS::EC2::AvailabilityZone::Name>",
      "Description": "The list of the AvailabilityZones in a current Region",
      "Default": "eu-west-2a, eu-west-2b"
    }
  },
...
```

Nett, add nested stacks and instead of the `{ "Fn::Select": ["0", { "Fn::GetAZs": "" }] }` use `{ "Fn::Select": ["0", { "Ref": "AvailabilityZones" }] }` – to obtain the first element from the list passed to the _AvailabilityZones_ Parameter:

```
...
    "AZNetworkStackA": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "eks-azs-networking.json",
        "Parameters": {
          "VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] },
          "AZ": { "Fn::Select": [ "0", { "Ref": "AvailabilityZones" } ] },
...
```

Pack them (see the [AvailabilityZonesAWS: CloudFormation – Nested Stacks and stacks parameters Import/Export](https://rtfm.co.ua/en/aws-cloudformation-nested-stacks-and-stacks-parameters-import-export/)):

```
$ aws --region eu-west-2 cloudformation package --template-file eks-root.json --output-template /tmp/packed-eks-stacks.json --s3-bucket eks-cloudformation-eu-west-2 --use-json
```

Run:

```
$ ansible-playbook eks-cluster.yml --tags infra
...
TASK [cloudformation : Getting AvailabilityZones list] ****
changed: [localhost]
TASK [cloudformation : set_fact] ****
ok: [localhost]
TASK [cloudformation : debug] ****
ok: [localhost] => {
"msg": "cluster_azs_names: eu-west-2a,eu-west-2b,eu-west-2c"
}
TASK [cloudformation : Create EKS EKSCTL-BTTRM-EKS-DEV-3-STACK CloudFormation stack] ****
changed: [localhost]
PLAY RECAP ****
localhost                  : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

Check:

[![](https://rtfm.co.ua/wp-content/uploads/2020/04/Screenshot_20200408_163957.png "AWS: CloudFormation - использование lists в Parameters")](https://rtfm.co.ua/wp-content/uploads/2020/04/Screenshot_20200408_163957.png)

Done.

### Similar posts

- <small>04/24/2020</small> [AWS Elastic Kubernetes Service: a cluster creation automation, part 1 – CloudFormation](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/) 
- <small>05/01/2020</small> [AWS Elastic Kubernetes Service: a cluster creation automation, part 2 – Ansible, eksctl](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-2-ansible-eksctl/)