From ea8bf40a393f6ebd6b4abd063a9c3e34a5dc1573 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 27 Sep 2024 17:22:40 +0100 Subject: [PATCH] Allow custom policies for custom S3 buckets * Conditionally add KMS key statements, or bucket policies for Custom S3 buckets --- README.md | 2 +- s3-custom-buckets-kms.tf | 2 +- s3-custom-buckets.tf | 2 +- variables.tf | 18 +++++++++++++++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2359d03..2a0caed 100644 --- a/README.md +++ b/README.md @@ -450,7 +450,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws\_region](#input\_aws\_region) | AWS region in which to launch resources | `string` | n/a | yes | | [custom\_cloudformation\_stacks](#input\_custom\_cloudformation\_stacks) | Map of CloudFormation stacks to deploy
{
stack-name = {
s3\_template\_store\_key: The filename of a CloudFormation template that is stored within the S3 bucket, created by the `enable_cloudformatian_s3_template_store`
template\_body: (Optional - use of s3\_template\_store\_key is preferred) The CloudFormation template body
parameters: The CloudFormation template parameters ({ parameter-name = parameter-value, ... })
on\_failure: What to do on failure, either 'DO\_NOTHING', 'ROLLBACK' or 'DELETE'
capabilities: A list of capabilities. Valid values: `CAPABILITY_NAMED_IAM`, `CAPABILITY_IAM`, `CAPABILITY_AUTO_EXPAND`
}
} |
map(object({
s3_template_store_key = optional(string, null)
template_body = optional(string, null)
parameters = optional(map(string), null)
on_failure = optional(string, null)
capabilities = optional(list(string), null)
}))
| n/a | yes | | [custom\_route53\_hosted\_zones](#input\_custom\_route53\_hosted\_zones) | Map of Route53 Hosted Zone configurations to create
{
example.com = {
ns\_records: Map of NS records to create ({ "domain.example.com" = { values = ["ns1.example.com", "ns2.example.com"], ttl = 300 })
a\_records: Map of A records to create ({ "domain.example.com" = { values = ["1.2.3.4", "5.6.7.8"], ttl = 300 })
alias\_records: Map of ALIAS records to create ({ "domain.example.com" = { value = "example.cloudfront.com", zone\_id = "Z2FDTNDATAQYW2" })
cname\_records: Map of CNAME records to create ({ "domain.example.com" = { values = ["external1.example.com", "external2.example.com"], ttl = 60 })
mx\_records: Map of MX records to create ({ "example.com" = { values = ["1 mail.example.com", "5 mail2.example.com"], ttl = 60 })
txt\_records: Map of TXT records to create ({ "example.com" = { values = ["v=spf1 include:spf.example.com -all"], ttl = 60 })
}
} |
map(object({
ns_records = optional(map(object({
values = list(string)
ttl = optional(number, 300)
})), null)
a_records = optional(map(object({
values = list(string)
ttl = optional(number, 300)
})), null)
alias_records = optional(map(object({
value = string
zone_id = string
})), null)
cname_records = optional(map(object({
values = list(string)
ttl = optional(number, 300)
})), null)
mx_records = optional(map(object({
values = list(string)
ttl = optional(number, 300)
})), null)
txt_records = optional(map(object({
values = list(string)
ttl = optional(number, 300)
})), null)
}))
| n/a | yes | -| [custom\_s3\_buckets](#input\_custom\_s3\_buckets) | Map of S3 buckets to create, and conditionally serve via CloudFront. The S3 configuration will follow AWS best practices (eg. Private, ACLS disabled, SSE, Versioning, Logging). The bucket must be emptied before attempting deletion/destruction."
{
bucket-name = {
create\_dedicated\_kms\_key: Conditionally create a KMS key specifically for this bucket's server side encryption (rather than using the Infrastructure's KMS key). It's recommended to use this if the S3 bucket will be accessed from external AWS accounts.
transition\_to\_ia\_days: Conditionally transition objects to 'Standard Infrequent Access' storage in N days
transition\_to\_glacier\_days: Conditionally transition objects to 'Glacier' storage in N days
cloudfront\_dedicated\_distribution: Conditionally create a CloudFront distribution to serve objects from the S3 bucket.
cloudfront\_s3\_root: Sets the S3 document root when being served from CloudFront. By default this will be '/'. If `cloudfront_infrastructure_ecs_cluster_service_path` has been set, this helps by modifying the request from `/sub-directory-path` to `/` by use of a CloudFront function.
cloudfront\_infrastructure\_ecs\_cluster\_service: Conditionally create an Origin on a CloudFront distribution that is serving the given Infrastructure ECS Cluster Service name
cloudfront\_infrastructure\_ecs\_cluster\_service\_path: If `cloudfront_infrastructure_ecs_cluster_service`, set this to the path that objects will be served from.
}
} |
map(object({
create_dedicated_kms_key = optional(bool, null)
transition_to_ia_days = optional(number, null)
transition_to_glacier_days = optional(number, null)
cloudfront_dedicated_distribution = optional(bool, null)
cloudfront_s3_root = optional(string, null)
cloudfront_infrastructure_ecs_cluster_service = optional(string, null)
cloudfront_infrastructure_ecs_cluster_service_path = optional(string, null)
}))
| n/a | yes | +| [custom\_s3\_buckets](#input\_custom\_s3\_buckets) | Map of S3 buckets to create, and conditionally serve via CloudFront. The S3 configuration will follow AWS best practices (eg. Private, ACLS disabled, SSE, Versioning, Logging). The bucket must be emptied before attempting deletion/destruction."
{
bucket-name = {
create\_dedicated\_kms\_key: Conditionally create a KMS key specifically for this bucket's server side encryption (rather than using the Infrastructure's KMS key). It's recommended to use this if the S3 bucket will be accessed from external AWS accounts.
custom\_kms\_key\_policy\_statements: Conditionally add a list of user-defined key policy statements
transition\_to\_ia\_days: Conditionally transition objects to 'Standard Infrequent Access' storage in N days
transition\_to\_glacier\_days: Conditionally transition objects to 'Glacier' storage in N days
cloudfront\_dedicated\_distribution: Conditionally create a CloudFront distribution to serve objects from the S3 bucket.
cloudfront\_s3\_root: Sets the S3 document root when being served from CloudFront. By default this will be '/'. If `cloudfront_infrastructure_ecs_cluster_service_path` has been set, this helps by modifying the request from `/sub-directory-path` to `/` by use of a CloudFront function.
cloudfront\_infrastructure\_ecs\_cluster\_service: Conditionally create an Origin on a CloudFront distribution that is serving the given Infrastructure ECS Cluster Service name
cloudfront\_infrastructure\_ecs\_cluster\_service\_path: If `cloudfront_infrastructure_ecs_cluster_service`, set this to the path that objects will be served from.
custom\_bucket\_policy\_statements: Conditionally add a list of user-defined bucket policy statements
}
} |
map(object({
create_dedicated_kms_key = optional(bool, null)
custom_kms_key_policy_statements = optional(list(object({
Effect = string,
Principal = map(any),
Action = string,
Resource = string,
Condition = optional(map(any), {})
})), [])
transition_to_ia_days = optional(number, null)
transition_to_glacier_days = optional(number, null)
cloudfront_dedicated_distribution = optional(bool, null)
cloudfront_s3_root = optional(string, null)
cloudfront_infrastructure_ecs_cluster_service = optional(string, null)
cloudfront_infrastructure_ecs_cluster_service_path = optional(string, null)
custom_bucket_policy_statements = optional(list(object({
Effect = string,
Principal = map(any),
Action = string,
Resource = string,
Condition = optional(map(any), {})
})), [])
}))
| n/a | yes | | [ecs\_cluster\_efs\_directories](#input\_ecs\_cluster\_efs\_directories) | ECS cluster EFS directories to create | `list(string)` | n/a | yes | | [ecs\_cluster\_efs\_infrequent\_access\_transition](#input\_ecs\_cluster\_efs\_infrequent\_access\_transition) | ECS cluser EFS IA transiton in days. Set to 0 to disable IA transition. | `number` | n/a | yes | | [ecs\_cluster\_efs\_performance\_mode](#input\_ecs\_cluster\_efs\_performance\_mode) | ECS cluser EFS performance mode | `string` | n/a | yes | diff --git a/s3-custom-buckets-kms.tf b/s3-custom-buckets-kms.tf index f27b6f7..f3dd4bc 100644 --- a/s3-custom-buckets-kms.tf +++ b/s3-custom-buckets-kms.tf @@ -24,7 +24,7 @@ resource "aws_kms_key" "custom_s3_buckets" { each.value["cloudfront_infrastructure_ecs_cluster_service"] != null ? [aws_cloudfront_distribution.infrastructure_ecs_cluster_service_cloudfront[each.value["cloudfront_infrastructure_ecs_cluster_service"]].arn] : [] ))) } - )} + )}${length(each.value["custom_kms_key_policy_statements"]) != 0 ? ",${trim(jsonencode(each.value["custom_kms_key_policy_statements"]), "[]")}" : ""} ] EOT } diff --git a/s3-custom-buckets.tf b/s3-custom-buckets.tf index dcc8944..045c493 100644 --- a/s3-custom-buckets.tf +++ b/s3-custom-buckets.tf @@ -27,7 +27,7 @@ resource "aws_s3_bucket_policy" "custom" { [for k, v in local.custom_s3_buckets : aws_cloudfront_distribution.infrastructure_ecs_cluster_service_cloudfront[v["cloudfront_infrastructure_ecs_cluster_service"]].arn if v["cloudfront_infrastructure_ecs_cluster_service"] != null] ))) } - )} + )}${length(each.value["custom_bucket_policy_statements"]) != 0 ? ",${trim(jsonencode(each.value["custom_bucket_policy_statements"]), "[]")}" : ""} ] EOT } diff --git a/variables.tf b/variables.tf index f7613f5..ce9cf9c 100644 --- a/variables.tf +++ b/variables.tf @@ -846,23 +846,39 @@ variable "custom_s3_buckets" { { bucket-name = { create_dedicated_kms_key: Conditionally create a KMS key specifically for this bucket's server side encryption (rather than using the Infrastructure's KMS key). It's recommended to use this if the S3 bucket will be accessed from external AWS accounts. + custom_kms_key_policy_statements: Conditionally add a list of user-defined key policy statements transition_to_ia_days: Conditionally transition objects to 'Standard Infrequent Access' storage in N days transition_to_glacier_days: Conditionally transition objects to 'Glacier' storage in N days cloudfront_dedicated_distribution: Conditionally create a CloudFront distribution to serve objects from the S3 bucket. cloudfront_s3_root: Sets the S3 document root when being served from CloudFront. By default this will be '/'. If `cloudfront_infrastructure_ecs_cluster_service_path` has been set, this helps by modifying the request from `/sub-directory-path` to `/` by use of a CloudFront function. cloudfront_infrastructure_ecs_cluster_service: Conditionally create an Origin on a CloudFront distribution that is serving the given Infrastructure ECS Cluster Service name cloudfront_infrastructure_ecs_cluster_service_path: If `cloudfront_infrastructure_ecs_cluster_service`, set this to the path that objects will be served from. + custom_bucket_policy_statements: Conditionally add a list of user-defined bucket policy statements } } EOT type = map(object({ - create_dedicated_kms_key = optional(bool, null) + create_dedicated_kms_key = optional(bool, null) + custom_kms_key_policy_statements = optional(list(object({ + Effect = string, + Principal = map(any), + Action = string, + Resource = string, + Condition = optional(map(any), {}) + })), []) transition_to_ia_days = optional(number, null) transition_to_glacier_days = optional(number, null) cloudfront_dedicated_distribution = optional(bool, null) cloudfront_s3_root = optional(string, null) cloudfront_infrastructure_ecs_cluster_service = optional(string, null) cloudfront_infrastructure_ecs_cluster_service_path = optional(string, null) + custom_bucket_policy_statements = optional(list(object({ + Effect = string, + Principal = map(any), + Action = string, + Resource = string, + Condition = optional(map(any), {}) + })), []) })) }