Skip to content

Commit

Permalink
feat: Improved iam-eks-role module (simplified, removed provider_url_…
Browse files Browse the repository at this point in the history
…sa_pairs, updated docs) (#236)
  • Loading branch information
max-rocket-internet authored Apr 25, 2022
1 parent 2fb3c30 commit d014730
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 65 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ Use [iam-read-only-policy module](https://github.com/terraform-aws-modules/terra
- [iam-assumable-role-with-saml](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-assumable-role-with-saml) - Create individual IAM role which can be assumed by users with a SAML Identity Provider
- [iam-assumable-roles](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-assumable-roles) - Create several IAM roles which can be assumed from specified ARNs (AWS accounts, IAM users, etc)
- [iam-assumable-roles-with-saml](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-assumable-roles-with-saml) - Create several IAM roles which can be assumed by users with a SAML Identity Provider
- [iam-eks-role](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-eks-role) - Create an IAM role which can be assumed by one or more EKS `ServiceAccount`
- [iam-eks-role](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-eks-role) - Create an IAM role that can be assumed by one or more EKS `ServiceAccount`
- [iam-group-complete](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-group-complete) - IAM group with users who are allowed to assume IAM roles in another AWS account and have access to specified IAM policies
- [iam-group-with-assumable-roles-policy](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-group-with-assumable-roles-policy) - IAM group with users who are allowed to assume IAM roles in the same or in separate AWS account
- [iam-group-with-policies](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-group-with-policies) - IAM group with users who are allowed specified IAM policies (eg, "manage their own IAM user")
Expand Down
10 changes: 1 addition & 9 deletions examples/iam-eks-role/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,7 @@ module "iam_eks_role" {
role_name = "my-app"

cluster_service_accounts = {
(random_pet.this.id) = ["default:my-app", "canary:my-app"]
}

provider_url_sa_pairs = {
"oidc.eks.us-east-1.amazonaws.com/id/5C54DDF35ER19312844C7333374CC09D" = ["default:my-app2"]
"oidc.eks.ap-southeast-1.amazonaws.com/id/5C54DDF35ER54476848E7333374FF09G" = [
"default:my-app2",
"canary:my-app2",
]
(random_pet.this.id) = ["default:my-app"]
}

tags = {
Expand Down
67 changes: 46 additions & 21 deletions modules/iam-eks-role/README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,78 @@
# iam-eks-role

Creates single IAM role which can be assumed by one or more EKS `ServiceAccount` and optionally also OpenID Connect Federated Users.
Creates an IAM role that can be assumed by one or more EKS `ServiceAccount` in one or more EKS clusters. Unlike [iam-assumable-role-with-oidc](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/modules/iam-assumable-role-with-oidc/), this module:

- Does not require any knowledge of cluster OIDC information as `data` resources are used
- Supports assuming the role from multiple EKS clusters, for example used in DR or when a workload is spread across clusters
- Support multiple `ServiceAccount` in the same cluster, for example when a workload runs in multiple namespaces
- More suitable for non-cluster admins as implementation is simpler
- More suitable for when the IAM role and cluster resources are in separate Terraform configurations

This module is for use with AWS EKS. For details of how a `ServiceAccount` in EKS can assume an IAM role, see the [EKS documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html).

This module supports multiple `ServiceAccount` in multiple clusters and/or namespaces. This allows for a single IAM role to be used when an application may span multiple clusters (e.g. for DR) or multiple namespaces (e.g. for canary deployments). The variables `cluster_service_accounts` and `provider_url_sa_pairs` are used for this as follows:
Implementation notes:

- The EKS cluster needs to exist first, in the current AWS account and region
- The key in the `cluster_service_accounts` is the exact name of the EKS cluster

## Basic example

To create an IAM role named `my-app` that can be assumed in EKS cluster `cluster1` by a `ServiceAccount` called `my-serviceaccount` in the `default` namespace:

```hcl
module "iam_eks_role" {
source = "terraform-aws-modules/iam/aws//modules/iam-eks-role"
role_name = "my-app"
cluster_service_accounts = {
"<EKS cluster name>" = [
"<namespace>:<ServiceAccount name>",
"<namespace>:<another ServiceAccount name>"
]
"cluster1" = ["default:my-serviceaccount"]
}
}
```

provider_url_sa_pairs = {
"<OIDC provider without protocol prefix>" = [
"<namespace>:<ServiceAccount name>",
"<namespace>:<another ServiceAccount name>"
]
## Multi cluster example:

To create an IAM role named `my-app` that can be assumed from:

- EKS cluster `staging-main-1`, namespace `default`, `ServiceAccount` called `my-app-staging`
- EKS cluster `staging-backup-1`, namespace `default`, `ServiceAccount` called `my-app-staging`

```hcl
module "iam_eks_role" {
source = "terraform-aws-modules/iam/aws//modules/iam-eks-role"
role_name = "my-app"
cluster_service_accounts = {
"staging-main-1" = ["default:my-app-staging"]
"staging-backup-1" = ["default:my-app-staging"]
}
}
```

For example, to create an IAM role named `my-app` that can be assumed from the `ServiceAccount` named `my-app-staging` in the namespace `default` and `canary` in EKS cluster named `cluster-main-1`; and also the `ServiceAccount` name `my-app-staging` in the namespace `default` in EKS cluster named `cluster-backup-1`, the configuration would be:
## Multi `ServiceAccount` example

To create an IAM role named `cloudwatch-exporter` that can be assumed in EKS cluster `production-main-1` from:

- namespace `kube-system`, `ServiceAccount` called `cloudwatch-exporter`
- namespace `app1`, `ServiceAccount` called `cloudwatch-exporter`

```hcl
module "iam_eks_role" {
source = "terraform-aws-modules/iam/aws//modules/iam-eks-role"
role_name = "my-app"
cluster_service_accounts = {
"cluster-main-1" = [
"default:my-app-staging",
"canary:my-app-staging"
],
"cluster-backup-1" = [
"default:my-app-staging",
"production-main-1" = [
"kube-system:cloudwatch-exporter",
"app1:cloudwatch-exporter",
]
}
}
```

Note: the EKS clusters must in the current AWS region and account as they use the default AWS provider.

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements

Expand Down Expand Up @@ -84,7 +110,6 @@ No modules.
| <a name="input_create_role"></a> [create\_role](#input\_create\_role) | Whether to create a role | `bool` | `true` | 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_max_session_duration"></a> [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `43200` | no |
| <a name="input_provider_url_sa_pairs"></a> [provider\_url\_sa\_pairs](#input\_provider\_url\_sa\_pairs) | OIDC provider URL and k8s ServiceAccount pairs. If the assume role policy requires a mix of EKS clusters and other OIDC providers then this can be used | `map(list(string))` | `{}` | no |
| <a name="input_role_description"></a> [role\_description](#input\_role\_description) | IAM Role description | `string` | `""` | no |
| <a name="input_role_name"></a> [role\_name](#input\_role\_name) | Name of IAM role | `string` | `null` | no |
| <a name="input_role_name_prefix"></a> [role\_name\_prefix](#input\_role\_name\_prefix) | IAM role name prefix | `string` | `null` | no |
Expand Down
24 changes: 0 additions & 24 deletions modules/iam-eks-role/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,6 @@ data "aws_iam_policy_document" "assume_role_with_oidc" {
}
}
}

dynamic "statement" {
for_each = var.provider_url_sa_pairs

content {
effect = "Allow"

actions = ["sts:AssumeRoleWithWebIdentity"]

principals {
type = "Federated"

identifiers = [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${statement.key}"
]
}

condition {
test = "StringEquals"
variable = "${statement.key}:sub"
values = [for s in statement.value : "system:serviceaccount:${s}"]
}
}
}
}

resource "aws_iam_role" "this" {
Expand Down
8 changes: 4 additions & 4 deletions modules/iam-eks-role/outputs.tf
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
output "iam_role_arn" {
description = "ARN of IAM role"
value = element(concat(aws_iam_role.this.*.arn, [""]), 0)
value = try(aws_iam_role.this[0].arn, "")
}

output "iam_role_name" {
description = "Name of IAM role"
value = element(concat(aws_iam_role.this.*.name, [""]), 0)
value = try(aws_iam_role.this[0].name, "")
}

output "iam_role_path" {
description = "Path of IAM role"
value = element(concat(aws_iam_role.this.*.path, [""]), 0)
value = try(aws_iam_role.this[0].path, "")
}

output "iam_role_unique_id" {
description = "Unique ID of IAM role"
value = element(concat(aws_iam_role.this.*.unique_id, [""]), 0)
value = try(aws_iam_role.this[0].unique_id, "")
}
6 changes: 0 additions & 6 deletions modules/iam-eks-role/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@ variable "cluster_service_accounts" {
default = {}
}

variable "provider_url_sa_pairs" {
description = "OIDC provider URL and k8s ServiceAccount pairs. If the assume role policy requires a mix of EKS clusters and other OIDC providers then this can be used"
type = map(list(string))
default = {}
}

variable "tags" {
description = "A map of tags to add the the IAM role"
type = map(any)
Expand Down

0 comments on commit d014730

Please sign in to comment.