AWS Elastic Kubernetes Service: a cluster creation automation, part 1 – CloudFormation
Tags: aws, devops, kubernetes, tutorial
Published: 2020-04-24 12:54:05

---
title: AWS Elastic Kubernetes Service: a cluster creation automation, part 1 – CloudFormation
published: true
date: 2020-04-24 11:37:09 UTC
tags: AWS,devops,kubernetes,tutorial
canonical_url: https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/
---

![](https://rtfm.co.ua/wp-content/uploads/2020/04/amazon-eks-1.png)The task is: create automation to roll out an AWS Elastic Kubernetes Service cluster from scratch.

Will use:

- Ansible: to automate CloudFormation stack creation and to execute `eksctl` with necessary parameters
- [CloudFormation with NestedStacks](https://rtfm.co.ua/en/aws-cloudformation-nested-stacks-and-stacks-parameters-import-export/): to create an infrastructure – VPC, subnets, SecurityGroups, IAM-roles, etc
- `eksctl`: to create a cluster itself using resources created by CloudFormation

The thinking is :

1. Ansible will use the [cloudformation module](https://rtfm.co.ua/ansible-modul-cloudformation/) to create an  infrastructure
2. by using `Outputs` of the stack created by CloudFormation – Ansible will generate a config file for the `eksctl`
3. Ansible calls `eksctl` passing the config-file and will create a cluster

The `eksctl` was chosen at first because of lack of time, and secondly – because it uses CloudFormation under the hood, which is used in my project for a long time, so all our infrastructure will stay homogeneous state.

Ansible will be running fro ma Jenkins job by using a Docker image with AWS CLI, Ansible and `eksctl`.

Actually, do not consider this post as some kind of a “Best Practice” for such automation, instead – it’s more like a Proof of Concept, and more is an example of how some vague idea in a head became in a real working code and services. Which tools exactly to use – Terraform или CloudFormation, [`kops`](https://github.com/kubernetes/kops) or [`eksctl`](https://github.com/weaveworks/eksctl) is a secondary question.

Also, there are two modules for Ansible to make work with Kubernetes easier – [`k8s`](https://docs.ansible.com/ansible/latest/modules/k8s_module.html) and [`kubectl`](https://docs.ansible.com/ansible/latest/plugins/connection/kubectl.html), but they both have statuses _preview_ and _community_ so I’ll do not use them here (yet).

The post is really long, so it’s divided into two parts:

- in this one, the first one, we will start writing a CloudFromation template
- in the second one – will start writing Ansible playbook and roles to run CloudFormation and `eksctl`

I hope there are not too many inaccuracies, but still, they can be, as this was written during a few days with repeated corrections and revamps, but it’s described step by step, so the general idea must be visible enough.

All the resulted files after writing this post are available in the [eksctl-cf-ansible](https://github.com/setevoy2/eksctl-cf-ansible/tree/rdy) Github repository. The link here is to the branch with an exact copy of the code below.

The second part – [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/).

- [CloudFormation stacks](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#CloudFormation_stacks)
- [The Root stack](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#The_Root_stack)
- [Parameters](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#Parameters)
- [The Network Region stack](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#Network_AvailabilityZones_stack)
- [VPC]()
- [Internet Gateway]()
- [Network AvailabilityZones stack]()
- [Parameters](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#Parameters-2)
- [Subnets](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#Subnets)
- [NAT Gateway](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#NAT_Gateway)
- [Public RouteTable](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#Public_RouteTable)
- [Private RouteTable](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#Private_RouteTable)
- [Mappings and CIDRs for subnets](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#Mappings_and_CIDRs_for_subnets)
- [eksctl – a stack creation](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#eksctl_%E2%80%93_a_stack_creation)
- [Useful links](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/#Useful_links)

### CloudFormation stacks

So, let’s begin with the CloudFormation stack.

We need to create:

- 1 VPC
- two public subnets for Application Load Balancers, Bastion hosts, Internet Gateways
- two private subnets for Kubernetes Worker Nodes EC2, NAT Gateways

EKS AMI for Kubernetes Worker Nodes `eksctl` will choose automatically, but you can find the whole list [here>>>](https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html).

Will use CloudFormation Nested Stacks here (see the [AWS: CloudFormation – Nested Stacks and stacks parameters Import/Export](https://rtfm.co.ua/en/aws-cloudformation-nested-stacks-and-stacks-parameters-import-export/) for more details):

1. the “ **Root-stack** “, template file `eks-root.json` – will describe stacks to be created, determines parameters, etc
  1. the “ **Region-stack** “, template file `eks-region-networking.json`:
    1. one VPC
    2. Internet Gateway
      1. Internet Gateway Association
  2. the “ **AvailabilityZones-stack** “, a template file `eks-azs-networking.json` – all resources will be duplicated over two different AvailabilityZones of a region:
    1. one public subnet
    2. one private subnet
    3. RouteTable for the public subnet
      1. a Route into the 0.0.0.0/0 network via an Internet Gateway
      2. and SubnetRouteTableAssociation to attach this RouteTable to a public subnet in this AvailabilityZone
    4. RouteTable for the private subnet
      1. a Route into the 0.0.0.0/0 network via a NAT Gateway
      2. SubnetRouteTableAssociation to attach this RouteTable to a private subnet in this AvailabilityZone
    5. NAT Gateway
      1. Elastic IP for the NAT Gateway

Go ahead with the root-stack template.

#### The Root stack

The first template will be used by the root-stack to create all other stacks.

Create directories for a future Ansible role:

```
$ mkdir -p roles/cloudformation/{tasks,files,templates}
```

In the `roles/cloudformation/files/` directory create a new file `eks-root.json` – this will be our root-template:

```
$ cd roles/cloudformation/files/
$ touch eks-root.json
```

##### Parameters

It’s a good idea to think about your future IP-addresses block that will be used in your project. At least you need to avoid using overlapping network blocks to prevent [VPN Peering](https://rtfm.co.ua/aws-nastrojka-vpc-peering/) issues.

The second thing to consider is a whole networking model for your cluster and network plugin to use.

By default, AWS Elastic Kubernetes Service uses the CNI (_Container Network Interface_) plugin which allows using a Worker Node ЕС2 network interface (ENI – _Elastic Network Interface_). By using this plugin – Kubernetes will allocate IP addresses from a VPC pool to pods created, see the [`amazon-vpc-cni-k8s`](https://rtfm.co.ua/goto/https://github.com/aws/amazon-vpc-cni-k8s) and [Pod Networking (CNI)](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/eks/latest/userguide/pod-networking.html).

This solution has some advantages and disadvantages, check the great overview from the Weave Net — [AWS and Kubernetes Networking Options and Trade-offs](https://rtfm.co.ua/goto/https://www.weave.works/blog/aws-and-kubernetes-networking-options-and-trade-offs-part-3), and read about other plugins in the Kubernetes documentation – [Cluster Networking](https://rtfm.co.ua/goto/https://kubernetes.io/docs/concepts/cluster-administration/networking/).

Also, worth to check the [VPC and Subnet Sizing](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Subnets.html#VPC_Sizing) document.

For now, let’s add only the _10.0.0.0/16_ block for the VPC – later it will be divided to 4 subnets:

```
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "AWS CloudFormation stack for Kubernetes cluster",
  
  "Parameters": {
    
    "VPCCIDRBlock": {
      "Description": "VPC CidrBlock",
      "Type": "String",
      "Default": "10.0.0.0/16"
    }
    
  },
```

Subnets will be the next:

- one public in an AvailabilityZone 1-А, /20, 4094 addresses
- one private in an AvailabilityZone 1-А, /20, 4094 addresses
- one public in an AvailabilityZone 1-В, /20, 4094 addresses
- one private in an AvailabilityZone 1-В, /20, 4094 addresses

The [`ipcalc`](https://linux.die.net/man/1/ipcalc) can be used here:

```
$ ipcalc 10.0.0.0/16 --s 4094 4094 4094 4094 | grep Network | cut -d" " -f 1,4 | tail -4
Network: 10.0.0.0/20
Network: 10.0.16.0/20
Network: 10.0.32.0/20
Network: 10.0.48.0/20
```

4094 address must be enough for all EС2 instances and pods.

During this post wring found the best subnets calculator here [http://www.subnetmask.info](http://www.subnetmask.info).

Also, add an _EKSClusterName_ parameter – we will pass a cluster name from Ansible here to create necessary CloudFormation tags:

```
...
    "EKSClusterName": {
      "Description": "EKS cluster name",
      "Type": "String"
    } 
...
```

#### The Network Region stack

Now we can create a template for the second stack. Let’s call it `eks-region-networking.json`.

##### VPC

In this template, we will describe our VPC, and from the root-template will pass a parameter with the VPC CIDR here, and back to the root – via `Outputs` will pass back the ID of the VPC created:

```
{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "AWS CloudFormation Region Networking stack for Kubernetes cluster",
  
  "Parameters" : {
    
    "VPCCIDRBlock": {
      "Description": "VPC CidrBlock",
      "Type": "String"
    },
    "EKSClusterName": {
      "Description": "EKS cluster name",
      "Type": "String"
   }
    
  },
  
  "Resources" : {
    
    "VPC" : {
      "Type" : "AWS::EC2::VPC",
      "Properties" : {
        "CidrBlock" : { "Ref": "VPCCIDRBlock" },
        "EnableDnsHostnames": true,
        "EnableDnsSupport": true,
        "Tags" : [
          {
            "Key" : "Name",
            "Value" : { "Fn::Join" : [ "-", [ {"Ref" : "AWS::StackName"}, "vpc"] ] } },
          {
            "Key" : { "Fn::Join" : [ "", [ "kubernetes.io/cluster/", {"Ref" : "EKSClusterName"}] ] },
            "Value" : "owned"
          }
        ]
      }
    }
    
  },
  
  "Outputs" : {
    
    "VPCID" : {
      "Description" : "EKS VPC ID",
      "Value" : { "Ref" : "VPC" }
    }
    
  }
}
```

Go back to the root template to add a first nested stack creation.

VPC ID will be taken from the `Outputs` of the network region stack and will be disclosed via root’s `Outputs` to make it available for Ansible to grab it to create a variable which will be used later for the  `eksctl` config-file.

At this moment the whole root template has to  look like the following:

```
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "AWS CloudFormation stack for Kubernetes cluster",
  "Parameters": {
    "VPCCIDRBlock": {
      "Description": "VPC CidrBlock",
      "Type": "String",
      "Default": "10.0.0.0/16"
    },
    "EKSClusterName": {
      "Description": "EKS cluster name",
      "Type": "String"
   }
  },
  "Resources": {
    "RegionNetworkStack": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "eks-region-networking.json",
        "Parameters": {
          "VPCCIDRBlock": { "Ref": "VPCCIDRBlock" },
          "EKSClusterName": { "Ref": "EKSClusterName"},
        }
      }
    }
  },
  "Outputs": {
    "VPCID" : {
      "Description" : "EKS VPC ID",
      "Value" : { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] }
    }
  }
}
```

Create an S3 bucket in a region planned to use:

```
$ aws --profile arseniy --region eu-west-2 s3api create-bucket --bucket eks-cloudformation-eu-west-2 --region eu-west-2 --create-bucket-configuration LocationConstraint=eu-west-2
```

In a Production setup, it will great to have [S3 Versioning](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html) enabled to have a backup and history (although all the templates will be stored in a Gitbuh repository).

Enable it:

```
$ aws --region eu-west-2 --profile arseniy s3api put-bucket-versioning --bucket eks-cloudformation-eu-west-2 --versioning-configuration Status=Enabled
```

Pack the `eks-root.json` and `eks-region-networking.json` to the AWS S3 and save a resulted file to the `/tmp` as `packed-eks-stacks.json`:

```
$ cd roles/cloudformation/files/
$ aws --profile arseniy --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
```

Deploy the stack:

```
$ aws --profile arseniy --region eu-west-2 cloudformation deploy --template-file /tmp/packed-eks-stacks.json --stack-name eks-dev
Waiting for changeset to be created.
Waiting for stack create/update to complete
Successfully created/updated stack - eks-dev
```

Check it:

[![](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_102207.png "AWS: Elastic Kubernetes Service - автоматизация создания кластера, часть 1 - CloudFormation")](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_102207.png)

The first child stack is created, VPC created – all good so far.

##### Internet Gateway

Add an Internet Gateway and [`VPCGatewayAttachment`](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-internetgateway.html), so the `Resources` block of the region-stack will be:

```
...
  "Resources" : {
    "VPC" : {
      "Type" : "AWS::EC2::VPC",
      "Properties" : {
        "CidrBlock" : { "Ref": "VPCCIDRBlock" },
        "EnableDnsHostnames": true,
        "EnableDnsSupport": true,
        "Tags" : [
          {
            "Key" : "Name",
            "Value" : { "Fn::Join" : [ "-", [ {"Ref" : "AWS::StackName"}, "vpc"] ] } },
          {
            "Key" : { "Fn::Join" : [ "", [ "kubernetes.io/cluster/", {"Ref" : "EKSClusterName"}] ] },
            "Value" : "owned"
          }
        ]
      }
    },
    "InternetGateway" : {
      "Type" : "AWS::EC2::InternetGateway",
      "Properties" : {
        "Tags" : [
          {"Key" : "Name", "Value" : { "Fn::Join" : [ "-", [ {"Ref" : "AWS::StackName"}, "igw"] ] } }
        ]
      }
    },
    
    "AttachGateway" : {
       "Type" : "AWS::EC2::VPCGatewayAttachment",
       "Properties" : {
         "VpcId" : { "Ref" : "VPC" },
         "InternetGatewayId" : { "Ref" : "InternetGateway" }
       }
    }
  },
...
```

In its `Outputs` add passing the InternetGateway ID back to the root stack, from where it will be passed to the Network AvailabilityZones stack to create future RouteTables for public subnets:

```
...
  "Outputs" : {
    "VPCID" : {
      "Description" : "EKS VPC ID",
      "Value" : { "Ref" : "VPC" }
    },
    "IGWID" : {
      "Description" : "InternetGateway ID",
      "Value" : { "Ref" : "InternetGateway" }
    }
  }
}
```

And it’s time to start writing the Network AvailabilityZones stack template.

#### Network AvailabilityZones stack

Now, we need to specify resources to be duplicated over two AvailabilityZones.

These include:

1. by one public subnet
2. by one private subnet
3. RouteTable for public subnets
  1. with a Route to the 0.0.0.0/0 network via an Internet Gateway
  2. and a SubnetRouteTableAssociation to attach the RouteTable to a public subnet in this AvailabilityZone
4. RouteTable for private subnets
  1. with a Route to the 0.0.0.0/0 network via a NAT Gateway
  2. and a SubnetRouteTableAssociation to attach the RouteTable to a private subnet in this AvailabilityZone
5. NAT Gateway
  1. Elastic IP for the NAT Gateway

The main question here is how to choose AvailabilityZones for those stacks, as some resources, like `AWS::EC2::Subnet` needs to have AvailabilityZone specified.

The possible solution is to use the [`Fn::GetAZs`](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getavailabilityzones.html) ColudFormation function, which will be called from the root stack to get all AvailabilityZones of the region used for the cluster, and then they will be passed to our NetworkAvailabilityZones-stacks.

Most regions have three AvailabilityZones, but in this case, only two will be used (fair enough for fault-tolerant).

Let’s begin with subnets – by one public and one private in both AvailabilityZones.

In this stack we need to pass a few new parameters:

- VPC ID from the region stack
- public subnet CIDR block
- private subnet CIDR block
- AvailabilityZone to create resources in
- Internet Gateway ID from the region stack to use for RouteTables

Create a new template file, call it `eks-azs-networking.json`.

##### Parameters

Add parameters here:

```
{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "AWS CloudFormation AvailabilityZones Networking stack for Kubernetes cluster",
  "Parameters" : {
    "VPCID": {
      "Description": "VPC for resources",
      "Type": "String"
    },
    "EKSClusterName": {
      "Description": "EKS cluster name",
      "Type": "String"
    },
    "PublicSubnetCIDR": {
      "Description": "PublicSubnetCIDR",
      "Type": "String"
    },
    "PrivateSubnetCIDR": {
      "Description": "PrivateSubnetCIDR",
      "Type": "String"
    },
    "AZ": {
      "Description": "AvailabilityZone for resources",
      "Type": "String"
    },
    "IGWID": {
      "Description": "InternetGateway for PublicRoutes",
      "Type": "String"
    }
  },
```

##### Subnets

Add the `Resources` section with two resources – the public and private subnets:

```
...
"Resources" : {
  
  "PublicSubnet" : {
    "Type" : "AWS::EC2::Subnet",
    "Properties" : {
      "VpcId" : { "Ref" : "VPCID" },
      "CidrBlock" : {"Ref" : "PublicSubnetCIDR"},
      "AvailabilityZone" : { "Ref": "AZ" },
      "Tags" : [
        {
          "Key" : "Name",
          "Value" : { "Fn::Join" : [ "-", [ {"Ref" : "AWS::StackName"}, "public-net", { "Ref": "AZ" } ] ] }
        },
        {
          "Key" : { "Fn::Join" : [ "", [ "kubernetes.io/cluster/", {"Ref" : "EKSClusterName"}] ] },
          "Value" : "shared"
        },
        {
          "Key" : "kubernetes.io/role/elb",
          "Value" : "1"
        }
      ]
    }
  },
  
  "PrivateSubnet" : {
    "Type" : "AWS::EC2::Subnet",
    "Properties" : {
      "VpcId" : { "Ref" : "VPCID" },
      "CidrBlock" : {"Ref" : "PrivateSubnetCIDR"},
      "AvailabilityZone" : { "Ref": "AZ" },
      "Tags" : [
        {
          "Key" : "Name",
          "Value" : { "Fn::Join" : [ "-", [ {"Ref" : "AWS::StackName"}, "private-net", { "Ref": "AZ" } ] ] }
        },
        {
          "Key" : { "Fn::Join" : [ "", [ "kubernetes.io/cluster/", {"Ref" : "EKSClusterName"}] ] },
          "Value" : "shared"
        },
        {
          "Key" : "kubernetes.io/role/internal-elb",
          "Value" : "1"
        }
      ]
    }
  }
  
},
```

Pay attention in the `"kubernetes.io/role/elb"` tag for the public subnet and `"kubernetes.io/role/internal-elb"` for the private one – they will be needed later for the [ALB Ingress controller](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-running-alb-ingress-controller/).

On the `Outputs` add subnets-ID to pass them to the root stack to make them available for Ansible to create a `eksctl` config-file for a future cluster, and add an AvailabilityZone here as well:

```
...
  "Outputs" : {
    
    "StackAZ" : {
      "Description" : "Stack location",
      "Value" : { "Ref" : "AZ" }
    },
    "PublicSubnetID" : {
      "Description" : "PublicSubnet ID",
      "Value" : { "Ref" : "PublicSubnet" }
    },
    "PrivateSubnetID" : {
      "Description" : "PrivateSubnet ID",
      "Value" : { "Ref" : "PrivateSubnet" }
    }
    
  }
}
```

Go back to the root template and add two more resources to create – one stack per each AvailabilityZone, so its `Resources` has to look like the next:

```
...
  "Resources": {
    "RegionNetworkStack": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "eks-region-networking.json",
        "Parameters": {
          "VPCCIDRBlock": { "Ref": "VPCCIDRBlock" },
          "EKSClusterName": { "Ref": "EKSClusterName"}
        }
      }
    },
    "AZNetworkStackA": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "eks-azs-networking.json",
        "Parameters": {
          "VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] },
          "AZ": { "Fn::Select": [ "0", { "Fn::GetAZs": "" } ] },
          "IGWID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.IGWID"] },
          "EKSClusterName": { "Ref": "EKSClusterName"},
          "PublicSubnetCIDR": "10.0.0.0/20",
          "PrivateSubnetCIDR": "10.0.32.0/20"
        }
      }
    },
    "AZNetworkStackB": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "eks-azs-networking.json",
        "Parameters": {
          "VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] },
          "AZ": { "Fn::Select": [ "1", { "Fn::GetAZs": "" } ] },
          "IGWID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.IGWID"] },
          "EKSClusterName": { "Ref": "EKSClusterName"},
          "PublicSubnetCIDR": "10.0.16.0/20",
          "PrivateSubnetCIDR": "10.0.48.0/20"
        }
      }
    }
  },
...
```

The Internet Gateway ID will be taken from the `Outputs` of the region-stack and will be passed via `Parameters` to the `AZNetworkStackА` и `AZNetworkStackB` to use for a public subnets Route.

CIDR can be hardcoded for now – later we will use the `Mappings`.

So, in the code above:

- `Fn::GetAZs"VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] }` — pass VPD ID from the region stack to AZ-stacks
- `"AZ": { "Fn::Select": ["0", { "Fn::GetAZs": "" }] }` — choose the first element (index “_0_“) from the AvailabilityZones list, and the second element (index “_1_“) for the second stack
- `PublicSubnetCIDR` and `PrivateSubnetCIDR` are hardcoded

Also, add subnets-ID from AvailabilityZones-stack to the root’s stack `Outputs` to make the accessible for Ansible for the `eksctl` parameters:

```
...
  "Outputs": {
    "VPCID" : {
      "Description" : "EKS VPC ID",
      "Value" : { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] }
    },
    "AStackAZ" : {
      "Description" : "Stack location",
      "Value" : { "Fn::GetAtt": ["AZNetworkStackA", "Outputs.StackAZ"] }
    },
    "APublicSubnetID" : {
      "Description" : "PublicSubnet ID",
      "Value" : { "Fn::GetAtt": ["AZNetworkStackA", "Outputs.PublicSubnetID"] }
    },
    "APrivateSubnetID" : {
      "Description" : "PrivateSubnet ID",
      "Value" : { "Fn::GetAtt": ["AZNetworkStackA", "Outputs.PrivateSubnetID"] }
    },
    "BStackAZ" : {
      "Description" : "Stack location",
      "Value" : { "Fn::GetAtt": ["AZNetworkStackB", "Outputs.StackAZ"] }
    },
    "BPublicSubnetID" : {
      "Description" : "PublicSubnet ID",
      "Value" : { "Fn::GetAtt": ["AZNetworkStackB", "Outputs.PublicSubnetID"] }
    },
    "BPrivateSubnetID" : {
      "Description" : "PrivateSubnet ID",
      "Value" : { "Fn::GetAtt": ["AZNetworkStackB", "Outputs.PrivateSubnetID"] }
    }
  }
}
```

Pack it, generate a new template as `/tmp/packed-eks-stacks.json`:

```
$ aws --profile arseniy --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

```
Deploy it:

```
$ aws --profile arseniy --region eu-west-2 cloudformation deploy --template-file /tmp/packed-eks-stacks.json --stack-name eks-dev
```

Check:

[![](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_130126.png "AWS: Elastic Kubernetes Service - автоматизация создания кластера, часть 1 - CloudFormation")](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_130126.png)

Okay.

Let’s finish here – need to add the following:

1. RouteTable for the public subnet
  - a Route to the 0.0.0.0/0 via Internet Gateway
  - and a SubnetRouteTableAssociation to attach this RouteTable to the public subnet in this AvailabilityZone
2. RouteTable for the private subnet
  - a Route to the 0.0.0.0/0 via NAT Gateway
  - and a SubnetRouteTableAssociation to attach this RouteTable to the private subnet in this AvailabilityZone
3. NAT Gateway
  - Elastic IP for the NAT Gateway

##### NAT Gateway

To the `Resources`  – add NAT Gateway and Elastic IP:

```
...
    "NatGwIPAddress" : {
      "Type" : "AWS::EC2::EIP",
      "Properties" : {
        "Domain" : "vpc"
      }
    },
    "NATGW" : {
      "DependsOn" : "NatGwIPAddress",
      "Type" : "AWS::EC2::NatGateway",
      "Properties" : {
        "AllocationId" : { "Fn::GetAtt" : ["NatGwIPAddress", "AllocationId"]},
        "SubnetId" : { "Ref" : "PublicSubnet"},
        "Tags" : [
          {"Key" : "Name", "Value" : { "Fn::Join" : [ "-", [ {"Ref" : "AWS::StackName"}, "nat-gw", { "Ref": "AZ" } ] ] } }
        ]
      }
    }
...
```

##### Public RouteTable

Add a RouteTable for public subnets.

For the public route, we need to have an Internet Gateway ID, which is passed from the Region stack to the Root stack, and then to the AvailabilityZones-stack.

Add a RouteTable, one Route to the _0.0.0.0/0_ via Internet Gateway and a SubnetRouteTableAssociation:

```
...
    "PublicRouteTable": {
      "Type": "AWS::EC2::RouteTable",
      "Properties": {
        "VpcId": { "Ref": "VPCID" },
        "Tags" : [
          {"Key" : "Name", "Value" : { "Fn::Join" : [ "-", [ {"Ref" : "AWS::StackName"}, "public-rtb"] ] } }
        ]
      }
    },
    
    "PublicRoute": {
      "Type": "AWS::EC2::Route",
      "Properties": {
        "RouteTableId": {
          "Ref": "PublicRouteTable"
        },
        "DestinationCidrBlock": "0.0.0.0/0",
        "GatewayId": {
          "Ref": "IGWID"
        }
      }
    },
    
    "PublicSubnetRouteTableAssociation": {
      "Type": "AWS::EC2::SubnetRouteTableAssociation",
      "DependsOn": "PublicRouteTable",
      "Properties": {
        "SubnetId": {
          "Ref": "PublicSubnet"
        },
        "RouteTableId": {
          "Ref": "PublicRouteTable"
        }
      }
    }
...
```

##### Private RouteTable

Similarly in the AvailabilityZones stack add a RouteTable and its resources, but in the Route use NAT Gateway instead of the Internet Gateway:

```
...
    "PrivateRouteTable": {
      "Type": "AWS::EC2::RouteTable",
      "Properties": {
        "VpcId": { "Ref": "VPCID" },
        "Tags" : [
          {"Key" : "Name", "Value" : { "Fn::Join" : [ "-", [ {"Ref" : "AWS::StackName"}, "priv-route", { "Ref": "AZ" } ] ] } }
        ]
      }
    },
    
    "PrivateRoute": {
      "Type": "AWS::EC2::Route",
      "Properties": {
        "RouteTableId": {
          "Ref": "PrivateRouteTable"
        },
        "DestinationCidrBlock": "0.0.0.0/0",
        "NatGatewayId": {
          "Ref": "NATGW"
        }
      }
    },
    
    "PrivateSubnetRouteTableAssociation": {
      "Type": "AWS::EC2::SubnetRouteTableAssociation",
      "Properties": {
        "SubnetId": {
          "Ref": "PrivateSubnet"
        },
        "RouteTableId": {
          "Ref": "PrivateRouteTable"
        }
      }
    }
...
```

Pack, deploy, check:

[![](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_131522.png "AWS: Elastic Kubernetes Service - автоматизация создания кластера, часть 1 - CloudFormation")](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_131522.png)

Nice – all networks and routes are Up – everything must be working now

At this moment we can spin up an EC2 instance in both Public and Private subnets to check:

1. SSH to an ЕС2 in the public subnet to check if its network connection is working
2. SSH from the ЕС2 in the public subnet – to an EC2 in the private subnet, to check the private subnet’s routing
3. `ping` from the EC2 in the private subnet somewhere to the world to check if NAT is working

#### Mappings and CIDRs for subnets

And one more thing I’d like to change in the AvailabilityZones stack is to realize a better way to create and pass CIDRs for subnets.

So, currently, we are passing a full CIDR like _10.0.0.0/16 to the_  `VPCCIDRBlock` parameter:

```
...
    "VPCCIDRBlock": {
      "Description": "VPC CidrBlock",
      "Type": "String",
      "Default": "10.0.0.0/16"
    }
...
```

And then we need to create 4 dedicated networks with /20 mask – two for public subnets, two for private.

Also, at this moment we just hardcoded those values into the template:

```
...
        "Parameters": {
          "VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] },
          "AZ": { "Fn::Select": [ "0", { "Fn::GetAZs": "" } ] },
          "IGWID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.IGWID"] },
          "PublicSubnetCIDR": "10.0.0.0/20",
          "PrivateSubnetCIDR": "10.0.32.0/20"
        }
...
```

Which is obviously not a too good idea as leaves us no flexibility at all, because we’d like to have an ability pass from a Jenkins-parameter just one block for the VPC, and let the CloufFomration do all the rest.

Let’s see, what do we have to compose such 4 networks _/20_ for a VPC with the _10.0.0.0/16_ block:

- _10.0_ – first two octets, the network “begin”
- a third octet block – _0_, _16_, _32_, _48_
- and the network mas – _/20_

Also, we will have VPCs with CIDRs _10.0.0.0/16_, _10.1.0.0/16_, _10.2.0.0/16_ for Dev, Stage, Prod, etc environments.

How can we combine all the data above?

Well – we can use the [`Fn::Split`](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-split.html) function to get the first two octets from a VPC CIDR – will get _10.0_. or _10.1_ and so on.

But what if a VPC CIDR will be _192.168.0.0/16_?… Well – then we have to grab the first two octets as dedicated objects.

And for the rest two octets and subnet mask, we could create a CloudFormation [`Mappings`](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html) and then combine all together using the [`Fn::Join`](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html) function.

Let’s try it – add a mapping to the root stack template:

```
...
  "Mappings": {
    "AZSubNets": {
      "public": {
        "zoneA": "0.0/20",
        "zoneB": "16.0/20"
      },
      "private": {
        "zoneA": "32.0/20",
        "zoneB": "48.0/20"
      }
    }
  },
...
```

And now the most interesting part here:  in the `AZNetworkStackА` and `AZNetworkStackB` resources of the root template in their `Parameters` instead of the:

```
...
"PublicSubnetCIDR": "10.0.0.0/20",
...
```

Need to construct something like:

"_\<VPC-CIDR-FIRST-OCTET\>  + \<VPC-CIDR-SECOND-OCTET\> + \<ZONE-FROM-MAPPING\>_"

I.e:

```
{ «Fn::Join» : [«.», [ { «VPC-CIDR-FIRST-TWO-OCTETS»] }, «AONE-FROM-MAPPING»] ] } }
```

To obtain the _VPC-CIDR-FIRST-OCTET_ use the [`Fn::Select`](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-select.html) and [`Fn::Split`](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-split.html) functions:

```
{ «Fn::Select» : [«0», { «Fn::Split»: [«.», { «Ref»: «VPCCIDRBlock»}]}] }
```

And in the same way for the second one, but in the [`Fn::Select`](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-select.html) use index _1_:

```
{ «Fn::Select» : [«1», { «Fn::Split»: [«.», { «Ref»: «VPCCIDRBlock»}]}] }
```

And to select data from the mapping – we can use [`Fn::FindInMap`](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-findinmap.html), where we will use a subnet’s type of _public_ or _private_ and chose by an AvailabilityZone:

```
{ «Fn::FindInMap» : [«AZSubNets», «public», «zone-a»»] }
```

So, for the `AZNetworkStackА` we will have the following code:

```
...
          "PublicSubnetCIDR": {
            "Fn::Join" : [".", [
              { "Fn::Select": [ "0", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::Select": [ "1", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::FindInMap" : [ "AZSubNets", "public", "zoneA" ] } 
            ]]
          },                  
          "PrivateSubnetCIDR": { 
            "Fn::Join" : [".", [
              { "Fn::Select": [ "0", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::Select": [ "1", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::FindInMap" : [ "AZSubNets", "private", "zoneA" ] } 
            ]]
          }
..
```

And for the `AZNetworkStackB` in the  `{ "Fn::FindInMap" : ["AZSubNets", "private", "zoneA"] }` will use the _zoneB_ selector.

All together our stacks resources have to look like the following:

```
...
    "AZNetworkStackA": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "eks-azs-networking.json",
        "Parameters": {
          "VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] },
          "AZ": { "Fn::Select": [ "0", { "Fn::GetAZs": "" } ] },
          "IGWID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.IGWID"] },
          "EKSClusterName": { "Ref": "EKSClusterName"},
          "PublicSubnetCIDR": {
            "Fn::Join" : [".", [
              { "Fn::Select": [ "0", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::Select": [ "1", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::FindInMap" : [ "AZSubNets", "public", "zoneA" ] }
            ]]
          },
          "PrivateSubnetCIDR": {
            "Fn::Join" : [".", [
              { "Fn::Select": [ "0", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::Select": [ "1", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::FindInMap" : [ "AZSubNets", "private", "zoneA" ] }
            ]]
          }
        }
      }
    },
    "AZNetworkStackB": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "eks-azs-networking.json",
        "Parameters": {
          "VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] },
          "AZ": { "Fn::Select": [ "1", { "Fn::GetAZs": "" } ] },
          "IGWID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.IGWID"] },
          "EKSClusterName": { "Ref": "EKSClusterName"},
          "PublicSubnetCIDR": {
            "Fn::Join" : [".", [
              { "Fn::Select": [ "0", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::Select": [ "1", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::FindInMap" : [ "AZSubNets", "public", "zoneB" ] }
            ]]
          },
          "PrivateSubnetCIDR": {
            "Fn::Join" : [".", [
              { "Fn::Select": [ "0", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::Select": [ "1", { "Fn::Split": [".",  { "Ref": "VPCCIDRBlock"} ] } ] },
              { "Fn::FindInMap" : [ "AZSubNets", "private", "zoneB" ] }
            ]]
          }
        }
      }
    }
...
```

Deploy, check:

[![](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_141758.png "AWS: Elastic Kubernetes Service - автоматизация создания кластера, часть 1 - CloudFormation")](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_141758.png)

Actually nothing changed, as we have our CIDRs the same as they were before this change.

### `eksctl` – a stack creation

Finally – let’s spin up a test cluster to check if everything is working, and then we can go to the Ansible and its roles.

Take the necessary parameters from the  `Outputs` of the root stack:

[![](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_141853.png "AWS: Elastic Kubernetes Service - автоматизация создания кластера, часть 1 - CloudFormation")](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_141853.png)

We can create directories now for the future Ansible `eksctl` rile, in the same ways as we did it at the very beginning of this post for the CloudFormation:

```
$ cd ../../../
$ mkdir -p roles/eksctl/{templates,tasks}
```

Now, create a cluster’s config-file `eks-cluster-config.yml`:

```
$ touch roles/eksctl/templates/eks-cluster-config.yml
$ cd roles/eksctl/templates/
```

Set the cluster’s parameters here:

```
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: eks-dev
  region: eu-west-2
  version: "1.15"
nodeGroups:
  - name: worker-nodes
    instanceType: t3.medium
    desiredCapacity: 2
    privateNetworking: true
vpc:
  id: "vpc-00f7f307d5c7ae70d"
  subnets:
    public:
      eu-west-2a:
        id: "subnet-06e8424b48709425a"
      eu-west-2b:
        id: "subnet-07a23a9e23cbb382a"
    private:
      eu-west-2a:
        id: "subnet-0c8a44bdc9aa6726f"
      eu-west-2b:
        id: "subnet-026c14589f4a41900"
  nat:
    gateway: Disable
cloudWatch:
  clusterLogging:
    enableTypes: ["*"]
```

Create the cluster:

```
$ eksctl --profile arseniy create cluster -f eks-cluster-config.yml
```

Pay attention here on the names used by the `eksctl` – it will append _eksctl_ + <CLUSTER-NAME> + _cluster_ for the cluster’s name – consider this when we will start writing Ansible roles.

The process to spin up an AWS Elastic Kubernetes Service cluster will take around 15-20 minutes, and after this CloudFormation will create another stack, for the Worker Nodes, so we can have some tea (or beer) here.

Wait for the Worker Nodes to be started:

```
...
[ℹ] nodegroup "worker-nodes" has 2 node(s)
[ℹ] node "ip-10-0-40-30.eu-west-2.compute.internal" is ready
[ℹ] node "ip-10-0-63-187.eu-west-2.compute.internal" is ready
[ℹ] kubectl command should work with "/home/setevoy/.kube/config", try 'kubectl get nodes'
[✔] EKS cluster "eks-dev" in "eu-west-2" region is ready
```

Check:

[![](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_144627.png "AWS: Elastic Kubernetes Service - автоматизация создания кластера, часть 1 - CloudFormation")](https://rtfm.co.ua/wp-content/uploads/2020/03/Screenshot_20200330_144627.png)

The stack and cluster are ready.

Our local `kubectl` already has to be configured by the `eksctl` – check the current context:

```
$ kubectl config current-context
arseniy@eks-dev.eu-west-2.eksctl.io
```

Check access to the cluster and its nodes:

```
$ kubectl get nodes
NAME                                        STATUS   ROLES    AGE   VERSION
ip-10-0-40-30.eu-west-2.compute.internal    Ready    <none>   84s   v1.15.10-eks-bac369
ip-10-0-63-187.eu-west-2.compute.internal   Ready    <none>   81s   v1.15.10-eks-bac369
```

Well, that’s all for now – we are done with the CloudFormation here.

The second part – [AWS: Elastic Kubernetes Service — автоматизация создания кластера, часть 2 — Ansible, eksctl](https://rtfm.co.ua/aws-elastic-kubernetes-service-avtomatizaciya-sozdaniya-klastera-chast-2-ansible-eksctl/) (in Russian yet, will be translated shortly).

### Useful links

#### Kubernetes

- [Introduction to Kubernetes Pod Networking](https://rtfm.co.ua/goto/https://www.weave.works/blog/introduction-to-kubernetes-pod-networking--part-1)
- [Kubernetes on AWS: Tutorial and Best Practices for Deployment](https://rtfm.co.ua/goto/https://www.weave.works/technologies/kubernetes-on-aws/)
- [How to Manage Kubernetes With Kubectl](https://rtfm.co.ua/goto/https://rancher.com/learning-paths/how-to-manage-kubernetes-with-kubectl/)
- [Building large clusters](https://rtfm.co.ua/goto/https://kubernetes.io/docs/setup/best-practices/cluster-large/)
- [Kubernetes production best practices](https://rtfm.co.ua/goto/https://learnk8s.io/production-best-practices)

#### Ansible

- [Best Practices](https://rtfm.co.ua/goto/https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html)
- [Ansible & CloudFormation for Complex Workflows](https://rtfm.co.ua/goto/https://www.trek10.com/blog/ansible-cloudformation/)

#### AWS

##### EKS

- [kubernetes cluster on AWS EKS](https://rtfm.co.ua/goto/https://www.codementor.io/@slavko/kubernetes-cluster-on-aws-eks-lecygk6rl)
- [EKS vs GKE vs AKS — Evaluating Kubernetes in the Cloud](https://rtfm.co.ua/goto/https://www.stackrox.com/post/2020/02/eks-vs-gke-vs-aks/)
- [Modular and Scalable Amazon EKS Architecture](https://rtfm.co.ua/goto/https://s3.amazonaws.com/aws-quickstart/quickstart-amazon-eks/doc/amazon-eks-architecture.pdf)
- [Build a kubernetes cluster with eksctl](https://rtfm.co.ua/goto/https://www.agilepartner.net/en/build-a-kubernetes-cluster-with-eksctl/)
- [Amazon EKS Security Group Considerations](https://rtfm.co.ua/goto/https://docs.aws.amazon.com/en_us/eks/latest/userguide/sec-group-reqs.html)

##### CloudFormation

- [Managing AWS Infrastructure as Code using Ansible, CloudFormation, and CodeBuild](https://rtfm.co.ua/goto/https://programmaticponderings.com/2019/07/30/managing-aws-infrastructure-as-code-using-ansible-cloudformation-and-codebuild/)
- [Nested CloudFormation Stack: a guide for developers and system administrators](https://rtfm.co.ua/goto/https://cloudacademy.com/blog/understanding-nested-cloudformation-stacks/)
- [Walkthrough with Nested CloudFormation Stacks](https://rtfm.co.ua/goto/https://medium.com/@janethavishka/walkthrough-with-nested-cloudformation-stacks-d7709b568162)
- [How do I pass CommaDelimitedList parameters to nested stacks in AWS CloudFormation?](https://rtfm.co.ua/goto/https://aws.amazon.com/ru/premiumsupport/knowledge-center/cloudformation-parameters-nested-stacks/)
- [How do I use multiple values for individual parameters in an AWS CloudFormation template?](https://rtfm.co.ua/goto/https://aws.amazon.com/ru/premiumsupport/knowledge-center/multiple-values-list-parameter-cli/)
- [Two years with CloudFormation: lessons learned](https://rtfm.co.ua/goto/https://sanderknape.com/2018/08/two-years-with-cloudformation-lessons-learned/)
- [Shrinking Bloated CloudFormation Templates With Nested Stack](https://rtfm.co.ua/goto/https://www.jamesqmurphy.com/blog/2019/10/nested-stack)
- [CloudFormation Best-Practices](https://rtfm.co.ua/goto/https://sbstjn.com/cloudformation.html#nested-stacks)
- [7 Awesome CloudFormation Hacks](https://rtfm.co.ua/goto/https://garbe.io/blog/2017/07/17/cloudformation-hacks/)
- [AWS CloudFormation Best Practices – Certification](https://rtfm.co.ua/goto/https://jayendrapatil.com/aws-cloudformation-best-practices-certification/)
- [Defining Resource Properties Conditionally Using AWS::NoValue on CloudFormation](https://rtfm.co.ua/goto/https://blog.shikisoft.com/aws-cloudformation-no-value-pseudo-parameter/)

- [![](https://rtfm.co.ua/wp-content/plugins/share-me//assets/images/cercle/facebook.png)](http://www.facebook.com/sharer.php?u=https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/&t=AWS+Elastic+Kubernetes+Service%3A+a+cluster+creation+automation%2C+part+1+%26%238211%3B+CloudFormation "Share on Facebook")
- [![](https://rtfm.co.ua/wp-content/plugins/share-me//assets/images/cercle/twitter.png)](http://twitter.com/share?text=AWS+Elastic+Kubernetes+Service%3A+a+cluster+creation+automation%2C+part+1+%26%238211%3B+CloudFormation-&url=https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/&via=RTFM:%20Linux,%20DevOps%20and%20system%20administration "Tweet This Post")
- [![](https://rtfm.co.ua/wp-content/plugins/share-me//assets/images/cercle/googleplus.png)](https://plusone.google.com/_/+1/confirm?hl=fr-FR&url=https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/ "Share on Google+")
- [![](https://rtfm.co.ua/wp-content/plugins/share-me//assets/images/cercle/tumblr.png)](http://www.tumblr.com/share/photo?source=https%3A%2F%2Frtfm.co.ua%2Fwp-content%2Fuploads%2F2020%2F04%2Famazon-eks-1.png&caption=AWS+Elastic+Kubernetes+Service%3A+a+cluster+creation+automation%2C+part+1+%26%238211%3B+CloudFormation&clickthru=https%3A%2F%2Frtfm.co.ua%2Fen%2Faws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation%2F "Share on Tumblr")
- [![](https://rtfm.co.ua/wp-content/plugins/share-me//assets/images/cercle/linkedin.png)](http://www.linkedin.com/shareArticle?mini=true&url=https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/&title=AWS+Elastic+Kubernetes+Service%3A+a+cluster+creation+automation%2C+part+1+%26%238211%3B+CloudFormation&source=RTFM:%20Linux,%20DevOps%20and%20system%20administration)

  

### Similar posts

- <small>03/31/2020</small> [AWS Elastic Kubernetes Service: автоматизация создания кластера, часть 1 – CloudFormation](https://rtfm.co.ua/aws-elastic-kubernetes-service-avtomatizaciya-sozdaniya-klastera-chast-1-cloudformation/)<small> (0)</small>  
- <small>03/31/2020</small> [AWS Elastic Kubernetes Service: – автоматизация создания кластера, часть 2 — Ansible, eksctl](https://rtfm.co.ua/aws-elastic-kubernetes-service-avtomatizaciya-sozdaniya-klastera-chast-2-ansible-eksctl/)<small> (0)</small>  
- <small>10/08/2017</small> [Ansible: миграция RTFM 2.2 – RTFM Jenkins provision](https://rtfm.co.ua/ansible-rtfm-jenkins-provision/)<small> (0)</small>  
- <small>04/21/2020</small> [AWS Elastic Kubernetes Service: running ALB Ingress controller](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-running-alb-ingress-controller/)<small> (0)</small>  
- <small>03/26/2020</small> [AWS Elastic Kubernetes Service: запуск ALB Ingress controller](https://rtfm.co.ua/kubernetes-zapusk-alb-ingress-controller-dlya-aws-elastic-kubernetes-service/)<small> (0)</small>