IaC Bazaar
AWSLive-tested

IAM Roles, Policies & OIDC Trust

Least-privilege IAM roles, managed policies, and GitHub/EKS OIDC federation in one composable module.

terraformAWS#aws
aws-iam-rolesterraform v1.7

Verification

Live-tested

Really deployed, verified, idempotent and destroyed in a cloud sandbox.

Conformance

  • Static validation (fmt · validate · tflint)
  • Security scan clean (Checkov)
  • Plan tests (mocked: validation rules · outputs)

Provenance

  • SHA-256 checksum
  • Signature (pending)

Functional

  • Live-tested — applied, verified, destroyed

Last verified 2026-06-11 · how we verify

Documentation

aws-iam-roles

Least-privilege IAM roles, managed policies, and GitHub/EKS OIDC federation in one composable module. Works with Terraform and OpenTofu (>= 1.6), AWS provider >= 6.0, < 7.0. Roles, customer-managed policies, OIDC identity providers, and instance profiles are all driven from maps, so a single module call provisions an account's whole role layout — and the policy-document plumbing (trust statements, aud/sub conditions, attachment fan-out) is already correct.

Secure defaults:

  • Trust policies are composed per-role from explicit sources only (trusted_services, trusted_role_arns with optional external_id, GitHub OIDC subjects, custom OIDC issuers) — no wildcard principals
  • GitHub OIDC trust pins the aud claim and uses repo:-prefixed sub patterns; built on v6-era standalone attachment resources (the removed inline_policy / managed_policy_arns role arguments are not used)
  • max_session_duration defaults to 1 hour; force_detach_policies on; permissions-boundary support per role

Usage

module "iam" {
  source = "./aws-iam-roles"

  create_github_oidc_provider = true

  policies = {
    artifact-read = {
      policy = jsonencode({
        Version = "2012-10-17"
        Statement = [{
          Effect   = "Allow"
          Action   = ["s3:GetObject", "s3:ListBucket"]
          Resource = ["arn:aws:s3:::my-artifacts", "arn:aws:s3:::my-artifacts/*"]
        }]
      })
    }
  }

  roles = {
    github-deploy = {
      github_repositories = ["my-org/my-app:ref:refs/heads/main"]
      policy_keys         = ["artifact-read"]
    }
    ec2-app = {
      trusted_services        = ["ec2.amazonaws.com"]
      managed_policy_arns     = ["arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"]
      create_instance_profile = true
    }
  }

  tags = { Environment = "prod" }
}

Inputs

NameTypeDefaultDescription
rolesmap(object){}Roles keyed by logical name. Trust sources: trusted_services, trusted_role_arns (+ external_id), github_repositories (sub patterns without repo: prefix), oidc (list of {provider_arn, provider_url, audiences, subjects}). Permissions: managed_policy_arns, policy_keys, inline_policies. Extras: name, description, path, max_session_duration, permissions_boundary_arn, force_detach_policies, create_instance_profile
policiesmap(object){}Customer-managed policies keyed by logical name: {name, description, path, policy} (JSON document)
create_github_oidc_providerboolfalseCreate the token.actions.githubusercontent.com provider (one per account)
github_oidc_provider_arnstringnullARN of a pre-existing GitHub OIDC provider (alternative to creating one)
oidc_providersmap(object){}Additional OIDC providers to create: {url, client_id_list, thumbprint_list}
tagsmap(string){}Tags applied to all resources

Outputs

NameDescription
role_arns / role_namesMap of role key => ARN / name
instance_profile_arns / instance_profile_namesMap of role key => instance profile ARN / name
policy_arnsMap of policy key => customer-managed policy ARN
github_oidc_provider_arnGitHub OIDC provider ARN (created or passed in)
oidc_provider_arnsMap of oidc_providers key => provider ARN

Requirements

  • Terraform or OpenTofu >= 1.6
  • hashicorp/aws >= 6.0, < 7.0

Notes

  • github_repositories trust requires either create_github_oidc_provider = true or github_oidc_provider_arn (enforced by a plan-time precondition).
  • Roles referencing module-created oidc_providers from the same call should pass the issuer via oidc with the provider's ARN from a prior apply, or split providers and roles across two module calls — IAM allows only one provider per issuer URL per account.

Verification

Static-validated (fmt, validate, tflint). Live apply/destroy testing pending cloud sandbox availability — see catalog status.

License

Commercial — IaC Bazaar EULA. © IaC Bazaar. Original work (not derived from a third-party module).