diff --git a/groups/linux-dev-03/README.md b/groups/linux-dev-03/README.md new file mode 100644 index 0000000..de9d6a9 --- /dev/null +++ b/groups/linux-dev-03/README.md @@ -0,0 +1,92 @@ +# linux-dev-03 - linux dev/test server + + + +## Requirements + +| Name | Version | +|------|---------| +| [aws](#requirement\_aws) | >= 5.37.0, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.3 | +| [vault](#requirement\_vault) | >= 3.25.0, < 5.0.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [instance_profile](#modules\_aws) | [git@github.com:companieshouse/terraform-modules//aws/instance_profile?ref=tags/1.0.283](https://github.com/companieshouse/terraform-modules/tree/1.0.283/aws/instance_profile) | tags/1.0.283 | + +## Resources + +| Name | Type | +|------|------| +| [aws_instance.linux-dev-02](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | +| [aws_route53_record.linux-dev-02](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | +| [aws_security_group.linux-dev-02](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_vpc_security_group_ingress_rule.linux-dev-02_ssh](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | +| [aws_vpc_security_group_egress_rule.linux-dev-02_all_out](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | +| [aws_ami.linux-dev-02_ami](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | +| [aws_ec2_managed_prefix_list.shared_services_management](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_managed_prefix_list) | data source | +| [aws_subnet.application](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | +| [aws_subnets.application](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnets) | data source | +| [aws_vpc.finance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source | +| [vault_generic_secret.account_ids](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/data-sources/generic_secret) | data source | +| [vault_generic_secret.internal_cidrs](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/data-sources/generic_secret) | data source | +| [vault_generic_secret.kms_keys](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/data-sources/generic_secret) | data source | +| [vault_generic_secret.security_kms_keys](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/data-sources/generic_secret) | data source | +| [vault_generic_secret.security_s3_buckets](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/data-sources/generic_secret) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [ami_version_pattern](#input\_ami_version_pattern) | The pattern to use when filtering for AMI version by name. | `string` | `"*"` | no | +| [application_subnet_pattern](#input\_application_subnet_pattern) | The pattern to use when filtering for application subnets by 'Name' tag. | `string` | `"sub-application-*"` | no | +| [aws_account](#input\_aws_account) | The name of the AWS account (e.g., "finance-development") | `string` | n/a | yes | +| [default_log_retention_in_days](#input\_default_log_retention_in_days) | The default log retention period in days to be used for CloudWatch log groups. | `number` | `7` | no | +| [dns_zone_suffix](#input\_dns_zone_suffix) | The common DNS hosted zone suffix used across accounts. | `string` | `"finance.aws.internal"` | no | +| [environment](#input\_environment) | The environment name (e.g., "development", "staging", "live") | `string` | n/a | yes | +| [instance_count](#input\_instance_count) | The number EC2 instances to create. | `number` | n/a | yes | +| [instance_type](#input\_instance_type) | The instance type to use for EC2 instances. | `string` | n/a | yes | +| [region](#input\_region) | The AWS region in which resources will be created. | `string` | n/a | yes | +| [root_volume_size](#input\_root_volume_size) | The size of the root volume in gibibytes (GiB). | `number` | `30` | no | +| [service](#input\_service) | The service name to be used when creating AWS resources. | `string` | `"e5"` | no | +| [service_subtype](#input\_service_subtype) | The service subtype name to be used when creating AWS resources. | `string` | `"e5-lfp"` | no | +| [team](#input\_team) | The team name for ownership of this service. | `string` | `"Finance"` | no | + +### Vault Authentication Variables + +Depending on the authentication method used for Vault, one of the following sets of variables will be required: + +#### UserPass Authentication - Preferred Method + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [hashicorp_vault_username](#input\_hashicorp_vault_username) | The username used when retrieving configuration from Hashicorp Vault | `string` | n/a | yes | +| [hashicorp_vault_password](#input\_hashicorp_vault_password) | The password used when retrieving configuration from Hashicorp Vault | `string` | n/a | yes | + +#### AppRole Authentication + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [hashicorp_vault_role_id](#input\_hashicorp_vault_role_id) | The role identifier used when retrieving configuration from Hashicorp Vault | `string` | n/a | yes | +| [hashicorp_vault_secret_id](#input\_hashicorp_vault_secret_id) | The secret identifier used when retrieving configuration from Hashicorp Vault | `string` | n/a | yes | + +#### Token Authentication - **Deprecated** + +This method is no longer in use, but used environment variables `VAULT_ADDR` and `VAULT_TOKEN`. + +## Locals + +| Name | Description | +|------|-------------| +| [account_ids_secrets](#local\_account_ids_secrets) | Account IDs retrieved from Vault | +| [application_subnet_ids_by_az](#local\_application_subnet_ids_by_az) | Map of availability zones to subnet IDs for application subnets | +| [common_resource_name](#local\_common_resource_name) | Common name format for resources | +| [common_tags](#local\_common_tags) | Common tags to be applied to all resources | +| [dns_zone](#local\_dns_zone) | The DNS zone for the environment | +| [linux-dev-02_ami_owner_id](#local\_linux-dev-02_ami_owner_id) | E5 lfp AMI owner ID | +| [security_kms_keys_data](#local\_security_kms_keys_data) | Security KMS keys data from Vault | +| [security_s3_data](#local\_security_s3_data) | Security S3 bucket data from Vault | +| [session_manager_bucket_name](#local\_session_manager_bucket_name) | Session Manager S3 bucket name | +| [ssm_kms_key_id](#local\_ssm_kms_key_id) | SSM KMS key ID | diff --git a/groups/linux-dev-03/data.tf b/groups/linux-dev-03/data.tf new file mode 100644 index 0000000..b665587 --- /dev/null +++ b/groups/linux-dev-03/data.tf @@ -0,0 +1,103 @@ +data "aws_ec2_managed_prefix_list" "administration_cidr_ranges" { + name = "administration-cidr-ranges" +} + +data "aws_kms_alias" "ebs" { + name = local.kms_key_alias +} + +data "vault_generic_secret" "kms_key_alias" { + path = "applications/${var.aws_account}-${var.aws_region}/${var.service}/${var.service_subtype}" +} + +data "aws_vpc" "heritage-development" { + filter { + name = "tag:Name" + values = ["vpc-${var.aws_account}"] + } +} + +data "aws_route53_zone" "linux_dev_02" { + name = local.dns_zone + vpc_id = data.aws_vpc.heritage-development.id +} + +data "vault_generic_secret" "internal_cidrs" { + path = "aws-accounts/network/internal_cidr_ranges" +} + +data "aws_subnets" "application" { + filter { + name = "vpc-id" + values = [data.aws_vpc.heritage-development.id] + } + + filter { + name = "tag:Name" + values = [var.application_subnet_pattern] + } +} + +data "aws_subnet" "application" { + count = length(data.aws_subnets.application.ids) + id = tolist(data.aws_subnets.application.ids)[count.index] +} + +data "aws_ami" "linux_dev_ami" { + most_recent = true + name_regex = "^rhel9-base-\\d.\\d.\\d" + + filter { + name = "name" + values = ["rhel9-base-${var.ami_version_pattern}"] + } + + filter { + name = "owner-id" + values = ["${local.ami_owner_id}"] + } +} + +data "vault_generic_secret" "ami_owner" { + path = "/applications/${var.aws_account}-${var.aws_region}/${var.service}/${var.service_subtype}" +} + +data "vault_generic_secret" "account_ids" { + path = "aws-accounts/account-ids" +} + +data "vault_generic_secret" "kms_keys" { + path = "aws-accounts/${var.aws_account}/kms" +} + +data "vault_generic_secret" "security_s3_buckets" { + path = "aws-accounts/security/s3" +} + +data "vault_generic_secret" "security_kms_keys" { + path = "aws-accounts/security/kms" +} + +data "vault_generic_secret" "shared_services_s3" { + path = "aws-accounts/shared-services/s3" +} + +data "vault_generic_secret" "sns_email" { + path = "/applications/${var.aws_account}-${var.aws_region}/${var.service}/sns/" +} + +data "vault_generic_secret" "sns_url" { + path = "/applications/${var.aws_account}-${var.aws_region}/${var.service}/sns/" +} + +data "template_file" "userdata" { + template = file("${path.module}/templates/user_data.tpl") + + count = var.instance_count + + vars = { + ENVIRONMENT = title(var.environment) + APPLICATION_NAME = var.service_subtype + ANSIBLE_INPUTS = jsonencode(merge(local.ansible_inputs, { hostname = format("%s", var.service_subtype) })) + } +} diff --git a/groups/linux-dev-03/dns.tf b/groups/linux-dev-03/dns.tf new file mode 100644 index 0000000..5aa06a7 --- /dev/null +++ b/groups/linux-dev-03/dns.tf @@ -0,0 +1,9 @@ +resource "aws_route53_record" "linux_dev_02" { + count = var.instance_count + + zone_id = data.aws_route53_zone.linux_dev_02.zone_id + name = "${var.service_subtype}" + type = "A" + ttl = 300 + records = [aws_instance.linux_dev_03[0].private_ip] +} diff --git a/groups/linux-dev-03/iam.tf b/groups/linux-dev-03/iam.tf new file mode 100644 index 0000000..f00c8c0 --- /dev/null +++ b/groups/linux-dev-03/iam.tf @@ -0,0 +1,19 @@ +module "instance_profile" { + source = "git@github.com:companieshouse/terraform-modules//aws/instance_profile?ref=tags/1.0.283" + name = local.common_resource_name + + enable_ssm = true + kms_key_refs = [local.ssm_kms_key_id] + + + custom_statements = [ + { + sid = "CloudWatchMetricsWrite" + effect = "Allow" + resources = ["*"] + actions = [ + "cloudwatch:PutMetricData" + ] + } + ] +} diff --git a/groups/linux-dev-03/instance.tf b/groups/linux-dev-03/instance.tf new file mode 100644 index 0000000..252fceb --- /dev/null +++ b/groups/linux-dev-03/instance.tf @@ -0,0 +1,58 @@ +resource "aws_instance" "linux_dev_03" { + count = var.instance_count + + ami = data.aws_ami.linux_dev_ami.id + instance_type = var.instance_type + subnet_id = element(local.application_subnet_ids_by_az, count.index) # use 'element' function for wrap-around behaviour + + iam_instance_profile = module.instance_profile.aws_iam_instance_profile.name + vpc_security_group_ids = [aws_security_group.linux_dev_02.id] + tags = { + Name = local.common_resource_name + Environment = var.environment + Service = var.service + ServiceSubType = var.service_subtype + Team = var.team + Backup = true + Domain = "${var.environment}.${var.dns_zone_suffix}" + Hostname = "${var.service_subtype}" + } + + root_block_device { + volume_size = var.root_volume_size + encrypted = var.encrypt_root_block_device + iops = var.root_block_device_iops + kms_key_id = data.aws_kms_alias.ebs.target_key_arn + throughput = var.root_block_device_throughput + volume_type = var.root_block_device_volume_type + tags = { + Name = "${local.common_resource_name}-root" + Environment = var.environment + Service = var.service + ServiceSubType = var.service_subtype + Team = var.team + Backup = true + } + + } + + ebs_block_device { + device_name = var.ebs_device_name + volume_size = var.data_volume_size_gib + encrypted = var.encrypt_ebs_block_device + iops = var.ebs_block_device_iops + kms_key_id = data.aws_kms_alias.ebs.target_key_arn + throughput = var.ebs_block_device_throughput + volume_type = var.ebs_block_device_volume_type + delete_on_termination = var.ebs_delete_on_termination + tags = { + Name = "${local.common_resource_name}-data" + Environment = var.environment + Service = var.service + ServiceSubType = var.service_subtype + Team = var.team + Backup = true + } + } + user_data = data.template_file.userdata[count.index].rendered +} diff --git a/groups/linux-dev-03/locals.tf b/groups/linux-dev-03/locals.tf new file mode 100644 index 0000000..6dc70e5 --- /dev/null +++ b/groups/linux-dev-03/locals.tf @@ -0,0 +1,42 @@ +locals { + application_subnet_ids_by_az = values(zipmap(data.aws_subnet.application[*].availability_zone, data.aws_subnet.application[*].id)) + + common_tags = { + Environment = var.environment + Service = var.service + ServiceSubType = var.service_subtype + Team = var.team + } + + common_resource_name = "${var.environment}-${var.service_subtype}" + dns_zone = "${var.environment}.${var.dns_zone_suffix}" + + security_s3_data = data.vault_generic_secret.security_s3_buckets.data + session_manager_bucket_name = local.security_s3_data.session-manager-bucket-name + + shared_services_s3_data = data.vault_generic_secret.shared_services_s3.data + resources_bucket_name = local.shared_services_s3_data["resources_bucket_name"] + + security_kms_keys_data = data.vault_generic_secret.security_kms_keys.data + ssm_kms_key_id = local.security_kms_keys_data.session-manager-kms-key-arn + + account_ids_secrets = jsondecode(data.vault_generic_secret.account_ids.data_json) + + kms_key = data.vault_generic_secret.kms_key_alias.data + kms_key_alias = local.kms_key["kms_key_alias"] + + sns_email_secret = data.vault_generic_secret.sns_email.data + linux_sns_email = local.sns_email_secret["linux_email"] + + ami_owner = data.vault_generic_secret.ami_owner.data + ami_owner_id = local.ami_owner["ami_owner"] + + ansible_inputs = { + environment = var.environment + region = var.aws_region + fqdn = "${var.service_subtype}.${var.environment}.${var.dns_zone_suffix}" + hostname = var.service_subtype + } + + +} diff --git a/groups/linux-dev-03/main.tf b/groups/linux-dev-03/main.tf new file mode 100644 index 0000000..fd616a8 --- /dev/null +++ b/groups/linux-dev-03/main.tf @@ -0,0 +1,20 @@ +terraform { + required_version = ">= 1.3" + + backend "s3" {} + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.37.0, < 6.0.0" + } + vault = { + source = "hashicorp/vault" + version = ">= 3.25.0, < 5.0.0" + } + } +} + +provider "aws" { + region = var.aws_region +} diff --git a/groups/linux-dev-03/profiles/heritage-development-eu-west-2/development/vars b/groups/linux-dev-03/profiles/heritage-development-eu-west-2/development/vars new file mode 100644 index 0000000..542dc83 --- /dev/null +++ b/groups/linux-dev-03/profiles/heritage-development-eu-west-2/development/vars @@ -0,0 +1,17 @@ +# Account details + +aws_account = "heritage-development" +aws_region = "eu-west-2" +environment = "development" +dns_zone_suffix = "heritage.aws.internal" + +# Instance details + +instance_count = "1" +instance_type = "t2.medium" +root_volume_size = 40 +data_volume_size_gib = 20 +service = "unix-development" +service_subtype = "linux-dev-03" +application_subnet_pattern = "sub-application-*" +default_log_retention_in_days = "7" diff --git a/groups/linux-dev-03/security_groups.tf b/groups/linux-dev-03/security_groups.tf new file mode 100644 index 0000000..475d735 --- /dev/null +++ b/groups/linux-dev-03/security_groups.tf @@ -0,0 +1,26 @@ +resource "aws_security_group" "linux_dev_02" { + name = local.common_resource_name + description = "Security group for the ${var.service_subtype} EC2 instances" + vpc_id = data.aws_vpc.heritage-development.id + + tags = merge(local.common_tags, { + Name = "${local.common_resource_name}" + }) +} + +resource "aws_vpc_security_group_ingress_rule" "linux_dev_02_ssh" { + description = "Allow SSH connectivity for application deployments" + security_group_id = aws_security_group.linux_dev_02.id + prefix_list_id = data.aws_ec2_managed_prefix_list.administration_cidr_ranges.id + ip_protocol = "tcp" + from_port = 22 + to_port = 22 +} + +resource "aws_vpc_security_group_egress_rule" "linux_dev_02_all_out" { + description = "Allow outbound traffic" + security_group_id = aws_security_group.linux_dev_02.id + cidr_ipv4 = "0.0.0.0/0" + ip_protocol = "-1" +} + diff --git a/groups/linux-dev-03/templates/user_data.tpl b/groups/linux-dev-03/templates/user_data.tpl new file mode 100755 index 0000000..5e74323 --- /dev/null +++ b/groups/linux-dev-03/templates/user_data.tpl @@ -0,0 +1,12 @@ +2#! /usr/bin/bash +# Redirect the user-data output to the console logs +exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1 + +#Restart iscsid to take in the new initiator name. +systemctl restart iscsid + +#Run Ansible playbook for deployment using provided inputs +cat <inputs.json +${ANSIBLE_INPUTS} +EOF +/usr/local/bin/ansible-playbook /root/deployment.yml -e "@inputs.json" diff --git a/groups/linux-dev-03/variables.tf b/groups/linux-dev-03/variables.tf new file mode 100644 index 0000000..f8b9e55 --- /dev/null +++ b/groups/linux-dev-03/variables.tf @@ -0,0 +1,136 @@ +variable "aws_account" { + type = string + description = "The name of the AWS account in which resources will be provisioned." +} + +variable "aws_region" { + type = string + description = "The AWS region in which resources will be created." +} + +variable "environment" { + type = string + description = "The environment name to be used when provisioning AWS resources." +} + +variable "ami_version_pattern" { + type = string + description = "The pattern to use when filtering for AMI version by name." + default = "*" +} + +variable "instance_count" { + type = number + description = "The number EC2 instances to create." + default = 1 +} + +variable "root_volume_size" { + type = number + description = "The size of the root volume in gibibytes (GiB)." + default = 20 +} + +variable "encrypt_root_block_device" { + default = true + description = "Defines whether the EBS volume should be encrypted with the cluster's KMS key" + type = bool +} + +variable "root_block_device_iops" { + default = 3000 + description = "The required IOPS on the EBS volume; 3000 is the gp3 default" + type = number +} + +variable "root_block_device_throughput" { + default = 125 + description = "The required EBS volume throughput in MiB/s; 125 is the gp3 default" + type = number +} + +variable "root_block_device_volume_type" { + default = "gp3" + description = "The type of EBS volume to provision" + type = string +} + +variable "data_volume_size_gib" { + type = number + default = 20 + description = "The EC2 instance data volume size in Gibibytes (GiB)" +} + +variable "ebs_device_name" { + default = "/dev/xvdc" + description = "The device name for the ebs device" + type = string +} + +variable "encrypt_ebs_block_device" { + default = true + description = "Defines whether the EBS volume should be encrypted with the cluster's KMS key" + type = bool +} + +variable "ebs_delete_on_termination" { + default = true + description = "Defines whether the EBS volume should be deleted on terination" + type = bool +} +variable "ebs_block_device_iops" { + default = 3000 + description = "The required IOPS on the EBS volume; 3000 is the gp3 default" + type = number +} + +variable "ebs_block_device_throughput" { + default = 125 + description = "The required EBS volume throughput in MiB/s; 125 is the gp3 default" + type = number +} + +variable "ebs_block_device_volume_type" { + default = "gp3" + description = "The type of EBS volume to provision" + type = string +} + +variable "instance_type" { + type = string + description = "The instance type to use for EC2 instances." + default = "t2.micro" +} + +variable "application_subnet_pattern" { + type = string + description = "The pattern to use when filtering for application subnets by 'Name' tag." +} + +variable "dns_zone_suffix" { + type = string + description = "The common DNS hosted zone suffix used across accounts." + default = "heritage.aws.internal" +} + +variable "default_log_retention_in_days" { + type = number + description = "The default log retention period in days to be used for CloudWatch log groups." +} + +variable "service" { + type = string + description = "The service name to be used when creating AWS resources." + +} + +variable "service_subtype" { + type = string + description = "The service subtype name to be used when creating AWS resources." +} + +variable "team" { + type = string + description = "The team name for ownership of this service." + default = "Linux/Storage" +} diff --git a/groups/linux-dev-03/vault-providers/approle b/groups/linux-dev-03/vault-providers/approle new file mode 100644 index 0000000..a4f8a36 --- /dev/null +++ b/groups/linux-dev-03/vault-providers/approle @@ -0,0 +1,20 @@ +variable "hashicorp_vault_role_id" { + description = "The role identifier used when retrieving configuration from Hashicorp Vault" + type = string +} + +variable "hashicorp_vault_secret_id" { + description = "The secret identifier used when retrieving configuration from Hashicorp Vault" + type = string +} + +provider "vault" { + auth_login { + path = "auth/approle/login" + + parameters = { + role_id = var.hashicorp_vault_role_id + secret_id = var.hashicorp_vault_secret_id + } + } +} diff --git a/groups/linux-dev-03/vault-providers/token b/groups/linux-dev-03/vault-providers/token new file mode 100644 index 0000000..37a40c9 --- /dev/null +++ b/groups/linux-dev-03/vault-providers/token @@ -0,0 +1,5 @@ +provider "vault" { + # Credentials read from the environment variables: + # ${VAULT_ADDR} + # ${VAULT_TOKEN} +} diff --git a/groups/linux-dev-03/vault-providers/userpass b/groups/linux-dev-03/vault-providers/userpass new file mode 100644 index 0000000..f3075ec --- /dev/null +++ b/groups/linux-dev-03/vault-providers/userpass @@ -0,0 +1,19 @@ +variable "hashicorp_vault_username" { + description = "The username used when retrieving configuration from Hashicorp Vault" + type = string +} + +variable "hashicorp_vault_password" { + description = "The password used when retrieving configuration from Hashicorp Vault" + type = string +} + +provider "vault" { + auth_login { + path = "auth/userpass/login/${var.hashicorp_vault_username}" + + parameters = { + password = var.hashicorp_vault_password + } + } +} diff --git a/groups/linux-dev-03/vault.tf b/groups/linux-dev-03/vault.tf new file mode 100644 index 0000000..f3075ec --- /dev/null +++ b/groups/linux-dev-03/vault.tf @@ -0,0 +1,19 @@ +variable "hashicorp_vault_username" { + description = "The username used when retrieving configuration from Hashicorp Vault" + type = string +} + +variable "hashicorp_vault_password" { + description = "The password used when retrieving configuration from Hashicorp Vault" + type = string +} + +provider "vault" { + auth_login { + path = "auth/userpass/login/${var.hashicorp_vault_username}" + + parameters = { + password = var.hashicorp_vault_password + } + } +} diff --git a/groups/linux-dev-03/version b/groups/linux-dev-03/version new file mode 100644 index 0000000..1b79b15 --- /dev/null +++ b/groups/linux-dev-03/version @@ -0,0 +1 @@ +linux-dev-02-1.0