payments-dashboard/terraform/s3.tf
Adam Moussa 0e3e95c240
Some checks are pending
Deploy / Deploy to prod (push) Waiting to run
feat(infra): migrate payments-dashboard to HCP Terraform (PLAT-79) (#109)
* feat(infra): migrate payments-dashboard to HCP Terraform (PLAT-79)

Replace the mgmt SAM stack with a prod-only HCP workspace using the afterhours stub-plus-zip-CD seam so GitHub Actions owns function code and Terraform owns infrastructure.

* fix(infra): pin secret and CMK ARNs for bootstrap-plan

hcptf-bootstrap-plan cannot ssm:GetParameter or DescribeSecret, so the first plan must not data-source those values.

* fix(infra): add EIP describe and DynamoDB CMK grants for first apply

Scoped apply missed ec2:DescribeAddressesAttribute and kms Encrypt/Decrypt/GenerateDataKey on the table CMK.
2026-09-16 18:29:01 +00:00

275 lines
5.8 KiB
HCL

# Lambda artifacts bucket. Terraform ships only the bootstrap stub.
# .github/workflows/deploy.yaml uploads functions/<name>/<sha>.zip and calls
# update-function-code. Functions ignore code attributes afterwards.
resource "aws_s3_bucket" "artifacts" {
bucket = local.artifacts_bucket_name
tags = {
Purpose = "Lambda deployment packages for payments-dashboard"
}
}
resource "aws_s3_bucket_public_access_block" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_ownership_controls" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
rule {
object_ownership = "BucketOwnerEnforced"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_versioning" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_lifecycle_configuration" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
rule {
id = "expire-noncurrent-packages"
status = "Enabled"
filter {}
noncurrent_version_expiration {
noncurrent_days = 180
}
}
rule {
id = "abort-incomplete-multipart"
status = "Enabled"
filter {}
abort_incomplete_multipart_upload {
days_after_initiation = 7
}
}
depends_on = [aws_s3_bucket_versioning.artifacts]
}
data "aws_iam_policy_document" "artifacts" {
statement {
sid = "DenyInsecureTransport"
effect = "Deny"
principals {
type = "*"
identifiers = ["*"]
}
actions = ["s3:*"]
resources = [
aws_s3_bucket.artifacts.arn,
"${aws_s3_bucket.artifacts.arn}/*",
]
condition {
test = "Bool"
variable = "aws:SecureTransport"
values = ["false"]
}
}
}
resource "aws_s3_bucket_policy" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
policy = data.aws_iam_policy_document.artifacts.json
depends_on = [aws_s3_bucket_public_access_block.artifacts]
}
data "archive_file" "bootstrap_stub" {
type = "zip"
source_dir = "${path.module}/bootstrap/stub"
output_path = "${path.module}/build/packages/bootstrap-stub.zip"
}
resource "aws_s3_object" "bootstrap_stub" {
bucket = aws_s3_bucket.artifacts.id
key = "functions/bootstrap-stub.zip"
content_base64 = filebase64(data.archive_file.bootstrap_stub.output_path)
source_hash = data.archive_file.bootstrap_stub.output_base64sha256
}
resource "aws_s3_bucket" "csv" {
bucket = local.csv_bucket_name
tags = {
Purpose = "Stampli payment CSV drop folder"
}
}
resource "aws_s3_bucket_public_access_block" "csv" {
bucket = aws_s3_bucket.csv.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_ownership_controls" "csv" {
bucket = aws_s3_bucket.csv.id
rule {
object_ownership = "BucketOwnerEnforced"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "csv" {
bucket = aws_s3_bucket.csv.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
data "aws_iam_policy_document" "csv" {
statement {
sid = "DenyInsecureTransport"
effect = "Deny"
principals {
type = "*"
identifiers = ["*"]
}
actions = ["s3:*"]
resources = [aws_s3_bucket.csv.arn, "${aws_s3_bucket.csv.arn}/*"]
condition {
test = "Bool"
variable = "aws:SecureTransport"
values = ["false"]
}
}
}
resource "aws_s3_bucket_policy" "csv" {
bucket = aws_s3_bucket.csv.id
policy = data.aws_iam_policy_document.csv.json
depends_on = [aws_s3_bucket_public_access_block.csv]
}
resource "aws_s3_bucket" "boa_raw" {
bucket = local.boa_raw_bucket_name
tags = {
Purpose = "BoA reporting API raw archive"
}
lifecycle {
prevent_destroy = true
}
}
resource "aws_s3_bucket_public_access_block" "boa_raw" {
bucket = aws_s3_bucket.boa_raw.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_ownership_controls" "boa_raw" {
bucket = aws_s3_bucket.boa_raw.id
rule {
object_ownership = "BucketOwnerEnforced"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "boa_raw" {
bucket = aws_s3_bucket.boa_raw.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_lifecycle_configuration" "boa_raw" {
bucket = aws_s3_bucket.boa_raw.id
rule {
id = "expire-raw-responses"
status = "Enabled"
filter {}
expiration {
days = 730
}
}
}
data "aws_iam_policy_document" "boa_raw" {
statement {
sid = "DenyInsecureTransport"
effect = "Deny"
principals {
type = "*"
identifiers = ["*"]
}
actions = ["s3:*"]
resources = [aws_s3_bucket.boa_raw.arn, "${aws_s3_bucket.boa_raw.arn}/*"]
condition {
test = "Bool"
variable = "aws:SecureTransport"
values = ["false"]
}
}
}
resource "aws_s3_bucket_policy" "boa_raw" {
bucket = aws_s3_bucket.boa_raw.id
policy = data.aws_iam_policy_document.boa_raw.json
depends_on = [aws_s3_bucket_public_access_block.boa_raw]
}
resource "aws_s3_bucket_notification" "csv" {
bucket = aws_s3_bucket.csv.id
lambda_function {
lambda_function_arn = aws_lambda_function.this["process_csv"].arn
events = ["s3:ObjectCreated:*"]
filter_suffix = ".csv"
}
depends_on = [aws_lambda_permission.s3_csv]
}