From d3efe3015951e62917b5eed5c86d4aa080fccd87 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Wed, 24 Jul 2024 16:50:22 +0100 Subject: [PATCH] RDS backups to S3 * Conditonally create a scheduled Fargate task to backup RDS databases to S3 * Fargate was chosen, so that it doesn't rely on the ECS infrastructure to run - meaning we can have RDS backups running even if an ECS Infrastructure hasn't been launched * A CodeBuild project is created, which clones, builds and pushes the dalmatian-sql-backup image to ECR, ready to be used for the backup scheduled task. The Dockerfile for this is stored at https://github.com/dxw/dalmatian-sql-backup. This CodeBuild project is triggered every 24 hours to keep up with any updates. * The backup task will backup all databases (except system databases), and store the SQL dumps into an S3 bucket, which can be configured with the retention time in days. --- README.md | 53 ++++++ buildspecs/dalmatian-sql-backup.yml | 24 +++ container-definitions/app.json.tpl | 7 +- ...r-infrastructure-service-scheduled-task.tf | 7 +- ecs-cluster-infrastructure-service.tf | 5 +- ecs-entrypoints/rds-s3-backups-mysql.txt.tpl | 1 + .../rds-s3-backups-postgres.txt.tpl | 1 + kms-infrastructure.tf | 12 ++ .../trigger-codedeploy-project.sh | 70 +++++++ locals.tf | 17 +- policies/codebuild-allow-builds.json.tpl | 16 ++ .../secrets-manager-get-secret-value.json.tpl | 12 ++ rds-infrastructure-cloudwatch-logs.tf | 9 + rds-infrastructure-s3-backups-ecr.tf | 17 ++ ...frastructure-s3-backups-image-codebuild.tf | 144 +++++++++++++++ rds-infrastructure-s3-backups-s3.tf | 87 +++++++++ ...nfrastructure-s3-backups-scheduled-task.tf | 106 +++++++++++ ...nfrastructure-s3-backups-security-group.tf | 77 ++++++++ ...frastructure-s3-backups-task-definition.tf | 172 ++++++++++++++++++ rds-infrastructure-security-group.tf | 12 ++ rds-infrastructure-tooling-ecs-cluster.tf | 26 +++ variables.tf | 15 ++ 22 files changed, 881 insertions(+), 9 deletions(-) create mode 100644 buildspecs/dalmatian-sql-backup.yml create mode 100644 ecs-entrypoints/rds-s3-backups-mysql.txt.tpl create mode 100644 ecs-entrypoints/rds-s3-backups-postgres.txt.tpl create mode 100755 local-exec-scripts/trigger-codedeploy-project.sh create mode 100644 policies/codebuild-allow-builds.json.tpl create mode 100644 policies/secrets-manager-get-secret-value.json.tpl create mode 100644 rds-infrastructure-s3-backups-ecr.tf create mode 100644 rds-infrastructure-s3-backups-image-codebuild.tf create mode 100644 rds-infrastructure-s3-backups-s3.tf create mode 100644 rds-infrastructure-s3-backups-scheduled-task.tf create mode 100644 rds-infrastructure-s3-backups-security-group.tf create mode 100644 rds-infrastructure-s3-backups-task-definition.tf create mode 100644 rds-infrastructure-tooling-ecs-cluster.tf diff --git a/README.md b/README.md index 4e8f6f3..efff024 100644 --- a/README.md +++ b/README.md @@ -63,20 +63,26 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_cloudwatch_event_rule.ecs_cluster_infrastructure_pending_task_metric_1_min_cron](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource | | [aws_cloudwatch_event_rule.infrastructure_ecs_cluster_service_ecr_scan](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource | | [aws_cloudwatch_event_rule.infrastructure_ecs_cluster_service_scheduled_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource | +| [aws_cloudwatch_event_rule.infrastructure_rds_s3_backups_image_build_trigger_codebuild](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource | +| [aws_cloudwatch_event_rule.infrastructure_rds_s3_backups_scheduled_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource | | [aws_cloudwatch_event_target.ecr_scan_event_target](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource | | [aws_cloudwatch_event_target.ecs_cluster_infrastructure_ecs_asg_diff_metric_1_min_cron](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource | | [aws_cloudwatch_event_target.ecs_cluster_infrastructure_pending_task_metric_1_min_cron](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource | | [aws_cloudwatch_event_target.infrastructure_ecs_cluster_service_scheduled_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource | +| [aws_cloudwatch_event_target.infrastructure_rds_s3_backups_image_build_trigger_codebuild](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource | +| [aws_cloudwatch_event_target.infrastructure_rds_s3_backups_scheduled_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource | | [aws_cloudwatch_log_group.ecs_cluster_infrastructure_draining_lambda_log_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_cloudwatch_log_group.ecs_cluster_infrastructure_ecs_asg_diff_metric_lambda_log_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_cloudwatch_log_group.ecs_cluster_infrastructure_pending_task_metric_lambda_log_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_cloudwatch_log_group.infrastructure_ecs_cluster_service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_cloudwatch_log_group.infrastructure_rds_exports](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_cloudwatch_log_group.infrastructure_rds_s3_backups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_cloudwatch_log_group.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_cloudwatch_metric_alarm.infrastructure_ecs_cluster_asg_cpu](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource | | [aws_cloudwatch_metric_alarm.infrastructure_ecs_cluster_ecs_asg_diff](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource | | [aws_cloudwatch_metric_alarm.infrastructure_ecs_cluster_pending_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource | | [aws_codebuild_project.infrastructure_ecs_cluster_service_build](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project) | resource | +| [aws_codebuild_project.infrastructure_rds_s3_backups_image_build](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project) | resource | | [aws_codedeploy_app.infrastructure_ecs_cluster_service_blue_green](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codedeploy_app) | resource | | [aws_codedeploy_deployment_config.infrastructure_ecs_cluster_service_blue_green](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codedeploy_deployment_config) | resource | | [aws_codedeploy_deployment_group.infrastructure_ecs_cluster_service_blue_green](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codedeploy_deployment_group) | resource | @@ -87,10 +93,13 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_db_subnet_group.infrastructure_rds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_subnet_group) | resource | | [aws_default_network_acl.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_network_acl) | resource | | [aws_ecr_repository.infrastructure_ecs_cluster_service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource | +| [aws_ecr_repository.infrastructure_rds_s3_backups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource | | [aws_ecs_cluster.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource | +| [aws_ecs_cluster.infrastrucutre_rds_tooling](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource | | [aws_ecs_service.infrastructure_ecs_cluster_service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource | | [aws_ecs_task_definition.infrastructure_ecs_cluster_service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource | | [aws_ecs_task_definition.infrastructure_ecs_cluster_service_scheduled_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource | +| [aws_ecs_task_definition.infrastructure_rds_s3_backups_scheduled_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource | | [aws_efs_file_system.infrastructure_ecs_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/efs_file_system) | resource | | [aws_efs_mount_target.infrastructure_ecs_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/efs_mount_target) | resource | | [aws_eip.infrastructure_nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource | @@ -153,6 +162,17 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_iam_policy.infrastructure_ecs_cluster_service_task_ssm_create_channels](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.infrastructure_ecs_cluster_ssm_service_setting_rw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.infrastructure_rds_monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_cloudwatch_schedule_ecs_run_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_cloudwatch_schedule_pass_role_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_cloudwatch_schedule_pass_role_task_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_image_codebuild_allow_builds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_image_codebuild_cloudwatch_rw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_image_codebuild_ecr_push](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_task_execution_cloudwatch_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_task_execution_ecr_pull](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_task_execution_get_secret_value](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_task_kms_encrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.infrastructure_rds_s3_backups_task_s3_write](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.ecs_cluster_infrastructure_draining_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.ecs_cluster_infrastructure_ecs_asg_diff_metric_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.ecs_cluster_infrastructure_pending_task_metric_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | @@ -165,6 +185,10 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_iam_role.infrastructure_ecs_cluster_service_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.infrastructure_ecs_cluster_service_task_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.infrastructure_rds_monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.infrastructure_rds_s3_backups_cloudwatch_schedule](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.infrastructure_rds_s3_backups_image_codebuild](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.infrastructure_rds_s3_backups_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.infrastructure_rds_s3_backups_task_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy.infrastructure_vpc_flow_logs_allow_cloudwatch_rw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy_attachment.ecs_cluster_infrastructure_draining_ecs_container_instance_state_update_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | @@ -208,6 +232,17 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_service_task_ssm_create_channels](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_ssm_service_setting_rw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.infrastructure_rds_monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_cloudwatch_schedule_ecs_run_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_cloudwatch_schedule_pass_role_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_cloudwatch_schedule_pass_role_task_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_image_codebuild_allow_builds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_image_codebuild_cloudwatch_rw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_image_codebuild_ecr_push](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_task_execution_cloudwatch_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_task_execution_ecr_pull](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_task_execution_get_secret_value](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_task_kms_encrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_task_s3_write](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_user.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource | | [aws_internet_gateway.infrastructure_public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource | | [aws_kms_alias.custom_s3_buckets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource | @@ -266,17 +301,20 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket.infrastructure_rds_s3_backups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket.infrastructure_vpc_transfer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket_acl.infrastructure_logs_log_delivery_write](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource | | [aws_s3_bucket_lifecycle_configuration.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | | [aws_s3_bucket_lifecycle_configuration.infrastructure_ecs_cluster_service_alb_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | | [aws_s3_bucket_lifecycle_configuration.infrastructure_ecs_cluster_service_build_pipeline_artifact_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | +| [aws_s3_bucket_lifecycle_configuration.infrastructure_rds_s3_backups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | | [aws_s3_bucket_lifecycle_configuration.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | | [aws_s3_bucket_logging.cloudformation_custom_stack_template_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | | [aws_s3_bucket_logging.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | | [aws_s3_bucket_logging.infrastructure_ecs_cluster_service_build_pipeline_artifact_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | | [aws_s3_bucket_logging.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | | [aws_s3_bucket_logging.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | +| [aws_s3_bucket_logging.infrastructure_rds_s3_backups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | | [aws_s3_bucket_logging.infrastructure_vpc_transfer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | | [aws_s3_bucket_ownership_controls.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource | | [aws_s3_bucket_ownership_controls.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource | @@ -287,6 +325,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket_policy.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | | [aws_s3_bucket_policy.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | | [aws_s3_bucket_policy.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | +| [aws_s3_bucket_policy.infrastructure_rds_s3_backups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | | [aws_s3_bucket_policy.infrastructure_vpc_transfer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | | [aws_s3_bucket_public_access_block.cloudformation_custom_stack_template_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_public_access_block.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | @@ -295,6 +334,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket_public_access_block.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_public_access_block.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_public_access_block.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | +| [aws_s3_bucket_public_access_block.infrastructure_rds_s3_backups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_public_access_block.infrastructure_vpc_transfer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_server_side_encryption_configuration.cloudformation_custom_stack_template_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_server_side_encryption_configuration.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | @@ -303,6 +343,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket_server_side_encryption_configuration.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_server_side_encryption_configuration.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_server_side_encryption_configuration.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | +| [aws_s3_bucket_server_side_encryption_configuration.infrastructure_rds_s3_backups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_server_side_encryption_configuration.infrastructure_vpc_transfer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_versioning.cloudformation_custom_stack_template_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_bucket_versioning.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | @@ -311,6 +352,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket_versioning.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_bucket_versioning.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_bucket_versioning.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | +| [aws_s3_bucket_versioning.infrastructure_rds_s3_backups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_bucket_versioning.infrastructure_vpc_transfer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_object.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | | [aws_secretsmanager_secret.infrastructure_rds_root_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource | @@ -320,6 +362,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_security_group.infrastructure_ecs_cluster_service_alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_security_group.infrastructure_elasticache](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_security_group.infrastructure_rds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_security_group.infrastructure_rds_s3_backups_scheduled_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_egress_dns_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_egress_dns_udp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | @@ -336,6 +379,12 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_security_group_rule.infrastructure_ecs_cluster_service_alb_https](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.infrastructure_elasticache_ingress_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.infrastructure_rds_ingress_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.infrastructure_rds_s3_backup_task_ingress_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.infrastructure_rds_s3_backups_scheduled_task_egress_dns_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.infrastructure_rds_s3_backups_scheduled_task_egress_dns_udp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.infrastructure_rds_s3_backups_scheduled_task_egress_https_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.infrastructure_rds_s3_backups_scheduled_task_egress_https_udp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.infrastructure_rds_s3_backups_scheduled_task_egress_rds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_sns_topic.infrastructure_ecs_cluster_autoscaling_lifecycle_termination](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | | [aws_sns_topic_subscription.ecs_cluster_infrastructure_draining_autoscaling_lifecycle_termination](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | | [aws_ssm_document.infrastructure_vpc_transfer_s3_download](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_document) | resource | @@ -352,6 +401,7 @@ This project creates and manages resources within an AWS account for infrastruct | [random_password.infrastructure_rds_root](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | | [terraform_data.infrastructure_ecs_cluster_service_blue_green_create_codedeploy_deployment](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource | | [terraform_data.infrastructure_ecs_cluster_service_env_file](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource | +| [terraform_data.infrastructure_rds_s3_backups_image_build_trigger_codebuild](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource | | [archive_file.ecs_cluster_infrastructure_draining_lambda](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | | [archive_file.ecs_cluster_infrastructure_ecs_asg_diff_metric_lambda](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | | [archive_file.ecs_cluster_infrastructure_pending_task_metric_lambda](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | @@ -388,6 +438,7 @@ This project creates and manages resources within an AWS account for infrastruct | [enable\_infrastructure\_ecs\_cluster\_efs](#input\_enable\_infrastructure\_ecs\_cluster\_efs) | Conditionally create and mount EFS to the ECS cluster instances | `bool` | n/a | yes | | [enable\_infrastructure\_ecs\_cluster\_pending\_task\_alert](#input\_enable\_infrastructure\_ecs\_cluster\_pending\_task\_alert) | Enable the ECS Cluster pending task alert | `bool` | n/a | yes | | [enable\_infrastructure\_ecs\_cluster\_services\_alb\_logs](#input\_enable\_infrastructure\_ecs\_cluster\_services\_alb\_logs) | Enable Infrastructure ECS cluster services ALB logs | `bool` | n/a | yes | +| [enable\_infrastructure\_rds\_backup\_to\_s3](#input\_enable\_infrastructure\_rds\_backup\_to\_s3) | Enable Infrastructure RDS backups to S3. This will create a scheduled Fargate task to take SQL dumps and upload them to S3 | `bool` | n/a | yes | | [enable\_infrastructure\_route53\_hosted\_zone](#input\_enable\_infrastructure\_route53\_hosted\_zone) | Creates a Route53 hosted zone, where DNS records will be created for resources launched within this module. | `bool` | n/a | yes | | [enable\_infrastructure\_vpc\_transfer\_s3\_bucket](#input\_enable\_infrastructure\_vpc\_transfer\_s3\_bucket) | Enable VPC transfer S3 bucket. This allows uploading/downloading files from resources within the infrastructure VPC | `bool` | n/a | yes | | [environment](#input\_environment) | The environment name to be used as part of the resource prefix | `string` | n/a | yes | @@ -440,6 +491,8 @@ This project creates and manages resources within an AWS account for infrastruct | [infrastructure\_logging\_bucket\_retention](#input\_infrastructure\_logging\_bucket\_retention) | Retention in days for the infrasrtucture S3 logs. This is for the default S3 logs bucket, where all AWS service logs will be delivered | `number` | n/a | yes | | [infrastructure\_name](#input\_infrastructure\_name) | The infrastructure name to be used as part of the resource prefix | `string` | n/a | yes | | [infrastructure\_rds](#input\_infrastructure\_rds) | Map of RDSs (The key will be the rds name). Values in here will override `infrastructure_rds_defaults` values if set."
{
rds-name = {
type: Choose either `instance` for RDS instance, or `cluster` for RDS Aurora
engine: RDS engine (Either `mysql` or `postgres`)
engine\_version: RDS Engine version (Specify the major version only, to prevent terraform attempting to downgrade minor versions)
parameters: Map of Parameters for the DB parameter group ({ parameter-name = parameter-value, ... })
instance\_class: RDS instance class
allocated\_storage: RDS allocated storage
storage\_type: RDS storage type
iops: RDS iops (When `type` is `instance`, this is only required for storage type of `io1` or `gp3` - When `cluster`, this must be a multiple between .5 and 50 of the storage amount for the DB cluster.`)
storage_throughput: RDS storage throughput (Only required when `storage\_type` is `gp3`. Only applicable for `type` of `instance`)
multi_az: Enable Multi-AZ RDS (Not applicable for `type` of `cluster`. For `cluster - set `storage_type`, `allocated_storage`, `iops` and `instance_class`)
monitoring\_interval: The interval, in seconds, between points when Enhanced Monitoring metrics are collected for the DB instance. Valid Values: 0, 1, 5, 10, 15, 30, 60.
cloudwatch\_logs\_export\_types: List of log types to enable for exporting to CloudWatch Logs. See `EnableCloudwatchLogsExports.member.N` (https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_CreateDBInstance.html) for valid values.
cluster\_instance\_count: Number of instances to launch within the Aurora DB cluster
cluster\_serverlessv2\_min\_capacity: Minimum capacity for an Aurora DB cluster
cluster\_serverlessv2\_max\_capacity: Maximum capacity for an Aurora DB cluster
}
} |
map(object({
type = optional(string, null)
engine = optional(string, null)
engine_version = optional(string, null)
parameters = optional(map(string), null)
instance_class = optional(string, null)
allocated_storage = optional(number, null)
storage_type = optional(string, null)
iops = optional(number, null)
storage_throughput = optional(number, null)
multi_az = optional(bool, null)
monitoring_interval = optional(number, null)
cloudwatch_logs_export_types = optional(list(string), null)
cluster_instance_count = optional(number, null)
cluster_serverlessv2_min_capacity = optional(number, null)
cluster_serverlessv2_max_capacity = optional(number, null)
}))
| n/a | yes | +| [infrastructure\_rds\_backup\_to\_s3\_cron\_expression](#input\_infrastructure\_rds\_backup\_to\_s3\_cron\_expression) | Cron expression for when to trigger the SQL backups to S3 | `string` | n/a | yes | +| [infrastructure\_rds\_backup\_to\_s3\_retention](#input\_infrastructure\_rds\_backup\_to\_s3\_retention) | Retention in days to keep the S3 SQL backups and logs | `number` | n/a | yes | | [infrastructure\_rds\_defaults](#input\_infrastructure\_rds\_defaults) | Default values for RDSs |
object({
type = optional(string, null)
engine = optional(string, null)
engine_version = optional(string, null)
parameters = optional(map(string), null)
instance_class = optional(string, null)
allocated_storage = optional(number, null)
storage_type = optional(string, null)
iops = optional(number, null)
storage_throughput = optional(number, null)
multi_az = optional(bool, null)
monitoring_interval = optional(number, null)
cloudwatch_logs_export_types = optional(list(string), null)
cluster_instance_count = optional(number, null)
cluster_serverlessv2_min_capacity = optional(number, null)
cluster_serverlessv2_max_capacity = optional(number, null)
})
| n/a | yes | | [infrastructure\_vpc](#input\_infrastructure\_vpc) | Enable infrastructure VPC | `bool` | n/a | yes | | [infrastructure\_vpc\_assign\_generated\_ipv6\_cidr\_block](#input\_infrastructure\_vpc\_assign\_generated\_ipv6\_cidr\_block) | Assign generated IPv6 CIDR block on infrastructure VPC | `bool` | n/a | yes | diff --git a/buildspecs/dalmatian-sql-backup.yml b/buildspecs/dalmatian-sql-backup.yml new file mode 100644 index 0000000..70b2383 --- /dev/null +++ b/buildspecs/dalmatian-sql-backup.yml @@ -0,0 +1,24 @@ +version: 0.2 + +phases: + pre_build: + commands: + - echo "Build started on $(date)" + - echo "Logging in to Amazon ECR..." + - aws ecr get-login-password --region "$AWS_DEFAULT_REGION" | docker login --username AWS --password-stdin "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com" + - | + if [ -n "$DOCKERHUB_USERNAME" ] && [ -n "DOCKERHUB_TOKEN" ]; + then + echo "Logging into Dockerhub ..."; + docker login --username "$DOCKERHUB_USERNAME" --password "$DOCKERHUB_PASSWORD"; + fi; + - echo Building dalmatian-sql-backup docker image ... + - docker build -t dalmatian-sql-backup:latest . + build: + commands: + - echo Adding ECR repo tag... + - docker tag dalmatian-sql-backup:latest "$REPOSITORY_URI:latest" + post_build: + commands: + - echo Pushing the Docker image... + - docker push "$REPOSITORY_URI:latest" diff --git a/container-definitions/app.json.tpl b/container-definitions/app.json.tpl index 6e9a10e..828323b 100644 --- a/container-definitions/app.json.tpl +++ b/container-definitions/app.json.tpl @@ -8,6 +8,9 @@ "logConfiguration": { "logDriver": "awslogs", "options": { + %{ if awslogs_stream_prefix != "" } + "awslogs-stream-prefix": "${awslogs_stream_prefix}", + %{ endif } "awslogs-group": "${cloudwatch_log_group}", "awslogs-region": "${region}" } @@ -30,10 +33,10 @@ ], %{ endif } %{ if environment != "[]" } - "environment": "${environment}, + "environment": ${environment}, %{ endif } %{ if secrets != "[]" } - "secrets": "${secrets}, + "secrets": ${secrets}, %{ endif } %{ if environment_file_s3 != "" } "environmentFiles": [ diff --git a/ecs-cluster-infrastructure-service-scheduled-task.tf b/ecs-cluster-infrastructure-service-scheduled-task.tf index 1fe402f..304e058 100644 --- a/ecs-cluster-infrastructure-service-scheduled-task.tf +++ b/ecs-cluster-infrastructure-service-scheduled-task.tf @@ -39,8 +39,9 @@ resource "aws_ecs_task_definition" "infrastructure_ecs_cluster_service_scheduled linux_parameters = jsonencode({ initProcessEnabled = false }) - cloudwatch_log_group = each.value["enable_cloudwatch_logs"] == true ? aws_cloudwatch_log_group.infrastructure_ecs_cluster_service[each.value["container_name"]].name : "" - region = local.aws_region + cloudwatch_log_group = each.value["enable_cloudwatch_logs"] == true ? aws_cloudwatch_log_group.infrastructure_ecs_cluster_service[each.value["container_name"]].name : "" + awslogs_stream_prefix = "" + region = local.aws_region } ) execution_role_arn = aws_iam_role.infrastructure_ecs_cluster_service_task_execution[each.value["container_name"]].arn @@ -123,7 +124,7 @@ resource "aws_iam_policy" "infrastructure_ecs_cluster_service_scheduled_task_pas "${path.root}/policies/pass-role.json.tpl", { role_arn = aws_iam_role.infrastructure_ecs_cluster_service_task_execution[each.value["container_name"]].arn - service = "events.amazonaws.com" + service = "ecs-tasks.amazonaws.com" } ) } diff --git a/ecs-cluster-infrastructure-service.tf b/ecs-cluster-infrastructure-service.tf index e6b61ed..91db4e4 100644 --- a/ecs-cluster-infrastructure-service.tf +++ b/ecs-cluster-infrastructure-service.tf @@ -241,8 +241,9 @@ resource "aws_ecs_task_definition" "infrastructure_ecs_cluster_service" { linux_parameters = each.value["enable_execute_command"] == true ? jsonencode({ initProcessEnabled = true }) : "{}" - cloudwatch_log_group = each.value["enable_cloudwatch_logs"] == true ? aws_cloudwatch_log_group.infrastructure_ecs_cluster_service[each.key].name : "" - region = local.aws_region + cloudwatch_log_group = each.value["enable_cloudwatch_logs"] == true ? aws_cloudwatch_log_group.infrastructure_ecs_cluster_service[each.key].name : "" + awslogs_stream_prefix = "" + region = local.aws_region } ) execution_role_arn = aws_iam_role.infrastructure_ecs_cluster_service_task_execution[each.key].arn diff --git a/ecs-entrypoints/rds-s3-backups-mysql.txt.tpl b/ecs-entrypoints/rds-s3-backups-mysql.txt.tpl new file mode 100644 index 0000000..d39bd52 --- /dev/null +++ b/ecs-entrypoints/rds-s3-backups-mysql.txt.tpl @@ -0,0 +1 @@ +/usr/local/bin/ump-to-s3-mysql.sh -b ${s3_bucket_name} diff --git a/ecs-entrypoints/rds-s3-backups-postgres.txt.tpl b/ecs-entrypoints/rds-s3-backups-postgres.txt.tpl new file mode 100644 index 0000000..614b015 --- /dev/null +++ b/ecs-entrypoints/rds-s3-backups-postgres.txt.tpl @@ -0,0 +1 @@ +/usr/local/bin/dump-to-s3-postgres.sh -b ${s3_bucket_name} diff --git a/kms-infrastructure.tf b/kms-infrastructure.tf index 6571c58..ed22a60 100644 --- a/kms-infrastructure.tf +++ b/kms-infrastructure.tf @@ -46,6 +46,18 @@ resource "aws_kms_key" "infrastructure" { for k, v in local.infrastructure_ecs_cluster_services : aws_iam_role.infrastructure_ecs_cluster_service_task_execution[k].arn if v["enable_execute_command"] == true && local.infrastructure_ecs_cluster_enable_execute_command_logging ]) } + )}${length(local.infrastructure_rds) > 0 && local.infrastructure_kms_encryption && local.enable_infrastructure_rds_backup_to_s3 ? "," : ""} + ${templatefile("${path.root}/policies/kms-key-policy-statements/role-allow-encrypt.json.tpl", + { + role_arns = jsonencode([ + for k, v in local.infrastructure_rds : aws_iam_role.infrastructure_rds_s3_backups_task[k].arn if local.enable_infrastructure_rds_backup_to_s3 + ]) + } + )}${length(local.infrastructure_rds) > 0 && local.infrastructure_kms_encryption && local.enable_infrastructure_rds_backup_to_s3 ? "," : ""} + ${templatefile("${path.root}/policies/kms-key-policy-statements/cloudwatch-logs-allow.json.tpl", + { + log_group_arn = length(local.infrastructure_rds) > 0 && local.infrastructure_kms_encryption && local.enable_infrastructure_rds_backup_to_s3 ? "arn:aws:logs:${local.aws_region}:${local.aws_account_id}:log-group:${local.resource_prefix}-infrastructure-rds-s3-backups-*" : "" + } )}${contains([for k, v in local.custom_s3_buckets : (v["cloudfront_dedicated_distribution"] == true || v["cloudfront_infrastructure_ecs_cluster_service"] != null) && v["create_dedicated_kms_key"] == false ? true : false], true) && local.infrastructure_kms_encryption ? "," : ""} ${templatefile("${path.root}/policies/kms-key-policy-statements/cloudfront-distribution-allow.json.tpl", { diff --git a/local-exec-scripts/trigger-codedeploy-project.sh b/local-exec-scripts/trigger-codedeploy-project.sh new file mode 100755 index 0000000..0adda46 --- /dev/null +++ b/local-exec-scripts/trigger-codedeploy-project.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# exit on failures +set -e +set -o pipefail + +usage() { + echo "Usage: $(basename "$0") [OPTIONS]" 1>&2 + echo " -h - help" + echo " -n - CodeBuild project name" + exit 1 +} + +while getopts "n:h" opt; do + case $opt in + n) + PROJECT_NAME=$OPTARG + ;; + h) + usage + ;; + *) + usage + ;; + esac +done + +if [ -z "$PROJECT_NAME" ] +then + usage +fi + +BUILD_ID="$( + aws codebuild start-build \ + --project-name "$PROJECT_NAME" \ + | jq -r '.build.id' +)" + +echo "Triggered $PROJECT_NAME CodeBuild project ($BUILD_ID) ..." + +COMPLETED="" +while [ -z "$COMPLETED" ] +do + sleep 10 + echo "Checking progress of CodeBuild $BUILD_ID ..." + BUILD_PROGRESS="$( + aws codebuild batch-get-builds \ + --ids "$BUILD_ID" \ + )" + COMPLETED="$( + echo "$BUILD_PROGRESS" \ + | jq -r \ + '.builds[0].phases[] | select(.phaseType == "COMPLETED")' + )" +done +echo "CodeBuild $BUILD_ID Completed, checking for failures ..." + +FAILURES="$( + echo "$BUILD_PROGRESS" \ + | jq -r \ + '.builds[0].phases[] | select(.phaseStatus == "FAILED")' +)" + +if [ -n "$FAILURES" ] +then + echo "$FAILURES" + exit 1 +fi + +echo "CodeBuild $BUILD_ID completed without failures" diff --git a/locals.tf b/locals.tf index 0b1cbe0..d4b3ea6 100644 --- a/locals.tf +++ b/locals.tf @@ -30,12 +30,14 @@ locals { length(local.custom_s3_buckets) != 0 || local.enable_cloudformatian_s3_template_store || local.enable_infrastructure_vpc_transfer_s3_bucket || - local.infrastructure_ecs_cluster_enable_execute_command_logging + local.infrastructure_ecs_cluster_enable_execute_command_logging || + local.enable_infrastructure_rds_backup_to_s3 ) logs_bucket_s3_source_arns = concat( length(local.infrastructure_ecs_cluster_services) != 0 ? [aws_s3_bucket.infrastructure_ecs_cluster_service_build_pipeline_artifact_store[0].arn] : [], local.enable_infrastructure_vpc_transfer_s3_bucket ? [aws_s3_bucket.infrastructure_vpc_transfer[0].arn] : [], - [for k, v in local.custom_s3_buckets : aws_s3_bucket.custom[k].arn] + [for k, v in local.custom_s3_buckets : aws_s3_bucket.custom[k].arn], + local.enable_infrastructure_rds_backup_to_s3 ? [aws_s3_bucket.infrastructure_rds_s3_backups[0].arn] : [], ) logs_bucket_logs_source_arns = concat( local.infrastructure_vpc_flow_logs_s3_with_athena ? ["arn:aws:logs:${local.aws_region}:${local.aws_account_id}:*"] : [] @@ -232,6 +234,17 @@ locals { "mysql" = 3306 "postgres" = 5432 } + rds_s3_backups_container_entrypoint_file = { + "mysql" = "${path.root}/ecs-entrypoints/rds-s3-backups-mysql.txt.tpl" + "postgres" = "${path.root}/ecs-entrypoints/rds-s3-backups-postgres.txt.tpl" + } + enable_infrastructure_rds_backup_to_s3 = var.enable_infrastructure_rds_backup_to_s3 + infrastructure_rds_backup_to_s3_cron_expression = var.infrastructure_rds_backup_to_s3_cron_expression + infrastructure_rds_backup_to_s3_retention = var.infrastructure_rds_backup_to_s3_retention + enable_infrastructure_rds_tooling_ecs_cluster = anytrue([ + local.enable_infrastructure_rds_backup_to_s3, + ]) + infrastructure_rds_tooling_ecs_cluster_name = "${local.resource_prefix}-infrastructure-rds-tooling" infrastructure_elasticache_defaults = var.infrastructure_elasticache_defaults infrastructure_elasticache_keys = length(var.infrastructure_elasticache) > 0 ? keys(values(var.infrastructure_elasticache)[0]) : [] diff --git a/policies/codebuild-allow-builds.json.tpl b/policies/codebuild-allow-builds.json.tpl new file mode 100644 index 0000000..3c5ca66 --- /dev/null +++ b/policies/codebuild-allow-builds.json.tpl @@ -0,0 +1,16 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "codebuild:StartBuild", + "codebuild:StopBuild", + "codebuild:BatchGet*", + "codebuild:Get*", + "codebuild:List*" + ], + "Effect": "Allow", + "Resource": "*" + } + ] +} diff --git a/policies/secrets-manager-get-secret-value.json.tpl b/policies/secrets-manager-get-secret-value.json.tpl new file mode 100644 index 0000000..bb86b25 --- /dev/null +++ b/policies/secrets-manager-get-secret-value.json.tpl @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": ${secret_name_arns} + } + ] +} diff --git a/rds-infrastructure-cloudwatch-logs.tf b/rds-infrastructure-cloudwatch-logs.tf index 63641b4..c35954a 100644 --- a/rds-infrastructure-cloudwatch-logs.tf +++ b/rds-infrastructure-cloudwatch-logs.tf @@ -10,3 +10,12 @@ resource "aws_cloudwatch_log_group" "infrastructure_rds_exports" { retention_in_days = 30 kms_key_id = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].id : null } + +resource "aws_cloudwatch_log_group" "infrastructure_rds_s3_backups" { + for_each = local.enable_infrastructure_rds_backup_to_s3 ? local.infrastructure_rds : {} + + name = "${local.resource_prefix}-infrastructure-rds-s3-backups-${each.key}" + retention_in_days = local.infrastructure_rds_backup_to_s3_retention + kms_key_id = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].arn : null + skip_destroy = true +} diff --git a/rds-infrastructure-s3-backups-ecr.tf b/rds-infrastructure-s3-backups-ecr.tf new file mode 100644 index 0000000..4312164 --- /dev/null +++ b/rds-infrastructure-s3-backups-ecr.tf @@ -0,0 +1,17 @@ +resource "aws_ecr_repository" "infrastructure_rds_s3_backups" { + count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0 + + name = "${local.resource_prefix}-rds-s3-backups" + + #tfsec:ignore:aws-ecr-enforce-immutable-repository + image_tag_mutability = "MUTABLE" + + encryption_configuration { + encryption_type = local.infrastructure_kms_encryption ? "KMS" : "AES256" + kms_key = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].arn : null + } + + image_scanning_configuration { + scan_on_push = true + } +} diff --git a/rds-infrastructure-s3-backups-image-codebuild.tf b/rds-infrastructure-s3-backups-image-codebuild.tf new file mode 100644 index 0000000..685f47c --- /dev/null +++ b/rds-infrastructure-s3-backups-image-codebuild.tf @@ -0,0 +1,144 @@ +resource "aws_iam_role" "infrastructure_rds_s3_backups_image_codebuild" { + count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0 + + name = "${local.resource_prefix}-${substr(sha512("rds-s3-backups-image-codebuild"), 0, 6)}" + description = "${local.resource_prefix}-rds-s3-backups-image-codebuild" + assume_role_policy = templatefile( + "${path.root}/policies/assume-roles/service-principle-standard.json.tpl", + { services = jsonencode(["codebuild.amazonaws.com", "events.amazonaws.com"]) } + ) +} + +resource "aws_iam_policy" "infrastructure_rds_s3_backups_image_codebuild_cloudwatch_rw" { + count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0 + + name = "${local.resource_prefix}-${substr(sha512("rds-s3-backups-image-codebuild-cloudwatch-rw"), 0, 6)}" + description = "${local.resource_prefix}-rds-s3-backups-image-codebuild-cloudwatch-rw" + policy = templatefile("${path.root}/policies/cloudwatch-logs-rw.json.tpl", {}) +} + +resource "aws_iam_role_policy_attachment" "infrastructure_rds_s3_backups_image_codebuild_cloudwatch_rw" { + count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0 + + role = aws_iam_role.infrastructure_rds_s3_backups_image_codebuild[0].name + policy_arn = aws_iam_policy.infrastructure_rds_s3_backups_image_codebuild_cloudwatch_rw[0].arn +} + +resource "aws_iam_policy" "infrastructure_rds_s3_backups_image_codebuild_allow_builds" { + count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0 + + name = "${local.resource_prefix}-${substr(sha512("rds-s3-backups-image-codebuild-allow-builds"), 0, 6)}" + description = "${local.resource_prefix}-rds-s3-backups-image-codebuild-allow-builds" + policy = templatefile("${path.root}/policies/codebuild-allow-builds.json.tpl", {}) +} + +resource "aws_iam_role_policy_attachment" "infrastructure_rds_s3_backups_image_codebuild_allow_builds" { + count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0 + + role = aws_iam_role.infrastructure_rds_s3_backups_image_codebuild[0].name + policy_arn = aws_iam_policy.infrastructure_rds_s3_backups_image_codebuild_allow_builds[0].arn +} + +resource "aws_iam_policy" "infrastructure_rds_s3_backups_image_codebuild_ecr_push" { + count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0 + + name = "${local.resource_prefix}-${substr(sha512("rds-s3-backups-image-codebuild-ecr-push"), 0, 6)}" + description = "${local.resource_prefix}-ecs-service-codepipeline-codebuild-ecr-push" + policy = templatefile( + "${path.root}/policies/ecr-push.json.tpl", + { ecr_repository_arn = aws_ecr_repository.infrastructure_rds_s3_backups[0].arn } + ) +} + +resource "aws_iam_role_policy_attachment" "infrastructure_rds_s3_backups_image_codebuild_ecr_push" { + count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0 + + role = aws_iam_role.infrastructure_rds_s3_backups_image_codebuild[0].name + policy_arn = aws_iam_policy.infrastructure_rds_s3_backups_image_codebuild_ecr_push[0].arn +} + +resource "aws_codebuild_project" "infrastructure_rds_s3_backups_image_build" { + count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0 + + name = "${local.resource_prefix}-rds-s3-backups-image-build" + description = "${local.resource_prefix} RDS S3 Backups Image Build" + build_timeout = "20" + service_role = aws_iam_role.infrastructure_rds_s3_backups_image_codebuild[0].arn + + artifacts { + type = "NO_ARTIFACTS" + } + + environment { + compute_type = "BUILD_GENERAL1_SMALL" + image = "aws/codebuild/standard:7.0" + type = "LINUX_CONTAINER" + privileged_mode = true + + environment_variable { + name = "AWS_ACCOUNT_ID" + value = local.aws_account_id + } + + environment_variable { + name = "REPOSITORY_URI" + value = aws_ecr_repository.infrastructure_rds_s3_backups[0].repository_url + } + + environment_variable { + name = "DOCKERHUB_USERNAME" + value = local.infrastructure_dockerhub_username + } + + environment_variable { + name = "DOCKERHUB_TOKEN" + value = local.infrastructure_dockerhub_token + } + } + + source { + type = "GITHUB" + location = "https://github.com/dxw/dalmatian-sql-backup" + git_clone_depth = 1 + buildspec = templatefile("${path.root}/buildspecs/dalmatian-sql-backup.yml", {}) + } + + depends_on = [ + aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_image_codebuild_cloudwatch_rw, + aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_image_codebuild_allow_builds, + aws_iam_role_policy_attachment.infrastructure_rds_s3_backups_image_codebuild_ecr_push, + ] +} + +resource "terraform_data" "infrastructure_rds_s3_backups_image_build_trigger_codebuild" { + count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0 + + triggers_replace = [ + md5(templatefile("${path.root}/buildspecs/dalmatian-sql-backup.yml", {})), + ] + + provisioner "local-exec" { + interpreter = ["/bin/bash", "-c"] + command = <