Skip to content

Commit 532c216

Browse files
committed
chore: security best-practices + minor improvements
1 parent 2c0ec7b commit 532c216

File tree

7 files changed

+39
-26
lines changed

7 files changed

+39
-26
lines changed

data.tf

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ data "aws_caller_identity" "current" {}
33

44
# VPC lookup by name (when vpc_name is provided)
55
data "aws_vpc" "selected" {
6-
count = length(var.vpc_name) > 0 ? 1 : 0
6+
count = var.vpc_name != null ? 1 : 0
77

88
filter {
99
name = "tag:Name"
@@ -19,6 +19,10 @@ data "aws_subnet" "selected" {
1919
name = "tag:Name"
2020
values = [each.value]
2121
}
22+
filter {
23+
name = "vpc-id"
24+
values = [local.vpc_id]
25+
}
2226
}
2327

2428
# Most recent Amazon Linux 2023 AMI
@@ -79,11 +83,7 @@ data "aws_iam_policy_document" "default" {
7983
}
8084
}
8185

82-
# S3 bucket for session logging (when using existing bucket)
83-
data "aws_s3_bucket" "logs_bucket" {
84-
count = var.session_logging_enabled ? 1 : 0
85-
bucket = try(coalesce(var.session_logging_bucket_name, module.logs_bucket.bucket_id), "")
86-
}
86+
8787

8888
# https://docs.aws.amazon.com/systems-manager/latest/userguide/getting-started-create-iam-instance-profile.html#create-iam-instance-profile-ssn-logging
8989
data "aws_iam_policy_document" "session_logging" {
@@ -95,19 +95,27 @@ data "aws_iam_policy_document" "session_logging" {
9595
actions = [
9696
"s3:PutObject"
9797
]
98-
resources = ["${join("", data.aws_s3_bucket.logs_bucket.*.arn)}/*"]
98+
resources = ["${local.session_logging_bucket_arn}/*"]
9999
}
100100

101101
statement {
102102
sid = "SSMAgentSessionAllowCloudWatchLogging"
103103
effect = "Allow"
104104
actions = [
105105
"logs:CreateLogStream",
106-
"logs:PutLogEvents",
106+
"logs:PutLogEvents"
107+
]
108+
resources = ["${local.session_logging_log_group_arn}:*"]
109+
}
110+
111+
statement {
112+
sid = "SSMAgentSessionAllowCloudWatchDescribe"
113+
effect = "Allow"
114+
actions = [
107115
"logs:DescribeLogGroups",
108116
"logs:DescribeLogStreams"
109117
]
110-
resources = ["*"]
118+
resources = [local.session_logging_log_group_arn]
111119
}
112120

113121
statement {
@@ -116,7 +124,7 @@ data "aws_iam_policy_document" "session_logging" {
116124
actions = [
117125
"s3:GetEncryptionConfiguration"
118126
]
119-
resources = ["*"]
127+
resources = [local.session_logging_bucket_arn]
120128
}
121129

122130
statement {
@@ -125,6 +133,6 @@ data "aws_iam_policy_document" "session_logging" {
125133
actions = [
126134
"kms:GenerateDataKey"
127135
]
128-
resources = ["*"]
136+
resources = [local.session_logging_kms_key_arn]
129137
}
130138
}

examples/complete/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module "vpc" {
1111
name = var.name
1212

1313
ipv4_primary_cidr_block = "10.0.0.0/16"
14-
assign_generated_ipv6_cidr_block = true
14+
assign_generated_ipv6_cidr_block = var.ipv6_enabled
1515
}
1616

1717
module "subnets" {

examples/with-names/variables.tf

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,3 @@ variable "availability_zones" {
2121
description = "List of availability zones"
2222
default = ["us-east-1a", "us-east-1b"]
2323
}
24-
25-
variable "ipv6_enabled" {
26-
type = bool
27-
description = "Enable IPv6"
28-
default = false
29-
}

main.tf

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ locals {
1515
)
1616

1717
# VPC and Subnet ID resolution - names take precedence over IDs
18-
vpc_id = length(var.vpc_name) > 0 ? one(data.aws_vpc.selected[*].id) : var.vpc_id
18+
vpc_id = var.vpc_name != null ? one(data.aws_vpc.selected[*].id) : var.vpc_id
1919
subnet_ids = length(var.subnet_names) > 0 ? values(data.aws_subnet.selected)[*].id : var.subnet_ids
2020

2121
}
@@ -35,7 +35,7 @@ resource "null_resource" "validate_instance_type" {
3535
resource "terraform_data" "vpc_subnet_validation" {
3636
lifecycle {
3737
precondition {
38-
condition = length(var.vpc_name) > 0 || length(var.vpc_id) > 0
38+
condition = var.vpc_name != null || var.vpc_id != null
3939
error_message = "Either vpc_name or vpc_id must be provided."
4040
}
4141

@@ -49,7 +49,7 @@ resource "terraform_data" "vpc_subnet_validation" {
4949
# Warning checks for VPC and subnet configuration (non-blocking)
5050
check "vpc_subnet_warnings" {
5151
assert {
52-
condition = !(length(var.vpc_name) > 0 && length(var.vpc_id) > 0)
52+
condition = !(var.vpc_name != null && var.vpc_id != null)
5353
error_message = "Both vpc_name and vpc_id are provided. When vpc_name is specified, vpc_id will be ignored."
5454
}
5555

@@ -81,6 +81,8 @@ locals {
8181

8282
session_logging_bucket_name = try(coalesce(var.session_logging_bucket_name, module.logs_label.id), "")
8383
session_logging_kms_key_arn = try(coalesce(var.session_logging_kms_key_arn, module.kms_key.key_arn), "")
84+
session_logging_bucket_arn = var.session_logging_enabled ? "arn:aws:s3:::${local.session_logging_bucket_name}" : ""
85+
session_logging_log_group_arn = var.session_logging_enabled ? "arn:aws:logs:${local.region}:${local.account_id}:log-group:${module.logs_label.id}" : ""
8486

8587
logs_bucket_enabled = var.session_logging_enabled && length(var.session_logging_bucket_name) == 0
8688
}

outputs.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ output "role_id" {
2424
}
2525

2626
output "session_logging_bucket_id" {
27-
value = local.logs_bucket_enabled ? join("", data.aws_s3_bucket.logs_bucket.*.id) : ""
27+
value = var.session_logging_enabled ? local.session_logging_bucket_name : ""
2828
description = "The ID of the SSM Agent Session Logging S3 Bucket."
2929
}
3030

3131
output "session_logging_bucket_arn" {
32-
value = local.logs_bucket_enabled ? join("", data.aws_s3_bucket.logs_bucket.*.arn) : ""
32+
value = local.session_logging_bucket_arn
3333
description = "The ARN of the SSM Agent Session Logging S3 Bucket."
3434
}

tests/vpc-subnet-locals-only.tftest.hcl renamed to tests/vpc-subnet-locals.tftest.hcl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ mock_provider "aws" {
5555
}
5656

5757
# Test precedence - names over IDs
58-
run "test_precedence_local" {
58+
run "verify_vpc_subnet_name_precedence" {
5959
command = plan
6060

6161
variables {

variables.tf

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
variable "vpc_id" {
22
type = string
33
description = "The ID of the VPC which the EC2 Instance will run in."
4-
default = ""
4+
default = null
55
}
66

77
variable "subnet_ids" {
@@ -13,7 +13,7 @@ variable "subnet_ids" {
1313
variable "vpc_name" {
1414
type = string
1515
description = "The name of the VPC which the EC2 Instance will run in. If provided, vpc_id will be ignored."
16-
default = ""
16+
default = null
1717
}
1818

1919
variable "subnet_names" {
@@ -172,6 +172,15 @@ variable "session_logging_bucket_name" {
172172
default = ""
173173
type = string
174174
description = "The name of the S3 Bucket to ship session logs to. This will remove creation of an independent session logging bucket. This is only relevant if the session_logging_enabled variable is `true`."
175+
176+
validation {
177+
condition = var.session_logging_bucket_name == "" || (
178+
can(regex("^[a-z0-9][a-z0-9.-]*[a-z0-9]$", var.session_logging_bucket_name)) &&
179+
length(var.session_logging_bucket_name) >= 3 &&
180+
length(var.session_logging_bucket_name) <= 63
181+
)
182+
error_message = "S3 bucket name must follow AWS naming conventions: 3-63 characters, lowercase letters, numbers, dots, and hyphens only, must start and end with letter or number."
183+
}
175184
}
176185

177186
variable "region" {

0 commit comments

Comments
 (0)