diff --git a/terraform/main.tf b/terraform/main.tf index 6cefdc5..8e2a3c7 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -7,13 +7,28 @@ module "common" { mwaa_dag_s3_bucket_name = var.mwaa_dag_s3_bucket_name } -# Terraform module to create archive for PDS Nucleus +# Terraform module to create primary archive for PDS Nucleus module "archive" { source = "./terraform-modules/archive" pds_node_names = var.pds_node_names depends_on = [module.common, module.ecs_ecr] pds_nucleus_hot_archive_bucket_name_postfix = var.pds_nucleus_hot_archive_bucket_name_postfix pds_nucleus_cold_archive_bucket_name_postfix = var.pds_nucleus_cold_archive_bucket_name_postfix + pds_nucleus_cold_archive_buckets = module.archive-secondary.pds_nucleus_cold_archive_buckets + permission_boundary_for_iam_roles = var.permission_boundary_for_iam_roles + pds_nucleus_cold_archive_storage_class = var.pds_nucleus_cold_archive_storage_class +} + +# Terraform module to create secondary archive for PDS Nucleus +module "archive-secondary" { + source = "./terraform-modules/archive-secondary" + pds_node_names = var.pds_node_names + depends_on = [module.common, module.ecs_ecr] + pds_nucleus_cold_archive_bucket_name_postfix = var.pds_nucleus_cold_archive_bucket_name_postfix + + providers = { + aws = aws.secondary + } } # The Terraform module to create the PDS Nucleus Baseline System (without any project specific components) diff --git a/terraform/providers.tf b/terraform/providers.tf index 7d91403..6be4fe4 100644 --- a/terraform/providers.tf +++ b/terraform/providers.tf @@ -3,12 +3,23 @@ terraform { aws = { source = "hashicorp/aws" version = "~> 5.0" + configuration_aliases = [ aws.secondary ] } } } provider "aws" { region = var.region + default_tags { + tags = { + product = "PDS Nucleus" + } + } +} + +provider "aws" { + region = var.region_secondary + alias = "secondary" default_tags { tags = { diff --git a/terraform/terraform-modules/archive-secondary/archive-secondary.tf b/terraform/terraform-modules/archive-secondary/archive-secondary.tf new file mode 100644 index 0000000..075d19c --- /dev/null +++ b/terraform/terraform-modules/archive-secondary/archive-secondary.tf @@ -0,0 +1,20 @@ +# Terraform script to create the PDS archive related resources + +# Create a cold archive for each PDS Node +resource "aws_s3_bucket" "pds_nucleus_cold_archive" { + count = length(var.pds_node_names) + # convert PDS node name to S3 bucket name compatible format + bucket = "${lower(replace(var.pds_node_names[count.index], "_", "-"))}-${var.pds_nucleus_cold_archive_bucket_name_postfix}" +} + +resource "aws_s3_bucket_versioning" "pds_nucleus_cold_archive" { + count = length(var.pds_node_names) + bucket = aws_s3_bucket.pds_nucleus_cold_archive[count.index].id + versioning_configuration { + status = "Enabled" + } +} + +output "pds_nucleus_cold_archive_buckets" { + value = aws_s3_bucket.pds_nucleus_cold_archive +} diff --git a/terraform/terraform-modules/archive-secondary/variables.tf b/terraform/terraform-modules/archive-secondary/variables.tf new file mode 100644 index 0000000..c28d425 --- /dev/null +++ b/terraform/terraform-modules/archive-secondary/variables.tf @@ -0,0 +1,12 @@ +variable "pds_node_names" { + description = "List of PDS Node Names" + type = list(string) + sensitive = true +} + +variable "pds_nucleus_cold_archive_bucket_name_postfix" { + description = "The postfix of the name of the cold archive s3 bucket" + default = "cold-archive-" + type = string + sensitive = true +} diff --git a/terraform/terraform-modules/archive/archive.tf b/terraform/terraform-modules/archive/archive.tf index 404a2e9..8faf77b 100644 --- a/terraform/terraform-modules/archive/archive.tf +++ b/terraform/terraform-modules/archive/archive.tf @@ -7,9 +7,105 @@ resource "aws_s3_bucket" "pds_nucleus_hot_archive" { bucket = "${lower(replace(var.pds_node_names[count.index], "_", "-"))}-${var.pds_nucleus_hot_archive_bucket_name_postfix}" } -# Create a cold archive for each PDS Node -resource "aws_s3_bucket" "pds_nucleus_cold_archive" { +resource "aws_s3_bucket_versioning" "pds_nucleus_hot_archive" { count = length(var.pds_node_names) - # convert PDS node name to S3 bucket name compatible format - bucket = "${lower(replace(var.pds_node_names[count.index], "_", "-"))}-${var.pds_nucleus_cold_archive_bucket_name_postfix}" + + bucket = aws_s3_bucket.pds_nucleus_hot_archive[count.index].id + versioning_configuration { + status = "Enabled" + } +} + +data "aws_iam_policy_document" "pds_nucleus_archive_replication_assume_role" { + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["s3.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } +} + +# The Policy for Permission Boundary +data "aws_iam_policy" "mcp_operator_policy" { + name = var.permission_boundary_for_iam_roles +} + +resource "aws_iam_role" "pds_nucleus_archive_replication_role" { + name = "pds-nucleus-archive-replication-role" + assume_role_policy = data.aws_iam_policy_document.pds_nucleus_archive_replication_assume_role.json + permissions_boundary = data.aws_iam_policy.mcp_operator_policy.arn +} + +data "aws_iam_policy_document" "pds_nucleus_archive_replication_policy" { + + statement { + effect = "Allow" + + actions = [ + "s3:GetReplicationConfiguration", + "s3:ListBucket", + ] + + resources = ["arn:aws:s3:::pds-*-archive-*"] + } + + statement { + effect = "Allow" + + actions = [ + "s3:GetObjectVersionForReplication", + "s3:GetObjectVersionAcl", + "s3:GetObjectVersionTagging", + ] + + resources = ["*"] + } + + statement { + effect = "Allow" + + actions = [ + "s3:ReplicateObject", + "s3:ReplicateDelete", + "s3:ReplicateTags", + ] + + resources = ["arn:aws:s3:::pds-*-archive-*"] + } +} + +resource "aws_iam_policy" "pds_nucleus_archive_replication_policy" { + name = "pds-nucleus-archive-replication-policy" + policy = data.aws_iam_policy_document.pds_nucleus_archive_replication_policy.json +} + +resource "aws_iam_role_policy_attachment" "pds_nucleus_archive_replication_policy_document" { + role = aws_iam_role.pds_nucleus_archive_replication_role.name + policy_arn = aws_iam_policy.pds_nucleus_archive_replication_policy.arn +} + +resource "aws_s3_bucket_replication_configuration" "pds_nucleus_s3_bucket_replication_configuration" { + + count = length(var.pds_node_names) + + # Must have bucket versioning enabled first + depends_on = [aws_s3_bucket_versioning.pds_nucleus_hot_archive] + + role = aws_iam_role.pds_nucleus_archive_replication_role.arn + bucket = aws_s3_bucket.pds_nucleus_hot_archive[count.index].id + + rule { + id = "pds-nucleus-replication-rule-${var.pds_node_names[count.index]}" + + status = "Enabled" + + destination { + bucket = var.pds_nucleus_cold_archive_buckets[count.index].arn + storage_class = var.pds_nucleus_cold_archive_storage_class + } + } } diff --git a/terraform/terraform-modules/archive/variables.tf b/terraform/terraform-modules/archive/variables.tf index 4c64ae3..3510789 100644 --- a/terraform/terraform-modules/archive/variables.tf +++ b/terraform/terraform-modules/archive/variables.tf @@ -17,3 +17,20 @@ variable "pds_nucleus_cold_archive_bucket_name_postfix" { type = string sensitive = true } + +variable "pds_nucleus_cold_archive_buckets" { + description = "The list of the names of the cold archive s3 buckets" + type = list + sensitive = true +} + +variable "pds_nucleus_cold_archive_storage_class" { + description = "The storage class of the cold archive s3 buckets" + type = string +} + +variable "permission_boundary_for_iam_roles" { + description = "Permission boundary for IAM roles" + type = string + sensitive = true +} diff --git a/terraform/variables.tf b/terraform/variables.tf index a7a81b9..ab812df 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -10,6 +10,12 @@ variable "region" { default = "us-west-2" } +variable "region_secondary" { + description = "Secondary Region for Archive" + type = string + default = "us-east-2" +} + variable "vpc_id" { description = "VPC ID" type = string @@ -67,6 +73,12 @@ variable "pds_nucleus_cold_archive_bucket_name_postfix" { sensitive = true } +variable "pds_nucleus_cold_archive_storage_class" { + description = "The storage class of the cold archive s3 buckets" + default = "DEEP_ARCHIVE" + type = string +} + variable "pds_nucleus_config_bucket_name" { description = "PDS Nucleus Configuration S3 Bucket Name" default = "pds-nucleus-config-"