---
title: "How To Setup Bastion Host on AWS using CloudFormation Template"
lang: "en"
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/post/how-to-setup-bastion-host-on-aws-using-cloudformation-template
---

![Blog post image for How To Setup Bastion Host on AWS using CloudFormation Template - Learn how to set up a secure Bastion Host on AWS using CloudFormation templates. This tutorial covers the steps to create a VPC, subnets, security groups, and instances, then test Internet connectivity. Detailed instructions and sample code are included.](/_astro/hero.DX_FnUff_Z2tzyHo.webp)

[Home](/)›[Blog](/blog)›[All Categories](/blog/categories)›[AWS](/blog/categories/aws)

Blog

[Prev in AWSHow To Setup Bastion Host on AWS using AWS CLI](/blog/post/how-to-setup-bastion-host-on-aws-using-aws-cli)[Next in AWSHow to Setup Jenkins on AWS Using CloudFormation](/blog/post/how-to-setup-jenkins-on-aws-using-cloudformation)

[AWS](/blog/categories/aws)[CloudFormation](/blog/categories/cloudformation)[Security](/blog/categories/security)[Networking](/blog/categories/networking)[DevOps](/blog/categories/devops)

# How To Setup Bastion Host on AWS using CloudFormation Template

[Mohammad Abu Mattar](/authors/mohammad-abu-mattar)Published: 10 Jan 202309 Mins read05 Mins listen

[Markdown for AI(opens in a new tab)](/post/how-to-setup-bastion-host-on-aws-using-cloudformation-template/index.md "Open the plain-Markdown version of this page, for pasting into an AI tool")

TL;DR

Learn how to set up a secure Bastion Host on AWS using CloudFormation templates. This tutorial covers the steps to create a VPC, subnets, security groups, and instances, then test Internet connectivity. Detailed instructions and sample code are included.

Series

[AWS Bastion Host Setup](/series/aws-bastion-host-setup)2/2

[PreviousHow To Setup Bastion Host on AWS using AWS CLI](/blog/post/how-to-setup-bastion-host-on-aws-using-aws-cli)

All posts in this series (2)

Blog2

1.  [How To Setup Bastion Host on AWS using AWS CLI](/blog/post/how-to-setup-bastion-host-on-aws-using-aws-cli)
2.  [How To Setup Bastion Host on AWS using CloudFormation TemplateYou are here](/blog/post/how-to-setup-bastion-host-on-aws-using-cloudformation-template)

### How To Setup Bastion Host on AWS using CloudFormation Template

Contents

[Introduction](#introduction)[Prerequisites](#prerequisites)[Setting up a Bastion host with a CloudFormation template](#setting-up-a-bastion-host-with-a-cloudformation-template)[Create a CloudFormation template](#create-a-cloudformation-template)[Deploying the stack](#deploying-the-stack)[Testing the stack](#testing-the-stack)[\# Step 1: Connect to the bastion host](#-step-1-connect-to-the-bastion-host)[\# Step 2: Connect to the private instance](#-step-2-connect-to-the-private-instance)[\# Step 3: Test the connection](#-step-3-test-the-connection)[Cleanup](#cleanup)[Conclusion](#conclusion)[References](#references)

## [Introduction](#introduction)

In the previous post, [How To Setup Bastion Host on AWS using AWS CLI](/blog/post/how-to-setup-bastion-host-on-aws-using-aws-cli), we set up a Bastion host with the AWS CLI. Here we will do the same thing with a CloudFormation template instead. We will walk through the process of creating the template, deploying it, and connecting to the instances created by the template. This is a simple and efficient way of setting up a Bastion host on AWS.

## [Prerequisites](#prerequisites)

Before starting, make sure that you have the following:

-   AWS CLI installed and configured on your local machine. You can follow the instructions on [Installing the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) to install and configure it.
-   An IAM user with the following permissions:
    -   AmazonVPCFullAccess
    -   AmazonEC2FullAccess
    -   AWSCloudFormationFullAccess
-   Basic knowledge of networking and SSH
-   Familiarity with YAML and AWS CloudFormation
-   AWS CLI version 2 or later.
-   The AWS CLI configured with the desired credentials
-   Knowledge of AWS basic building blocks such as VPCs, Subnets, Security Groups, Elastic IPs, and EC2 instances.

To create an IAM user, follow the instructions on [Creating an IAM User](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html).

## [Setting up a Bastion host with a CloudFormation template](#setting-up-a-bastion-host-with-a-cloudformation-template)

### [Create a CloudFormation template](#create-a-cloudformation-template)

Create a new file called `bastion-host-with-vpc.yml` and include the CloudFormation template code. This should include the necessary resources such as VPC, subnets, security groups, Elastic IPs, Internet Gateway, NAT Gateway, and EC2 instances. Make sure to specify the correct parameters such as subnet IDs, security group IDs, and key pair names.

bastion-host-with-vpc.yml

```
1AWSTemplateFormatVersion: '2010-09-09'2Description: >-3  This template creates a VPC with 2 subnets, 1 public and 1 private. It also4  Internet Gateway, NAT Gateway, Route Tables, Security Groups and EC2 Instances5  for Bastion Host and Private Instance.6
7Parameters:8  EnvironmentName:9    Description: >-10      An environment name that will be prefixed to resource names11    Type: String12    AllowedValues:13      - dev14      - test15      - prod16    Default: dev17  VPCName:18    Description: >-19      The name of the VPC. This name is used as a tag value for the VPC and20      subnets.21    Type: String22    Default: bastion-host-vpc23    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$24    ConstraintDescription: >-25      VPC name can include numbers, lowercase letters, uppercase letters, and26      hyphens (-). It cannot start or end with a hyphen (-).27  VPCCIDR:28    Description: >-29      The CIDR block for the VPC. This should be a valid private (RFC 1918)30      CIDR range.31    Type: String32    Default: 10.0.0.0/1633    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-9]|3[0-2]))$34    ConstraintDescription: >-35      CIDR block parameter must be in the form x.x.x.x/16-32, where x is a36      number from 0-255, and /16-32 is a valid CIDR range.37  PublicSubnetName:38    Description: >-39      The name of the public subnet. This name is used as a tag value for the40      subnet.41    Type: String42    Default: bastion-host-public-subnet43    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$44    ConstraintDescription: >-45      Subnet name can include numbers, lowercase letters, uppercase letters, and46      hyphens (-). It cannot start or end with a hyphen (-).47  PublicSubnetCIDR:48    Description: >-49      The CIDR block for the public subnet. This should be a valid private (RFC50      1918) CIDR range that is /24 or larger.51    Type: String52    Default: 10.0.0.0/2453    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(2[4-9]|3[0-2]))$54    ConstraintDescription: >-55      CIDR block parameter must be in the form x.x.x.x/24-32, where x is a56      number from 0-255, and /24-32 is a valid CIDR range.57  PrivateSubnetName:58    Description: >-59      The name of the private subnet. This name is used as a tag value for the60      subnet.61    Type: String62    Default: bastion-host-private-subnet63    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$64    ConstraintDescription: >-65      Subnet name can include numbers, lowercase letters, uppercase letters, and66      hyphens (-). It cannot start or end with a hyphen (-).67  PrivateSubnetCIDR:68    Description: >-69      The CIDR block for the private subnet. This should be a valid private (RFC70      1918) CIDR range that is /24 or larger.71    Type: String72    Default: 10.0.16.0/2473    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(2[4-9]|3[0-2]))$74    ConstraintDescription: >-75      CIDR block parameter must be in the form x.x.x.x/24-32, where x is a76      number from 0-255, and /24-32 is a valid CIDR range.77  InternetGatewayName:78    Description: >-79      The name of the Internet Gateway. This name is used as a tag value for the80      Internet Gateway.81    Type: String82    Default: bastion-host-igw83    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$84    ConstraintDescription: >-85      Internet Gateway name can include numbers, lowercase letters, uppercase86      letters, and hyphens (-). It cannot start or end with a hyphen (-).87  NATGatewayName:88    Description: >-89      The name of the NAT Gateway. This name is used as a tag value for the NAT90      Gateway.91    Type: String92    Default: bastion-host-ngw93    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$94    ConstraintDescription: >-95      NAT Gateway name can include numbers, lowercase letters, uppercase96      letters, and hyphens (-). It cannot start or end with a hyphen (-).97  PublicRouteTableName:98    Description: >-99      The name of the public route table. This name is used as a tag value for100      the route table.101    Type: String102    Default: bastion-host-public-route-table103    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$104    ConstraintDescription: >-105      Route table name can include numbers, lowercase letters, uppercase106      letters, and hyphens (-). It cannot start or end with a hyphen (-).107  PrivateRouteTableName:108    Description: >-109      The name of the private route table. This name is used as a tag value for110      the route table.111    Type: String112    Default: bastion-host-private-route-table113    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$114    ConstraintDescription: >-115      Route table name can include numbers, lowercase letters, uppercase116      letters, and hyphens (-). It cannot start or end with a hyphen (-).117  BastionHostName:118    Description: >-119      The name of the Bastion Host instance. This name is used as a tag value120      for the instance.121    Type: String122    Default: bastion-host-instance123    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$124  BastionHostKeyPair:125    Description: >-126      The name of an existing EC2 KeyPair to enable SSH access to the Bastion127      Host.128    Type: AWS::EC2::KeyPair::KeyName129  BastionHostType:130    Description: >-131      The instance type to use for the Bastion Host.132    Type: String133    AllowedValues:134      - t2.nano135      - t2.micro136      - t2.small137      - t2.medium138      - t2.large139      - t2.xlarge140      - t2.2xlarge141      - t3.nano142      - t3.micro143      - t3.small144      - t3.medium145      - t3.large146      - t3.xlarge147      - t3.2xlarge148      - m1.small149      - m1.medium150      - m1.large151      - m1.xlarge152      - m2.xlarge153      - m2.2xlarge154      - m2.4xlarge155      - m3.medium156      - m3.large157      - m3.xlarge158      - m3.2xlarge159      - m4.large160      - m4.xlarge161      - m4.2xlarge162      - m4.4xlarge163      - m4.10xlarge164      - m4.16xlarge165      - m5.large166      - m5.xlarge167      - m5.2xlarge168      - m5.4xlarge169      - m5.12xlarge170      - m5.24xlarge171      - m5d.large172      - m5d.xlarge173      - m5d.2xlarge174      - m5d.4xlarge175      - m5d.12xlarge176      - m5d.24xlarge177      - c1.medium178      - c1.xlarge179      - c3.large180      - c3.xlarge181      - c3.2xlarge182      - c3.4xlarge183      - c3.8xlarge184      - c4.large185      - c4.xlarge186      - c4.2xlarge187      - c4.4xlarge188      - c4.8xlarge189      - c5.large190      - c5.xlarge191      - c5.2xlarge192      - c5.4xlarge193      - c5.9xlarge194    Default: t2.micro195    ConstraintDescription: >-196      Must be a valid EC2 instance type.197  BastionHostAMI:198    Description: >-199      The ID of the Amazon Machine Image (AMI) that you want to use to launch200      the Bastion Host instance.201    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>202    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2203  BastionHostSecurityGroupName:204    Description: >-205      The name of the security group to assign to the Bastion Host instance.206    Type: String207    Default: bastion-host-security-group208    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$209    ConstraintDescription: >-210      Security group name can include numbers, lowercase letters, uppercase211      letters, and hyphens (-). It cannot start or end with a hyphen (-).212  BastionHostSecurityGroupDescription:213    Description: >-214      The description of the security group to assign to the Bastion Host215      instance.216    Type: String217    Default: Bastion Host security group218    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9\s]*[a-zA-Z0-9]$219    ConstraintDescription: >-220      Security group description can include numbers, lowercase letters,221      uppercase letters, and hyphens (-). It cannot start or end with a hyphen222      (-).223  PrivateInstanceName:224    Description: >-225      The name of the private instance. This name is used as a tag value for the226      instance.227    Type: String228    Default: bastion-host-private-instance229    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$230    ConstraintDescription: >-231      Instance name can include numbers, lowercase letters, uppercase letters,232      and hyphens (-). It cannot start or end with a hyphen (-).233  PrivateInstanceKeyPair:234    Description: >-235      The name of the Amazon EC2 key pair that you want to use to connect to the236      private instance.237    Type: AWS::EC2::KeyPair::KeyName238    ConstraintDescription: >-239      Must be the name of an existing Amazon EC2 key pair.240  PrivateInstanceType:241    Description: >-242      The instance type to use for the private instance.243    Type: String244    AllowedValues:245      - t2.nano246      - t2.micro247      - t2.small248      - t2.medium249      - t2.large250      - t2.xlarge251      - t2.2xlarge252      - t3.nano253      - t3.micro254      - t3.small255      - t3.medium256      - t3.large257      - t3.xlarge258      - t3.2xlarge259      - m1.small260      - m1.medium261      - m1.large262      - m1.xlarge263      - m2.xlarge264      - m2.2xlarge265      - m2.4xlarge266      - m3.medium267      - m3.large268      - m3.xlarge269      - m3.2xlarge270      - m4.large271      - m4.xlarge272      - m4.2xlarge273      - m4.4xlarge274      - m4.10xlarge275      - m4.16xlarge276      - m5.large277      - m5.xlarge278      - m5.2xlarge279      - m5.4xlarge280      - m5.12xlarge281      - m5.24xlarge282      - m5d.large283      - m5d.xlarge284      - m5d.2xlarge285      - m5d.4xlarge286      - m5d.12xlarge287      - m5d.24xlarge288      - c1.medium289      - c1.xlarge290      - c3.large291      - c3.xlarge292      - c3.2xlarge293      - c3.4xlarge294      - c3.8xlarge295      - c4.large296      - c4.xlarge297      - c4.2xlarge298      - c4.4xlarge299      - c4.8xlarge300      - c5.large301      - c5.xlarge302      - c5.2xlarge303      - c5.4xlarge304      - c5.9xlarge305    Default: t2.micro306    ConstraintDescription: >-307      Must be a valid EC2 instance type.308  PrivateInstanceAMI:309    Description: >-310      The ID of the Amazon Machine Image (AMI) that you want to use to launch311      the private instance.312    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>313    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2314  PrivateInstanceSecurityGroupName:315    Description: >-316      The name of the security group to assign to the private instance.317    Type: String318    Default: bastion-host-private-instance-security-group319    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$320    ConstraintDescription: >-321      Security group name can include numbers, lowercase letters, uppercase322      letters, and hyphens (-). It cannot start or end with a hyphen (-).323  PrivateInstanceSecurityGroupDescription:324    Description: >-325      The description of the security group to assign to the private instance.326    Type: String327    Default: Bastion Host private instance security group328    AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9\s]*[a-zA-Z0-9]$329    ConstraintDescription: >-330      Security group description can include numbers, lowercase letters,331      uppercase letters, and hyphens (-). It cannot start or end with a hyphen332      (-).333
334Metadata:335  Author: Mohammad Abu Mattar336  Version: 1.0337  AWS::CloudFormation::Interface:338    ParameterGroups:339      - Label:340          default: Environment341        Parameters:342          - EnvironmentName343      - Label:344          default: Natwork Configuration345        Parameters:346          - VPCName347          - VPCCIDR348          - PublicSubnetName349          - PublicSubnetCIDR350          - PrivateSubnetName351          - PrivateSubnetCIDR352          - InternetGatewayName353          - NATGatewayName354          - PublicRouteTableName355          - PrivateRouteTableName356      - Label:357          default: Bastion Host Configuration358        Parameters:359          - BastionHostName360          - BastionHostKeyPair361          - BastionHostType362          - BastionHostAMI363          - BastionHostSecurityGroupName364          - BastionHostSecurityGroupDescription365      - Label:366          default: Private Instance Configuration367        Parameters:368          - PrivateInstanceName369          - PrivateInstanceKeyPair370          - PrivateInstanceType371          - PrivateInstanceAMI372          - PrivateInstanceSecurityGroupName373          - PrivateInstanceSecurityGroupDescription374
375    ParameterLabels:376      EnvironmentName:377        default: Environment Name378      VPCName:379        default: VPC Name380      VPCCIDR:381        default: VPC CIDR382      PublicSubnetName:383        default: Public Subnet Name384      PublicSubnetCIDR:385        default: Public Subnet CIDR386      PrivateSubnetName:387        default: Private Subnet Name388      PrivateSubnetCIDR:389        default: Private Subnet CIDR390      InternetGatewayName:391        default: Internet Gateway Name392      NATGatewayName:393        default: NAT Gateway Name394      PublicRouteTableName:395        default: Public Route Table Name396      PrivateRouteTableName:397        default: Private Route Table Name398      BastionHostName:399        default: Bastion Host Name400      BastionHostKeyPair:401        default: Bastion Host Key Pair402      BastionHostType:403        default: Bastion Host Type404      BastionHostAMI:405        default: Bastion Host AMI406      BastionHostSecurityGroupName:407        default: Bastion Host Security Group Name408      BastionHostSecurityGroupDescription:409        default: Bastion Host Security Group Description410      PrivateInstanceName:411        default: Private Instance Name412      PrivateInstanceKeyPair:413        default: Private Instance Key Pair414      PrivateInstanceType:415        default: Private Instance Type416      PrivateInstanceAMI:417        default: Private Instance AMI418      PrivateInstanceSecurityGroupName:419        default: Private Instance Security Group Name420      PrivateInstanceSecurityGroupDescription:421        default: Private Instance Security Group Description422
423Resources:424  VPC:425    Type: AWS::EC2::VPC426    Properties:427      CidrBlock: !Ref VPCCIDR428      EnableDnsSupport: true429      EnableDnsHostnames: true430      Tags:431        - Key: Name432          Value: !Ref VPCName433
434  PublicSubnet:435    Type: AWS::EC2::Subnet436    DependsOn:437      - VPC438    Properties:439      VpcId: !Ref VPC440      CidrBlock: !Ref PublicSubnetCIDR441      AvailabilityZone: !Select [0, !GetAZs '']442      Tags:443        - Key: Name444          Value: !Ref PublicSubnetName445  PriveSubnet:446    Type: AWS::EC2::Subnet447    DependsOn:448      - VPC449    Properties:450      VpcId: !Ref VPC451      CidrBlock: !Ref PrivateSubnetCIDR452      AvailabilityZone: !Select [0, !GetAZs '']453      Tags:454        - Key: Name455          Value: !Ref PrivateSubnetName456
457  IGW:458    Type: AWS::EC2::InternetGateway459    DependsOn:460      - VPC461    Properties:462      Tags:463        - Key: Name464          Value: !Ref InternetGatewayName465  IGWAttachment:466    Type: AWS::EC2::VPCGatewayAttachment467    DependsOn:468      - VPC469      - IGW470    Properties:471      VpcId: !Ref VPC472      InternetGatewayId: !Ref IGW473
474  NATGatewayEIP:475    Type: AWS::EC2::EIP476    DependsOn:477      - VPC478    Properties:479      Domain: vpc480  NATGateway:481    Type: AWS::EC2::NatGateway482    DependsOn:483      - VPC484      - PublicSubnet485      - NATGatewayEIP486    Properties:487      AllocationId: !GetAtt NATGatewayEIP.AllocationId488      SubnetId: !Ref PublicSubnet489      Tags:490        - Key: Name491          Value: !Ref NATGatewayName492
493  PublicRouteTable:494    Type: AWS::EC2::RouteTable495    DependsOn:496      - VPC497    Properties:498      VpcId: !Ref VPC499      Tags:500        - Key: Name501          Value: !Ref PublicRouteTableName502  PrivateRouteTable:503    Type: AWS::EC2::RouteTable504    DependsOn:505      - VPC506    Properties:507      VpcId: !Ref VPC508      Tags:509        - Key: Name510          Value: !Ref PrivateRouteTableName511
512  PublicRoute:513    Type: AWS::EC2::Route514    DependsOn:515      - PublicRouteTable516      - IGW517    Properties:518      RouteTableId: !Ref PublicRouteTable519      DestinationCidrBlock: 0.0.0.0/0520      GatewayId: !Ref IGW521  PrivateRoute:522    Type: AWS::EC2::Route523    DependsOn:524      - PrivateRouteTable525      - NATGateway526    Properties:527      RouteTableId: !Ref PrivateRouteTable528      DestinationCidrBlock: 0.0.0.0/0529      NatGatewayId: !Ref NATGateway530
531  PublicSubnetRouteTableAssociation:532    Type: AWS::EC2::SubnetRouteTableAssociation533    DependsOn:534      - PublicSubnet535      - PublicRouteTable536    Properties:537      SubnetId: !Ref PublicSubnet538      RouteTableId: !Ref PublicRouteTable539  PrivateSubnetRouteTableAssociation:540    Type: AWS::EC2::SubnetRouteTableAssociation541    DependsOn:542      - PriveSubnet543      - PrivateRouteTable544    Properties:545      SubnetId: !Ref PriveSubnet546      RouteTableId: !Ref PrivateRouteTable547
548  BastionHostSecurityGroup:549    Type: AWS::EC2::SecurityGroup550    DependsOn:551      - VPC552    Properties:553      GroupDescription: !Ref BastionHostSecurityGroupDescription554      VpcId: !Ref VPC555      SecurityGroupIngress:556        - IpProtocol: tcp557          FromPort: 22558          ToPort: 22559          CidrIp: 0.0.0.0/0560      SecurityGroupEgress:561        - IpProtocol: tcp562          FromPort: 0563          ToPort: 65535564          CidrIp: 0.0.0.0/0565      Tags:566        - Key: Name567          Value: !Ref BastionHostSecurityGroupName568  PrivateInstanceSecurityGroup:569    Type: AWS::EC2::SecurityGroup570    DependsOn:571      - VPC572    Properties:573      GroupDescription: !Ref PrivateInstanceSecurityGroupDescription574      VpcId: !Ref VPC575      SecurityGroupIngress:576        - IpProtocol: tcp577          FromPort: 22578          ToPort: 22579          SourceSecurityGroupId: !Ref BastionHostSecurityGroup580      SecurityGroupEgress:581        - IpProtocol: tcp582          FromPort: 0583          ToPort: 65535584          CidrIp: 0.0.0.0/0585      Tags:586        - Key: Name587          Value: !Ref PrivateInstanceSecurityGroupName588
589  BastionHost:590    Type: AWS::EC2::Instance591    DependsOn:592      - PublicSubnet593      - BastionHostSecurityGroup594    Properties:595      ImageId: !Ref BastionHostAMI596      InstanceType: !Ref BastionHostType597      KeyName: !Ref BastionHostKeyPair598      NetworkInterfaces:599        - AssociatePublicIpAddress: true600          DeviceIndex: 0601          GroupSet:602            - !Ref BastionHostSecurityGroup603          SubnetId: !Ref PublicSubnet604      Tags:605        - Key: Name606          Value: !Ref BastionHostName607  PrivateInstance:608    Type: AWS::EC2::Instance609    DependsOn:610      - PriveSubnet611      - PrivateInstanceSecurityGroup612    Properties:613      ImageId: !Ref PrivateInstanceAMI614      InstanceType: !Ref PrivateInstanceType615      KeyName: !Ref PrivateInstanceKeyPair616      NetworkInterfaces:617        - AssociatePublicIpAddress: false618          DeviceIndex: 0619          GroupSet:620            - !Ref PrivateInstanceSecurityGroup621          SubnetId: !Ref PriveSubnet622      Tags:623        - Key: Name624          Value: !Ref PrivateInstanceName625
626Outputs:627  BastionHostSSH:628    Description: SSH command to connect to the bastion host629    Value: !Join630      - ''631      - - ssh -i ~/.ssh/632        - !Ref BastionHostKeyPair633        - .pem ec2-user@634        - !GetAtt BastionHost.PublicIp635  PrivateInstanceSSH:636    Description: SSH command to connect to the private instance637    Value: !Join638      - ''639      - - ssh -i ~/.ssh/640        - !Ref PrivateInstanceKeyPair641        - .pem ec2-user@642        - !GetAtt PrivateInstance.PrivateIp
```

### [Deploying the stack](#deploying-the-stack)

Use the AWS CLI or the AWS CloudFormation console to create a new stack using the `bastion-host-with-vpc.yml` template file. Provide the necessary parameters such as stack name and region. Make sure that the IAM user has the correct permissions to create the resources specified in the template.

![AWS CloudFormation console](/assets/blog/0035-how-to-setup-bastion-host-on-aws-using-cloudformation-template/aws-cloudformation-console.png)

### [Testing the stack](#testing-the-stack)

Once the stack is deployed, use the SSH commands provided in the outputs to connect to the bastion host and the private instance. Verify that the instances are running, and that the Internet connectivity is working by running the appropriate commands on the instances.

#### [Step 1: Connect to the bastion host](#step-1-connect-to-the-bastion-host)

We can use the SSH command provided in the outputs to connect to the bastion host.

Terminal window

```
1ssh -i ~/.ssh/BastionHostKeyPair.pem ec2-user@<BastionHostPublicIp>
```

![Bastion host](/assets/blog/0035-how-to-setup-bastion-host-on-aws-using-cloudformation-template/connect-to-the-bastion-host.png)

#### [Step 2: Connect to the private instance](#step-2-connect-to-the-private-instance)

We can use the SSH command provided in the outputs to connect to the private instance.

Terminal window

```
1ssh -i ~/.ssh/PrivateInstanceKeyPair.pem ec2-user@<PrivateInstancePrivateIp>
```

#### [Step 3: Test the connection](#step-3-test-the-connection)

Now that you are connected to the private host, you can check if the host has internet connectivity by pinging a public IP or URL:

Terminal window

```
1ping -c 4 google.com
```

The above command will send 4 ICMP echo requests to the IP address of Google’s website, and the private host will respond with 4 ICMP echo replies if it can reach the internet. This verifies that the NAT gateway and route tables are configured correctly.

Alternatively, you can check internet connectivity by using the `curl` command to download a webpage:

![Internet Connectivity](/assets/blog/0035-how-to-setup-bastion-host-on-aws-using-cloudformation-template/internet-connectivity.png)

Terminal window

```
1curl -s https://www.mkabumattar.tech
```

This downloads the website’s source code and prints it to the terminal, which tells you whether a response came back at all. If the private host has internet connectivity, it will show the webpage’s source code.

If you run these commands from the bastion host, you might not face the same restrictions as the private host. In that case, run them from the private host, or pick a site for the test that is blocked for your private network.

![Internet Connectivity](/assets/blog/0035-how-to-setup-bastion-host-on-aws-using-cloudformation-template/internet-connectivity-2.png)

### [Cleanup](#cleanup)

When you are done testing and using the stack, it is a good practice to clean up and delete the stack using the AWS CLI, AWS Console, or the AWS CloudFormation console to avoid unnecessary charges.

## [Conclusion](#conclusion)

In this tutorial, we’ve learned how to set up a Bastion Host on AWS using CloudFormation templates. We’ve gone through the process of creating the template, deploying the stack, testing the connection, and deleting the stack. With CloudFormation templates, we can provision and manage our infrastructure in an automated and repeatable way. It’s an efficient method to set up and scale infrastructure. However, it’s important to make sure to clean up the resources when no longer in use to avoid incurring unnecessary costs.

## [References](#references)

-   [AWS CloudFormation User Guide](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html)
-   [AWS CloudFormation Template Reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-reference.html)
-   [AWS Command Line Interface (CLI) User Guide](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)
-   [AWS Identity and Access Management (IAM) User Guide](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html)
-   [Amazon Virtual Private Cloud (VPC) User Guide](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html)
-   [Amazon EC2 User Guide for Linux Instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html)
-   [Security Groups for your VPC](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html)
-   [NAT Gateways - Amazon VPC](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html)
-   [Internet Gateways - Amazon VPC](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html)
-   [AWS Key Pairs and Amazon EC2 Instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html)
-   [Linux Bastion Hosts on AWS (Quick Start Reference Deployment)](https://aws.amazon.com/quickstart/architecture/linux-bastion/)
-   [Controlling Network Traffic with Security Groups](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)
-   [AWS CloudFormation Best Practices](https://aws.amazon.com/blogs/devops/aws-cloudformation-best-practices/)
-   [What is an Elastic IP address?](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html)
-   [Connect to your Linux instance using SSH](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html)

Was this useful?

## Tags

[#AWS Bastion Host](/blog/tags/aws-bastion-host)[#CloudFormation Template](/blog/tags/cloudformation-template)[#VPC Setup](/blog/tags/vpc-setup)[#EC2 Instance](/blog/tags/ec2-instance)[#AWS Security Groups](/blog/tags/aws-security-groups)[#NAT Gateway](/blog/tags/nat-gateway)[#Internet Gateway](/blog/tags/internet-gateway)[#Secure Remote Access](/blog/tags/secure-remote-access)[#Infrastructure as Code](/blog/tags/infrastructure-as-code)[#AWS CLI](/blog/tags/aws-cli)

## Share

[Facebook](https://facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fhow-to-setup-bastion-host-on-aws-using-cloudformation-template "Share on Facebook")[Twitter](https://twitter.com/intent/tweet/?text=How%20To%20Setup%20Bastion%20Host%20on%20AWS%20using%20CloudFormation%20Template&url=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fhow-to-setup-bastion-host-on-aws-using-cloudformation-template "Share on Twitter")[LinkedIn](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fhow-to-setup-bastion-host-on-aws-using-cloudformation-template&title=How%20To%20Setup%20Bastion%20Host%20on%20AWS%20using%20CloudFormation%20Template&summary=Learn%20how%20to%20set%20up%20a%20secure%20Bastion%20Host%20on%20AWS%20using%20CloudFormation%20templates.%20This%20tutorial%20covers%20the%20steps%20to%20create%20a%20VPC%2C%20subnets%2C%20security%20groups%2C%20and%20instances%2C%20then%20test%20Internet%20connectivity.%20Detailed%20instructions%20and%20sample%20code%20are%20included.&source=https://mkabumattar.com "Share on LinkedIn")[WhatsApp](https://wa.me/?text=How%20To%20Setup%20Bastion%20Host%20on%20AWS%20using%20CloudFormation%20Template%20https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fhow-to-setup-bastion-host-on-aws-using-cloudformation-template "Share on WhatsApp")[Telegram](https://t.me/share/url?url=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fhow-to-setup-bastion-host-on-aws-using-cloudformation-template&text=How%20To%20Setup%20Bastion%20Host%20on%20AWS%20using%20CloudFormation%20Template "Share on Telegram")[Reddit](https://www.reddit.com/submit?url=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fhow-to-setup-bastion-host-on-aws-using-cloudformation-template&title=How%20To%20Setup%20Bastion%20Host%20on%20AWS%20using%20CloudFormation%20Template "Share on Reddit")[Hacker News](http://news.ycombinator.com/submitlink?u=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fhow-to-setup-bastion-host-on-aws-using-cloudformation-template&t=How%20To%20Setup%20Bastion%20Host%20on%20AWS%20using%20CloudFormation%20Template "Share on Hacker News")[Pinterest](https://pinterest.com/pin/create/button/?url=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fhow-to-setup-bastion-host-on-aws-using-cloudformation-template&media=&description=Learn%20how%20to%20set%20up%20a%20secure%20Bastion%20Host%20on%20AWS%20using%20CloudFormation%20templates.%20This%20tutorial%20covers%20the%20steps%20to%20create%20a%20VPC%2C%20subnets%2C%20security%20groups%2C%20and%20instances%2C%20then%20test%20Internet%20connectivity.%20Detailed%20instructions%20and%20sample%20code%20are%20included. "Share on Pinterest")[Email](<mailto:?subject=How%20To%20Setup%20Bastion%20Host%20on%20AWS%20using%20CloudFormation%20Template&body=Check out this article: https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fhow-to-setup-bastion-host-on-aws-using-cloudformation-template>)

## Comments

## You might also enjoy

More posts on similar topics

[![How To Setup Bastion Host on AWS using AWS CLI](/_astro/hero.DGhoBA0r_KL9IM.webp)](/blog/post/how-to-setup-bastion-host-on-aws-using-aws-cli)

## [How To Setup Bastion Host on AWS using AWS CLI](/blog/post/how-to-setup-bastion-host-on-aws-using-aws-cli)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [AWS](/blog/categories/aws)
-   [AWS CLI](/blog/categories/aws-cli)
-   [Security](/blog/categories/security)
-   [Networking](/blog/categories/networking)
-   [DevOps](/blog/categories/devops)

Introduction Security is the top priority for any infrastructure and application, and that's why a Bastion host is a must-have in your infrastructure if you want to secure your remote connections.

[#AWS Bastion Host](/blog/tags/aws-bastion-host)[#AWS CLI](/blog/tags/aws-cli)[#VPC Setup](/blog/tags/vpc-setup)+7 tags

[read more](/blog/post/how-to-setup-bastion-host-on-aws-using-aws-cli)

[![How to Setup Jenkins on AWS Using CloudFormation](/_astro/hero.r9yMhIoW_Z3Ovj6.webp)](/blog/post/how-to-setup-jenkins-on-aws-using-cloudformation)

## [How to Setup Jenkins on AWS Using CloudFormation](/blog/post/how-to-setup-jenkins-on-aws-using-cloudformation)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [AWS](/blog/categories/aws)
-   [Jenkins](/blog/categories/jenkins)
-   [CloudFormation](/blog/categories/cloudformation)
-   [DevOps](/blog/categories/devops)
-   [CI/CD](/blog/categories/cicd)

Introduction In a previous blog post, we set up Jenkins on AWS using the AWS CLI (How to Install Jenkins on AWS EC2 Instance). In this blog post,

[#AWS CloudFormation](/blog/tags/aws-cloudformation)[#Jenkins Setup](/blog/tags/jenkins-setup)[#EC2 Instance](/blog/tags/ec2-instance)+5 tags

[read more](/blog/post/how-to-setup-jenkins-on-aws-using-cloudformation)

[![How to Deploy a Spring Boot Application to AWS CloudFormation](/_astro/hero.C8XnJRc3_Z1OiL51.webp)](/blog/post/how-to-deploy-a-spring-boot-application-to-aws-cloudformation)

## [How to Deploy a Spring Boot Application to AWS CloudFormation](/blog/post/how-to-deploy-a-spring-boot-application-to-aws-cloudformation)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [AWS](/blog/categories/aws)
-   [Spring Boot](/blog/categories/spring-boot)
-   [CloudFormation](/blog/categories/cloudformation)
-   [DevOps](/blog/categories/devops)
-   [Java](/blog/categories/java)

Introduction Deploying a Spring Boot application to the cloud can provide many benefits such as scalability and easy management. AWS CloudFormation is a service that allows for the creation and ma

[#AWS CloudFormation](/blog/tags/aws-cloudformation)[#Spring Boot Deployment](/blog/tags/spring-boot-deployment)[#Java on AWS](/blog/tags/java-on-aws)+5 tags

[read more](/blog/post/how-to-deploy-a-spring-boot-application-to-aws-cloudformation)

[![How To Connect A Two EC2 Instances Database and Files Transfer Using AWS CLI](/_astro/hero.839lq2GB_1XSap0.webp)](/blog/post/how-to-connect-a-two-ec2-instances-database-and-files-transfer-using-aws-cli)

## [How To Connect A Two EC2 Instances Database and Files Transfer Using AWS CLI](/blog/post/how-to-connect-a-two-ec2-instances-database-and-files-transfer-using-aws-cli)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [AWS](/blog/categories/aws)
-   [EC2](/blog/categories/ec2)
-   [EBS](/blog/categories/ebs)
-   [EFS](/blog/categories/efs)
-   [Database](/blog/categories/database)
-   [Networking](/blog/categories/networking)

Introduction In this post, I will show you how to share a database and files between two EC2 instances using AWS CLI. I will use AWS CLI to create a VPC, EC2 instances, EBS, EFS, and security grou

[#AWS CLI](/blog/tags/aws-cli)[#EC2 Instance Communication](/blog/tags/ec2-instance-communication)[#EBS Volume](/blog/tags/ebs-volume)+7 tags

[read more](/blog/post/how-to-connect-a-two-ec2-instances-database-and-files-transfer-using-aws-cli)

[![How to CI/CD AWS With Github using Jenkins](/_astro/hero.B1mpyM8T_14dvIE.webp)](/blog/post/how-to-ci-cd-aws-with-github-using-jenkins)

## [How to CI/CD AWS With Github using Jenkins](/blog/post/how-to-ci-cd-aws-with-github-using-jenkins)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [CI/CD](/blog/categories/cicd)
-   [DevOps](/blog/categories/devops)
-   [AWS](/blog/categories/aws)
-   [Jenkins](/blog/categories/jenkins)
-   [GitHub](/blog/categories/github)

Introduction In a previous post, I showed you how to set up Jenkins on an AWS EC2 instance. You can read that post here. In this post, I will sho

[#CI/CD Pipeline](/blog/tags/cicd-pipeline)[#Jenkins](/blog/tags/jenkins)[#GitHub Integration](/blog/tags/github-integration)+6 tags

[read more](/blog/post/how-to-ci-cd-aws-with-github-using-jenkins)

[![How to Install Jenkins on AWS EC2 Instance](/_astro/hero.BzqOEmzv_YuUyx.webp)](/blog/post/install-jenkins-on-aws-ec2-instance)

## [How to Install Jenkins on AWS EC2 Instance](/blog/post/install-jenkins-on-aws-ec2-instance)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [AWS](/blog/categories/aws)
-   [Jenkins](/blog/categories/jenkins)
-   [EC2](/blog/categories/ec2)
-   [DevOps](/blog/categories/devops)
-   [CI/CD](/blog/categories/cicd)

Introduction In this post, I will show you how to create an EC2 instance on AWS and install Jenkins on it. PrerequisitesAWS CLI installed and configured IAM user with the following permi

[#Jenkins Installation](/blog/tags/jenkins-installation)[#AWS EC2 Setup](/blog/tags/aws-ec2-setup)[#Linux Server](/blog/tags/linux-server)+4 tags

[read more](/blog/post/install-jenkins-on-aws-ec2-instance)

6 related posts
