[AWS][CloudFormation] Bastion Host Setup
Table of contents
Context
This is a high-level walkthrough of the YAML definition of the CloudFormation templates used in my article on setting up a Bastion Host. Please refer to this GitHub repository for the CloudFormation templates.
Prerequisites
You will need to have
Some basic knowledge in navigating AWS Management Console
An existing S3 bucket to be able to run nested CloudFormation stack
An IAM account that has the required permission to run CloudFormation
An existing KeyPair
Why CloudFormation?
CloudFormation is native to AWS. So there will be minimal installation and/or setup required by the readers, other than having an AWS account.
I understand that the trending IaC tool is Terraform. I am still learning, so perhaps I will write an article for the Terraform equivalent as well!
Shorthands and Pseudo Parameters
In the CloudFormation templates, you will notice the following shorthands such as:
!GetAtt
is to use the values fromOutputs
section or an attribute from a resource.!Ref
is to return the value of a specific parameter or resource.!Sub
is to substitute variables in an input string with values.!FindInMap
is to return the value corresponding to keys in a two-level map declared in theMappings
section.
As this is a small-scale usage of CloudFormation, I only use 1 pseudo parameter in the templates, i.e. ${AWS::StackName}
which represents the CloudFormation stack name.
Walkthrough of CloudFormation Templates
The CloudFormation templates are main.yml
and components.yml
.
main.yml
In this CloudFormation template, it is meant to take in the parameters and run the nested stacks. You can think of this script as the wrapper script to run other CloudFormation templates.
This template is known as the master template, which is used to generate the parent stack and create the EC2 instances in the respective subnets.
Mappings
Section
This section is optional and used to create unique name-value pairs. The values can be String
or List
types. You can find it at Line 4 - 11
.
In the template, it is used to map the resource specifications (memory and CPU) to the instance class. For this exercise, I have limited the choices so that you will not accidentally set any larger instances that cost much more.
Parameters
Section
This section is self-explanatory, i.e. where you can define the required inputs for the template to run. You can observe this when you are creating the CloudFormation Stack. You can find it at Line 13 - 66
.
AWS::CloudFormation::Interface
To make the template more user-friendly, I used the AWS::CloudFormation::Interface
under the Metadata
section. This allows me to group the parameters together and classify them under a more meaningful parameter group label. You can find it at Line 68 - 88
.
Resources
Section
This is the most important section, where you will be defining what you want CloudFormation to provision for you. The master template will reference the component template, which I named ComponentStack
. The TemplateURL
property will refer to one of our parameters i.e. ComponentTemplateURL
. The ComponentStack
is a user-defined nested stack. As we provision EC2 instances in the private and public subnet respectively, you can observe that the mapping for the instance class is being used here along with outputs from the ComponentStack
. You can find it from Line 90
onwards.
components.yml
In this CloudFormation template, it will be used to provision the following components that will be utilized by the EC2 instance
Private subnet
Route table for the private subnet
Security groups
Referencing from the ComponentStack
defined in main.yml
, you can see that there are matching parameters defined in component.yml
where the parameters' values are passed from the master template to the component template.
ComponentStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref ComponentTemplateURL
Parameters:
MyPublicIp: !Ref MyPublicIp
VpcId: !Ref VpcId
DefaultPublicSubnetCidr: !Ref DefaultPublicSubnetCidr
PrivateSubnetCidr: !Ref PrivateSubnetCidr
TimeoutInMinutes: 3
Outputs
Section
In this section, you can simply think it as a return value from the component stack. This is useful especially when you have values you want to use in our CloudFormation stack. In the templates, I used to output the security group ID, private subnet ID and private route table association ID.
Usage
High-Level Flow
- Clone the repository
# Clone the repository
git clone https://github.com/bernicecpz/hashnode_resources
# Navigate to location of the CloudFormation templates
cd hashnode_resources/aws_series/cf_templates/01_bastionhostsetup/
Create an S3 bucket if not done so.
Upload both files into the S3 bucket created in step 1
Create a new CloudFormation stack
- Provide the S3 bucket URL for the
main.yml
file
- Upon the completion of the provisioning of the resources via the CloudFormation, you can now access the setup of the EC2 instance.
Create CloudFormation Stack
In the searchbar, enter
CloudFormation
. Select the CloudFormation serviceClick
Create Stack
Under the
Specify template
section, provide the S3 URL formain.yml
.Please provide the following:
Stack name
PublicSubnet
DefaultPublicSubnetCidr
KeyName
Click
Next
3 times.- You can leave the configurations under
Configure stack options
as default.
- You can leave the configurations under
Review the parameters under Step 2. Make sure all of the values provided are filled.
Acknowledge the following capabilities. Click
Create stack
Additional Note
Want to find out how to create the components and EC2 instances via the AWS Management Console? You can refer to this article instead!
Resources
Here are some resources you can refer to for more information. Happy reading!