diff --git a/infrastructure/terraform/components/reporting/README.md b/infrastructure/terraform/components/reporting/README.md index 9f1f25ed..c47af7b0 100644 --- a/infrastructure/terraform/components/reporting/README.md +++ b/infrastructure/terraform/components/reporting/README.md @@ -41,11 +41,12 @@ No requirements. | [periodic\_s3backup\_copy\_retention\_days](#input\_periodic\_s3backup\_copy\_retention\_days) | number of days to retain weekly s3 backups in the destination vault | `number` | `31` | no | | [periodic\_s3backup\_retention\_days](#input\_periodic\_s3backup\_retention\_days) | number of days to retain weekly s3 backups | `number` | `31` | no | | [periodic\_s3backup\_schedule](#input\_periodic\_s3backup\_schedule) | Crontab formatted schedule for Periodic S3 Backups | `string` | `"cron(0 5 ? * 7 *)"` | no | +| [powerbi\_gateway\_instance\_count](#input\_powerbi\_gateway\_instance\_count) | Number of standalone Power BI On-Premises Gateway instances created directly from the launch template. | `number` | `2` | no | | [private\_subnet\_cidrs](#input\_private\_subnet\_cidrs) | List of CIDR blocks for private subnets. | `list(string)` | `[]` | no | | [project](#input\_project) | The name of the Project we are bootstrapping tfscaffold for | `string` | n/a | yes | | [public\_subnet\_cidrs](#input\_public\_subnet\_cidrs) | List of CIDR blocks for public subnets. | `list(string)` | `[]` | no | | [region](#input\_region) | The AWS Region | `string` | n/a | yes | -| [root\_volume\_size](#input\_root\_volume\_size) | Size of root volume for the Power BI On-Premises Gateway instances - 30GB minimum for Windows Server | `number` | `30` | no | +| [root\_volume\_size](#input\_root\_volume\_size) | Size of root volume for the Power BI On-Premises Gateway instances - 30GB minimum for Windows Server | `number` | `80` | no | | [scale\_in\_recurrence\_schedule](#input\_scale\_in\_recurrence\_schedule) | The cron expression for the scale in schedule. Set to null if no recurrence is needed. | `string` | `null` | no | | [scale\_out\_recurrence\_schedule](#input\_scale\_out\_recurrence\_schedule) | The cron expression for the scale out schedule. Set to null if no recurrence is needed. | `string` | `null` | no | | [shared\_infra\_account\_id](#input\_shared\_infra\_account\_id) | The AWS Account ID of the shared infrastructure account | `string` | `"000000000000"` | no | diff --git a/infrastructure/terraform/components/reporting/autoscaling_group_powerbi_gateway.tf b/infrastructure/terraform/components/reporting/autoscaling_group_powerbi_gateway.tf index 5e7c6872..5400b7d4 100644 --- a/infrastructure/terraform/components/reporting/autoscaling_group_powerbi_gateway.tf +++ b/infrastructure/terraform/components/reporting/autoscaling_group_powerbi_gateway.tf @@ -4,7 +4,7 @@ resource "aws_autoscaling_group" "powerbi_gateway" { name = local.csi launch_template { - id = aws_launch_template.powerbi_gateway[0].id + id = aws_launch_template.powerbi_gateway_asg[0].id version = "$Latest" } diff --git a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_patch_task_failed.tf b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_patch_task_failed.tf new file mode 100644 index 00000000..bf230769 --- /dev/null +++ b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_patch_task_failed.tf @@ -0,0 +1,18 @@ +resource "aws_cloudwatch_metric_alarm" "patch_task_failed" { + count = var.enable_powerbi_gateway ? 1 : 0 + + alarm_name = "${local.csi}-patch-task-failed" + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + metric_name = "FailedCommands" + namespace = "AWS/SSM-RunCommand" + period = 300 + statistic = "Sum" + threshold = 1 + alarm_description = "Alarm when the AWS-RunPatchBaseline maintenance window task reports a failed run" + treat_missing_data = "notBreaching" + + dimensions = { + DocumentName = "AWS-RunPatchBaseline" + } +} diff --git a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_powerbi_gateway_standalone_status.tf b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_powerbi_gateway_standalone_status.tf new file mode 100644 index 00000000..a84763fb --- /dev/null +++ b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_powerbi_gateway_standalone_status.tf @@ -0,0 +1,25 @@ +resource "aws_cloudwatch_metric_alarm" "powerbi_gateway_standalone_status_check_failed" { + for_each = var.enable_powerbi_gateway ? { + for idx, instance in aws_instance.powerbi_gateway_standalone : + idx => { + id = instance.id + name = format("%s-powerbi-gateway-standalone-%02d-status-check-failed", local.csi, idx + 1) + } + } : {} + + alarm_name = each.value.name + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 2 + datapoints_to_alarm = 2 + metric_name = "StatusCheckFailed" + namespace = "AWS/EC2" + period = 300 + statistic = "Maximum" + threshold = 1 + alarm_description = "Instance or system status check failed for a standalone Power BI gateway host" + treat_missing_data = "breaching" + + dimensions = { + InstanceId = each.value.id + } +} diff --git a/infrastructure/terraform/components/reporting/ec2_instances_powerbi_gateway.tf b/infrastructure/terraform/components/reporting/ec2_instances_powerbi_gateway.tf new file mode 100644 index 00000000..5d4f189e --- /dev/null +++ b/infrastructure/terraform/components/reporting/ec2_instances_powerbi_gateway.tf @@ -0,0 +1,13 @@ +# Standalone EC2 instances launched directly from the Power BI gateway launch template. +resource "aws_instance" "powerbi_gateway_standalone" { + count = var.enable_powerbi_gateway ? var.powerbi_gateway_instance_count : 0 + + launch_template { + id = aws_launch_template.powerbi_gateway_standalone[0].id + version = "$Latest" + } + + tags = { + Name = format("%s-powerbi-gateway-standalone-%02d", local.csi, count.index + 1) + } +} diff --git a/infrastructure/terraform/components/reporting/launch_template_powerbi_gateway.tf b/infrastructure/terraform/components/reporting/launch_template_powerbi_gateway_asg.tf similarity index 92% rename from infrastructure/terraform/components/reporting/launch_template_powerbi_gateway.tf rename to infrastructure/terraform/components/reporting/launch_template_powerbi_gateway_asg.tf index 00c0a657..08e14a75 100644 --- a/infrastructure/terraform/components/reporting/launch_template_powerbi_gateway.tf +++ b/infrastructure/terraform/components/reporting/launch_template_powerbi_gateway_asg.tf @@ -1,8 +1,8 @@ -resource "aws_launch_template" "powerbi_gateway" { +resource "aws_launch_template" "powerbi_gateway_asg" { count = var.enable_powerbi_gateway ? 1 : 0 - name = local.csi - description = "Template for the Power BI On-Premises Gateway" + name = "${local.csi}-asg" + description = "Template for the Power BI On-Premises Gateway (ASG)" update_default_version = true image_id = "resolve:ssm:/aws/service/ami-windows-latest/Windows_Server-2022-English-Full-Base" instance_type = var.instance_type diff --git a/infrastructure/terraform/components/reporting/launch_template_powerbi_gateway_standalone.tf b/infrastructure/terraform/components/reporting/launch_template_powerbi_gateway_standalone.tf new file mode 100644 index 00000000..1199f786 --- /dev/null +++ b/infrastructure/terraform/components/reporting/launch_template_powerbi_gateway_standalone.tf @@ -0,0 +1,67 @@ +resource "aws_launch_template" "powerbi_gateway_standalone" { + count = var.enable_powerbi_gateway ? 1 : 0 + + name = "${local.csi}-standalone" + description = "Template for the Power BI On-Premises Gateway (standalone instances)" + update_default_version = true + image_id = "resolve:ssm:/aws/service/ami-windows-latest/Windows_Server-2022-English-Full-Base" + instance_type = var.instance_type + user_data = data.cloudinit_config.powerbi_gateway[0].rendered + instance_initiated_shutdown_behavior = var.enable_spot ? "terminate" : "stop" + ebs_optimized = true + + block_device_mappings { + device_name = "/dev/sda1" + ebs { + delete_on_termination = true + encrypted = true + kms_key_id = aws_kms_key.ebs[0].arn + volume_size = var.root_volume_size + volume_type = "gp3" + } + } + + iam_instance_profile { + name = aws_iam_instance_profile.powerbi_gateway[0].name + } + + dynamic "instance_market_options" { + for_each = var.enable_spot ? [1] : [] + content { + market_type = "spot" + spot_options { + max_price = var.spot_max_price + spot_instance_type = "one-time" + } + } + } + + monitoring { + enabled = true + } + + network_interfaces { + delete_on_termination = true + associate_public_ip_address = false + security_groups = [ + aws_security_group.powerbi_gateway[0].id + ] + subnet_id = element(module.powerbi_gateway_vpc[0].private_subnets, count.index) + } + + metadata_options { + http_endpoint = "enabled" + http_tokens = "required" + http_put_response_hop_limit = 5 + } + + tag_specifications { + resource_type = "instance" + tags = local.deployment_default_tags + } + + tag_specifications { + resource_type = "volume" + tags = local.deployment_default_tags + } +} diff --git a/infrastructure/terraform/components/reporting/ssm_maintenance_window_patch_window.tf b/infrastructure/terraform/components/reporting/ssm_maintenance_window_patch_window.tf index 605433ec..456bc65b 100644 --- a/infrastructure/terraform/components/reporting/ssm_maintenance_window_patch_window.tf +++ b/infrastructure/terraform/components/reporting/ssm_maintenance_window_patch_window.tf @@ -1,10 +1,21 @@ -resource "aws_ssm_maintenance_window" "patch_window" { +resource "aws_ssm_maintenance_window" "patch_window_sunday" { count = var.enable_powerbi_gateway ? 1 : 0 - name = "${local.csi}-windows-patch-window" - description = "Windows Server 2022 Patch Window" + name = "${local.csi}-windows-patch-window-sun" + description = "Windows Server 2022 Sunday Patch Window" schedule = "cron(0 3 ? * SUN *)" # Every Sunday at 3 AM duration = 4 cutoff = 1 allow_unassociated_targets = true } + +resource "aws_ssm_maintenance_window" "patch_window_wednesday" { + count = var.enable_powerbi_gateway ? 1 : 0 + + name = "${local.csi}-windows-patch-window-wed" + description = "Windows Server 2022 Wednesday Patch Window" + schedule = "cron(0 3 ? * WED *)" # Every Wednesday at 3 AM + duration = 4 + cutoff = 1 + allow_unassociated_targets = true +} diff --git a/infrastructure/terraform/components/reporting/ssm_maintenance_window_target_windows_instances.tf b/infrastructure/terraform/components/reporting/ssm_maintenance_window_target_windows_instances.tf index 9425386f..3abb17d5 100644 --- a/infrastructure/terraform/components/reporting/ssm_maintenance_window_target_windows_instances.tf +++ b/infrastructure/terraform/components/reporting/ssm_maintenance_window_target_windows_instances.tf @@ -1,13 +1,41 @@ -resource "aws_ssm_maintenance_window_target" "windows_instances" { +resource "aws_ssm_maintenance_window_target" "windows_instances_sunday_asg" { count = var.enable_powerbi_gateway ? 1 : 0 - description = "Windows Server 2022 Maintenance Window Target" - window_id = aws_ssm_maintenance_window.patch_window[0].id + description = "Windows Server 2022 Sunday Maintenance Window Target (ASG)" + window_id = aws_ssm_maintenance_window.patch_window_sunday[0].id resource_type = "INSTANCE" - name = "${local.csi}-maintenance-window-target" + name = "${local.csi}-maintenance-window-target-sun-asg" targets { key = "tag:Patch Group" values = ["${local.csi}-windows-group"] } } + +resource "aws_ssm_maintenance_window_target" "windows_instances_sunday_standalone" { + count = var.enable_powerbi_gateway && var.powerbi_gateway_instance_count >= 1 ? 1 : 0 + + description = "Windows Server 2022 Sunday Maintenance Window Target (Standalone)" + window_id = aws_ssm_maintenance_window.patch_window_sunday[0].id + resource_type = "INSTANCE" + name = "${local.csi}-maintenance-window-target-sun-standalone" + + targets { + key = "InstanceIds" + values = [aws_instance.powerbi_gateway_standalone[0].id] + } +} + +resource "aws_ssm_maintenance_window_target" "windows_instances_wednesday" { + count = var.enable_powerbi_gateway && var.powerbi_gateway_instance_count >= 2 ? 1 : 0 + + description = "Windows Server 2022 Wednesday Maintenance Window Target" + window_id = aws_ssm_maintenance_window.patch_window_wednesday[0].id + resource_type = "INSTANCE" + name = "${local.csi}-maintenance-window-target-wed" + + targets { + key = "InstanceIds" + values = [aws_instance.powerbi_gateway_standalone[1].id] + } +} diff --git a/infrastructure/terraform/components/reporting/ssm_maintenance_window_task_patch_task.tf b/infrastructure/terraform/components/reporting/ssm_maintenance_window_task_patch_task.tf index a5b62955..98dff887 100644 --- a/infrastructure/terraform/components/reporting/ssm_maintenance_window_task_patch_task.tf +++ b/infrastructure/terraform/components/reporting/ssm_maintenance_window_task_patch_task.tf @@ -1,27 +1,66 @@ -resource "aws_ssm_maintenance_window_task" "patch_task" { +resource "aws_ssm_maintenance_window_task" "patch_task_sunday" { count = var.enable_powerbi_gateway ? 1 : 0 - description = "Windows Server 2022 Patch Task" - window_id = aws_ssm_maintenance_window.patch_window[0].id + description = "Windows Server 2022 Sunday Patch Task" + window_id = aws_ssm_maintenance_window.patch_window_sunday[0].id + task_arn = "AWS-RunPatchBaseline" + task_type = "RUN_COMMAND" + + targets { + key = "WindowTargetIds" + values = concat( + [aws_ssm_maintenance_window_target.windows_instances_sunday_asg[0].id], + var.powerbi_gateway_instance_count >= 1 ? [aws_ssm_maintenance_window_target.windows_instances_sunday_standalone[0].id] : [] + ) + } + + task_invocation_parameters { + run_command_parameters { + comment = "Patching Sunday instances" + parameter { + name = "Operation" + values = ["Install"] + } + parameter { + name = "RebootOption" + values = ["RebootIfNeeded"] + } + } + } + + priority = 1 + max_concurrency = "1" + max_errors = "1" +} + +resource "aws_ssm_maintenance_window_task" "patch_task_wednesday" { + count = var.enable_powerbi_gateway && var.powerbi_gateway_instance_count >= 2 ? 1 : 0 + + description = "Windows Server 2022 Wednesday Patch Task" + window_id = aws_ssm_maintenance_window.patch_window_wednesday[0].id task_arn = "AWS-RunPatchBaseline" task_type = "RUN_COMMAND" targets { key = "WindowTargetIds" - values = [aws_ssm_maintenance_window_target.windows_instances[0].id] + values = [aws_ssm_maintenance_window_target.windows_instances_wednesday[0].id] } task_invocation_parameters { run_command_parameters { - comment = "Patching Windows Instances" + comment = "Patching Wednesday instance" parameter { name = "Operation" values = ["Install"] } + parameter { + name = "RebootOption" + values = ["RebootIfNeeded"] + } } } priority = 1 - max_concurrency = "2" + max_concurrency = "1" max_errors = "1" } diff --git a/infrastructure/terraform/components/reporting/templates/cloudinit_config.tmpl b/infrastructure/terraform/components/reporting/templates/cloudinit_config.tmpl index a54f609c..5555c9f1 100644 --- a/infrastructure/terraform/components/reporting/templates/cloudinit_config.tmpl +++ b/infrastructure/terraform/components/reporting/templates/cloudinit_config.tmpl @@ -19,7 +19,7 @@ if (-not (Get-Command choco -ErrorAction SilentlyContinue)) { } # Install PowerBI On-Premises Gateway and Desktop -choco install -y powerbigateway --version=3000.230.14 --ignore-checksums +choco install -y powerbigateway --version=3000.298.8 --ignore-checksums choco install -y powerbi --ignore-checksums # Install vim @@ -29,7 +29,7 @@ choco install -y vim choco install -y powershell-core # Install Amazon Athena ODBC 2.x Driver -`$athenaDriverUrl = "https://s3.amazonaws.com/athena-downloads/drivers/ODBC/v2.0.3.0/Windows/AmazonAthenaODBC-2.0.3.0.msi" +`$athenaDriverUrl = "https://downloads.athena.us-east-1.amazonaws.com/drivers/ODBC/v2.0.6.0/Windows/AmazonAthenaODBC-2.0.6.0.msi" `$athenaDriverInstaller = "C:\scripts\SimbaAthenaODBC.msi" Invoke-WebRequest -Uri `$athenaDriverUrl -OutFile `$athenaDriverInstaller diff --git a/infrastructure/terraform/components/reporting/variables.tf b/infrastructure/terraform/components/reporting/variables.tf index ede4b17c..fccc5200 100644 --- a/infrastructure/terraform/components/reporting/variables.tf +++ b/infrastructure/terraform/components/reporting/variables.tf @@ -103,6 +103,12 @@ variable "enable_powerbi_gateway" { default = true } +variable "powerbi_gateway_instance_count" { + description = "Number of standalone Power BI On-Premises Gateway instances created directly from the launch template." + type = number + default = 2 +} + variable "public_subnet_cidrs" { description = "List of CIDR blocks for public subnets." type = list(string) @@ -154,7 +160,7 @@ variable "spot_max_price" { variable "root_volume_size" { type = number description = "Size of root volume for the Power BI On-Premises Gateway instances - 30GB minimum for Windows Server" - default = 30 + default = 80 } variable "scale_out_recurrence_schedule" { diff --git a/infrastructure/terraform/etc/env_eu-west-2_int.tfvars b/infrastructure/terraform/etc/env_eu-west-2_int.tfvars index 28dd77f9..e0ecde25 100644 --- a/infrastructure/terraform/etc/env_eu-west-2_int.tfvars +++ b/infrastructure/terraform/etc/env_eu-west-2_int.tfvars @@ -22,14 +22,6 @@ private_subnet_cidrs = [ "10.0.6.0/24" ] -instance_type = "t3.medium" -root_volume_size = 30 -desired_capacity = 1 -min_size = 1 -max_size = 1 -enable_spot = false -spot_max_price = "0.3" - enable_s3_backup = false shared_infra_account_id = "099709604300" diff --git a/infrastructure/terraform/etc/env_eu-west-2_main.tfvars b/infrastructure/terraform/etc/env_eu-west-2_main.tfvars index f717770f..8180b2db 100644 --- a/infrastructure/terraform/etc/env_eu-west-2_main.tfvars +++ b/infrastructure/terraform/etc/env_eu-west-2_main.tfvars @@ -15,6 +15,10 @@ core_account_ids = [ # PowerBI On-Premises Gateway variables: enable_powerbi_gateway = true +min_size = 2 +max_size = 2 +desired_capacity = 2 + public_subnet_cidrs = [ "10.0.1.0/24", @@ -28,14 +32,6 @@ private_subnet_cidrs = [ "10.0.6.0/24" ] -instance_type = "t3.medium" -root_volume_size = 30 -desired_capacity = 1 -min_size = 1 -max_size = 1 -enable_spot = false -spot_max_price = "0.3" - shared_infra_account_id = "099709604300" destination_backup_vault_arn = "arn:aws:backup:eu-west-2:390844765011:backup-vault:nhs-notify-reporting-dev-backup-vault" diff --git a/infrastructure/terraform/etc/env_eu-west-2_prod.tfvars b/infrastructure/terraform/etc/env_eu-west-2_prod.tfvars index 148a03fb..d3e79ce3 100644 --- a/infrastructure/terraform/etc/env_eu-west-2_prod.tfvars +++ b/infrastructure/terraform/etc/env_eu-west-2_prod.tfvars @@ -11,6 +11,11 @@ core_account_ids = [ # PowerBI On-Premises Gateway variables: enable_powerbi_gateway = true +min_size = 2 +max_size = 2 +desired_capacity = 2 +instance_type = "t3.xlarge" +root_volume_size = 200 public_subnet_cidrs = [ "10.0.1.0/24", @@ -24,14 +29,6 @@ private_subnet_cidrs = [ "10.0.6.0/24" ] -instance_type = "t3.medium" -root_volume_size = 30 -desired_capacity = 1 -min_size = 1 -max_size = 1 -enable_spot = false -spot_max_price = "0.3" - batch_client_ids = [ "c10ab104-86ae-48dc-b243-4906760952d3", "688040bc-92ea-4037-89f4-d105c9ae59a4" diff --git a/infrastructure/terraform/etc/env_eu-west-2_ref.tfvars b/infrastructure/terraform/etc/env_eu-west-2_ref.tfvars index bcc8c97f..609bbaa5 100644 --- a/infrastructure/terraform/etc/env_eu-west-2_ref.tfvars +++ b/infrastructure/terraform/etc/env_eu-west-2_ref.tfvars @@ -22,14 +22,6 @@ private_subnet_cidrs = [ "10.0.6.0/24" ] -instance_type = "t3.medium" -root_volume_size = 30 -desired_capacity = 1 -min_size = 1 -max_size = 1 -enable_spot = false -spot_max_price = "0.3" - batch_client_ids = [ "perf-test-client-1", "perf-test-client-2" diff --git a/infrastructure/terraform/etc/env_eu-west-2_uat.tfvars b/infrastructure/terraform/etc/env_eu-west-2_uat.tfvars index 72f697a0..030a0b98 100644 --- a/infrastructure/terraform/etc/env_eu-west-2_uat.tfvars +++ b/infrastructure/terraform/etc/env_eu-west-2_uat.tfvars @@ -22,14 +22,6 @@ private_subnet_cidrs = [ "10.0.6.0/24" ] -instance_type = "t3.medium" -root_volume_size = 30 -desired_capacity = 1 -min_size = 1 -max_size = 1 -enable_spot = false -spot_max_price = "0.3" - enable_s3_backup = false shared_infra_account_id = "099709604300"