Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up publishing-amazonmq ready for moving. #1826

Merged
merged 2 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.7
1.7.5
215 changes: 83 additions & 132 deletions terraform/projects/app-publishing-amazonmq/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,45 @@
*/
terraform {
backend "s3" {}
required_version = ">= 1.1.0"
required_version = "~> 1.7"

required_providers {
archive = {
source = "hashicorp/archive"
version = "~> 2.4"
}
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
version = "~> 5.0"
}
dns = {
source = "hashicorp/dns"
version = "~> 3.4"
}
local = {
version = "~> 2.2.3"
source = "hashicorp/local"
version = "~> 2.5"
}
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
}
}

provider "aws" {
region = var.aws_region
default_tags {
tags = {
Product = "GOV.UK"
Environment = var.aws_environment
Owner = "[email protected]"
repository = "govuk-aws"
terraform_deployment = basename(abspath(path.root))
}
}
}

# --------------------------------------------------------------
# Generate passwords for the user accounts
#
resource "random_password" "root" {
length = 24
special = false
Expand Down Expand Up @@ -68,15 +87,7 @@ locals {
content_data_api = random_password.content_data_api.result
email_alert_service = random_password.email_alert_service.result
}

tags = {
Project = var.stackname
aws_stackname = var.stackname
aws_environment = var.aws_environment
}
}
# --------------------------------------------------------------
# Lookups to existing resources which we need to reference

data "aws_subnet" "lb_subnets" {
count = var.publishing_amazonmq_instance_count
Expand All @@ -89,38 +100,42 @@ data "aws_acm_certificate" "internal_cert" {
statuses = ["ISSUED"]
}

# The ip_address attributes of the AmazonMQ instances are blank in the outputs
# (see https://stackoverflow.com/a/69221987)
# So the only way we can get the IPs in order to add them to the
# Load Balancer target group, is by a DNS lookup
data "dns_a_record_set" "mq_instances" {
host = regex("://([^/:]+)", aws_mq_broker.publishing_amazonmq.instances.0.console_url)[0]
data "aws_vpc_endpoint" "mq" {
depends_on = [aws_mq_broker.publishing_amazonmq]
vpc_id = data.terraform_remote_state.infra_vpc.outputs.vpc_id
tags = { Broker = aws_mq_broker.publishing_amazonmq.id }
}

data "aws_network_interface" "mq" {
count = var.publishing_amazonmq_instance_count
id = sort(tolist(data.aws_vpc_endpoint.mq.network_interface_ids))[count.index]
}

# --------------------------------------------------------------
# The broker itself
resource "aws_mq_broker" "publishing_amazonmq" {
broker_name = var.publishing_amazonmq_broker_name

engine_type = var.engine_type
engine_version = var.engine_version
deployment_mode = var.deployment_mode
host_instance_type = var.host_instance_type
publicly_accessible = var.publicly_accessible
publicly_accessible = false
# use the existing RabbitMQ security group. We can move it
# over to this module at the point of migration
security_groups = ["${data.terraform_remote_state.infra_security_groups.outputs.sg_rabbitmq_id}"]
subnet_ids = var.deployment_mode == "SINGLE_INSTANCE" ? [data.terraform_remote_state.infra_networking.outputs.private_subnet_ids[0]] : data.terraform_remote_state.infra_networking.outputs.private_subnet_ids

security_groups = [data.terraform_remote_state.infra_security_groups.outputs.sg_rabbitmq_id]
subnet_ids = (
var.deployment_mode == "SINGLE_INSTANCE"
? [data.terraform_remote_state.infra_networking.outputs.private_subnet_ids[0]]
: data.terraform_remote_state.infra_networking.outputs.private_subnet_ids
)

auto_minor_version_upgrade = true
maintenance_window_start_time {
day_of_week = var.maintenance_window_start_time_day_of_week
time_of_day = var.maintenance_window_start_time_time_of_day
time_zone = var.maintenance_window_start_time_time_zone
day_of_week = var.maintenance_window_start_day_of_week
time_of_day = var.maintenance_window_start_time_utc
time_zone = "UTC"
}

logs {
general = true
}
logs { general = true }

# The Terraform provider will only allow us to create a single user
# All other users must be added from the web UI
Expand All @@ -129,52 +144,19 @@ resource "aws_mq_broker" "publishing_amazonmq" {
username = "root"
password = local.publishing_amazonmq_passwords["root"]
}

}

# --------------------------------------------------------------
# Security group rules

# Allow HTTPS access to Publishing's AmazonMQ from anything in the 'management' SG
# (i.e. all EC2 instances)
resource "aws_security_group_rule" "publishingamazonmq_ingress_management_https" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
description = "HTTPS ingress for Publishing AmazonMQ"

# Which security group is the rule assigned to
security_group_id = data.terraform_remote_state.infra_security_groups.outputs.sg_rabbitmq_id
source_security_group_id = data.terraform_remote_state.infra_security_groups.outputs.sg_management_id
}
# and also from the load balancer, so that the health checks don't fail
resource "aws_security_group_rule" "publishingamazonmq_ingress_lb_https" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
description = "HTTPS ingress for Publishing AmazonMQ"
description = "LB healthchecks to Publishing AmazonMQ"

# Which security group is the rule assigned to
security_group_id = data.terraform_remote_state.infra_security_groups.outputs.sg_rabbitmq_id
cidr_blocks = data.aws_subnet.lb_subnets[*].cidr_block
}

# existing RabbitMQ runs on port 5672, this AmazonMQ will be on port 5671
# so we need to allow that through the SG
resource "aws_security_group_rule" "publishingamazonmq_ingress_management_amqps" {
type = "ingress"
from_port = 5671
to_port = 5671
protocol = "tcp"
description = "AMQPS ingress for Publishing AmazonMQ"

# Which security group is the rule assigned to
security_group_id = data.terraform_remote_state.infra_security_groups.outputs.sg_rabbitmq_id
source_security_group_id = data.terraform_remote_state.infra_security_groups.outputs.sg_management_id
}
# and also from the load balancer as before
resource "aws_security_group_rule" "publishingamazonmq_ingress_lb_amqps" {
type = "ingress"
from_port = 5671
Expand All @@ -198,28 +180,18 @@ resource "aws_security_group_rule" "rabbitmq_egress_self_self" {
security_group_id = data.terraform_remote_state.infra_security_groups.outputs.sg_rabbitmq_id
}

# --------------------------------------------------------------
# Network Load Balancer

resource "aws_lb" "publishingmq_lb_internal" {
name = "publishingamazonmq-lb-internal"
tags = { "Name" = "publishingamazonmq-lb-internal" }
internal = true
load_balancer_type = "network"
subnets = aws_mq_broker.publishing_amazonmq.subnet_ids

access_logs {
bucket = data.terraform_remote_state.infra_monitoring.outputs.aws_logging_bucket_id
prefix = "lb/${var.stackname}-publishingamazonmq-internal-lb"
prefix = "lb/publishingamazonmq-internal-lb"
}

enable_deletion_protection = var.lb_delete_protection

tags = {
"Name" = "${var.stackname}-publishingamazonmq-internal"
"Project" = var.stackname
"aws_environment" = var.aws_environment
"aws_migration" = "publishingamazonmq"
}
}

# HTTPS listener & target group for web admin UI traffic
Expand Down Expand Up @@ -249,21 +221,17 @@ resource "aws_lb_target_group" "internal_https" {
}
}

# Attach all the IP addresses from the broker DNS lookup
# to the LB target group
resource "aws_lb_target_group_attachment" "internal_https_ips" {
count = var.publishing_amazonmq_instance_count
target_group_arn = aws_lb_target_group.internal_https.arn
target_id = data.dns_a_record_set.mq_instances.addrs[count.index]
port = 443

count = var.publishing_amazonmq_instance_count
depends_on = [
aws_mq_broker.publishing_amazonmq,
aws_lb_target_group.internal_https
aws_lb_target_group.internal_https,
]
target_group_arn = aws_lb_target_group.internal_https.arn
target_id = data.aws_network_interface.mq[count.index].private_ip
port = 443
}

# AMQPS listener for port 5671 traffic
resource "aws_lb_listener" "internal_amqps" {
load_balancer_arn = aws_lb.publishingmq_lb_internal.arn
port = "5671"
Expand Down Expand Up @@ -295,26 +263,22 @@ resource "aws_lb_target_group" "internal_amqps" {
# Attach all the IP addresses from the broker DNS lookup
# to the LB target group
resource "aws_lb_target_group_attachment" "internal_amqps_ips" {
count = var.publishing_amazonmq_instance_count
target_group_arn = aws_lb_target_group.internal_amqps.arn
target_id = data.dns_a_record_set.mq_instances.addrs[count.index]
port = 5671

count = var.publishing_amazonmq_instance_count
depends_on = [
aws_mq_broker.publishing_amazonmq,
aws_lb_target_group.internal_amqps
aws_lb_target_group.internal_amqps,
]
target_group_arn = aws_lb_target_group.internal_amqps.arn
target_id = data.aws_network_interface.mq[count.index].private_ip
port = 5671
}


# --------------------------------------------------------------
# DNS Entry

# DNS entry to go via the NLB
resource "aws_route53_record" "publishing_amazonmq_internal_root_domain_name" {
zone_id = data.terraform_remote_state.infra_root_dns_zones.outputs.internal_root_zone_id
name = "${lower(aws_mq_broker.publishing_amazonmq.broker_name)}.${data.terraform_remote_state.infra_root_dns_zones.outputs.internal_root_domain_name}"
type = "A"
# TODO: remove redundant FQDN.
name = "${lower(aws_mq_broker.publishing_amazonmq.broker_name)}.${data.terraform_remote_state.infra_root_dns_zones.outputs.internal_root_domain_name}"
type = "A"

alias {
name = aws_lb.publishingmq_lb_internal.dns_name
Expand All @@ -323,43 +287,39 @@ resource "aws_route53_record" "publishing_amazonmq_internal_root_domain_name" {
}
}

# --------------------------------------------------------------
# POST full RabbitMQ config to the management API
# We have to do this by creating and invoking a Lambda function
# in the target environment, as the Jenkins box in integration
# does not have access to other environments
# Create and invoke a Lambda function to POST the full RabbitMQ config to the
# management API in the target environment.
#
# Write the decrypted definitions from govuk-aws-data to a local file
# Write the decrypted definitions from govuk-aws-data to a local file.
resource "local_sensitive_file" "amazonmq_rabbitmq_definitions" {
filename = join(".", ["/tmp/amazonmq_rabbitmq_definitions", formatdate("YYYY-MM-DD-hhmm-ZZZ", timestamp()), "json"])
filename = join(".", [
"/tmp/amazonmq_rabbitmq_definitions",
formatdate("YYYY-MM-DD-hhmm-ZZZ", timestamp()),
"json",
])
content = templatefile("${path.cwd}/publishing-rabbitmq-schema.json.tpl", {
publishing_amazonmq_passwords = local.publishing_amazonmq_passwords
publishing_amazonmq_broker_name = var.publishing_amazonmq_broker_name
})
}

data "local_sensitive_file" "amazonmq_rabbitmq_definitions_interpolated" {
filename = local_sensitive_file.amazonmq_rabbitmq_definitions.filename

depends_on = [
local_sensitive_file.amazonmq_rabbitmq_definitions
]
depends_on = [local_sensitive_file.amazonmq_rabbitmq_definitions]
filename = local_sensitive_file.amazonmq_rabbitmq_definitions.filename
}


# Get the existing IAM policy by name
data "aws_iam_policy" "lambda_vpc_access" {
name = "AWSLambdaVPCAccessExecutionRole"
}

# Build a zip file which can be deployed to AWS Lambda
# Build a zip file for deploying the lambda.
data "archive_file" "artefact_lambda" {
type = "zip"
source_file = "${path.module}/../../lambda/PostConfigToAmazonMQ/post_config_to_amazonmq.py"
output_path = "${path.module}/../../lambda/PostConfigToAmazonMQ/post_config_to_amazonmq.zip"
}

# The Lambda function itself
resource "aws_lambda_function" "post_config_to_amazonmq" {
filename = data.archive_file.artefact_lambda.output_path
source_code_hash = data.archive_file.artefact_lambda.output_base64sha256
Expand All @@ -375,10 +335,8 @@ resource "aws_lambda_function" "post_config_to_amazonmq" {
}
}

# AWS Lambda Role
resource "aws_iam_role" "post_config_to_amazonmq_role" {
name = "post_config_to_amazonmq_role"

name = "post_config_to_amazonmq_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
Expand All @@ -395,27 +353,20 @@ resource "aws_iam_role" "post_config_to_amazonmq_role" {
}
EOF
}
# Attach the policy to the role

resource "aws_iam_role_policy_attachment" "lambda_role_policy" {
role = aws_iam_role.post_config_to_amazonmq_role.name
policy_arn = data.aws_iam_policy.lambda_vpc_access.arn
}

# Call the function
data "aws_lambda_invocation" "post_config_to_amazonmq" {
depends_on = [aws_security_group_rule.rabbitmq_egress_self_self]
function_name = aws_lambda_function.post_config_to_amazonmq.function_name

input = <<JSON
{
"url": "${aws_mq_broker.publishing_amazonmq.instances.0.console_url}/api/definitions",
"username": "root",
"password": "${local.publishing_amazonmq_passwords["root"]}",
"json_b64": "${base64encode(data.local_sensitive_file.amazonmq_rabbitmq_definitions_interpolated.content)}"
}
JSON

depends_on = [
aws_security_group_rule.rabbitmq_egress_self_self
]
input = jsonencode({
url = "${aws_mq_broker.publishing_amazonmq.instances[0].console_url}/api/definitions"
username = "root"
password = local.publishing_amazonmq_passwords["root"]
json_b64 = base64encode(data.local_sensitive_file.amazonmq_rabbitmq_definitions_interpolated.content)
})
}

Loading
Loading