diff --git a/terraform/account/README.md b/terraform/account/README.md index 56f5996592..7ef528eda8 100644 --- a/terraform/account/README.md +++ b/terraform/account/README.md @@ -90,17 +90,17 @@ For terraform_environment, this will be based on your PR and can be found in the | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | 1.7.1 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [terraform](#requirement\_terraform) | 1.7.2 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.eu\_west\_1](#provider\_aws.eu\_west\_1) | 5.34.0 | -| [aws.eu\_west\_2](#provider\_aws.eu\_west\_2) | 5.34.0 | -| [aws.global](#provider\_aws.global) | 5.34.0 | -| [aws.management\_global](#provider\_aws.management\_global) | 5.34.0 | +| [aws.eu\_west\_1](#provider\_aws.eu\_west\_1) | 5.35.0 | +| [aws.eu\_west\_2](#provider\_aws.eu\_west\_2) | 5.35.0 | +| [aws.global](#provider\_aws.global) | 5.35.0 | +| [aws.management\_global](#provider\_aws.management\_global) | 5.35.0 | ## Modules diff --git a/terraform/account/region/README.md b/terraform/account/region/README.md index 03932b080b..95bb8d42d1 100644 --- a/terraform/account/region/README.md +++ b/terraform/account/region/README.md @@ -4,16 +4,16 @@ | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | ~> 5.34.0 | -| [aws.global](#provider\_aws.global) | ~> 5.34.0 | -| [aws.management](#provider\_aws.management) | ~> 5.34.0 | -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws](#provider\_aws) | ~> 5.35.0 | +| [aws.global](#provider\_aws.global) | ~> 5.35.0 | +| [aws.management](#provider\_aws.management) | ~> 5.35.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/account/region/modules/antivirus_definitions/README.md b/terraform/account/region/modules/antivirus_definitions/README.md index a93b903274..6b30d6bb4d 100644 --- a/terraform/account/region/modules/antivirus_definitions/README.md +++ b/terraform/account/region/modules/antivirus_definitions/README.md @@ -8,13 +8,13 @@ This module creates a S3 bucket for antivirus definitions, and a Lambda function | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/account/region/modules/dns_firewall/README.md b/terraform/account/region/modules/dns_firewall/README.md index 972161d78c..2921b18ae6 100644 --- a/terraform/account/region/modules/dns_firewall/README.md +++ b/terraform/account/region/modules/dns_firewall/README.md @@ -8,13 +8,13 @@ This module creates a DNS Firewall rule group and rule group associations. | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/account/region/modules/s3_batch_manifests/README.md b/terraform/account/region/modules/s3_batch_manifests/README.md index 07d8d8f015..47aa07fe07 100644 --- a/terraform/account/region/modules/s3_batch_manifests/README.md +++ b/terraform/account/region/modules/s3_batch_manifests/README.md @@ -8,13 +8,13 @@ This module creates a S3 bucket for S3 Batch Job Manifests. | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/account/region/modules/s3_bucket_event_notifications/README.md b/terraform/account/region/modules/s3_bucket_event_notifications/README.md index 4fa9e69bd9..042c227d04 100644 --- a/terraform/account/region/modules/s3_bucket_event_notifications/README.md +++ b/terraform/account/region/modules/s3_bucket_event_notifications/README.md @@ -8,13 +8,13 @@ This module creates a S3 bucket event notifications and event notification filte | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | ~> 5.34.0 | +| [aws](#provider\_aws) | ~> 5.35.0 | ## Modules diff --git a/terraform/environment/README.md b/terraform/environment/README.md index cee91d873c..df2fbb9ff7 100644 --- a/terraform/environment/README.md +++ b/terraform/environment/README.md @@ -113,19 +113,19 @@ For terraform_environment, this will be based on your PR and can be found in the | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | 1.7.1 | -| [aws](#requirement\_aws) | ~> 5.34.0 | -| [pagerduty](#requirement\_pagerduty) | 3.5.2 | +| [terraform](#requirement\_terraform) | 1.7.2 | +| [aws](#requirement\_aws) | ~> 5.35.0 | +| [pagerduty](#requirement\_pagerduty) | 3.7.0 | ## Providers | Name | Version | |------|---------| -| [aws.eu\_west\_1](#provider\_aws.eu\_west\_1) | 5.34.0 | -| [aws.eu\_west\_2](#provider\_aws.eu\_west\_2) | 5.34.0 | -| [aws.global](#provider\_aws.global) | 5.34.0 | -| [aws.management\_eu\_west\_1](#provider\_aws.management\_eu\_west\_1) | 5.34.0 | -| [aws.management\_global](#provider\_aws.management\_global) | 5.34.0 | +| [aws.eu\_west\_1](#provider\_aws.eu\_west\_1) | 5.35.0 | +| [aws.eu\_west\_2](#provider\_aws.eu\_west\_2) | 5.35.0 | +| [aws.global](#provider\_aws.global) | 5.35.0 | +| [aws.management\_eu\_west\_1](#provider\_aws.management\_eu\_west\_1) | 5.35.0 | +| [aws.management\_global](#provider\_aws.management\_global) | 5.35.0 | ## Modules @@ -167,7 +167,7 @@ For terraform_environment, this will be based on your PR and can be found in the |------|-------------|------|---------|:--------:| | [container\_version](#input\_container\_version) | n/a | `string` | `"latest"` | no | | [default\_role](#input\_default\_role) | n/a | `string` | `"modernising-lpa-ci"` | no | -| [environments](#input\_environments) | n/a |
map(
object({
account_id = string
account_name = string
is_production = bool
regions = list(string)
app = object({
env = object({
app_public_url = string
auth_redirect_base_url = string
notify_is_production = string
onelogin_url = string
})
autoscaling = object({
minimum = number
maximum = number
})
dependency_health_check_alarm_enabled = bool
service_health_check_alarm_enabled = bool
cloudwatch_application_insights_enabled = bool
})
mock_onelogin_enabled = bool
uid_service = object({
base_url = string
api_arns = list(string)
})
lpa_store_service = object({
base_url = string
api_arns = list(string)
})
backups = object({
backup_plan_enabled = bool
copy_action_enabled = bool
})
dynamodb = object({
region_replica_enabled = bool
stream_enabled = bool
})
ecs = object({
fargate_spot_capacity_provider_enabled = bool

})
cloudwatch_log_groups = object({
application_log_retention_days = number
})
application_load_balancer = object({
deletion_protection_enabled = bool
})
cloudwatch_application_insights_enabled = bool
pagerduty_service_name = string
event_bus = object({
target_event_bus_arn = string
receive_account_ids = list(string)
})
reduced_fees = object({
enabled = bool
s3_object_replication_enabled = bool
target_environment = string
destination_account_id = string
enable_s3_batch_job_replication_scheduler = bool
})
s3_antivirus_provisioned_concurrency = number
})
)
| n/a | yes | +| [environments](#input\_environments) | n/a |
map(
object({
account_id = string
account_name = string
is_production = bool
regions = list(string)
app = object({
env = object({
app_public_url = string
auth_redirect_base_url = string
notify_is_production = string
onelogin_url = string
})
autoscaling = object({
minimum = number
maximum = number
})
dependency_health_check_alarm_enabled = bool
service_health_check_alarm_enabled = bool
cloudwatch_application_insights_enabled = bool
fault_injection_experiments_enabled = bool
})
mock_onelogin_enabled = bool
uid_service = object({
base_url = string
api_arns = list(string)
})
lpa_store_service = object({
base_url = string
api_arns = list(string)
})
backups = object({
backup_plan_enabled = bool
copy_action_enabled = bool
})
dynamodb = object({
region_replica_enabled = bool
stream_enabled = bool
})
ecs = object({
fargate_spot_capacity_provider_enabled = bool

})
cloudwatch_log_groups = object({
application_log_retention_days = number
})
application_load_balancer = object({
deletion_protection_enabled = bool
})
cloudwatch_application_insights_enabled = bool
pagerduty_service_name = string
event_bus = object({
target_event_bus_arn = string
receive_account_ids = list(string)
})
reduced_fees = object({
enabled = bool
s3_object_replication_enabled = bool
target_environment = string
destination_account_id = string
enable_s3_batch_job_replication_scheduler = bool
})
s3_antivirus_provisioned_concurrency = number
})
)
| n/a | yes | | [pagerduty\_api\_key](#input\_pagerduty\_api\_key) | n/a | `string` | n/a | yes | | [public\_access\_enabled](#input\_public\_access\_enabled) | n/a | `bool` | `false` | no | diff --git a/terraform/environment/global/README.md b/terraform/environment/global/README.md index 0b0e0e1fe6..fd07a93374 100644 --- a/terraform/environment/global/README.md +++ b/terraform/environment/global/README.md @@ -58,14 +58,15 @@ No modules. | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | -| [pagerduty](#requirement\_pagerduty) | 3.5.2 | +| [aws](#requirement\_aws) | ~> 5.35.0 | +| [pagerduty](#requirement\_pagerduty) | 3.7.0 | ## Providers | Name | Version | |------|---------| -| [aws.global](#provider\_aws.global) | ~> 5.34.0 | +| [aws](#provider\_aws) | ~> 5.35.0 | +| [aws.global](#provider\_aws.global) | ~> 5.35.0 | ## Modules @@ -79,15 +80,27 @@ No modules. | [aws_iam_role.app_task_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.cross_account_put](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.fault_injection_simulator](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.s3_antivirus](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.ssm_register_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy.execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.fault_injection_simulator_create_fis_service_linked_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.ssm_register_instance_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy_attachment.cloudwatch_logs_full_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.fault_injection_simulator_ecs_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.fault_injection_simulator_ssm_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.s3_antivirus_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_resourcegroups_group.environment_global](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/resourcegroups_group) | resource | +| [aws_caller_identity.global](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_default_tags.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/default_tags) | data source | | [aws_iam_policy_document.cross_account_put_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.execution_role_assume_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.fault_injection_simulator_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.fault_injection_simulator_create_fis_service_linked_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.lambda_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.ssm_register_instance_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.ssm_register_instance_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.task_role_assume_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | ## Inputs diff --git a/terraform/environment/global/data_sources.tf b/terraform/environment/global/data_sources.tf index 591c6a74d6..5e1cd451a7 100644 --- a/terraform/environment/global/data_sources.tf +++ b/terraform/environment/global/data_sources.tf @@ -1,3 +1,7 @@ data "aws_default_tags" "current" { provider = aws.global } + +data "aws_caller_identity" "global" { + provider = aws.global +} diff --git a/terraform/environment/global/iam_ecs_task_roles.tf b/terraform/environment/global/iam_ecs_task_roles.tf index 7ece0ae8db..1c72dad062 100644 --- a/terraform/environment/global/iam_ecs_task_roles.tf +++ b/terraform/environment/global/iam_ecs_task_roles.tf @@ -16,3 +16,4 @@ data "aws_iam_policy_document" "task_role_assume_policy" { } provider = aws.global } + diff --git a/terraform/environment/global/iam_fault_injection_simulator.tf b/terraform/environment/global/iam_fault_injection_simulator.tf new file mode 100644 index 0000000000..ee37e7c17f --- /dev/null +++ b/terraform/environment/global/iam_fault_injection_simulator.tf @@ -0,0 +1,123 @@ +# Create role for running experiments + +resource "aws_iam_role" "fault_injection_simulator" { + name = "fault-injection-simulator-${data.aws_default_tags.current.tags.environment-name}" + assume_role_policy = data.aws_iam_policy_document.fault_injection_simulator_assume.json + provider = aws.global +} + +data "aws_iam_policy_document" "fault_injection_simulator_assume" { + statement { + actions = ["sts:AssumeRole"] + + principals { + type = "Service" + identifiers = ["fis.amazonaws.com"] + } + } + provider = aws.global +} + +# Add permissions for FIS to run experiments (ECS, Logging, SSM) + +resource "aws_iam_role_policy_attachment" "fault_injection_simulator_ecs_access" { + role = aws_iam_role.fault_injection_simulator.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSFaultInjectionSimulatorECSAccess" + provider = aws.global +} + +resource "aws_iam_role_policy_attachment" "fault_injection_simulator_ssm_access" { + role = aws_iam_role.fault_injection_simulator.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSFaultInjectionSimulatorSSMAccess" + provider = aws.global +} + +resource "aws_iam_role_policy_attachment" "cloudwatch_logs_full_access" { + role = aws_iam_role.fault_injection_simulator.name + policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" + provider = aws.global +} + +resource "aws_iam_role_policy" "fault_injection_simulator_create_fis_service_linked_role" { + name = "create-fis-service-linked-role-permissions" + role = aws_iam_role.fault_injection_simulator.name + policy = data.aws_iam_policy_document.fault_injection_simulator_create_fis_service_linked_role.json + provider = aws.global +} + +data "aws_iam_policy_document" "fault_injection_simulator_create_fis_service_linked_role" { + policy_id = "fix experiment permissions" + statement { + sid = "AllowServiceLinkedRole" + effect = "Allow" + resources = ["*"] #tfsec:ignore:aws-iam-no-policy-wildcards + actions = [ + "iam:CreateServiceLinkedRole", + ] + condition { + test = "StringLike" + variable = "iam:AWSServiceName" + values = ["fis.amazonaws.com"] + } + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [data.aws_caller_identity.global.account_id] + } + condition { + test = "ArnLike" + variable = "aws:SourceArn" + values = ["arn:aws:fis:*:${data.aws_caller_identity.global.account_id}:experiment/*"] + } + } +} + +# Create role for registering instance + +resource "aws_iam_role" "ssm_register_instance" { + name = "ssm-register-instance-${data.aws_default_tags.current.tags.environment-name}" + assume_role_policy = data.aws_iam_policy_document.ssm_register_instance_assume.json + provider = aws.global +} + +data "aws_iam_policy_document" "ssm_register_instance_assume" { + statement { + actions = ["sts:AssumeRole"] + + principals { + type = "Service" + identifiers = ["ssm.amazonaws.com"] + } + } + provider = aws.global +} + +resource "aws_iam_role_policy" "ssm_register_instance_permissions" { + name = "ssm-register-instance-permissions" + role = aws_iam_role.fault_injection_simulator.name + policy = data.aws_iam_policy_document.ssm_register_instance_permissions.json + provider = aws.global +} + +data "aws_iam_policy_document" "ssm_register_instance_permissions" { + policy_id = "ssm instance activation permissions" + statement { + sid = "AllowSSMCommands" + effect = "Allow" + resources = ["*"] #tfsec:ignore:aws-iam-no-policy-wildcards + actions = [ + "ssm:CreateActivation", + "ssm:AddTagsToResource", + ] + } + + statement { + sid = "ManagedInstancePermissions" + effect = "Allow" + resources = ["*"] #tfsec:ignore:aws-iam-no-policy-wildcards + actions = [ + "ssm:DeleteActivation", + "ssm:DeregisterManagedInstance", + ] + } +} diff --git a/terraform/environment/global/outputs.tf b/terraform/environment/global/outputs.tf index 70303b1306..7cafcd989c 100644 --- a/terraform/environment/global/outputs.tf +++ b/terraform/environment/global/outputs.tf @@ -4,9 +4,10 @@ output "resource_group_arn" { output "iam_roles" { value = { - ecs_execution_role = aws_iam_role.execution_role, - app_ecs_task_role = aws_iam_role.app_task_role, - s3_antivirus = aws_iam_role.s3_antivirus, - cross_account_put = aws_iam_role.cross_account_put, + ecs_execution_role = aws_iam_role.execution_role, + app_ecs_task_role = aws_iam_role.app_task_role, + s3_antivirus = aws_iam_role.s3_antivirus, + cross_account_put = aws_iam_role.cross_account_put, + fault_injection_simulator = aws_iam_role.fault_injection_simulator, } } diff --git a/terraform/environment/region/README.md b/terraform/environment/region/README.md index e85a3c2559..f09c9be840 100644 --- a/terraform/environment/region/README.md +++ b/terraform/environment/region/README.md @@ -8,18 +8,18 @@ This module creates the regional resources for an environment. | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | -| [pagerduty](#requirement\_pagerduty) | 3.5.2 | +| [aws](#requirement\_aws) | ~> 5.35.0 | +| [pagerduty](#requirement\_pagerduty) | 3.7.0 | ## Providers | Name | Version | |------|---------| -| [aws.global](#provider\_aws.global) | ~> 5.34.0 | -| [aws.management](#provider\_aws.management) | ~> 5.34.0 | -| [aws.management\_global](#provider\_aws.management\_global) | ~> 5.34.0 | -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | -| [pagerduty](#provider\_pagerduty) | 3.5.2 | +| [aws.global](#provider\_aws.global) | ~> 5.35.0 | +| [aws.management](#provider\_aws.management) | ~> 5.35.0 | +| [aws.management\_global](#provider\_aws.management\_global) | ~> 5.35.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | +| [pagerduty](#provider\_pagerduty) | 3.7.0 | ## Modules @@ -30,6 +30,7 @@ This module creates the regional resources for an environment. | [application\_logs](#module\_application\_logs) | ./modules/application_logs | n/a | | [event\_bus](#module\_event\_bus) | ./modules/event_bus | n/a | | [event\_received](#module\_event\_received) | ./modules/event_received | n/a | +| [fault\_injection\_simulator\_experiments](#module\_fault\_injection\_simulator\_experiments) | ./modules/fault_injection_simulator_experiments | n/a | | [mock\_onelogin](#module\_mock\_onelogin) | ./modules/mock_onelogin | n/a | | [s3\_antivirus](#module\_s3\_antivirus) | ./modules/s3_antivirus | n/a | | [uploads\_s3\_bucket](#module\_uploads\_s3\_bucket) | ./modules/uploads_s3_bucket | n/a | @@ -60,10 +61,10 @@ This module creates the regional resources for an environment. | [aws_sns_topic_subscription.dependency_health_check](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | | [aws_sns_topic_subscription.ecs_autoscaling_alarms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | | [aws_sns_topic_subscription.service_health_check](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | -| [pagerduty_service_integration.cloudwatch_application_insights](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.5.2/docs/resources/service_integration) | resource | -| [pagerduty_service_integration.dependency_health_check](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.5.2/docs/resources/service_integration) | resource | -| [pagerduty_service_integration.ecs_autoscaling_alarms](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.5.2/docs/resources/service_integration) | resource | -| [pagerduty_service_integration.service_health_check](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.5.2/docs/resources/service_integration) | resource | +| [pagerduty_service_integration.cloudwatch_application_insights](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.7.0/docs/resources/service_integration) | resource | +| [pagerduty_service_integration.dependency_health_check](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.7.0/docs/resources/service_integration) | resource | +| [pagerduty_service_integration.ecs_autoscaling_alarms](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.7.0/docs/resources/service_integration) | resource | +| [pagerduty_service_integration.service_health_check](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.7.0/docs/resources/service_integration) | resource | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_default_tags.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/default_tags) | data source | @@ -92,8 +93,8 @@ This module creates the regional resources for an environment. | [aws_subnet.application](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | | [aws_subnet.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | | [aws_vpc.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source | -| [pagerduty_service.main](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.5.2/docs/data-sources/service) | data source | -| [pagerduty_vendor.cloudwatch](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.5.2/docs/data-sources/vendor) | data source | +| [pagerduty_service.main](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.7.0/docs/data-sources/service) | data source | +| [pagerduty_vendor.cloudwatch](https://registry.terraform.io/providers/PagerDuty/pagerduty/3.7.0/docs/data-sources/vendor) | data source | ## Inputs @@ -109,7 +110,8 @@ This module creates the regional resources for an environment. | [dns\_weighting](#input\_dns\_weighting) | Weighting for DNS records | `number` | n/a | yes | | [ecs\_capacity\_provider](#input\_ecs\_capacity\_provider) | Name of the capacity provider to use. Valid values are FARGATE\_SPOT and FARGATE | `string` | n/a | yes | | [ecs\_task\_autoscaling](#input\_ecs\_task\_autoscaling) | task minimum and maximum values for autoscaling | `any` | n/a | yes | -| [iam\_roles](#input\_iam\_roles) | ARN of IAM role that allows your Amazon ECS container task to make calls to other AWS services. |
object({
ecs_execution_role = any
app_ecs_task_role = any
s3_antivirus = any
cross_account_put = any
})
| n/a | yes | +| [fault\_injection\_experiments\_enabled](#input\_fault\_injection\_experiments\_enabled) | Enable fault injection | `bool` | n/a | yes | +| [iam\_roles](#input\_iam\_roles) | ARN of IAM role that allows your Amazon ECS container task to make calls to other AWS services. |
object({
ecs_execution_role = any
app_ecs_task_role = any
s3_antivirus = any
cross_account_put = any
fault_injection_simulator = any
})
| n/a | yes | | [ingress\_allow\_list\_cidr](#input\_ingress\_allow\_list\_cidr) | List of CIDR ranges permitted to access the service | `list(string)` | n/a | yes | | [lpa\_store\_service](#input\_lpa\_store\_service) | n/a |
object({
base_url = string
api_arns = list(string)
})
| n/a | yes | | [lpas\_table](#input\_lpas\_table) | DynamoDB table for storing LPAs | `any` | n/a | yes | diff --git a/terraform/environment/region/ecs.tf b/terraform/environment/region/ecs.tf index 90e957a9ae..c4dd2cbe07 100644 --- a/terraform/environment/region/ecs.tf +++ b/terraform/environment/region/ecs.tf @@ -25,7 +25,7 @@ module "app" { ecs_cluster = aws_ecs_cluster.main.id ecs_execution_role = var.iam_roles.ecs_execution_role ecs_task_role = var.iam_roles.app_ecs_task_role - ecs_service_desired_count = 1 + ecs_service_desired_count = var.ecs_task_autoscaling.minimum ecs_application_log_group_name = module.application_logs.cloudwatch_log_group.name ecs_capacity_provider = var.ecs_capacity_provider app_env_vars = var.app_env_vars @@ -55,6 +55,7 @@ module "app" { uid_base_url = var.uid_service.base_url lpa_store_base_url = var.lpa_store_service.base_url mock_onelogin_enabled = data.aws_default_tags.current.tags.environment-name != "production" && var.mock_onelogin_enabled + fault_injection_experiments_enabled = var.fault_injection_experiments_enabled providers = { aws.region = aws.region } diff --git a/terraform/environment/region/fault_injections_simulator_experiments.tf b/terraform/environment/region/fault_injections_simulator_experiments.tf new file mode 100644 index 0000000000..0e5febb364 --- /dev/null +++ b/terraform/environment/region/fault_injections_simulator_experiments.tf @@ -0,0 +1,10 @@ +module "fault_injection_simulator_experiments" { + count = var.fault_injection_experiments_enabled ? 1 : 0 + source = "./modules/fault_injection_simulator_experiments" + providers = { + aws.region = aws.region + } + fault_injection_simulator_role = var.iam_roles.fault_injection_simulator + ecs_cluster = aws_ecs_cluster.main.id + +} diff --git a/terraform/environment/region/modules/app/README.md b/terraform/environment/region/modules/app/README.md index 00066eb3df..c10abb35c6 100644 --- a/terraform/environment/region/modules/app/README.md +++ b/terraform/environment/region/modules/app/README.md @@ -8,13 +8,14 @@ The module creates an ECS service for the Modernising LPA application, and assoc | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws](#provider\_aws) | ~> 5.35.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules @@ -47,6 +48,8 @@ No modules. | [aws_wafv2_web_acl_association.app](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl_association) | resource | | [aws_acm_certificate.certificate_app](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/acm_certificate) | data source | | [aws_default_tags.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/default_tags) | data source | +| [aws_iam_policy_document.combined](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.ecs_task_role_fis_related_task_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.task_role_access_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_ip_ranges.route53_healthchecks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ip_ranges) | data source | | [aws_kms_alias.dynamodb_encryption_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/kms_alias) | data source | @@ -82,6 +85,7 @@ No modules. | [ecs\_service\_desired\_count](#input\_ecs\_service\_desired\_count) | Number of instances of the task definition to place and keep running. Defaults to 0. Do not specify if using the DAEMON scheduling strategy. | `number` | `0` | no | | [ecs\_task\_role](#input\_ecs\_task\_role) | ARN of IAM role that allows your Amazon ECS container task to make calls to other AWS services. | `any` | n/a | yes | | [event\_bus](#input\_event\_bus) | Name and ARN of the event bus to send events to |
object({
name = string
arn = string
})
| n/a | yes | +| [fault\_injection\_experiments\_enabled](#input\_fault\_injection\_experiments\_enabled) | Enable fault injection | `bool` | n/a | yes | | [ingress\_allow\_list\_cidr](#input\_ingress\_allow\_list\_cidr) | List of CIDR ranges permitted to access the service | `list(string)` | n/a | yes | | [lpa\_store\_base\_url](#input\_lpa\_store\_base\_url) | n/a | `string` | n/a | yes | | [lpas\_table](#input\_lpas\_table) | DynamoDB table for storing LPAs | `any` | n/a | yes | diff --git a/terraform/environment/region/modules/app/ecs.tf b/terraform/environment/region/modules/app/ecs.tf index 30f5ed7996..510fc6fa71 100644 --- a/terraform/environment/region/modules/app/ecs.tf +++ b/terraform/environment/region/modules/app/ecs.tf @@ -83,7 +83,7 @@ resource "aws_ecs_task_definition" "app" { operating_system_family = "LINUX" cpu_architecture = "X86_64" } - container_definitions = "[${local.app}, ${local.aws_otel_collector}]" + container_definitions = var.fault_injection_experiments_enabled ? "[${local.app}, ${local.aws_otel_collector}, ${local.amazon_ssm_agent}]" : "[${local.app}, ${local.aws_otel_collector}]" task_role_arn = var.ecs_task_role.arn execution_role_arn = var.ecs_execution_role.arn provider = aws.region @@ -91,11 +91,18 @@ resource "aws_ecs_task_definition" "app" { resource "aws_iam_role_policy" "app_task_role" { name = "${data.aws_default_tags.current.tags.environment-name}-${data.aws_region.current.name}-app-task-role" - policy = data.aws_iam_policy_document.task_role_access_policy.json + policy = var.fault_injection_experiments_enabled ? data.aws_iam_policy_document.combined.json : data.aws_iam_policy_document.task_role_access_policy.json role = var.ecs_task_role.name provider = aws.region } +data "aws_iam_policy_document" "combined" { + source_policy_documents = [ + data.aws_iam_policy_document.task_role_access_policy.json, + data.aws_iam_policy_document.ecs_task_role_fis_related_task_permissions.json + ] +} + data "aws_kms_alias" "secrets_manager_secret_encryption_key" { name = "alias/${data.aws_default_tags.current.tags.application}_secrets_manager_secret_encryption_key" provider = aws.region @@ -151,10 +158,6 @@ data "aws_secretsmanager_secret" "rum_monitor_identity_pool_id" { provider = aws.region } -locals { - policy_region_prefix = lower(replace(data.aws_region.current.name, "-", "")) -} - data "aws_iam_policy_document" "task_role_access_policy" { policy_id = "${local.policy_region_prefix}task_role_access_policy" statement { @@ -444,4 +447,80 @@ locals { }, environment = [] }) + + amazon_ssm_agent = jsonencode( + { + name = "amazon-ssm-agent", + image = "public.ecr.aws/amazon-ssm-agent/amazon-ssm-agent:latest", + cpu = 0, + links = [], + portMappings = [], + essential = false, + entryPoint = [], + readonlyRootFilesystem = false + command = [ + "/bin/bash", + "-c", + "set -e; yum upgrade -y; yum install jq procps awscli -y; term_handler() { echo \"Deleting SSM activation $ACTIVATION_ID\"; if ! aws ssm delete-activation --activation-id $ACTIVATION_ID --region $ECS_TASK_REGION; then echo \"SSM activation $ACTIVATION_ID failed to be deleted\" 1>&2; fi; MANAGED_INSTANCE_ID=$(jq -e -r .ManagedInstanceID /var/lib/amazon/ssm/registration); echo \"Deregistering SSM Managed Instance $MANAGED_INSTANCE_ID\"; if ! aws ssm deregister-managed-instance --instance-id $MANAGED_INSTANCE_ID --region $ECS_TASK_REGION; then echo \"SSM Managed Instance $MANAGED_INSTANCE_ID failed to be deregistered\" 1>&2; fi; kill -SIGTERM $SSM_AGENT_PID; }; trap term_handler SIGTERM SIGINT; if [[ -z $MANAGED_INSTANCE_ROLE_NAME ]]; then echo \"Environment variable MANAGED_INSTANCE_ROLE_NAME not set, exiting\" 1>&2; exit 1; fi; if ! ps ax | grep amazon-ssm-agent | grep -v grep > /dev/null; then if [[ -n $ECS_CONTAINER_METADATA_URI_V4 ]] ; then echo \"Found ECS Container Metadata, running activation with metadata\"; TASK_METADATA=$(curl \"$${ECS_CONTAINER_METADATA_URI_V4}/task\"); ECS_TASK_AVAILABILITY_ZONE=$(echo $TASK_METADATA | jq -e -r '.AvailabilityZone'); ECS_TASK_ARN=$(echo $TASK_METADATA | jq -e -r '.TaskARN'); ECS_TASK_REGION=$(echo $ECS_TASK_AVAILABILITY_ZONE | sed 's/.$//'); ECS_TASK_AVAILABILITY_ZONE_REGEX='^(af|ap|ca|cn|eu|me|sa|us|us-gov)-(central|north|(north(east|west))|south|south(east|west)|east|west)-[0-9]{1}[a-z]{1}$'; if ! [[ $ECS_TASK_AVAILABILITY_ZONE =~ $ECS_TASK_AVAILABILITY_ZONE_REGEX ]]; then echo \"Error extracting Availability Zone from ECS Container Metadata, exiting\" 1>&2; exit 1; fi; ECS_TASK_ARN_REGEX='^arn:(aws|aws-cn|aws-us-gov):ecs:[a-z0-9-]+:[0-9]{12}:task/[a-zA-Z0-9_-]+/[a-zA-Z0-9]+$'; if ! [[ $ECS_TASK_ARN =~ $ECS_TASK_ARN_REGEX ]]; then echo \"Error extracting Task ARN from ECS Container Metadata, exiting\" 1>&2; exit 1; fi; CREATE_ACTIVATION_OUTPUT=$(aws ssm create-activation --iam-role $MANAGED_INSTANCE_ROLE_NAME --tags Key=ECS_TASK_AVAILABILITY_ZONE,Value=$ECS_TASK_AVAILABILITY_ZONE Key=ECS_TASK_ARN,Value=$ECS_TASK_ARN Key=FAULT_INJECTION_SIDECAR,Value=true --region $ECS_TASK_REGION); ACTIVATION_CODE=$(echo $CREATE_ACTIVATION_OUTPUT | jq -e -r .ActivationCode); ACTIVATION_ID=$(echo $CREATE_ACTIVATION_OUTPUT | jq -e -r .ActivationId); if ! amazon-ssm-agent -register -code $ACTIVATION_CODE -id $ACTIVATION_ID -region $ECS_TASK_REGION; then echo \"Failed to register with AWS Systems Manager (SSM), exiting\" 1>&2; exit 1; fi; amazon-ssm-agent & SSM_AGENT_PID=$!; wait $SSM_AGENT_PID; else echo \"ECS Container Metadata not found, exiting\" 1>&2; exit 1; fi; else echo \"SSM agent is already running, exiting\" 1>&2; exit 1; fi" + ], + environment = [ + { + name = "MANAGED_INSTANCE_ROLE_NAME", + value = "ssm-register-instance-${data.aws_default_tags.current.tags.environment-name}" + } + ], + logConfiguration = { + logDriver = "awslogs", + options = { + awslogs-group = var.ecs_application_log_group_name, + awslogs-region = data.aws_region.current.name, + awslogs-stream-prefix = "${data.aws_default_tags.current.tags.environment-name}.otel.app" + } + }, + environmentFiles = [], + mountPoints = [], + volumesFrom = [], + secrets = [], + dnsServers = [], + dnsSearchDomains = [], + extraHosts = [], + dockerSecurityOptions = [], + dockerLabels = {}, + ulimits = [], + systemControls = [] + }) +} + +# Additional permissions for the ECS task role to run experiments + +data "aws_iam_policy_document" "ecs_task_role_fis_related_task_permissions" { + policy_id = "${local.policy_region_prefix}_fis_ecs_task_actions" + statement { + sid = "AllowSSMCommands" + effect = "Allow" + resources = ["*"] #tfsec:ignore:aws-iam-no-policy-wildcards + actions = [ + "ssm:CreateActivation", + "ssm:AddTagsToResource", + ] + } + + statement { + sid = "ManagedInstancePermissions" + effect = "Allow" + resources = ["*"] #tfsec:ignore:aws-iam-no-policy-wildcards + actions = [ + "ssm:DeleteActivation", + "ssm:DeregisterManagedInstance", + ] + } + + statement { + sid = "AllowPassRole" + effect = "Allow" + resources = ["*"] #tfsec:ignore:aws-iam-no-policy-wildcards + actions = [ + "iam:PassRole", + ] + } } diff --git a/terraform/environment/region/modules/app/variables.tf b/terraform/environment/region/modules/app/variables.tf index 41fc5d69a2..0c7562549d 100644 --- a/terraform/environment/region/modules/app/variables.tf +++ b/terraform/environment/region/modules/app/variables.tf @@ -2,6 +2,10 @@ locals { name_prefix = "${data.aws_default_tags.current.tags.environment-name}-${data.aws_region.current.name}" } +locals { + policy_region_prefix = lower(replace(data.aws_region.current.name, "-", "")) +} + variable "ecs_execution_role" { type = object({ id = string @@ -128,3 +132,8 @@ variable "lpa_store_base_url" { variable "mock_onelogin_enabled" { type = bool } + +variable "fault_injection_experiments_enabled" { + type = bool + description = "Enable fault injection" +} diff --git a/terraform/environment/region/modules/application_logs/README.md b/terraform/environment/region/modules/application_logs/README.md index ce4c2106ac..b92fbcddf7 100644 --- a/terraform/environment/region/modules/application_logs/README.md +++ b/terraform/environment/region/modules/application_logs/README.md @@ -8,13 +8,13 @@ The module creates a cloudwatch log group and useful log queries for application | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/environment/region/modules/ecs_autoscaling/README.md b/terraform/environment/region/modules/ecs_autoscaling/README.md index 2b61f01409..2d2a473df5 100644 --- a/terraform/environment/region/modules/ecs_autoscaling/README.md +++ b/terraform/environment/region/modules/ecs_autoscaling/README.md @@ -8,13 +8,13 @@ This module creates the autoscaling resources for an ECS service. | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/environment/region/modules/event_bus/README.md b/terraform/environment/region/modules/event_bus/README.md index b982c7dd88..fa66554596 100644 --- a/terraform/environment/region/modules/event_bus/README.md +++ b/terraform/environment/region/modules/event_bus/README.md @@ -18,14 +18,14 @@ aws-vault exec mlpa-dev -- aws events put-events --entries file://reduced_fees_u | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | ~> 5.34.0 | -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws](#provider\_aws) | ~> 5.35.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/environment/region/modules/event_received/README.md b/terraform/environment/region/modules/event_received/README.md index 6a32717b21..403a471557 100644 --- a/terraform/environment/region/modules/event_received/README.md +++ b/terraform/environment/region/modules/event_received/README.md @@ -8,14 +8,14 @@ This module creates the resources required to receive and process events from th | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | ~> 5.34.0 | -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws](#provider\_aws) | ~> 5.35.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/environment/region/modules/fault_injection_simulator_experiments/README.md b/terraform/environment/region/modules/fault_injection_simulator_experiments/README.md new file mode 100644 index 0000000000..3bfb0adeaf --- /dev/null +++ b/terraform/environment/region/modules/fault_injection_simulator_experiments/README.md @@ -0,0 +1,43 @@ + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5.2 | +| [aws](#requirement\_aws) | ~> 5.35.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_cloudwatch_log_group.fis_app_ecs_tasks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_cloudwatch_log_resource_policy.fis_app_ecs_tasks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_resource_policy) | resource | +| [aws_fis_experiment_template.ecs_app](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/fis_experiment_template) | resource | +| [aws_iam_role_policy.fis_role_log_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_default_tags.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/default_tags) | data source | +| [aws_iam_policy_document.cloudwatch_log_group_policy_fis_app_ecs_tasks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.fis_role_log_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_kms_alias.cloudwatch_application_logs_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/kms_alias) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [ecs\_cluster](#input\_ecs\_cluster) | Name of the ECS cluster to run the experiments on. | `string` | n/a | yes | +| [fault\_injection\_simulator\_role](#input\_fault\_injection\_simulator\_role) | ARN of IAM role that allows AWS FIS to make calls to other AWS services. | `any` | n/a | yes | + +## Outputs + +No outputs. + diff --git a/terraform/environment/region/modules/fault_injection_simulator_experiments/data_sources.tf b/terraform/environment/region/modules/fault_injection_simulator_experiments/data_sources.tf new file mode 100644 index 0000000000..ad61091f3f --- /dev/null +++ b/terraform/environment/region/modules/fault_injection_simulator_experiments/data_sources.tf @@ -0,0 +1,7 @@ +data "aws_default_tags" "current" { + provider = aws.region +} + +data "aws_caller_identity" "current" { + provider = aws.region +} diff --git a/terraform/environment/region/modules/fault_injection_simulator_experiments/main.tf b/terraform/environment/region/modules/fault_injection_simulator_experiments/main.tf new file mode 100644 index 0000000000..8a96f37dad --- /dev/null +++ b/terraform/environment/region/modules/fault_injection_simulator_experiments/main.tf @@ -0,0 +1,215 @@ +# Create encrypted logging for fault injection experiments + +data "aws_kms_alias" "cloudwatch_application_logs_encryption" { + name = "alias/${data.aws_default_tags.current.tags.application}_cloudwatch_application_logs_encryption" + provider = aws.region +} + +resource "aws_cloudwatch_log_group" "fis_app_ecs_tasks" { + name = "/aws/fis/app-ecs-tasks-experiment-${data.aws_default_tags.current.tags.environment-name}" + retention_in_days = 7 + kms_key_id = data.aws_kms_alias.cloudwatch_application_logs_encryption.target_key_arn + provider = aws.region +} + +# Add resource policy to allow FIS or the FIS role to write logs + +data "aws_iam_policy_document" "cloudwatch_log_group_policy_fis_app_ecs_tasks" { + provider = aws.region + statement { + sid = "AWSLogDeliveryWrite" + effect = "Allow" + + principals { + identifiers = [ + "delivery.logs.amazonaws.com" + ] + type = "Service" + } + + actions = [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + + resources = [ + "${aws_cloudwatch_log_group.fis_app_ecs_tasks.arn}:*", + ] + + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [data.aws_caller_identity.current.account_id] + } + } + statement { + sid = "AWSLogEncrypt" + effect = "Allow" + + principals { + identifiers = [ + "delivery.logs.amazonaws.com" + ] + type = "Service" + } + + actions = [ + "kms:Encrypt", + "kms:GenerateDataKey", + ] + + resources = [ + data.aws_kms_alias.cloudwatch_application_logs_encryption.target_key_arn + ] + + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [data.aws_caller_identity.current.account_id] + } + } +} + +resource "aws_cloudwatch_log_resource_policy" "fis_app_ecs_tasks" { + provider = aws.region + policy_document = data.aws_iam_policy_document.cloudwatch_log_group_policy_fis_app_ecs_tasks.json + policy_name = "fis_app_ecs_tasks_logging" +} + +# Add log encryption and log write/delivery permissions to the FIS role + +data "aws_iam_policy_document" "fis_role_log_encryption" { + provider = aws.region + policy_id = "log_access" + statement { + sid = "AllowCloudWatchLogsEncryption" + actions = [ + "kms:Encrypt", + "kms:GenerateDataKey", + ] + + resources = [ + data.aws_kms_alias.cloudwatch_application_logs_encryption.target_key_arn + ] + } + + statement { + sid = "AllowCloudWatchLogs" + effect = "Allow" + actions = [ + "logs:CreateLogDelivery", + "logs:DescribeLogGroups", + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:DescribeResourcePolicies", + "logs:PutResourcePolicy", + ] + + resources = [ + aws_cloudwatch_log_group.fis_app_ecs_tasks.arn, + "${aws_cloudwatch_log_group.fis_app_ecs_tasks.arn}:*", + ] + } +} + +resource "aws_iam_role_policy" "fis_role_log_encryption" { + provider = aws.region + name = "fis-role-log-permissions" + role = var.fault_injection_simulator_role.name + policy = data.aws_iam_policy_document.fis_role_log_encryption.json +} + +# Create experiment template for ECS tasks + +resource "aws_fis_experiment_template" "ecs_app" { + provider = aws.region + description = "Run ECS task experiments for the app service" + role_arn = var.fault_injection_simulator_role.arn + tags = { + Name = "${data.aws_default_tags.current.tags.environment-name} - APP ECS Task Experiments" + } + + action { + action_id = "aws:ecs:stop-task" + name = "stop_two_tasks" + start_after = [] + + target { + key = "Tasks" + value = "two-tasks" + } + } + + action { + action_id = "aws:ecs:task-cpu-stress" + description = null + name = "cpu_stress_100_percent_10_mins" + start_after = ["stop_tasks"] + parameter { + key = "duration" + value = "PT10M" + } + target { + key = "Tasks" + value = "all-tasks" + } + } + + action { + action_id = "aws:ecs:task-io-stress" + description = null + name = "io_stress_10_mins" + start_after = [ + "cpu_stress_100_percent_10_mins" + ] + parameter { + key = "duration" + value = "PT10M" + } + target { + key = "Tasks" + value = "all-tasks" + } + } + + stop_condition { + source = "none" + value = null + } + + log_configuration { + log_schema_version = 2 + + cloudwatch_logs_configuration { + log_group_arn = "${aws_cloudwatch_log_group.fis_app_ecs_tasks.arn}:*" # tfsec:ignore:aws-cloudwatch-log-group-wildcard + } + } + + target { + name = "all-tasks" + resource_tag { + key = "environment-name" + value = data.aws_default_tags.current.tags.environment-name + } + parameters = { + "cluster" = var.ecs_cluster + "service" = "app" + } + resource_type = "aws:ecs:task" + selection_mode = "ALL" + } + + target { + name = "two-tasks" + resource_tag { + key = "environment-name" + value = data.aws_default_tags.current.tags.environment-name + } + parameters = { + "cluster" = var.ecs_cluster + "service" = "app" + } + resource_type = "aws:ecs:task" + selection_mode = "COUNT(2)" + } +} diff --git a/terraform/environment/region/modules/fault_injection_simulator_experiments/variables.tf b/terraform/environment/region/modules/fault_injection_simulator_experiments/variables.tf new file mode 100644 index 0000000000..d997fb70aa --- /dev/null +++ b/terraform/environment/region/modules/fault_injection_simulator_experiments/variables.tf @@ -0,0 +1,10 @@ +variable "fault_injection_simulator_role" { + type = any + description = "ARN of IAM role that allows AWS FIS to make calls to other AWS services." +} + +variable "ecs_cluster" { + type = string + description = "Name of the ECS cluster to run the experiments on." + +} diff --git a/terraform/environment/region/modules/fault_injection_simulator_experiments/versions.tf b/terraform/environment/region/modules/fault_injection_simulator_experiments/versions.tf new file mode 100644 index 0000000000..785d777364 --- /dev/null +++ b/terraform/environment/region/modules/fault_injection_simulator_experiments/versions.tf @@ -0,0 +1,13 @@ +terraform { + required_version = ">= 1.5.2" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.35.0" + configuration_aliases = [ + aws.region, + ] + } + } +} diff --git a/terraform/environment/region/modules/lambda/README.md b/terraform/environment/region/modules/lambda/README.md index f0f6c093ed..5e64b2910f 100644 --- a/terraform/environment/region/modules/lambda/README.md +++ b/terraform/environment/region/modules/lambda/README.md @@ -8,13 +8,13 @@ This module creates the resources required to deploy an image based Lambda funct | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/environment/region/modules/mock_onelogin/README.md b/terraform/environment/region/modules/mock_onelogin/README.md index fd2f93344c..3f061e3334 100644 --- a/terraform/environment/region/modules/mock_onelogin/README.md +++ b/terraform/environment/region/modules/mock_onelogin/README.md @@ -8,13 +8,13 @@ This module creates the resources required to mock One Login. | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/environment/region/modules/s3_antivirus/README.md b/terraform/environment/region/modules/s3_antivirus/README.md index 0ec0f03765..f6a4e5f351 100644 --- a/terraform/environment/region/modules/s3_antivirus/README.md +++ b/terraform/environment/region/modules/s3_antivirus/README.md @@ -8,13 +8,13 @@ This module deploys a lambda function that scans S3 objects for viruses on put. | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/environment/region/modules/uploads_s3_bucket/README.md b/terraform/environment/region/modules/uploads_s3_bucket/README.md index 399cd2a0f5..6c2b0fd0ed 100644 --- a/terraform/environment/region/modules/uploads_s3_bucket/README.md +++ b/terraform/environment/region/modules/uploads_s3_bucket/README.md @@ -8,13 +8,13 @@ This module creates an S3 bucket for storing uploads, triggers for virus scannin | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.2 | -| [aws](#requirement\_aws) | ~> 5.34.0 | +| [aws](#requirement\_aws) | ~> 5.35.0 | ## Providers | Name | Version | |------|---------| -| [aws.region](#provider\_aws.region) | ~> 5.34.0 | +| [aws.region](#provider\_aws.region) | ~> 5.35.0 | ## Modules diff --git a/terraform/environment/region/variables.tf b/terraform/environment/region/variables.tf index 6481693694..435c6f15f8 100644 --- a/terraform/environment/region/variables.tf +++ b/terraform/environment/region/variables.tf @@ -4,10 +4,11 @@ locals { variable "iam_roles" { type = object({ - ecs_execution_role = any - app_ecs_task_role = any - s3_antivirus = any - cross_account_put = any + ecs_execution_role = any + app_ecs_task_role = any + s3_antivirus = any + cross_account_put = any + fault_injection_simulator = any }) description = "ARN of IAM role that allows your Amazon ECS container task to make calls to other AWS services." } @@ -146,3 +147,8 @@ variable "cloudwatch_application_insights_enabled" { type = bool description = "Enable CloudWatch Application Insights" } + +variable "fault_injection_experiments_enabled" { + type = bool + description = "Enable fault injection" +} diff --git a/terraform/environment/regions.tf b/terraform/environment/regions.tf index 1fe2ac0da9..2cd2508bed 100644 --- a/terraform/environment/regions.tf +++ b/terraform/environment/regions.tf @@ -16,10 +16,11 @@ module "eu_west_1" { source = "./region" count = contains(local.environment.regions, "eu-west-1") ? 1 : 0 iam_roles = { - ecs_execution_role = module.global.iam_roles.ecs_execution_role - app_ecs_task_role = module.global.iam_roles.app_ecs_task_role - s3_antivirus = module.global.iam_roles.s3_antivirus - cross_account_put = module.global.iam_roles.cross_account_put + ecs_execution_role = module.global.iam_roles.ecs_execution_role + app_ecs_task_role = module.global.iam_roles.app_ecs_task_role + s3_antivirus = module.global.iam_roles.s3_antivirus + cross_account_put = module.global.iam_roles.cross_account_put + fault_injection_simulator = module.global.iam_roles.fault_injection_simulator } application_log_retention_days = local.environment.cloudwatch_log_groups.application_log_retention_days ecs_capacity_provider = local.ecs_capacity_provider @@ -60,6 +61,7 @@ module "eu_west_1" { dependency_health_check_alarm_enabled = local.environment.app.dependency_health_check_alarm_enabled service_health_check_alarm_enabled = local.environment.app.service_health_check_alarm_enabled cloudwatch_application_insights_enabled = local.environment.app.cloudwatch_application_insights_enabled + fault_injection_experiments_enabled = local.environment.app.fault_injection_experiments_enabled providers = { aws.region = aws.eu_west_1 aws.global = aws.global @@ -72,10 +74,11 @@ module "eu_west_2" { source = "./region" count = contains(local.environment.regions, "eu-west-2") ? 1 : 0 iam_roles = { - ecs_execution_role = module.global.iam_roles.ecs_execution_role - app_ecs_task_role = module.global.iam_roles.app_ecs_task_role - s3_antivirus = module.global.iam_roles.s3_antivirus - cross_account_put = module.global.iam_roles.cross_account_put + ecs_execution_role = module.global.iam_roles.ecs_execution_role + app_ecs_task_role = module.global.iam_roles.app_ecs_task_role + s3_antivirus = module.global.iam_roles.s3_antivirus + cross_account_put = module.global.iam_roles.cross_account_put + fault_injection_simulator = module.global.iam_roles.fault_injection_simulator } application_log_retention_days = local.environment.cloudwatch_log_groups.application_log_retention_days ecs_capacity_provider = local.ecs_capacity_provider @@ -116,6 +119,7 @@ module "eu_west_2" { dependency_health_check_alarm_enabled = local.environment.app.dependency_health_check_alarm_enabled service_health_check_alarm_enabled = local.environment.app.service_health_check_alarm_enabled cloudwatch_application_insights_enabled = local.environment.app.cloudwatch_application_insights_enabled + fault_injection_experiments_enabled = local.environment.app.fault_injection_experiments_enabled providers = { aws.region = aws.eu_west_2 aws.global = aws.global diff --git a/terraform/environment/terraform.tfvars.json b/terraform/environment/terraform.tfvars.json index 2d770078b9..886e8b5f9c 100644 --- a/terraform/environment/terraform.tfvars.json +++ b/terraform/environment/terraform.tfvars.json @@ -20,7 +20,8 @@ }, "dependency_health_check_alarm_enabled": false, "service_health_check_alarm_enabled": false, - "cloudwatch_application_insights_enabled": false + "cloudwatch_application_insights_enabled": false, + "fault_injection_experiments_enabled": false }, "mock_onelogin_enabled": false, "uid_service": { @@ -95,7 +96,8 @@ }, "dependency_health_check_alarm_enabled": true, "service_health_check_alarm_enabled": true, - "cloudwatch_application_insights_enabled": true + "cloudwatch_application_insights_enabled": true, + "fault_injection_experiments_enabled": false }, "mock_onelogin_enabled": true, "uid_service": { @@ -170,7 +172,8 @@ }, "dependency_health_check_alarm_enabled": false, "service_health_check_alarm_enabled": false, - "cloudwatch_application_insights_enabled": false + "cloudwatch_application_insights_enabled": false, + "fault_injection_experiments_enabled": false }, "mock_onelogin_enabled": true, "uid_service": { @@ -245,7 +248,8 @@ }, "dependency_health_check_alarm_enabled": false, "service_health_check_alarm_enabled": false, - "cloudwatch_application_insights_enabled": true + "cloudwatch_application_insights_enabled": true, + "fault_injection_experiments_enabled": false }, "mock_onelogin_enabled": true, "uid_service": { @@ -320,7 +324,8 @@ }, "dependency_health_check_alarm_enabled": false, "service_health_check_alarm_enabled": false, - "cloudwatch_application_insights_enabled": true + "cloudwatch_application_insights_enabled": true, + "fault_injection_experiments_enabled": false }, "mock_onelogin_enabled": false, "uid_service": { @@ -395,7 +400,8 @@ }, "dependency_health_check_alarm_enabled": true, "service_health_check_alarm_enabled": true, - "cloudwatch_application_insights_enabled": true + "cloudwatch_application_insights_enabled": true, + "fault_injection_experiments_enabled": false }, "mock_onelogin_enabled": false, "uid_service": { diff --git a/terraform/environment/variables.tf b/terraform/environment/variables.tf index 916d429787..9e8207b844 100644 --- a/terraform/environment/variables.tf +++ b/terraform/environment/variables.tf @@ -46,6 +46,7 @@ variable "environments" { dependency_health_check_alarm_enabled = bool service_health_check_alarm_enabled = bool cloudwatch_application_insights_enabled = bool + fault_injection_experiments_enabled = bool }) mock_onelogin_enabled = bool uid_service = object({