Skip to content

ministryofjustice/modernisation-platform-terraform-ec2-instance

Repository files navigation

Modernisation Platform Terraform EC2 Instance

Standards Icon Format Code Icon Scorecards Icon SCA Icon Terraform SCA Icon

Usage

module "ec2_test_instance" {
  source = "github.com/ministryofjustice/modernisation-platform-terraform-ec2-instance"

  providers = {
    aws.core-vpc = aws.core-vpc # core-vpc-(environment) holds the networking for all accounts
  }

  for_each = try(local.ec2_test.ec2_test_instances, {})

  name = each.key

  ami_name                      = each.value.ami_name
  ami_owner                     = try(each.value.ami_owner, "core-shared-services-production")
  instance                      = merge(local.ec2_test.instance, lookup(each.value, "instance", {}))
  ebs_volumes_copy_all_from_ami = try(each.value.ebs_volumes_copy_all_from_ami, true)
  ebs_kms_key_id                = module.environment.kms_keys["ebs"].arn
  ebs_volume_config             = lookup(each.value, "ebs_volume_config", {})
  ebs_volumes                   = lookup(each.value, "ebs_volumes", {})
  ebs_volume_tags               = lookup(each.value, "ebs_volume_tags", {})
  ssm_parameters_prefix         = lookup(each.value, "ssm_parameters_prefix", "test/")
  ssm_parameters                = lookup(each.value, "ssm_parameters", null)
  route53_records               = merge(local.ec2_test.route53_records, lookup(each.value, "route53_records", {}))

  iam_resource_names_prefix = "ec2-test-instance"
  instance_profile_policies = local.ec2_common_managed_policies

  business_unit            = local.business_unit
  application_name         = local.application_name
  environment              = local.environment
  region                   = local.region
  availability_zone        = local.availability_zone_1
  subnet_id                = module.environment.subnet["private"][local.availability_zone_1].id
  tags                     = merge(local.tags, local.ec2_test.tags, try(each.value.tags, {}))
  account_ids_lookup       = local.environment_management.account_ids
  cloudwatch_metric_alarms = {}
}

For a deployed example, please check example. A second fully self-contained example has been added for ease of use.

Setting backup tags

Read the Modernisation Platform backup functionality to understand how the backup plan works. The following is a summary of the backup behaviour based on the tags that are set and passed in this module.

Production environment

By default, all production resources (EC2 and EBS) will be backed up. This is determined by the is-production tag being set to true. Production backups can be skipped by setting backup tag to false (passed in tags input).

Non-production environments

Additionally, you are able to control backups in non-production environments by setting backup tag to true (passed in tags input).

Backup duplication problem

NOTE, setting backup tag to true that is passed in tags input will set backup tag to true on all EC2 and EBS resources. This will result in duplicated backups as EBS resources that are part of an EC2 will get backed up during the EC2 backup and EBS backup selection by the backup plan.

In order to select either EC2 backups or EBS backups it is possible to set backup tag to true on only EC2 instance, by passing the tag as part of the instance.tags input or setting the backup tag to true on only EBS by setting the backup tag to true and passing it in the ebs_volume_tags input. NOTE, if the backup tag is passed in tags and instance.tags/ebs_volume_tags, the tag set on the specific resources will take priority. For example, backup tag is set to:

  • true via tags input
  • false via instance.tags input
  • true via ebs_volume_tags input will result in:
  • EC2 not being backed up
  • all EBS being backed up (that includes root ebs, inline ebs and attached ebs).

Looking for issues?

If you're looking to raise an issue with this module, please create a new issue in the Modernisation Platform repository.

Requirements

Name Version
terraform >= 1.1.7
aws ~> 5.0
cloudinit ~> 2.3.5
random ~> 3.0
time > 0.9.0

Providers

Name Version
aws ~> 5.0
aws.core-vpc ~> 5.0
cloudinit ~> 2.3.5
random ~> 3.0

Modules

No modules.

Resources

Name Type
aws_cloudwatch_metric_alarm.this resource
aws_ebs_volume.this resource
aws_eip.this resource
aws_eip_association.this resource
aws_iam_instance_profile.this resource
aws_iam_role.this resource
aws_iam_role_policy.ssm_params_and_secrets resource
aws_instance.this resource
aws_route53_record.external resource
aws_route53_record.internal resource
aws_secretsmanager_secret.fixed resource
aws_secretsmanager_secret.placeholder resource
aws_secretsmanager_secret_version.fixed resource
aws_ssm_parameter.placeholder resource
aws_ssm_parameter.this resource
aws_volume_attachment.this resource
random_password.secrets resource
random_password.this resource
aws_ami.this data source
aws_caller_identity.current data source
aws_ec2_instance_type.this data source
aws_iam_policy_document.ssm_params_and_secrets data source
aws_route53_zone.external data source
aws_route53_zone.internal data source
cloudinit_config.this data source

Inputs

Name Description Type Default Required
account_ids_lookup A map of account names to account ids that can be used for AMI owner map(any) {} no
ami_name Name of AMI to be used to launch the database ec2 instance string n/a yes
ami_owner Owner of AMI to be used to launch the database ec2 instance string "self" no
application_name The name of the application. This will be name of the environment in Modernisation Platform string "nomis" no
availability_zone The availability zone in which to deploy the infrastructure string "eu-west-2a" no
business_unit This corresponds to the VPC in which the application resides string "hmpps" no
cloudwatch_metric_alarms Map of cloudwatch metric alarms. The alarm name is set to the ec2 instance name plus the map key.
map(object({
comparison_operator = string
evaluation_periods = number
metric_name = string
namespace = string
period = number
statistic = string
threshold = number
alarm_actions = list(string)
ok_actions = optional(list(string), [])
actions_enabled = optional(bool, false)
alarm_description = optional(string)
datapoints_to_alarm = optional(number)
treat_missing_data = optional(string, "missing")
dimensions = optional(map(string), {})
}))
{} no
ebs_kms_key_id KMS Key to use for EBS volumes if not explicitly set in ebs_volumes variable string null no
ebs_volume_config EC2 volume configurations, where key is a label, e.g. flash, which is assigned to the disk in ebs_volumes. All disks with same label have the same configuration. If not specified, use values from the AMI. If total_size specified, the volume size is this divided by the number of drives with the given label
map(object({
iops = optional(number)
throughput = optional(number)
total_size = optional(number)
type = optional(string)
kms_key_id = optional(string)
}))
n/a yes
ebs_volume_tags Additional tags to apply to ebs volumes map(string) {} no
ebs_volumes EC2 volumes, see aws_ebs_volume for documentation. key=volume name, value=ebs_volume_config key. label is used as part of the Name tag
map(object({
label = optional(string)
snapshot_id = optional(string)
iops = optional(number)
throughput = optional(number)
size = optional(number)
type = optional(string)
kms_key_id = optional(string)
}))
n/a yes
ebs_volumes_copy_all_from_ami If true, ensure all volumes in AMI are also present in EC2. If false, only create volumes specified in ebs_volumes var bool true no
environment Application environment - i.e. the terraform workspace string n/a yes
iam_resource_names_prefix Prefix IAM resources with this prefix, e.g. ec2-database string "ec2" no
instance EC2 instance settings, see aws_instance documentation
object({
associate_public_ip_address = optional(bool, false)
disable_api_termination = bool
disable_api_stop = bool
instance_type = string
key_name = string
metadata_endpoint_enabled = optional(string, "enabled")
metadata_options_http_tokens = optional(string, "required")
monitoring = optional(bool, true)
ebs_block_device_inline = optional(bool, false)
vpc_security_group_ids = list(string)
private_dns_name_options = optional(object({
enable_resource_name_dns_aaaa_record = optional(bool)
enable_resource_name_dns_a_record = optional(bool)
hostname_type = string
}))
tags = optional(map(string), {})
})
n/a yes
instance_profile_policies A list of managed IAM policy document ARNs to be attached to the database instance profile list(string) n/a yes
name Provide a unique name for the instance string n/a yes
region Destination AWS Region for the infrastructure string "eu-west-2" no
route53_records Optionally create internal and external DNS records
object({
create_internal_record = bool
create_external_record = bool
})
n/a yes
secretsmanager_secrets A map of secretsmanager secrets to create. Set a specific value or a randomly generated value. If neither random or value are set, a placeholder value is created which can be updated outside of terraform
map(object({
description = optional(string)
kms_key_id = optional(string)
recovery_window_in_days = optional(number)
random = optional(object({
length = number
special = optional(bool)
}))
value = optional(string)
}))
null no
secretsmanager_secrets_prefix Optionally prefix secretsmanager secrets with this prefix. Add a trailing / string "" no
ssm_parameters A map of SSM parameters to create. Set a specific value or a randomly generated value. If neither random or value are set, a placeholder value is created which can be updated outside of terraform
map(object({
description = optional(string)
type = optional(string, "SecureString")
kms_key_id = optional(string)
random = optional(object({
length = number
special = optional(bool)
}))
value = optional(string)
}))
null no
ssm_parameters_prefix Optionally prefix ssm parameters with this prefix. Add a trailing / string "" no
subnet_id The subnet id in which to deploy the infrastructure string n/a yes
tags Default tags to be applied to resources. Additional tags can be added to EBS volumes or EC2s, see instance.tags and ebs_volume_tags variables. map(any) n/a yes
user_data_cloud_init Use this instead of user_data_raw to run multiple scripts using cloud_init
object({
args = optional(map(string))
scripts = optional(list(string))
write_files = optional(map(object({
path = string
owner = string
permissions = string
})), {})
})
null no
user_data_raw Base64 encoded user data, script or cloud formation template string null no

Outputs

Name Description
aws_ebs_volume aws_ebs_volume resource
aws_instance aws_instance resource