From c8eba9a6f85a8bcebf1b468d19fa5c687a70f3d1d4920865aecaad7c9d1b550c Mon Sep 17 00:00:00 2001 From: xpk Date: Tue, 31 Mar 2026 07:59:05 +0800 Subject: [PATCH] feat: updates on eks example, cmk, and s3bucket --- EksIp6Nginxpod/pod_identities.tf | 8 +- .../CustomerManagedKmsKeys/main.tf | 8 +- .../CustomerManagedKmsKeys/outputs.tf | 4 + modules/storage/S3Bucket/README.md | 71 +++++++ modules/storage/S3Bucket/example/main.tf | 13 ++ modules/storage/S3Bucket/main.tf | 178 ++++++++++++++++++ modules/storage/S3Bucket/outputs.tf | 7 + modules/storage/S3Bucket/variables.tf | 127 +++++++++++++ modules/storage/S3Bucket/versions.tf | 10 + 9 files changed, 424 insertions(+), 2 deletions(-) create mode 100644 modules/storage/S3Bucket/README.md create mode 100644 modules/storage/S3Bucket/example/main.tf create mode 100644 modules/storage/S3Bucket/main.tf create mode 100644 modules/storage/S3Bucket/outputs.tf create mode 100644 modules/storage/S3Bucket/variables.tf create mode 100644 modules/storage/S3Bucket/versions.tf diff --git a/EksIp6Nginxpod/pod_identities.tf b/EksIp6Nginxpod/pod_identities.tf index 3b8035f..001a9b3 100644 --- a/EksIp6Nginxpod/pod_identities.tf +++ b/EksIp6Nginxpod/pod_identities.tf @@ -13,6 +13,7 @@ module "aws_lb_controller_pod_identity" { } } +# https://aws.amazon.com/blogs/containers/amazon-eks-pod-identity-a-new-way-for-applications-on-eks-to-obtain-iam-credentials/ module "CsiPodIdentity" { source = "../modules/security_identity_compliance/iam-role-v2" description = "EKSCSIDriverRole" @@ -29,7 +30,12 @@ module "CsiPodIdentity" { "Action" : [ "sts:AssumeRole", "sts:TagSession" - ] + ], + "Condition" : { + "StringEquals" : { + "aws:SourceAccount" : data.aws_caller_identity.current.account_id + } + } } ] } diff --git a/modules/security_identity_compliance/CustomerManagedKmsKeys/main.tf b/modules/security_identity_compliance/CustomerManagedKmsKeys/main.tf index 8d0c88d..24f5e82 100644 --- a/modules/security_identity_compliance/CustomerManagedKmsKeys/main.tf +++ b/modules/security_identity_compliance/CustomerManagedKmsKeys/main.tf @@ -129,7 +129,7 @@ resource "aws_kms_key" "notify" { bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check } -resource "aws_kms_key" "notify" { +resource "aws_kms_key" "ssm" { count = var.create-ssm-key ? 1 : 0 description = "Customer-managed KMS key for encrypting ssm parameters" enable_key_rotation = var.enable_key_rotation @@ -196,6 +196,12 @@ resource "aws_kms_alias" "notify" { target_key_id = aws_kms_key.notify[0].id } +resource "aws_kms_alias" "ssm" { + count = var.create-ssm-key ? 1 : 0 + name = "alias/${local.prefix}ssm" + target_key_id = aws_kms_key.notify[0].id +} + # Policies data "aws_iam_policy_document" "allpurpose" { source_policy_documents = [data.aws_iam_policy_document.base.json] diff --git a/modules/security_identity_compliance/CustomerManagedKmsKeys/outputs.tf b/modules/security_identity_compliance/CustomerManagedKmsKeys/outputs.tf index 76d2804..227e62b 100644 --- a/modules/security_identity_compliance/CustomerManagedKmsKeys/outputs.tf +++ b/modules/security_identity_compliance/CustomerManagedKmsKeys/outputs.tf @@ -25,6 +25,10 @@ output "cmks" { alias = one(aws_kms_alias.notify.*.name) arn = one(aws_kms_key.notify.*.arn) }, + ssm = { + alias = one(aws_kms_alias.ssm.*.name) + arn = one(aws_kms_key.ssm.*.arn) + } storage = { alias = one(aws_kms_alias.storage.*.name) arn = one(aws_kms_key.storage.*.arn) diff --git a/modules/storage/S3Bucket/README.md b/modules/storage/S3Bucket/README.md new file mode 100644 index 0000000..3c56fcb --- /dev/null +++ b/modules/storage/S3Bucket/README.md @@ -0,0 +1,71 @@ + +## Requirements + +| Name | Version | +|------|---------| +| terraform | >= 1.13.0 | +| aws | >= 6.38.0 | + +## Providers + +| Name | Version | +|------|---------| +| aws | >= 6.38.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_s3_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket_intelligent_tiering_configuration.intel_tiering_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_intelligent_tiering_configuration) | resource | +| [aws_s3_bucket_lifecycle_configuration.lifecycle](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | +| [aws_s3_bucket_logging.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | +| [aws_s3_bucket_policy.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | +| [aws_s3_bucket_public_access_block.block_public_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | +| [aws_s3_bucket_replication_configuration.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_replication_configuration) | resource | +| [aws_s3_bucket_server_side_encryption_configuration.encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | +| [aws_s3_bucket_versioning.versioning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | +| [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| bucket-namespace | Use global or account-regional namespace. Defaults to global | `string` | `"global"` | no | +| bucket\_name | Name of bucket | `string` | n/a | yes | +| bucket\_policy\_json | Json-encoded bucket policy. The AllowSSLRequestsOnly policy is merged with this input. | `string` | `"{}"` | no | +| current-version-archive-tier | Current version archive storage class. Valid values are GLACIER, STANDARD\_IA, ONEZONE\_IA, INTELLIGENT\_TIERING, DEEP\_ARCHIVE, GLACIER\_IR | `string` | `null` | no | +| current-version-transition-days | Days to transition current version to archive | `number` | `15` | no | +| current\_version\_expiration\_days | 731 for flowlogs | `number` | `2560` | no | +| enable\_bucket\_lifecycle | Enable s3 bucket lifecycle | `bool` | n/a | yes | +| enable\_bucket\_logging | Enable bucket logging | `bool` | n/a | yes | +| enable\_encryption | Enable encryption for s3 bucket | `bool` | n/a | yes | +| enable\_intelligent\_tiering | Enable intelligent tiering | `bool` | `false` | no | +| enable\_replication | Enable s3 bucket replication | `bool` | `false` | no | +| enable\_versioning | Enable s3 bucket versioning | `bool` | n/a | yes | +| encryption-enable-bucket-key | Enable bucket key | `bool` | `false` | no | +| encryption\_key\_arn | Leave blank to use AES256 | `string` | `""` | no | +| force-destroy | Boolean that indicates all objects should be deleted from the bucket when the bucket is destroyed | `bool` | `true` | no | +| logging\_bucket\_id | Logging bucket id | `string` | `null` | no | +| noncurrent-version-archive-tier | Non-current version archive storage class. Valid values are GLACIER, STANDARD\_IA, ONEZONE\_IA, INTELLIGENT\_TIERING, DEEP\_ARCHIVE, GLACIER\_IR | `string` | `"GLACIER"` | no | +| noncurrent-version-transition-days | Days to transition non-current version to archive | `number` | `15` | no | +| noncurrent\_version\_expiration\_days | 731 for flowlogs | `number` | `2560` | no | +| replication\_dest\_bucket\_name | Replica bucket name | `string` | `null` | no | +| replication\_destination\_aws\_account\_id | AWS account id of replica bucket | `number` | `null` | no | +| replication\_destination\_kms\_key\_arn | KMS key ARN of destination bucket | `string` | `null` | no | +| replication\_role\_arn | IAM role of s3 bucket replication | `string` | `null` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| bucket\_arn | n/a | +| bucket\_name | n/a | + +--- +## Authorship +This module was developed by xpk. \ No newline at end of file diff --git a/modules/storage/S3Bucket/example/main.tf b/modules/storage/S3Bucket/example/main.tf new file mode 100644 index 0000000..8d3cbb4 --- /dev/null +++ b/modules/storage/S3Bucket/example/main.tf @@ -0,0 +1,13 @@ +module "example-bucket" { + source = "../" + + bucket_name = "example-bucket" + enable_bucket_lifecycle = true + current-version-archive-tier = "DEEP_ARCHIVE" + current-version-transition-days = 30 + current_version_expiration_days = 365 + enable_intelligent_tiering = false + enable_bucket_logging = false + enable_encryption = true + enable_versioning = false +} \ No newline at end of file diff --git a/modules/storage/S3Bucket/main.tf b/modules/storage/S3Bucket/main.tf new file mode 100644 index 0000000..69ee8f1 --- /dev/null +++ b/modules/storage/S3Bucket/main.tf @@ -0,0 +1,178 @@ +resource "aws_s3_bucket" "this" { + bucket = var.bucket_name + bucket_namespace = var.bucket-namespace + force_destroy = var.force-destroy +} + +resource "aws_s3_bucket_public_access_block" "block_public_access" { + bucket = aws_s3_bucket.this.id + + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +# Add SecureTransport restriction by default +data "aws_iam_policy_document" "bucket_policy" { + source_policy_documents = [var.bucket_policy_json] + + statement { + sid = "AllowSSLRequestsOnly" + actions = ["s3:*"] + effect = "Deny" + principals { + type = "*" + identifiers = ["*"] + } + resources = [ + aws_s3_bucket.this.arn, + "${aws_s3_bucket.this.arn}/*" + ] + condition { + test = "Bool" + values = [false] + variable = "aws:SecureTransport" + } + } +} + +resource "aws_s3_bucket_policy" "bucket_policy" { + bucket = aws_s3_bucket.this.id + # policy = var.bucket_policy_json + policy = data.aws_iam_policy_document.bucket_policy.json +} + +resource "aws_s3_bucket_lifecycle_configuration" "lifecycle" { + count = var.enable_bucket_lifecycle ? 1 : 0 + bucket = aws_s3_bucket.this.id + rule { + id = "CurrentVersion" + filter {} + expiration { + days = var.current_version_expiration_days + } + + status = "Enabled" + + dynamic "transition" { + for_each = var.enable_intelligent_tiering ? [1] : [] + content { + days = var.current-version-transition-days + storage_class = "INTELLIGENT_TIERING" + } + } + } + + rule { + id = "NonCurrentVersion" + filter {} + noncurrent_version_expiration { + noncurrent_days = var.noncurrent_version_expiration_days + } + + noncurrent_version_transition { + noncurrent_days = var.noncurrent-version-transition-days + storage_class = var.enable_intelligent_tiering ? "INTELLIGENT_TIERING" : var.noncurrent-version-archive-tier + } + + status = var.enable_versioning ? "Enabled" : "Disabled" + } +} + + +resource "aws_s3_bucket_intelligent_tiering_configuration" "intel_tiering_config" { + count = var.enable_intelligent_tiering ? 1 : 0 + bucket = aws_s3_bucket.this.id + name = "IntelligentTieringArchiveConfigurations" + + tiering { + access_tier = "DEEP_ARCHIVE_ACCESS" + days = 180 # minimum + } + tiering { + access_tier = "ARCHIVE_ACCESS" + days = 90 + } +} + +resource "aws_s3_bucket_logging" "logging" { + count = var.enable_bucket_logging ? 1 : 0 + bucket = aws_s3_bucket.this.id + target_bucket = var.logging_bucket_id + target_prefix = "s3-log/" +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { + count = var.enable_encryption ? 1 : 0 + bucket = aws_s3_bucket.this.id + rule { + apply_server_side_encryption_by_default { + kms_master_key_id = var.encryption_key_arn + sse_algorithm = length(var.encryption_key_arn) > 0 ? "aws:kms" : "AES256" + } + bucket_key_enabled = var.encryption-enable-bucket-key + } +} + +resource "aws_s3_bucket_versioning" "versioning" { + count = var.enable_versioning ? 1 : 0 + bucket = aws_s3_bucket.this.id + versioning_configuration { + status = "Enabled" + } +} + +resource "aws_s3_bucket_replication_configuration" "replication" { + count = var.enable_replication && var.enable_versioning ? 1 : 0 + role = var.replication_role_arn + bucket = aws_s3_bucket.this.id + + + rule { + id = "replrule1" + status = "Enabled" + delete_marker_replication { + status = "Enabled" + } + + source_selection_criteria { + replica_modifications { + status = "Enabled" + } + sse_kms_encrypted_objects { + status = "Enabled" + } + } + + destination { + bucket = var.replication_dest_bucket_name + storage_class = "INTELLIGENT_TIERING" + + account = var.replication_destination_aws_account_id + + encryption_configuration { + replica_kms_key_id = var.replication_destination_kms_key_arn + } + + access_control_translation { + owner = "Destination" + } + + replication_time { + status = "Enabled" + time { + minutes = 15 + } + } + + metrics { + status = "Enabled" + event_threshold { + minutes = 15 + } + } + } + } +} + diff --git a/modules/storage/S3Bucket/outputs.tf b/modules/storage/S3Bucket/outputs.tf new file mode 100644 index 0000000..366fcc1 --- /dev/null +++ b/modules/storage/S3Bucket/outputs.tf @@ -0,0 +1,7 @@ +output bucket_name { + value = aws_s3_bucket.this.id +} + +output bucket_arn { + value = aws_s3_bucket.this.arn +} \ No newline at end of file diff --git a/modules/storage/S3Bucket/variables.tf b/modules/storage/S3Bucket/variables.tf new file mode 100644 index 0000000..138b02c --- /dev/null +++ b/modules/storage/S3Bucket/variables.tf @@ -0,0 +1,127 @@ +variable "bucket_name" { + type = string + description = "Name of bucket" +} + +variable "bucket_policy_json" { + type = string + default = "{}" + description = "Json-encoded bucket policy. The AllowSSLRequestsOnly policy is merged with this input." +} + +variable "current_version_expiration_days" { + type = number + default = 2560 + description = "731 for flowlogs" +} + +variable "noncurrent_version_expiration_days" { + type = number + default = 2560 + description = "731 for flowlogs" +} + +variable "enable_bucket_logging" { + type = bool + description = "Enable bucket logging" +} +variable "logging_bucket_id" { + type = string + default = null + description = "Logging bucket id" +} +variable "enable_encryption" { + type = bool + description = "Enable encryption for s3 bucket" +} +variable "encryption_key_arn" { + type = string + default = "" + description = "Leave blank to use AES256" +} +variable "enable_versioning" { + type = bool + description = "Enable s3 bucket versioning" +} +variable "enable_bucket_lifecycle" { + type = bool + description = "Enable s3 bucket lifecycle" +} + +variable "enable_replication" { + type = bool + default = false + description = "Enable s3 bucket replication" +} + +variable "replication_role_arn" { + type = string + default = null + description = "IAM role of s3 bucket replication" +} + +variable "replication_dest_bucket_name" { + type = string + default = null + description = "Replica bucket name" +} + +variable "replication_destination_aws_account_id" { + type = number + default = null + description = "AWS account id of replica bucket" +} + +variable "replication_destination_kms_key_arn" { + type = string + default = null + description = "KMS key ARN of destination bucket" +} + +variable "enable_intelligent_tiering" { + type = bool + default = false + description = "Enable intelligent tiering" +} + +variable "current-version-archive-tier" { + type = string + description = "Current version archive storage class. Valid values are GLACIER, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, DEEP_ARCHIVE, GLACIER_IR" + default = null +} + +variable "noncurrent-version-archive-tier" { + type = string + description = "Non-current version archive storage class. Valid values are GLACIER, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, DEEP_ARCHIVE, GLACIER_IR" + default = "GLACIER" +} + +variable "current-version-transition-days" { + type = number + description = "Days to transition current version to archive" + default = 15 +} + +variable "noncurrent-version-transition-days" { + type = number + description = "Days to transition non-current version to archive" + default = 15 +} + +variable "encryption-enable-bucket-key" { + type = bool + default = false + description = "Enable bucket key" +} + +variable "bucket-namespace" { + type = string + default = "global" + description = "Use global or account-regional namespace. Defaults to global" +} + +variable "force-destroy" { + type = bool + default = true + description = "Boolean that indicates all objects should be deleted from the bucket when the bucket is destroyed" +} \ No newline at end of file diff --git a/modules/storage/S3Bucket/versions.tf b/modules/storage/S3Bucket/versions.tf new file mode 100644 index 0000000..523dc02 --- /dev/null +++ b/modules/storage/S3Bucket/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.13.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.38.0" + } + } +}