Type something to search...
How To Setup Bastion Host on AWS using CloudFormation Template

How To Setup Bastion Host on AWS using CloudFormation Template

Introduction

In previous post How To Setup Bastion Host on AWS using CloudFormation Template, we will learn how to setup a Bastion host on AWS using a CloudFormation template. We will walk through the process of creating a CloudFormation 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

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 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

Setup Bastion Host on AWS using 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
1
AWSTemplateFormatVersion: '2010-09-09'
2
Description: >-
3
This template creates a VPC with 2 subnets, 1 public and 1 private. It also
4
Internet Gateway, NAT Gateway, Route Tables, Security Groups and EC2 Instances
5
for Bastion Host and Private Instance.
6
7
Parameters:
8
EnvironmentName:
9
Description: >-
10
An environment name that will be prefixed to resource names
11
Type: String
12
AllowedValues:
13
- dev
14
- test
15
- prod
16
Default: dev
17
VPCName:
18
Description: >-
19
The name of the VPC. This name is used as a tag value for the VPC and
20
subnets.
21
Type: String
22
Default: bastion-host-vpc
23
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, and
26
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: String
32
Default: 10.0.0.0/16
33
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 a
36
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 the
40
subnet.
41
Type: String
42
Default: bastion-host-public-subnet
43
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, and
46
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 (RFC
50
1918) CIDR range that is /24 or larger.
51
Type: String
52
Default: 10.0.0.0/24
53
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 a
56
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 the
60
subnet.
61
Type: String
62
Default: bastion-host-private-subnet
63
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, and
66
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 (RFC
70
1918) CIDR range that is /24 or larger.
71
Type: String
72
Default: 10.0.16.0/24
73
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 a
76
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 the
80
Internet Gateway.
81
Type: String
82
Default: bastion-host-igw
83
AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$
84
ConstraintDescription: >-
85
Internet Gateway name can include numbers, lowercase letters, uppercase
86
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 NAT
90
Gateway.
91
Type: String
92
Default: bastion-host-ngw
93
AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$
94
ConstraintDescription: >-
95
NAT Gateway name can include numbers, lowercase letters, uppercase
96
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 for
100
the route table.
101
Type: String
102
Default: bastion-host-public-route-table
103
AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$
104
ConstraintDescription: >-
105
Route table name can include numbers, lowercase letters, uppercase
106
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 for
110
the route table.
111
Type: String
112
Default: bastion-host-private-route-table
113
AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$
114
ConstraintDescription: >-
115
Route table name can include numbers, lowercase letters, uppercase
116
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 value
120
for the instance.
121
Type: String
122
Default: bastion-host-instance
123
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 Bastion
127
Host.
128
Type: AWS::EC2::KeyPair::KeyName
129
BastionHostType:
130
Description: >-
131
The instance type to use for the Bastion Host.
132
Type: String
133
AllowedValues:
134
- t2.nano
135
- t2.micro
136
- t2.small
137
- t2.medium
138
- t2.large
139
- t2.xlarge
140
- t2.2xlarge
141
- t3.nano
142
- t3.micro
143
- t3.small
144
- t3.medium
145
- t3.large
146
- t3.xlarge
147
- t3.2xlarge
148
- m1.small
149
- m1.medium
150
- m1.large
151
- m1.xlarge
152
- m2.xlarge
153
- m2.2xlarge
154
- m2.4xlarge
155
- m3.medium
156
- m3.large
157
- m3.xlarge
158
- m3.2xlarge
159
- m4.large
160
- m4.xlarge
161
- m4.2xlarge
162
- m4.4xlarge
163
- m4.10xlarge
164
- m4.16xlarge
165
- m5.large
166
- m5.xlarge
167
- m5.2xlarge
168
- m5.4xlarge
169
- m5.12xlarge
170
- m5.24xlarge
171
- m5d.large
172
- m5d.xlarge
173
- m5d.2xlarge
174
- m5d.4xlarge
175
- m5d.12xlarge
176
- m5d.24xlarge
177
- c1.medium
178
- c1.xlarge
179
- c3.large
180
- c3.xlarge
181
- c3.2xlarge
182
- c3.4xlarge
183
- c3.8xlarge
184
- c4.large
185
- c4.xlarge
186
- c4.2xlarge
187
- c4.4xlarge
188
- c4.8xlarge
189
- c5.large
190
- c5.xlarge
191
- c5.2xlarge
192
- c5.4xlarge
193
- c5.9xlarge
194
Default: t2.micro
195
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 launch
200
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-gp2
203
BastionHostSecurityGroupName:
204
Description: >-
205
The name of the security group to assign to the Bastion Host instance.
206
Type: String
207
Default: bastion-host-security-group
208
AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$
209
ConstraintDescription: >-
210
Security group name can include numbers, lowercase letters, uppercase
211
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 Host
215
instance.
216
Type: String
217
Default: Bastion Host security group
218
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 hyphen
222
(-).
223
PrivateInstanceName:
224
Description: >-
225
The name of the private instance. This name is used as a tag value for the
226
instance.
227
Type: String
228
Default: bastion-host-private-instance
229
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 the
236
private instance.
237
Type: AWS::EC2::KeyPair::KeyName
238
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: String
244
AllowedValues:
245
- t2.nano
246
- t2.micro
247
- t2.small
248
- t2.medium
249
- t2.large
250
- t2.xlarge
251
- t2.2xlarge
252
- t3.nano
253
- t3.micro
254
- t3.small
255
- t3.medium
256
- t3.large
257
- t3.xlarge
258
- t3.2xlarge
259
- m1.small
260
- m1.medium
261
- m1.large
262
- m1.xlarge
263
- m2.xlarge
264
- m2.2xlarge
265
- m2.4xlarge
266
- m3.medium
267
- m3.large
268
- m3.xlarge
269
- m3.2xlarge
270
- m4.large
271
- m4.xlarge
272
- m4.2xlarge
273
- m4.4xlarge
274
- m4.10xlarge
275
- m4.16xlarge
276
- m5.large
277
- m5.xlarge
278
- m5.2xlarge
279
- m5.4xlarge
280
- m5.12xlarge
281
- m5.24xlarge
282
- m5d.large
283
- m5d.xlarge
284
- m5d.2xlarge
285
- m5d.4xlarge
286
- m5d.12xlarge
287
- m5d.24xlarge
288
- c1.medium
289
- c1.xlarge
290
- c3.large
291
- c3.xlarge
292
- c3.2xlarge
293
- c3.4xlarge
294
- c3.8xlarge
295
- c4.large
296
- c4.xlarge
297
- c4.2xlarge
298
- c4.4xlarge
299
- c4.8xlarge
300
- c5.large
301
- c5.xlarge
302
- c5.2xlarge
303
- c5.4xlarge
304
- c5.9xlarge
305
Default: t2.micro
306
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 launch
311
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-gp2
314
PrivateInstanceSecurityGroupName:
315
Description: >-
316
The name of the security group to assign to the private instance.
317
Type: String
318
Default: bastion-host-private-instance-security-group
319
AllowedPattern: ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$
320
ConstraintDescription: >-
321
Security group name can include numbers, lowercase letters, uppercase
322
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: String
327
Default: Bastion Host private instance security group
328
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 hyphen
332
(-).
333
334
Metadata:
335
Author: Mohammad Abu Mattar
336
Version: 1.0
337
AWS::CloudFormation::Interface:
338
ParameterGroups:
339
- Label:
340
default: Environment
341
Parameters:
342
- EnvironmentName
343
- Label:
344
default: Natwork Configuration
345
Parameters:
346
- VPCName
347
- VPCCIDR
348
- PublicSubnetName
349
- PublicSubnetCIDR
350
- PrivateSubnetName
351
- PrivateSubnetCIDR
352
- InternetGatewayName
353
- NATGatewayName
354
- PublicRouteTableName
355
- PrivateRouteTableName
356
- Label:
357
default: Bastion Host Configuration
358
Parameters:
359
- BastionHostName
360
- BastionHostKeyPair
361
- BastionHostType
362
- BastionHostAMI
363
- BastionHostSecurityGroupName
364
- BastionHostSecurityGroupDescription
365
- Label:
366
default: Private Instance Configuration
367
Parameters:
368
- PrivateInstanceName
369
- PrivateInstanceKeyPair
370
- PrivateInstanceType
371
- PrivateInstanceAMI
372
- PrivateInstanceSecurityGroupName
373
- PrivateInstanceSecurityGroupDescription
374
375
ParameterLabels:
376
EnvironmentName:
377
default: Environment Name
378
VPCName:
379
default: VPC Name
380
VPCCIDR:
381
default: VPC CIDR
382
PublicSubnetName:
383
default: Public Subnet Name
384
PublicSubnetCIDR:
385
default: Public Subnet CIDR
386
PrivateSubnetName:
387
default: Private Subnet Name
388
PrivateSubnetCIDR:
389
default: Private Subnet CIDR
390
InternetGatewayName:
391
default: Internet Gateway Name
392
NATGatewayName:
393
default: NAT Gateway Name
394
PublicRouteTableName:
395
default: Public Route Table Name
396
PrivateRouteTableName:
397
default: Private Route Table Name
398
BastionHostName:
399
default: Bastion Host Name
400
BastionHostKeyPair:
401
default: Bastion Host Key Pair
402
BastionHostType:
403
default: Bastion Host Type
404
BastionHostAMI:
405
default: Bastion Host AMI
406
BastionHostSecurityGroupName:
407
default: Bastion Host Security Group Name
408
BastionHostSecurityGroupDescription:
409
default: Bastion Host Security Group Description
410
PrivateInstanceName:
411
default: Private Instance Name
412
PrivateInstanceKeyPair:
413
default: Private Instance Key Pair
414
PrivateInstanceType:
415
default: Private Instance Type
416
PrivateInstanceAMI:
417
default: Private Instance AMI
418
PrivateInstanceSecurityGroupName:
419
default: Private Instance Security Group Name
420
PrivateInstanceSecurityGroupDescription:
421
default: Private Instance Security Group Description
422
423
Resources:
424
VPC:
425
Type: AWS::EC2::VPC
426
Properties:
427
CidrBlock: !Ref VPCCIDR
428
EnableDnsSupport: true
429
EnableDnsHostnames: true
430
Tags:
431
- Key: Name
432
Value: !Ref VPCName
433
434
PublicSubnet:
435
Type: AWS::EC2::Subnet
436
DependsOn:
437
- VPC
438
Properties:
439
VpcId: !Ref VPC
440
CidrBlock: !Ref PublicSubnetCIDR
441
AvailabilityZone: !Select [0, !GetAZs '']
442
Tags:
443
- Key: Name
444
Value: !Ref PublicSubnetName
445
PriveSubnet:
446
Type: AWS::EC2::Subnet
447
DependsOn:
448
- VPC
449
Properties:
450
VpcId: !Ref VPC
451
CidrBlock: !Ref PrivateSubnetCIDR
452
AvailabilityZone: !Select [0, !GetAZs '']
453
Tags:
454
- Key: Name
455
Value: !Ref PrivateSubnetName
456
457
IGW:
458
Type: AWS::EC2::InternetGateway
459
DependsOn:
460
- VPC
461
Properties:
462
Tags:
463
- Key: Name
464
Value: !Ref InternetGatewayName
465
IGWAttachment:
466
Type: AWS::EC2::VPCGatewayAttachment
467
DependsOn:
468
- VPC
469
- IGW
470
Properties:
471
VpcId: !Ref VPC
472
InternetGatewayId: !Ref IGW
473
474
NATGatewayEIP:
475
Type: AWS::EC2::EIP
476
DependsOn:
477
- VPC
478
Properties:
479
Domain: vpc
480
NATGateway:
481
Type: AWS::EC2::NatGateway
482
DependsOn:
483
- VPC
484
- PublicSubnet
485
- NATGatewayEIP
486
Properties:
487
AllocationId: !GetAtt NATGatewayEIP.AllocationId
488
SubnetId: !Ref PublicSubnet
489
Tags:
490
- Key: Name
491
Value: !Ref NATGatewayName
492
493
PublicRouteTable:
494
Type: AWS::EC2::RouteTable
495
DependsOn:
496
- VPC
497
Properties:
498
VpcId: !Ref VPC
499
Tags:
500
- Key: Name
501
Value: !Ref PublicRouteTableName
502
PrivateRouteTable:
503
Type: AWS::EC2::RouteTable
504
DependsOn:
505
- VPC
506
Properties:
507
VpcId: !Ref VPC
508
Tags:
509
- Key: Name
510
Value: !Ref PrivateRouteTableName
511
512
PublicRoute:
513
Type: AWS::EC2::Route
514
DependsOn:
515
- PublicRouteTable
516
- IGW
517
Properties:
518
RouteTableId: !Ref PublicRouteTable
519
DestinationCidrBlock: 0.0.0.0/0
520
GatewayId: !Ref IGW
521
PrivateRoute:
522
Type: AWS::EC2::Route
523
DependsOn:
524
- PrivateRouteTable
525
- NATGateway
526
Properties:
527
RouteTableId: !Ref PrivateRouteTable
528
DestinationCidrBlock: 0.0.0.0/0
529
NatGatewayId: !Ref NATGateway
530
531
PublicSubnetRouteTableAssociation:
532
Type: AWS::EC2::SubnetRouteTableAssociation
533
DependsOn:
534
- PublicSubnet
535
- PublicRouteTable
536
Properties:
537
SubnetId: !Ref PublicSubnet
538
RouteTableId: !Ref PublicRouteTable
539
PrivateSubnetRouteTableAssociation:
540
Type: AWS::EC2::SubnetRouteTableAssociation
541
DependsOn:
542
- PriveSubnet
543
- PrivateRouteTable
544
Properties:
545
SubnetId: !Ref PriveSubnet
546
RouteTableId: !Ref PrivateRouteTable
547
548
BastionHostSecurityGroup:
549
Type: AWS::EC2::SecurityGroup
550
DependsOn:
551
- VPC
552
Properties:
553
GroupDescription: !Ref BastionHostSecurityGroupDescription
554
VpcId: !Ref VPC
555
SecurityGroupIngress:
556
- IpProtocol: tcp
557
FromPort: 22
558
ToPort: 22
559
CidrIp: 0.0.0.0/0
560
SecurityGroupEgress:
561
- IpProtocol: tcp
562
FromPort: 0
563
ToPort: 65535
564
CidrIp: 0.0.0.0/0
565
Tags:
566
- Key: Name
567
Value: !Ref BastionHostSecurityGroupName
568
PrivateInstanceSecurityGroup:
569
Type: AWS::EC2::SecurityGroup
570
DependsOn:
571
- VPC
572
Properties:
573
GroupDescription: !Ref PrivateInstanceSecurityGroupDescription
574
VpcId: !Ref VPC
575
SecurityGroupIngress:
576
- IpProtocol: tcp
577
FromPort: 22
578
ToPort: 22
579
SourceSecurityGroupId: !Ref BastionHostSecurityGroup
580
SecurityGroupEgress:
581
- IpProtocol: tcp
582
FromPort: 0
583
ToPort: 65535
584
CidrIp: 0.0.0.0/0
585
Tags:
586
- Key: Name
587
Value: !Ref PrivateInstanceSecurityGroupName
588
589
BastionHost:
590
Type: AWS::EC2::Instance
591
DependsOn:
592
- PublicSubnet
593
- BastionHostSecurityGroup
594
Properties:
595
ImageId: !Ref BastionHostAMI
596
InstanceType: !Ref BastionHostType
597
KeyName: !Ref BastionHostKeyPair
598
NetworkInterfaces:
599
- AssociatePublicIpAddress: true
600
DeviceIndex: 0
601
GroupSet:
602
- !Ref BastionHostSecurityGroup
603
SubnetId: !Ref PublicSubnet
604
Tags:
605
- Key: Name
606
Value: !Ref BastionHostName
607
PrivateInstance:
608
Type: AWS::EC2::Instance
609
DependsOn:
610
- PriveSubnet
611
- PrivateInstanceSecurityGroup
612
Properties:
613
ImageId: !Ref PrivateInstanceAMI
614
InstanceType: !Ref PrivateInstanceType
615
KeyName: !Ref PrivateInstanceKeyPair
616
NetworkInterfaces:
617
- AssociatePublicIpAddress: false
618
DeviceIndex: 0
619
GroupSet:
620
- !Ref PrivateInstanceSecurityGroup
621
SubnetId: !Ref PriveSubnet
622
Tags:
623
- Key: Name
624
Value: !Ref PrivateInstanceName
625
626
Outputs:
627
BastionHostSSH:
628
Description: SSH command to connect to the bastion host
629
Value: !Join
630
- ''
631
- - ssh -i ~/.ssh/
632
- !Ref BastionHostKeyPair
633
- .pem ec2-user@
634
- !GetAtt BastionHost.PublicIp
635
PrivateInstanceSSH:
636
Description: SSH command to connect to the private instance
637
Value: !Join
638
- ''
639
- - ssh -i ~/.ssh/
640
- !Ref PrivateInstanceKeyPair
641
- .pem ec2-user@
642
- !GetAtt PrivateInstance.PrivateIp

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

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

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

Terminal window
ssh -i ~/.ssh/BastionHostKeyPair.pem ec2-user@<BastionHostPublicIp>

Bastion host

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
ssh -i ~/.ssh/PrivateInstanceKeyPair.pem ec2-user@<PrivateInstancePrivateIp>

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
ping -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 also check internet connectivity by using curl command to download a webpage:

Internet Connectivity

Terminal window
curl -s https://www.mkabumattar.tech

This will download the website’s source code and will return it to the terminal, and check if the response is received from the website. If the private host has internet connectivity, it will show the webpage’s source code.

It is important to note that, if you are running these commands from the bastion host, you might not face the same restrictions as the private host, in that case you should run the commands from the private host, or you could use a specific website for the test that is blocked for your private network

Internet Connectivity

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

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 a CloudFormation Template, deploying the stack, testing the connection, and deleting the stack. By using CloudFormation Templates, we can easily 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

These references should provide you with more in-depth information on the various services and concepts used in this tutorial, such as VPC, EC2, IAM, and the AWS CLI. Additionally, it includes more information about bastion host. It would be very helpful if you go through those references to gain more knowledge and information in order to improve the setup even more.

Related Posts

Check out some of our other posts

How To Create A Custom VPC Using AWS CLI

How To Create A Custom VPC Using AWS CLI

Introduction In the sample that follows, an IPv4 CIDR block, a public subnet, and a private subnet are all created using AWS CLI instructions. You can run an instance in the public subnet and con

read more
How to Install and Setup FireWall on Amazon Linux 2

How to Install and Setup FireWall on Amazon Linux 2

Introduction We will learn how to install and setup FireWall on Amazon Linux 2 in this tutorial. We will also discover how to set up FireWall so that it functions with the Amazon Linux 2. Pre

read more
How to Install Apache Web Server on Amazon Linux 2

How to Install Apache Web Server on Amazon Linux 2

Introduction In this tutorial, we will learn how to install Apache web server on Amazon Linux 2. We will also learn how to configure Apache web server to run simple HTML web page. Prerequisit

read more
How to Install PHP and MariaDB on Amazon Linux 2

How to Install PHP and MariaDB on Amazon Linux 2

Introduction We will learn how to set up PHP and MariaDB on Amazon Linux 2 in this tutorial. We will also discover how to set up PHP so that it functions with the Apache web server. We will also

read more
How to Install WordPress on Amazon Linux 2

How to Install WordPress on Amazon Linux 2

Introduction We will learn how to install WordPress on Amazon Linux 2 in this tutorial. We will also discover how to set up WordPress so that it functions with the Apache web server. We will also

read more
How To Create An AWS EC2 Instance Using AWS CLI

How To Create An AWS EC2 Instance Using AWS CLI

Introduction We will learn how to create an AWS EC2 instance using AWS CLI in this tutorial. We will also discover how to set up an AWS EC2 instance so that it functions with the Apache web serve

read more