diff --git a/README.md b/README.md
index 1f38279c..2e4a4757 100644
--- a/README.md
+++ b/README.md
@@ -108,6 +108,7 @@ As of version 1.6.0, AFT collects anonymous operational metrics to help AWS impr
| [account\_provisioning\_customizations\_repo\_name](#input\_account\_provisioning\_customizations\_repo\_name) | Repository name for the account provisioning customizations files. For non-CodeCommit repos, name should be in the format of Org/Repo | `string` | `"aft-account-provisioning-customizations"` | no |
| [account\_request\_repo\_branch](#input\_account\_request\_repo\_branch) | Branch to source account request repo from | `string` | `"main"` | no |
| [account\_request\_repo\_name](#input\_account\_request\_repo\_name) | Repository name for the account request files. For non-CodeCommit repos, name should be in the format of Org/Repo | `string` | `"aft-account-request"` | no |
+| [aft\_backend\_bucket\_access\_logs\_object\_expiration\_days](#input\_aft\_backend\_bucket\_access\_logs\_object\_expiration\_days) | Amount of days to keep the objects stored in the access logs bucket for AFT backend buckets | `number` | `365` | no |
| [aft\_enable\_vpc](#input\_aft\_enable\_vpc) | Flag turning use of VPC on/off for AFT | `bool` | `true` | no |
| [aft\_feature\_cloudtrail\_data\_events](#input\_aft\_feature\_cloudtrail\_data\_events) | Feature flag toggling CloudTrail data events on/off | `bool` | `false` | no |
| [aft\_feature\_delete\_default\_vpcs\_enabled](#input\_aft\_feature\_delete\_default\_vpcs\_enabled) | Feature flag toggling deletion of default VPCs on/off | `bool` | `false` | no |
@@ -139,7 +140,7 @@ As of version 1.6.0, AFT collects anonymous operational metrics to help AWS impr
| [terraform\_distribution](#input\_terraform\_distribution) | Terraform distribution being used for AFT - valid values are oss, tfc, or tfe | `string` | `"oss"` | no |
| [terraform\_org\_name](#input\_terraform\_org\_name) | Organization name for Terraform Cloud or Enterprise | `string` | `"null"` | no |
| [terraform\_token](#input\_terraform\_token) | Terraform token for Cloud or Enterprise | `string` | `"null"` | no |
-| [terraform\_version](#input\_terraform\_version) | Terraform version being used for AFT | `string` | `"1.5.7"` | no |
+| [terraform\_version](#input\_terraform\_version) | Terraform version being used for AFT | `string` | `"1.6.0"` | no |
| [tf\_backend\_secondary\_region](#input\_tf\_backend\_secondary\_region) | AFT creates a backend for state tracking for its own state as well as OSS cases. The backend's primary region is the same as the AFT region, but this defines the secondary region to replicate to. | `string` | `""` | no |
| [vcs\_provider](#input\_vcs\_provider) | Customer VCS Provider - valid inputs are codecommit, bitbucket, github, or githubenterprise | `string` | `"codecommit"` | no |
diff --git a/VERSION b/VERSION
index 6b89d58f..feaae22b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.12.2
+1.13.0
diff --git a/main.tf b/main.tf
index cbdbdb04..0d2d7a67 100644
--- a/main.tf
+++ b/main.tf
@@ -65,9 +65,11 @@ module "aft_backend" {
aws.primary_region = aws.aft_management
aws.secondary_region = aws.tf_backend_secondary_region
}
- source = "./modules/aft-backend"
- primary_region = var.ct_home_region
- secondary_region = var.tf_backend_secondary_region
+ source = "./modules/aft-backend"
+ primary_region = var.ct_home_region
+ secondary_region = var.tf_backend_secondary_region
+ aft_management_account_id = var.aft_management_account_id
+ aft_backend_bucket_access_logs_object_expiration_days = var.aft_backend_bucket_access_logs_object_expiration_days
}
module "aft_code_repositories" {
diff --git a/modules/aft-backend/main.tf b/modules/aft-backend/main.tf
index 544799e4..f4aa32fb 100644
--- a/modules/aft-backend/main.tf
+++ b/modules/aft-backend/main.tf
@@ -16,6 +16,12 @@ resource "aws_s3_bucket" "primary-backend-bucket" {
"Name" = "aft-backend-${data.aws_caller_identity.current.account_id}-primary-region"
}
}
+resource "aws_s3_bucket_logging" "primary-backend-bucket-logging" {
+ provider = aws.primary_region
+ bucket = aws_s3_bucket.primary-backend-bucket.id
+ target_bucket = aws_s3_bucket.aft_access_logs_primary_backend_bucket.id
+ target_prefix = "log/"
+}
#tfsec:ignore:aws-s3-enable-bucket-logging
resource "aws_s3_bucket" "secondary-backend-bucket" {
@@ -253,6 +259,72 @@ resource "aws_iam_role_policy_attachment" "replication" {
policy_arn = aws_iam_policy.replication[0].arn
}
+#tfsec:ignore:aws-s3-enable-bucket-logging
+resource "aws_s3_bucket" "aft_access_logs_primary_backend_bucket" {
+ provider = aws.primary_region
+ bucket = "aft-backend-${data.aws_caller_identity.current.account_id}-primary-region-access-logs"
+}
+
+resource "aws_s3_bucket_policy" "aft_access_logs_primary_backend_bucket" {
+ provider = aws.primary_region
+ bucket = aws_s3_bucket.aft_access_logs_primary_backend_bucket.id
+ policy = templatefile("${path.module}/s3/bucket-policies/aft_access_logs_primary_backend_bucket.tpl", {
+ aws_s3_bucket_aft_access_logs_arn = aws_s3_bucket.aft_access_logs_primary_backend_bucket.arn
+ aws_s3_bucket_primary_backend_arn = aws_s3_bucket.primary-backend-bucket.arn
+ aft_management_account_id = var.aft_management_account_id
+ })
+}
+
+resource "aws_s3_bucket_versioning" "aft_access_logs_primary_backend_bucket" {
+ provider = aws.primary_region
+ bucket = aws_s3_bucket.aft_access_logs_primary_backend_bucket.id
+ versioning_configuration {
+ status = "Enabled"
+ }
+}
+
+resource "aws_kms_key" "aft_access_logs_primary_backend_bucket" {
+ provider = aws.primary_region
+ enable_key_rotation = true
+}
+
+resource "aws_s3_bucket_server_side_encryption_configuration" "aft_access_logs_primary_backend_bucket" {
+ provider = aws.primary_region
+ bucket = aws_s3_bucket.aft_access_logs_primary_backend_bucket.id
+
+ rule {
+ apply_server_side_encryption_by_default {
+ kms_master_key_id = aws_kms_key.aft_access_logs_primary_backend_bucket.arn
+ sse_algorithm = "aws:kms"
+ }
+ }
+}
+
+resource "aws_s3_bucket_lifecycle_configuration" "aft_access_logs_primary_backend_bucket" {
+ provider = aws.primary_region
+ bucket = aws_s3_bucket.aft_access_logs_primary_backend_bucket.id
+ rule {
+ status = "Enabled"
+ filter {
+ prefix = "log/"
+ }
+ id = "aft_primary_backend_bucket_access_logs_lifecycle_configuration_rule"
+
+ noncurrent_version_expiration {
+ noncurrent_days = var.aft_backend_bucket_access_logs_object_expiration_days
+ }
+ }
+
+}
+
+resource "aws_s3_bucket_public_access_block" "aft_access_logs_primary_backend_bucket" {
+ provider = aws.primary_region
+ bucket = aws_s3_bucket.aft_access_logs_primary_backend_bucket.id
+ block_public_acls = true
+ block_public_policy = true
+ ignore_public_acls = true
+ restrict_public_buckets = true
+}
# DynamoDB Resources
# TFSec incorrectly reports no DAX SSE encryption for a DDB table (SSE encryption is default-on)
diff --git a/modules/aft-backend/s3/bucket-policies/aft_access_logs_primary_backend_bucket.tpl b/modules/aft-backend/s3/bucket-policies/aft_access_logs_primary_backend_bucket.tpl
new file mode 100644
index 00000000..04e225c4
--- /dev/null
+++ b/modules/aft-backend/s3/bucket-policies/aft_access_logs_primary_backend_bucket.tpl
@@ -0,0 +1,26 @@
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "Allow PutObject",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": [
+ "logging.s3.amazonaws.com"
+ ]
+ },
+ "Action": "s3:PutObject",
+ "Resource": [
+ "${aws_s3_bucket_aft_access_logs_arn}/*"
+ ],
+ "Condition": {
+ "ArnLike": {
+ "aws:SourceArn": "${aws_s3_bucket_primary_backend_arn}"
+ },
+ "StringEquals": {
+ "aws:SourceAccount": "${aft_management_account_id}"
+ }
+ }
+ }
+ ]
+}
diff --git a/modules/aft-backend/variables.tf b/modules/aft-backend/variables.tf
index 80b28895..3914b0d2 100644
--- a/modules/aft-backend/variables.tf
+++ b/modules/aft-backend/variables.tf
@@ -8,3 +8,11 @@ variable "primary_region" {
variable "secondary_region" {
type = string
}
+
+variable "aft_management_account_id" {
+ type = string
+}
+
+variable "aft_backend_bucket_access_logs_object_expiration_days" {
+ type = number
+}
diff --git a/modules/aft-lambda-layer/lambda.tf b/modules/aft-lambda-layer/lambda.tf
index 2adf23ba..e5f3c8ef 100644
--- a/modules/aft-lambda-layer/lambda.tf
+++ b/modules/aft-lambda-layer/lambda.tf
@@ -13,7 +13,6 @@ resource "aws_lambda_function" "codebuild_trigger" {
memory_size = 1024
runtime = var.lambda_runtime_python_version
timeout = 900
-
dynamic "vpc_config" {
for_each = var.aft_enable_vpc ? [1] : []
content {
diff --git a/sources/aft-lambda-layer/pyproject.toml b/sources/aft-lambda-layer/pyproject.toml
index 15887c81..29f67a2e 100644
--- a/sources/aft-lambda-layer/pyproject.toml
+++ b/sources/aft-lambda-layer/pyproject.toml
@@ -24,8 +24,8 @@ classifiers=[
"Operating System :: OS Independent",
]
dependencies = [
- "boto3 == 1.27.1",
- "botocore == 1.30.1",
+ "boto3 == 1.28.17",
+ "botocore == 1.31.17",
"requests == 2.31.0",
"jsonschema == 4.3.2",
]
diff --git a/variables.tf b/variables.tf
index aa0473b4..eabf36d8 100644
--- a/variables.tf
+++ b/variables.tf
@@ -62,7 +62,7 @@ variable "ct_home_region" {
description = "The region from which this module will be executed. This MUST be the same region as Control Tower is deployed."
type = string
validation {
- condition = can(regex("(us(-gov)?|ap|ca|cn|eu|sa|me|af)-(central|(north|south)?(east|west)?)-\\d", var.ct_home_region))
+ condition = can(regex("(us(-gov)?|ap|ca|cn|eu|sa|me|af|il)-(central|(north|south)?(east|west)?)-\\d", var.ct_home_region))
error_message = "Variable var: region is not valid."
}
}
@@ -96,6 +96,16 @@ variable "log_archive_bucket_object_expiration_days" {
}
}
+variable "aft_backend_bucket_access_logs_object_expiration_days" {
+ description = "Amount of days to keep the objects stored in the access logs bucket for AFT backend buckets"
+ type = number
+ default = 365
+ validation {
+ condition = var.aft_backend_bucket_access_logs_object_expiration_days > 0
+ error_message = "aft_backend_bucket_access_logs_object_expiration_days must be an integer greater than 0."
+ }
+}
+
variable "maximum_concurrent_customizations" {
description = "Maximum number of customizations/pipelines to run at once"
type = number
@@ -280,7 +290,7 @@ variable "account_provisioning_customizations_repo_branch" {
variable "terraform_version" {
description = "Terraform version being used for AFT"
type = string
- default = "1.5.7"
+ default = "1.6.0"
validation {
condition = can(regex("\\bv?\\d+(\\.\\d+)+[\\-\\w]*\\b", var.terraform_version))
error_message = "Invalid value for var: terraform_version."