Skip to content

storage module

Aleksej Komnenovic edited this page Jun 30, 2026 · 3 revisions

Storage Module

S3 bucket for rule storage, IAM policies, and optional secrets-based auth.

Files

File Purpose
main.tf S3 bucket, encryption, versioning, lifecycle, cross-account policy
iam.tf Read-write and read-only IAM policies
secrets.tf IAM user + Secrets Manager for secrets auth mode
variables.tf Input variables
outputs.tf Module outputs

S3 Bucket Configuration (main.tf)

Bucket naming: {name_prefix}-rules-{random_hex} (8-char suffix for uniqueness)

Resource Purpose
random_id.bucket_suffix 4 bytes → 8 hex chars
aws_s3_bucket The rules storage bucket
aws_s3_bucket_versioning Optional (default: enabled)
aws_s3_bucket_server_side_encryption_configuration AES256 + bucket key
aws_s3_bucket_public_access_block All 4 block options enabled
aws_s3_bucket_lifecycle_configuration Aborts incomplete multipart after 7 days

Security Features

  • Encryption: AES256 server-side encryption with bucket key enabled
  • Public access: Fully blocked (all 4 settings)
  • Transport security: A bucket policy that denies non-TLS requests (aws:SecureTransport = false) is attached only when cross_account_write_principals is non-empty. With the default empty list no bucket policy is created, so there is no SecureTransport deny. Callers should still use HTTPS endpoints regardless.
  • Versioning: Enabled by default for rule file history

Authentication Modes

Two modes controlled by storage.auth:

IAM Mode (default: auth = "iam")

No IAM user created. ECS task roles get S3 access via IAM Architecture:

  • BRMS task roles3_access policy (read/write)
  • Agent task roles3_read_only policy (read only)

Secrets Mode (auth = "secrets")

Creates an IAM user with access keys stored in Secrets Manager:

Resource Purpose
aws_iam_user.s3_access Dedicated IAM user for S3
aws_iam_access_key.s3_access Access key pair
aws_secretsmanager_secret.s3_credentials Secret container
aws_secretsmanager_secret_version JSON: access_key_id, secret_access_key, bucket_name, region

See Secrets Management for how secrets are consumed.

IAM Policies (iam.tf)

Two policies created for different access levels:

Read-Write Policy (s3_access)

Used by BRMS, full access to manage rule files. Defined as an aws_iam_policy resource with an inline jsonencode policy document (not a separate data source). It references local.bucket_arn, which resolves to the created bucket's ARN or var.storage.existing_bucket_arn when using an existing bucket:

resource "aws_iam_policy" "s3_access" {
  name        = "${var.name_prefix}-s3-access"
  description = "IAM policy for S3 bucket access for GoRules"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "ListBucket"
        Effect = "Allow"
        Action = [
          "s3:ListBucket",
          "s3:GetBucketLocation"
        ]
        Resource = local.bucket_arn
      },
      {
        Sid    = "ObjectAccess"
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:PutObject",
          "s3:DeleteObject",
          "s3:GetObjectVersion",
          "s3:DeleteObjectVersion"
        ]
        Resource = "${local.bucket_arn}/*"
      }
    ]
  })

  tags = local.common_tags
}

Read-Only Policy (s3_read_only)

Used by Agent, can only read rules, never modify them. Defined as an aws_iam_policy resource with an inline jsonencode policy document. Like s3_access, it references local.bucket_arn, so it works for both a created bucket and an existing one:

resource "aws_iam_policy" "s3_read_only" {
  name        = "${var.name_prefix}-s3-read-only"
  description = "IAM policy for read-only S3 bucket access for GoRules Agent"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "ListBucket"
        Effect = "Allow"
        Action = [
          "s3:ListBucket",
          "s3:GetBucketLocation"
        ]
        Resource = local.bucket_arn
      },
      {
        Sid    = "ReadObjects"
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:GetObjectVersion"
        ]
        Resource = "${local.bucket_arn}/*"
      }
    ]
  })

  tags = local.common_tags
}

Cross-Account Access

For multi-environment deployments, the bucket can grant access to other AWS accounts:

storage = {
  cross_account_write_principals = [
    "123456789012",  # Staging account ID
    "987654321098"   # Production account ID
  ]
}

This creates a bucket policy granting the specified principals bucket-level actions (s3:ListBucket, s3:GetBucketLocation) and object-level actions (s3:GetObject, s3:GetObjectVersion, s3:PutObject, s3:DeleteObject, s3:DeleteObjectVersion). Every grant is conditioned on aws:SecureTransport = true, so cross-account access is allowed only over TLS.

How Other Modules Use This

Key Variables

storage = {
  create_bucket                  = true
  existing_bucket_arn            = null
  existing_bucket_name           = null
  auth                           = "iam"     # or "secrets"
  versioning                     = true
  cross_account_write_principals = []
}

Set storage = null to disable entirely.

Clone this wiki locally