What will you need

Bastion (or jumpbox) hosts are typically used, to provide a door into your private network. The fact that you are reading this, means you probably already know that.

Answering the question on how to setup a bastion host on aws using Terraform, takes a lot of components..

  • AMI selection
  • Security groups and routing
  • IAM roles, roles and policies
  • Instance types
  • Users, keys and login
  • Hostname setup

That’s a lot of things, lets see how can we make that easy and simple for you.

The git repository available here and the module we are using here.

Create a Bastion host, using a RevDB module

It wont get any more simple than this, all you need to do is

module "bastion" {
  source = "revenants-cie/revdb-bastion/aws"
  key_pair_name = "deployer"
  subnet_id = "subnet-0a957113a9566cf64"

This will create a single EC2 instance, using the module default settings, which are:

  • Latest Ubuntu Bionic AMI from Canonical (var.ami_id)
  • t3.nano instance type (var.instance_type)
  •  Sets up the hostname to be bastion.infrastructureascode.blog (var.hostname_prefix + var.dns_zones)
  • Uses the deployer keypair. I’d assume you already have an ssh key pair setup, but if not, a later example will set that up too
  • Uses a default user data, which configures the server to be ready with Chef and installs some basic utilities

A more wholesome example

We use this module for a variety of things and for that reason, it needs to be able to handle a lot of different use cases.

Secrets and encryption keys

We have chosen to use secrets with individual encryption keys to store tokens, usernames and passwords. For example as we use datadog for monitoring, the agent needs a token to talk to the datadog API. This is stored in a secret, with its own encryption key. By passing in the names of these, our module will automatically create IAM roles and policies to generate access to those.

Read only and Read-Write access to S3 buckets

S3 buckets are an essential part of operating servers. Some of them we need write access to, others are to read from. We use the buckets primarely for backups and logging (RW) and to share files from an authentic source (RO).

For this purpose, the module supports passing in the names of the buckets for each RW/RO lists, IAM roles and policies will be setup accordingly.

Module depends on variables

Since we use secrets, those might be created from other files of the terraform folder. For example we have a separate secrets.tf and a bastion.tf. To make sure the bastion module waits for the secrets to be created, we use the module_depends_on variable.

 This example creates and passes on:
  • deployer key you can use to access the machine
  • A secret with a mock value
  • An s3 bucket

The example

resource "aws_key_pair" "deployer" {
  public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDBOhywtJIt0uXyvK6uFuMOLgMHywrJ/uyYNoXhRmRbc11rLrhJRYNv7LiJFBlduhge1v6w4X+tcdDCDmY1RBbjgheL160cd2ComNsLulL6qVkduSKkiN7pnY1jjXZ9MEwnAZDeKNoPp0gZ1aL2sEoJU6LGtZerKQ/7/sGXc2qORMDnHndDC7uBF35VME4mYX9y9/UTaK/F19FSOiQqyQYF8UKj9snZG5zEdEPDovPU8fpZdYcfnYtPqV3f0GZsYj1kWcnWKWd/lbu8fnQtOWA+D92IP9Tf4czjVpcuCPSpK2H3LoStjcoKi/UlJcwP7TT8GnLUC1THspUtMCJnbYLogmtBqeJRatVDrRADjTnof9N+zt2nCjvceu9/nnxzt/hwbGifM4aAtQMtZD2sT+6ip6HDi+JHcLyy5SeUkfS7Ep1VQxTMTnS+CpZijUE7HTV+MWSLpfqf7e/EEc3v2Nx1lX/LNQcFb+ZiNAAeYO15lL/hzy7kXz5QgR/i9oQ9r30pvLz2Vd74qU0pdSTPGBk7sxuJwQqS8addrXOiLbcM1hsXhMRkBUsq/u4w=="
  key_name   = "deployer"

resource "aws_secretsmanager_secret" "datadog_api_key" {
  name                    = "datadog_api_key"
  description             = "API Key for datadog client"
  recovery_window_in_days = 0

resource "aws_secretsmanager_secret_version" "datadog_api_key" {
  secret_id     = aws_secretsmanager_secret.datadog_api_key.id
  secret_string = "our secret api key"

resource "aws_s3_bucket" "revdb_backup_bucket" {
  bucket = "revdb-terraform-backup_bucket"
  tags   = {Name = "my_simple_bucket"}

module "bastion" {
  source = "revenants-cie/revdb-bastion/aws"
  dns_zones = ["infrastructureascode.blog"]
  key_pair_name = "deployer"
  subnet_id = "subnet-0a957113a9566cf64"
  environment = "test"
  service_name = "bastion1"
  region = "us-east-2"
  bastion_secrets = [aws_secretsmanager_secret.datadog_api_key.name]
  s3_bucket_rw_list = [aws_s3_bucket.revdb_backup_bucket.bucket]
  module_depends_on = [

output "hostname" {
  value = module.bastion.hostname
output "ipaddress" {
  value = module.bastion.public_bastion_ip


We really have this bastion host, with our Chef server configuration, doing everything we really need. Most of the examples I have found online when started working on this, were really simple and didn’t include any of the hostname, IAM policy or other components. It feels like a useful resource to have it publicly available.

We would absolutely love and appreciate any and all help, comments, contribution, feature requests or bug reports.

You can find the git repository here.

Categories: RevDB


Leave a Reply

Your email address will not be published. Required fields are marked *