Skip to content

Commit

Permalink
Inline Terraform State Policies (#9)
Browse files Browse the repository at this point in the history
* feat: inlining the terraform plan, apply and remote state policies to the role

* fix: there was a typo on the comment

* feat: allowing the user to specific either the permission name or arn
  • Loading branch information
gambol99 authored Apr 24, 2024
1 parent c8f69bc commit ed32fd3
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 52 deletions.
2 changes: 1 addition & 1 deletion examples/role/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module "common_provider_example" {
// Relative path to the repository for the given provider
repository = "appvia/something"
// Set the permission boundary for both the read-only and read-write role
permission_boundary = "AdministratorAccess"
permission_boundary_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
// List of policy ARNs to attach to the read-only role
read_only_policy_arns = [
"arn:aws:iam::aws:policy/ReadOnlyAccess",
Expand Down
14 changes: 14 additions & 0 deletions modules/role/checks.tf
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,17 @@ check "policy_config" {
error_message = "At least one of 'read_write_policy_arns' or 'read_write_inline_policies' must be specified"
}
}

check "permission_boundary" {
# Either permission_boundary or permission_boundary_arn must be specified
assert {
condition = !(var.permission_boundary == null && var.permission_boundary_arn == null)
error_message = "Either 'permission_boundary' or 'permission_boundary_arn' must be specified"
}

# Both permission_boundary and permission_boundary_arn cannot be specified
assert {
condition = !(var.permission_boundary != null && var.permission_boundary_arn != null)
error_message = "Only one of 'permission_boundary' or 'permission_boundary_arn' may be specified"
}
}
10 changes: 7 additions & 3 deletions modules/role/locals.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@

locals {
# The current account ID
account_id = data.aws_caller_identity.current.account_id
## The common OIDC providers to use
common_providers = {
github = {
url = "https://token.actions.githubusercontent.com"
Expand All @@ -25,8 +28,10 @@ locals {
subject_tag_mapping = "project_path:{repo}:ref_type:{type}:ref:{ref}"
}
}
# The devired permission_boundary arn
permission_boundary_by_name = var.permission_boundary != null ? format("arn:aws:iam::%s:policy/%s", local.account_id, var.permission_boundary) : null
# The full ARN of the permission boundary to attach to the role
permission_boundary_arn = format("arn:aws:iam::%s:policy/%s", data.aws_caller_identity.current.account_id, var.permission_boundary)
permission_boundary_arn = var.permission_boundary_arn == null ? local.permission_boundary_by_name : var.permission_boundary_arn
}

locals {
Expand All @@ -40,7 +45,6 @@ locals {

# Keys to search for in the subject mapping template
template_keys_regex = "{(repo|type|ref)}"

account_id = data.aws_caller_identity.current.account_id
# The prefix for the terraform state key in the S3 bucket
tf_state_prefix = format("%s-%s", local.account_id, data.aws_region.current.name)
}
21 changes: 18 additions & 3 deletions modules/role/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ locals {
state_reader_role_name = format("%s-sr", var.name)
}

## Craft a assume role policy document
## Craft a trust policy for the readonly role
data "aws_iam_policy_document" "ro" {
statement {
actions = [
Expand Down Expand Up @@ -54,6 +54,11 @@ resource "aws_iam_role" "ro" {
permissions_boundary = local.permission_boundary_arn
tags = merge(var.tags, { Name = local.readonly_role_name })

inline_policy {
name = "tfstate_plan"
policy = data.aws_iam_policy_document.tfstate_plan.json
}

dynamic "inline_policy" {
for_each = var.read_only_inline_policies

Expand All @@ -72,7 +77,7 @@ resource "aws_iam_role_policy_attachment" "ro" {
role = aws_iam_role.ro.name
}

## Craft the read write policy document
## Craft the trust policy for the read write role
data "aws_iam_policy_document" "rw" {
statement {
actions = [
Expand Down Expand Up @@ -128,6 +133,11 @@ resource "aws_iam_role" "rw" {
permissions_boundary = local.permission_boundary_arn
tags = merge(var.tags, { Name = local.readwrite_role_name })

inline_policy {
name = "tfstate_apply"
policy = data.aws_iam_policy_document.tfstate_apply.json
}

dynamic "inline_policy" {
for_each = var.read_write_inline_policies

Expand All @@ -146,7 +156,7 @@ resource "aws_iam_role_policy_attachment" "rw" {
role = aws_iam_role.rw.name
}

## Craft the state reader policy
## Craft the trust policy for the state reader role
data "aws_iam_policy_document" "sr" {
statement {
actions = [
Expand Down Expand Up @@ -189,4 +199,9 @@ resource "aws_iam_role" "sr" {
name = local.state_reader_role_name
path = var.role_path
tags = merge(var.tags, { Name = local.state_reader_role_name })

inline_policy {
name = "tfstate_remote"
policy = data.aws_iam_policy_document.tfstate_remote.json
}
}
56 changes: 11 additions & 45 deletions modules/role/policies.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Base policy shared by all roles

## Craft a IAM policy for all terraform roles
data "aws_iam_policy_document" "base" {
statement {
actions = [
Expand All @@ -24,7 +25,7 @@ data "aws_iam_policy_document" "base" {
}
}

// DynamoDB policy shared by terraform roles
## Craft a IAM policy for access to the DynamoDB table
data "aws_iam_policy_document" "dynamo" {
statement {
actions = [
Expand All @@ -40,27 +41,7 @@ data "aws_iam_policy_document" "dynamo" {
}
}

// Policy for terraform plan role
data "aws_iam_policy_document" "tfstate_plan" {
source_policy_documents = [
data.aws_iam_policy_document.base.json,
data.aws_iam_policy_document.dynamo.json,
]
}

resource "aws_iam_policy" "tfstate_plan" {
name = format("%s-tfstate-plan", var.name)
description = "Policy allowing read access to the Terraform state bucket and DynamoDB table for the ${var.name} role"
policy = data.aws_iam_policy_document.tfstate_plan.json
tags = var.tags
}

resource "aws_iam_role_policy_attachment" "tfstate_plan" {
policy_arn = aws_iam_policy.tfstate_plan.arn
role = aws_iam_role.ro.name
}

// Policy for terraform apply role
## Craft an IAM policy with the necessary permissions for terraform apply
data "aws_iam_policy_document" "tfstate_apply" {
source_policy_documents = [
data.aws_iam_policy_document.base.json,
Expand All @@ -80,33 +61,18 @@ data "aws_iam_policy_document" "tfstate_apply" {
}
}

resource "aws_iam_policy" "tfstate_apply" {
name = format("%s-tfstate-apply", var.name)
description = "Policy allowing write access to the Terraform state bucket and DynamoDB table for the ${var.name} role"
policy = data.aws_iam_policy_document.tfstate_apply.json
tags = var.tags
}

resource "aws_iam_role_policy_attachment" "tfstate_apply" {
policy_arn = aws_iam_policy.tfstate_apply.arn
role = aws_iam_role.rw.name
## Craft an IAM policy with the necessary permissions for terraform plan
data "aws_iam_policy_document" "tfstate_plan" {
source_policy_documents = [
data.aws_iam_policy_document.base.json,
data.aws_iam_policy_document.dynamo.json,
]
}

// Policy for terraform remote state reading
## Craft an IAM policy with the necessary permissions for terraform remote state
data "aws_iam_policy_document" "tfstate_remote" {
source_policy_documents = [
data.aws_iam_policy_document.base.json,
]
}

resource "aws_iam_policy" "tfstate_remote" {
name = format("%s-tfstate-remote", var.name)
description = "Policy allowing read access to the Terraform state bucket for the ${var.name} role"
policy = data.aws_iam_policy_document.tfstate_remote.json
tags = var.tags
}

resource "aws_iam_role_policy_attachment" "tfstate_remote" {
policy_arn = aws_iam_policy.tfstate_remote.arn
role = aws_iam_role.sr.name
}
7 changes: 7 additions & 0 deletions modules/role/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ variable "force_detach_policies" {
variable "permission_boundary" {
type = string
description = "The name of the policy that is used to set the permissions boundary for the IAM role"
default = null
}

variable "permission_boundary_arn" {
type = string
description = "The full ARN of the permission boundary to attach to the role"
default = null
}

variable "tags" {
Expand Down

0 comments on commit ed32fd3

Please sign in to comment.