Skip to content

Commit

Permalink
Improve federated features for iam-role module (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
posquit0 authored Dec 12, 2022
1 parent a40f8d2 commit 05f0ce9
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 65 deletions.
97 changes: 97 additions & 0 deletions examples/github-trusted-iam-roles/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
provider "aws" {
region = "us-east-1"
}


###################################################
# IAM OIDC Identity Providers
###################################################

locals {
providers = [
{
url = "https://token.actions.githubusercontent.com"
audiences = ["sts.amazonaws.com"]
},
]
}

module "oidc_provider" {
source = "../../modules/iam-oidc-identity-provider"
# source = "tedilabs/account/aws//modules/iam-oidc-identity-provider"
# version = "~> 0.23.0"

for_each = {
for provider in try(local.providers, []) :
provider.url => provider
}

url = each.key
audiences = try(each.value.audiences, null)

thumbprints = try(each.value.thumbprints, null)
auto_thumbprint_enabled = try(each.value.auto_thumbprint_enabled, true)

tags = {
"project" = "terraform-aws-account-examples"
}
}


###################################################
# IAM Roles
###################################################

locals {
roles = [
{
name = "github-readonly"
policies = ["arn:aws:iam::aws:policy/ReadOnlyAccess"]
},
]
}

module "role" {
source = "../../modules/iam-role"
# source = "tedilabs/account/aws//modules/iam-role"
# version = "~> 0.23.0"

for_each = {
for role in try(local.roles, []) :
role.name => role
}

name = each.key
description = try(each.value.description, "Managed by Terraform.")
path = try(each.value.path, "/")

trusted_oidc_providers = [
{
name = "github"
url = "token.actions.githubusercontent.com"
conditions = [
{
key = "aud"
condition = "StringEquals"
values = ["sts.amazonaws.com"]
},
{
key = "sub"
condition = "StringLike"
values = ["repo:tedilabs/*"]
},
]
}
]

assumable_roles = try(each.value.assumable_roles, [])
policies = try(each.value.policies, [])
inline_policies = {
for name, path in try(each.value.inline_policies, {}) :
name => file(path)
}

tags = {
"project" = "terraform-aws-account-examples"
}
}
7 changes: 7 additions & 0 deletions examples/github-trusted-iam-roles/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "oidc_providers" {
value = module.oidc_provider
}

output "roles" {
value = module.role
}
14 changes: 14 additions & 0 deletions examples/github-trusted-iam-roles/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
terraform {
required_version = "~> 1.3"

required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
tls = {
source = "hashicorp/tls"
version = "~> 4.0"
}
}
}
10 changes: 4 additions & 6 deletions modules/iam-role/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ When `pgp_key` is specified as `keybase:username`, make sure that that user has
| <a name="input_name"></a> [name](#input\_name) | Desired name for the IAM role. | `string` | n/a | yes |
| <a name="input_assumable_roles"></a> [assumable\_roles](#input\_assumable\_roles) | List of IAM roles ARNs which can be assumed by the role. | `list(string)` | `[]` | no |
| <a name="input_conditions"></a> [conditions](#input\_conditions) | Required conditions to assume the role. | <pre>list(object({<br> key = string<br> condition = string<br> values = list(string)<br> }))</pre> | `[]` | no |
| <a name="input_description"></a> [description](#input\_description) | The description of the role. | `string` | `""` | no |
| <a name="input_description"></a> [description](#input\_description) | The description of the role. | `string` | `"Managed by Terraform."` | no |
| <a name="input_effective_date"></a> [effective\_date](#input\_effective\_date) | Allow to assume IAM role only after a specific date and time. | `string` | `null` | no |
| <a name="input_expiration_date"></a> [expiration\_date](#input\_expiration\_date) | Allow to assume IAM role only before a specific date and time. | `string` | `null` | no |
| <a name="input_force_detach_policies"></a> [force\_detach\_policies](#input\_force\_detach\_policies) | Specifies to force detaching any policies the role has before destroying it. | `bool` | `false` | no |
Expand All @@ -71,7 +71,7 @@ When `pgp_key` is specified as `keybase:username`, make sure that that user has
| <a name="input_mfa_ttl"></a> [mfa\_ttl](#input\_mfa\_ttl) | Max age of valid MFA (in seconds) for roles which require MFA. | `number` | `86400` | no |
| <a name="input_module_tags_enabled"></a> [module\_tags\_enabled](#input\_module\_tags\_enabled) | Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no |
| <a name="input_path"></a> [path](#input\_path) | Desired path for the IAM role. | `string` | `"/"` | no |
| <a name="input_permissions_boundary"></a> [permissions\_boundary](#input\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the role. | `string` | `""` | no |
| <a name="input_permissions_boundary"></a> [permissions\_boundary](#input\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the role. | `string` | `null` | no |
| <a name="input_policies"></a> [policies](#input\_policies) | List of IAM policies ARNs to attach to IAM role. | `list(string)` | `[]` | no |
| <a name="input_resource_group_description"></a> [resource\_group\_description](#input\_resource\_group\_description) | The description of Resource Group. | `string` | `"Managed by Terraform."` | no |
| <a name="input_resource_group_enabled"></a> [resource\_group\_enabled](#input\_resource\_group\_enabled) | Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no |
Expand All @@ -80,10 +80,8 @@ When `pgp_key` is specified as `keybase:username`, make sure that that user has
| <a name="input_source_ip_whitelist"></a> [source\_ip\_whitelist](#input\_source\_ip\_whitelist) | A list of source IP addresses or CIDRs allowed to assume IAM role from. | `list(string)` | `[]` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources. | `map(string)` | `{}` | no |
| <a name="input_trusted_iam_entities"></a> [trusted\_iam\_entities](#input\_trusted\_iam\_entities) | A list of ARNs of AWS IAM entities who can assume the role. | `list(string)` | `[]` | no |
| <a name="input_trusted_oidc_conditions"></a> [trusted\_oidc\_conditions](#input\_trusted\_oidc\_conditions) | Required conditions to assume the role for OIDC providers. | <pre>list(object({<br> key = string<br> condition = string<br> values = list(string)<br> }))</pre> | `[]` | no |
| <a name="input_trusted_oidc_providers"></a> [trusted\_oidc\_providers](#input\_trusted\_oidc\_providers) | A list of OIDC identity providers. Supported types are `amazon`, `aws-cognito`, `aws-iam`, `facebook`, `google`. `type` is required. `url` is required only when `type` is `aws-iam`. | `list(map(string))` | `[]` | no |
| <a name="input_trusted_saml_conditions"></a> [trusted\_saml\_conditions](#input\_trusted\_saml\_conditions) | Required conditions to assume the role for SAML providers. | <pre>list(object({<br> key = string<br> condition = string<br> values = list(string)<br> }))</pre> | `[]` | no |
| <a name="input_trusted_saml_providers"></a> [trusted\_saml\_providers](#input\_trusted\_saml\_providers) | A list of ARNs of SAML identity providers in AWS IAM. | `list(string)` | `[]` | no |
| <a name="input_trusted_oidc_providers"></a> [trusted\_oidc\_providers](#input\_trusted\_oidc\_providers) | (Optional) A list of configurations of OIDC identity providers. Each value of `trusted_oidc_providers` as defined below.<br> (Required) `name` - The name of the OIDC identity provider.<br> (Required) `url` - The URL of the OIDC identity provider. If the provider is not common, the corresponding IAM OIDC Provider should be created before. Supported common OIDC providers are `accounts.google.com`, `cognito-identity.amazonaws.com`, `graph.facebook.com`, `www.amazon.com`.<br> (Optional) `conditions` - A list of required conditions to assume the role via OIDC providers. | <pre>list(object({<br> name = string<br> url = string<br> conditions = optional(list(object({<br> key = string<br> condition = string<br> values = list(string)<br> })), [])<br> }))</pre> | `[]` | no |
| <a name="input_trusted_saml_providers"></a> [trusted\_saml\_providers](#input\_trusted\_saml\_providers) | (Optional) A list of configurations of SAML identity providers. Each value of `trusted_saml_providers` as defined below.<br> (Required) `name` - The name of the SAML identity provider.<br> (Required) `arn` - The ARN of the SAML identity provider.<br> (Optional) `conditions` - A list of required conditions to assume the role via SAML providers. | <pre>list(object({<br> name = string<br> arn = string<br> conditions = optional(list(object({<br> key = string<br> condition = string<br> values = list(string)<br> })), [])<br> }))</pre> | `[]` | no |
| <a name="input_trusted_services"></a> [trusted\_services](#input\_trusted\_services) | AWS Services that can assume the role. | `list(string)` | `[]` | no |

## Outputs
Expand Down
34 changes: 15 additions & 19 deletions modules/iam-role/trusted-oidc-providers.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,42 @@ data "aws_caller_identity" "this" {}
data "aws_partition" "this" {}

locals {
oidc_provider_urls = {
"google" = "accounts.google.com"
"facebook" = "graph.facebook.com"
"amazon" = "www.amazon.com"
"aws-cognito" = "cognito-identity.amazonaws.com"
}
oidc_provider_common_urls = [
"accounts.google.com",
"cognito-identity.amazonaws.com",
"graph.facebook.com",
"www.amazon.com",
]
oidc_arn_prefix = "arn:${data.aws_partition.this.partition}:iam::${data.aws_caller_identity.this.account_id}:oidc-provider/"
}

data "aws_iam_policy_document" "trusted_oidc_providers" {
for_each = {
for idx, provider in var.trusted_oidc_providers :
idx + 1 => provider
for provider in var.trusted_oidc_providers :
provider.name => provider
}

statement {
sid = "TrustedOidcProviders${each.key}"
sid = "TrustedOIDC${each.key}"
effect = "Allow"
actions = ["sts:AssumeRoleWithWebIdentity"]

principals {
type = "Federated"
identifiers = [
each.value.type != "aws-iam"
? local.oidc_provider_urls[each.value.type]
contains(local.oidc_provider_common_urls, each.value.url)
? each.value.url
: "${local.oidc_arn_prefix}${each.value.url}"
]
}

dynamic "condition" {
for_each = var.trusted_oidc_conditions
for_each = each.value.conditions

content {
variable = (
each.value.type != "aws-iam"
? "${local.oidc_provider_urls[each.value.type]}:${condition.value.key}"
: "${each.value.url}:${condition.value.key}"
)
test = condition.value.condition
values = condition.value.values
variable = "${each.value.url}:${condition.value.key}"
test = condition.value.condition
values = condition.value.values
}
}

Expand Down
13 changes: 7 additions & 6 deletions modules/iam-role/trusted-saml-providers.tf
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
data "aws_iam_policy_document" "trusted_saml_providers" {
for_each = toset(
length(var.trusted_saml_providers) > 0 ? ["this"] : []
)
for_each = {
for provider in var.trusted_saml_providers :
provider.name => provider
}

statement {
sid = "TrustedSamlProviders"
sid = "TrustedSAML${each.key}"
effect = "Allow"
actions = ["sts:AssumeRoleWithSAML"]

principals {
type = "Federated"
identifiers = var.trusted_saml_providers
identifiers = [each.value.arn]
}

dynamic "condition" {
for_each = var.trusted_saml_conditions
for_each = each.value.conditions

content {
variable = "saml:${condition.value.key}"
Expand Down
Loading

0 comments on commit 05f0ce9

Please sign in to comment.