Skip to content

Commit

Permalink
feat: Add support for inline policy creation (#479)
Browse files Browse the repository at this point in the history
Co-authored-by: Jan Schumann <[email protected]>
  • Loading branch information
fatmcgav and janschumann authored Jul 5, 2024
1 parent de95e21 commit e13cb1e
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 9 deletions.
1 change: 1 addition & 0 deletions examples/iam-assumable-role-with-oidc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ No providers.
| Name | Source | Version |
|------|--------|---------|
| <a name="module_iam_assumable_role_admin"></a> [iam\_assumable\_role\_admin](#module\_iam\_assumable\_role\_admin) | ../../modules/iam-assumable-role-with-oidc | n/a |
| <a name="module_iam_assumable_role_inline_policy"></a> [iam\_assumable\_role\_inline\_policy](#module\_iam\_assumable\_role\_inline\_policy) | ../../modules/iam-assumable-role-with-oidc | n/a |
| <a name="module_iam_assumable_role_self_assume"></a> [iam\_assumable\_role\_self\_assume](#module\_iam\_assumable\_role\_self\_assume) | ../../modules/iam-assumable-role-with-oidc | n/a |

## Resources
Expand Down
57 changes: 48 additions & 9 deletions examples/iam-assumable-role-with-oidc/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@ module "iam_assumable_role_admin" {
Role = "role-with-oidc"
}

provider_url = "oidc.eks.eu-west-1.amazonaws.com/id/BA9E170D464AF7B92084EF72A69B9DC8"
provider_urls = ["oidc.eks.eu-west-1.amazonaws.com/id/AA9E170D464AF7B92084EF72A69B9DC8"]
provider_url = "oidc.circleci.com/org/<CIRCLECI_ORG_UUID>"

oidc_fully_qualified_audiences = ["<CIRCLECI_ORG_UUID>"]

role_policy_arns = [
"arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy",
"arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser",
]

oidc_fully_qualified_subjects = ["system:serviceaccount:default:sa1", "system:serviceaccount:default:sa2"]
}

#####################################
Expand All @@ -41,12 +40,52 @@ module "iam_assumable_role_self_assume" {
Role = "role-with-oidc-self-assume"
}

provider_url = "oidc.eks.eu-west-1.amazonaws.com/id/BA9E170D464AF7B92084EF72A69B9DC8"
provider_urls = ["oidc.eks.eu-west-1.amazonaws.com/id/AA9E170D464AF7B92084EF72A69B9DC8"]
provider_url = "oidc.circleci.com/org/<CIRCLECI_ORG_UUID>"

oidc_fully_qualified_audiences = ["<CIRCLECI_ORG_UUID>"]

role_policy_arns = [
"arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy",
"arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser",
]
}

oidc_fully_qualified_subjects = ["system:serviceaccount:default:sa1", "system:serviceaccount:default:sa2"]
#####################################
# IAM assumable role with inline policy
#####################################
module "iam_assumable_role_inline_policy" {
source = "../../modules/iam-assumable-role-with-oidc"

create_role = true

role_name = "role-with-oidc-inline-policy"

tags = {
Role = "role-with-oidc-inline-policy"
}

provider_url = "oidc.circleci.com/org/<CIRCLECI_ORG_UUID>"

oidc_fully_qualified_audiences = ["<CIRCLECI_ORG_UUID>"]

inline_policy_statements = [
{
sid = "AllowECRPushPull"
actions = [
"ecr:GetAuthorizationToken",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability",
"ecr:DescribeImages",
"ecr:DescribeRepositories",
"ecr:GetDownloadUrlForLayer",
"ecr:ListImages",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
]
effect = "Allow"
resources = ["*"]
}
]
}
1 change: 1 addition & 0 deletions examples/iam-assumable-role/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Run `terraform destroy` when you don't need these resources.
| <a name="module_iam_assumable_role_admin"></a> [iam\_assumable\_role\_admin](#module\_iam\_assumable\_role\_admin) | ../../modules/iam-assumable-role | n/a |
| <a name="module_iam_assumable_role_custom"></a> [iam\_assumable\_role\_custom](#module\_iam\_assumable\_role\_custom) | ../../modules/iam-assumable-role | n/a |
| <a name="module_iam_assumable_role_custom_trust_policy"></a> [iam\_assumable\_role\_custom\_trust\_policy](#module\_iam\_assumable\_role\_custom\_trust\_policy) | ../../modules/iam-assumable-role | n/a |
| <a name="module_iam_assumable_role_inline_policy"></a> [iam\_assumable\_role\_inline\_policy](#module\_iam\_assumable\_role\_inline\_policy) | ../../modules/iam-assumable-role | n/a |
| <a name="module_iam_assumable_role_sts"></a> [iam\_assumable\_role\_sts](#module\_iam\_assumable\_role\_sts) | ../../modules/iam-assumable-role | n/a |
| <a name="module_iam_policy"></a> [iam\_policy](#module\_iam\_policy) | ../../modules/iam-policy | n/a |

Expand Down
44 changes: 44 additions & 0 deletions examples/iam-assumable-role/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,50 @@ module "iam_assumable_role_custom" {
# number_of_custom_role_policy_arns = 3
}

##########################################
# IAM assumable role with inline policy
##########################################
module "iam_assumable_role_inline_policy" {
source = "../../modules/iam-assumable-role"

trusted_role_arns = [
"arn:aws:iam::307990089504:root",
]

trusted_role_services = [
"codedeploy.amazonaws.com"
]

create_role = true

role_name_prefix = "custom-"
role_requires_mfa = false

role_sts_externalid = "some-id-goes-here"

inline_policy_statements = [
{
sid = "AllowECRPushPull"
actions = [
"ecr:GetAuthorizationToken",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability",
"ecr:DescribeImages",
"ecr:DescribeRepositories",
"ecr:GetDownloadUrlForLayer",
"ecr:ListImages",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
]
effect = "Allow"
resources = ["*"]
}
]
}

####################################################
# IAM assumable role with multiple sts external ids
####################################################
Expand Down
3 changes: 3 additions & 0 deletions modules/iam-assumable-role-with-oidc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ No modules.
| Name | Type |
|------|------|
| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_iam_role_policy_attachment.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy_document.assume_role_with_oidc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |

## Inputs
Expand All @@ -42,6 +44,7 @@ No modules.
| <a name="input_aws_account_id"></a> [aws\_account\_id](#input\_aws\_account\_id) | The AWS account ID where the OIDC provider lives, leave empty to use the account for the AWS provider | `string` | `""` | no |
| <a name="input_create_role"></a> [create\_role](#input\_create\_role) | Whether to create a role | `bool` | `false` | no |
| <a name="input_force_detach_policies"></a> [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `false` | no |
| <a name="input_inline_policy_statements"></a> [inline\_policy\_statements](#input\_inline\_policy\_statements) | List of inline policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) to attach to IAM role as an inline policy | `any` | `[]` | no |
| <a name="input_max_session_duration"></a> [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `3600` | no |
| <a name="input_number_of_role_policy_arns"></a> [number\_of\_role\_policy\_arns](#input\_number\_of\_role\_policy\_arns) | Number of IAM policies to attach to IAM role | `number` | `null` | no |
| <a name="input_oidc_fully_qualified_audiences"></a> [oidc\_fully\_qualified\_audiences](#input\_oidc\_fully\_qualified\_audiences) | The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise. | `set(string)` | `[]` | no |
Expand Down
61 changes: 61 additions & 0 deletions modules/iam-assumable-role-with-oidc/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,64 @@ resource "aws_iam_role_policy_attachment" "custom" {
role = aws_iam_role.this[0].name
policy_arn = var.role_policy_arns[count.index]
}

###############################
# IAM Role Inline policy
###############################

locals {
create_iam_role_inline_policy = var.create_role && length(var.inline_policy_statements) > 0
}

data "aws_iam_policy_document" "inline" {
count = local.create_iam_role_inline_policy ? 1 : 0

dynamic "statement" {
for_each = var.inline_policy_statements

content {
sid = try(statement.value.sid, null)
actions = try(statement.value.actions, null)
not_actions = try(statement.value.not_actions, null)
effect = try(statement.value.effect, null)
resources = try(statement.value.resources, null)
not_resources = try(statement.value.not_resources, null)

dynamic "principals" {
for_each = try(statement.value.principals, [])

content {
type = principals.value.type
identifiers = principals.value.identifiers
}
}

dynamic "not_principals" {
for_each = try(statement.value.not_principals, [])

content {
type = not_principals.value.type
identifiers = not_principals.value.identifiers
}
}

dynamic "condition" {
for_each = try(statement.value.conditions, [])

content {
test = condition.value.test
values = condition.value.values
variable = condition.value.variable
}
}
}
}
}

resource "aws_iam_role_policy" "inline" {
count = local.create_iam_role_inline_policy ? 1 : 0

role = aws_iam_role.this[0].name
name_prefix = "${var.role_name}_inline_"
policy = data.aws_iam_policy_document.inline[0].json
}
6 changes: 6 additions & 0 deletions modules/iam-assumable-role-with-oidc/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ variable "number_of_role_policy_arns" {
default = null
}

variable "inline_policy_statements" {
description = "List of inline policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) to attach to IAM role as an inline policy"
type = any
default = []
}

variable "oidc_fully_qualified_subjects" {
description = "The fully qualified OIDC subjects to be added to the role policy"
type = set(string)
Expand Down
3 changes: 3 additions & 0 deletions modules/iam-assumable-role/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ No modules.
|------|------|
| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource |
| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_iam_role_policy_attachment.admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.poweruser](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.readonly](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.assume_role_with_mfa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |

## Inputs
Expand All @@ -52,6 +54,7 @@ No modules.
| <a name="input_custom_role_policy_arns"></a> [custom\_role\_policy\_arns](#input\_custom\_role\_policy\_arns) | List of ARNs of IAM policies to attach to IAM role | `list(string)` | `[]` | no |
| <a name="input_custom_role_trust_policy"></a> [custom\_role\_trust\_policy](#input\_custom\_role\_trust\_policy) | A custom role trust policy. (Only valid if create\_custom\_role\_trust\_policy = true) | `string` | `""` | no |
| <a name="input_force_detach_policies"></a> [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `false` | no |
| <a name="input_inline_policy_statements"></a> [inline\_policy\_statements](#input\_inline\_policy\_statements) | List of inline policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) to attach to IAM role as an inline policy | `any` | `[]` | no |
| <a name="input_max_session_duration"></a> [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `3600` | no |
| <a name="input_mfa_age"></a> [mfa\_age](#input\_mfa\_age) | Max age of valid MFA (in seconds) for roles which require MFA | `number` | `86400` | no |
| <a name="input_number_of_custom_role_policy_arns"></a> [number\_of\_custom\_role\_policy\_arns](#input\_number\_of\_custom\_role\_policy\_arns) | Number of IAM policies to attach to IAM role | `number` | `null` | no |
Expand Down
61 changes: 61 additions & 0 deletions modules/iam-assumable-role/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,64 @@ resource "aws_iam_instance_profile" "this" {

tags = var.tags
}

###############################
# IAM Role Inline policy
###############################

locals {
create_iam_role_inline_policy = var.create_role && length(var.inline_policy_statements) > 0
}

data "aws_iam_policy_document" "inline" {
count = local.create_iam_role_inline_policy ? 1 : 0

dynamic "statement" {
for_each = var.inline_policy_statements

content {
sid = try(statement.value.sid, null)
actions = try(statement.value.actions, null)
not_actions = try(statement.value.not_actions, null)
effect = try(statement.value.effect, null)
resources = try(statement.value.resources, null)
not_resources = try(statement.value.not_resources, null)

dynamic "principals" {
for_each = try(statement.value.principals, [])

content {
type = principals.value.type
identifiers = principals.value.identifiers
}
}

dynamic "not_principals" {
for_each = try(statement.value.not_principals, [])

content {
type = not_principals.value.type
identifiers = not_principals.value.identifiers
}
}

dynamic "condition" {
for_each = try(statement.value.conditions, [])

content {
test = condition.value.test
values = condition.value.values
variable = condition.value.variable
}
}
}
}
}

resource "aws_iam_role_policy" "inline" {
count = local.create_iam_role_inline_policy ? 1 : 0

role = aws_iam_role.this[0].name
name_prefix = "${var.role_name}_inline_"
policy = data.aws_iam_policy_document.inline[0].json
}
6 changes: 6 additions & 0 deletions modules/iam-assumable-role/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ variable "number_of_custom_role_policy_arns" {
default = null
}

variable "inline_policy_statements" {
description = "List of inline policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) to attach to IAM role as an inline policy"
type = any
default = []
}

# Pre-defined policies
variable "admin_role_policy_arn" {
description = "Policy ARN to use for admin role"
Expand Down
1 change: 1 addition & 0 deletions wrappers/iam-assumable-role-with-oidc/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module "wrapper" {
aws_account_id = try(each.value.aws_account_id, var.defaults.aws_account_id, "")
create_role = try(each.value.create_role, var.defaults.create_role, false)
force_detach_policies = try(each.value.force_detach_policies, var.defaults.force_detach_policies, false)
inline_policy_statements = try(each.value.inline_policy_statements, var.defaults.inline_policy_statements, [])
max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, 3600)
number_of_role_policy_arns = try(each.value.number_of_role_policy_arns, var.defaults.number_of_role_policy_arns, null)
oidc_fully_qualified_audiences = try(each.value.oidc_fully_qualified_audiences, var.defaults.oidc_fully_qualified_audiences, [])
Expand Down
1 change: 1 addition & 0 deletions wrappers/iam-assumable-role/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module "wrapper" {
custom_role_policy_arns = try(each.value.custom_role_policy_arns, var.defaults.custom_role_policy_arns, [])
custom_role_trust_policy = try(each.value.custom_role_trust_policy, var.defaults.custom_role_trust_policy, "")
force_detach_policies = try(each.value.force_detach_policies, var.defaults.force_detach_policies, false)
inline_policy_statements = try(each.value.inline_policy_statements, var.defaults.inline_policy_statements, [])
max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, 3600)
mfa_age = try(each.value.mfa_age, var.defaults.mfa_age, 86400)
number_of_custom_role_policy_arns = try(each.value.number_of_custom_role_policy_arns, var.defaults.number_of_custom_role_policy_arns, null)
Expand Down

0 comments on commit e13cb1e

Please sign in to comment.