AWS supports authenticating your pods using an identity provider that your account is configured to trust.
This tutorial will guide you through the process of creating an IAM role that your kubernetes pods will be able to assume.
Pre-requisites:
- Familiarity with oidc provider in AWS
- Have an oidc provider setup in AWS
- Have a kubernetes cluster running in AWS (of course!)
Creating oidc provider for AWS is beyond the scope of this tutorial.
Now that that's out of the way,
Let's start with some terraform variables:
#variables.tf
# this is the url of the oidc_issuer
variable "oidc_issuer" {
default = "oidc-example-irsa.s3.ap-southeast-2.amazonaws.com/subdomain.example.com"
}
# role arn of the oidc provider
variable "oidc_arn" {
default = "arn:aws:iam:01234567898:oidc-provider/oidc-example-irsa.s3.ap-southeast-2.amazonaws.com/subdomain.example.com"
}
variable "cluster_name" {
default = "cluster.example.com"
}
# service account variables
variable "service_accounts" {
default = [
{
name = "blabladefault",
namespace = "blablans"
statements = [
{
Action = ["*:*"]
Effect = "Deny"
Resource = "*"
}
]
}
]
}
I've prefilled all defaults here because we're going to rely on these defaults. You can also set them to null
and pass your variables separately.
Next, we'll need a resource block for the iam role.
#main.tf
resource "aws_iam_role" "service_accounts" {
for_each = {for service_account in (var.service_accounts) : service_account.name => service_account }
name = "${each.value.name}.${each.value.namespace}.sa.${var.cluster_name}"
#allow federated role assumption using webIdentity(oidc)
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRoleWithWebIdentity",
Condition = {
StringEquals = {
"${var.oidc_issuer}:sub" = "system:serviceaccount:${each.value.namespace}:${each.value.name}"
}
}
Effect = "Allow"
Principal = {
Federated = "${var.oidc_arn}"
}
},
]
})
#generate multiple inline_policy resources
dynamic "inline_policy" {
for_each = "${each.value.statements}"
content {
name = "policy-${inline_policy.key}"
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = inline_policy.value.Action
Effect = inline_policy.value.Effect
Resource = inline_policy.value.Resource
}]
})
}
}
tags = {
tag-key = "tag-value"
}
}
What's happening here?
We're using terraform's for_each
meta argument to create multiple iam roles. The resource aws_iam_role.service_accounts
is thus a list
of iam roles.
We're then using terraform's dynamic
block to create multiple inline_policy
resources within each iam role.
Using a dynamic block inside a for_each
argument allows us to render nested configurations like above.
The beauty of this type of configuration is that you can reuse this as much as you'd like.
Let's see an example,
I've updated the variables.tf
to add one more service account to the service_accounts variable:
#variables.tf
# ...
# ...
# [updated] service account variables with two service-accounts
variable "service_accounts" {
default = [
{
name = "blabladefault",
namespace = "blablans"
statements = [
{
Action = ["*:*"]
Effect = "Deny"
Resource = "*"
}
]
},
{
name = "blabladefault2",
namespace = "blablans2"
statements = [
{
Action = ["*:*"]
Effect = "Deny"
Resource = "*"
}
]
}
]
}
Let's run terraform plan
on this configuration.
We see that it's now creating two service accounts:
Terraform will perform the following actions:
# aws_iam_role.service_accounts["blabladefault"] will be created
+ resource "aws_iam_role" "service_accounts" {
+ arn = (known after apply)
+ assume_role_policy = jsonencode(
{
+ Statement = [
+ {
+ Action = "sts:AssumeRoleWithWebIdentity"
+ Condition = {
+ StringEquals = {
+ oidc-example-irsa.s3.ap-southeast-2.amazonaws.com/subdomain.example.com:sub = "system:serviceaccount:blablans:blabladefault"
}
}
+ Effect = "Allow"
+ Principal = {
+ Federated = "arn:aws:iam:01234567898:oidc-provider/oidc-example-irsa.s3.ap-southeast-2.amazonaws.com/subdomain.example.com"
}
},
]
+ Version = "2012-10-17"
}
)
+ create_date = (known after apply)
+ force_detach_policies = false
+ id = (known after apply)
+ managed_policy_arns = (known after apply)
+ max_session_duration = 3600
+ name = "blabladefault.blablans.sa.cluster.example.com"
+ name_prefix = (known after apply)
+ path = "/"
+ tags = {
+ "tag-key" = "tag-value"
}
+ tags_all = {
+ "tag-key" = "tag-value"
}
+ unique_id = (known after apply)
+ inline_policy {
+ name = "policy-0"
+ policy = jsonencode(
{
+ Statement = [
+ {
+ Action = [
+ "*:*",
]
+ Effect = "Deny"
+ Resource = "*"
},
]
+ Version = "2012-10-17"
}
)
}
}
# aws_iam_role.service_accounts["blabladefault2"] will be created
+ resource "aws_iam_role" "service_accounts" {
+ arn = (known after apply)
+ assume_role_policy = jsonencode(
{
+ Statement = [
+ {
+ Action = "sts:AssumeRoleWithWebIdentity"
+ Condition = {
+ StringEquals = {
+ oidc-example-irsa.s3.ap-southeast-2.amazonaws.com/subdomain.example.com:sub = "system:serviceaccount:blablans2:blabladefault2"
}
}
+ Effect = "Allow"
+ Principal = {
+ Federated = "arn:aws:iam:01234567898:oidc-provider/oidc-example-irsa.s3.ap-southeast-2.amazonaws.com/subdomain.example.com"
}
},
]
+ Version = "2012-10-17"
}
)
+ create_date = (known after apply)
+ force_detach_policies = false
+ id = (known after apply)
+ managed_policy_arns = (known after apply)
+ max_session_duration = 3600
+ name = "blabladefault2.blablans2.sa.cluster.example.com"
+ name_prefix = (known after apply)
+ path = "/"
+ tags = {
+ "tag-key" = "tag-value"
}
+ tags_all = {
+ "tag-key" = "tag-value"
}
+ unique_id = (known after apply)
+ inline_policy {
+ name = "policy-0"
+ policy = jsonencode(
{
+ Statement = [
+ {
+ Action = [
+ "*:*",
]
+ Effect = "Deny"
+ Resource = "*"
},
]
+ Version = "2012-10-17"
}
)
}
}
Plan: 2 to add, 0 to change, 0 to destroy.
This plan was generated from the example above. If it doesn't work for you, please leave a comment and I'll try and help you out.
Top comments (0)