title | description |
---|---|
EKS IRSA - IAM Roles for Service Accounts |
Learn the concept EKS IRSA - IAM Roles for Service Accounts |
- Verify OIDC Provider in EKS Cluster Terraform Manifests
- Key Resources for discission in this section
- Terraform Element Function
- Terraform Split Function
- Terraform merge Function
- Terraform JSONEncode Function
- Folder:
13-EKS-IRSA/01-ekscluster-terraform-manifests
- Verify Terraform State Storage S3 Bucket in
c1-versions.tf
and AWS Mgmt Console
# Adding Backend as S3 for Remote State Storage
backend "s3" {
bucket = "terraform-on-aws-eks"
key = "dev/eks-cluster/terraform.tfstate"
region = "us-east-1"
# For State Locking
dynamodb_table = "dev-ekscluster"
}
- Folder:
13-EKS-IRSA/01-ekscluster-terraform-manifests
- Verify Terraform State Locking AWS DynamoDB Table in
c1-versions.tf
and AWS Mgmt Console
# Adding Backend as S3 for Remote State Storage
backend "s3" {
bucket = "terraform-on-aws-eks"
key = "dev/eks-cluster/terraform.tfstate"
region = "us-east-1"
# For State Locking
dynamodb_table = "dev-ekscluster"
}
- Folder:
13-EKS-IRSA/01-ekscluster-terraform-manifests
# EKS OIDC ROOT CA Thumbprint - valid until 2037
variable "eks_oidc_root_ca_thumbprint" {
type = string
description = "Thumbprint of Root CA for EKS OIDC, Valid until 2037"
default = "9e99a48a9960b14926bb7f3b02e22da2b0ab7280"
}
- Folder:
13-EKS-IRSA/01-ekscluster-terraform-manifests
- Add variable
eks_oidc_root_ca_thumbprint
ineks.auto.tfvars
cluster_name = "eksdemo1"
cluster_service_ipv4_cidr = "172.20.0.0/16"
cluster_version = "1.21"
cluster_endpoint_private_access = false
cluster_endpoint_public_access = true
cluster_endpoint_public_access_cidrs = ["0.0.0.0/0"]
eks_oidc_root_ca_thumbprint = "9e99a48a9960b14926bb7f3b02e22da2b0ab7280"
- Folder:
13-EKS-IRSA/01-ekscluster-terraform-manifests
# Datasource: AWS Partition
# Use this data source to lookup information about the current AWS partition in which Terraform is working
data "aws_partition" "current" {}
# Resource: AWS IAM Open ID Connect Provider
resource "aws_iam_openid_connect_provider" "oidc_provider" {
client_id_list = ["sts.${data.aws_partition.current.dns_suffix}"]
thumbprint_list = [var.eks_oidc_root_ca_thumbprint]
url = aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer
tags = merge(
{
Name = "${var.cluster_name}-eks-irsa"
},
local.common_tags
)
}
# Output: AWS IAM Open ID Connect Provider ARN
output "aws_iam_openid_connect_provider_arn" {
description = "AWS IAM Open ID Connect Provider ARN"
value = aws_iam_openid_connect_provider.oidc_provider.arn
}
- Folder:
13-EKS-IRSA/01-ekscluster-terraform-manifests
- File Name: c6-02-iam-oidc-connect-provider.tf
# Extract OIDC Provider from OIDC Provider ARN
locals {
aws_iam_oidc_connect_provider_extract_from_arn = element(split("oidc-provider/", "${aws_iam_openid_connect_provider.oidc_provider.arn}"), 1)
}
# Output: AWS IAM Open ID Connect Provider
output "aws_iam_openid_connect_provider_extract_from_arn" {
description = "AWS IAM Open ID Connect Provider extract from ARN"
value = local.aws_iam_oidc_connect_provider_extract_from_arn
}
- Sample Output for reference
# Sample Outputs for Reference
aws_iam_openid_connect_provider_arn = "arn:aws:iam::180789647333:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/A9DED4A4FA341C2A5D985A260650F232"
aws_iam_openid_connect_provider_extract_from_arn = "oidc.eks.us-east-1.amazonaws.com/id/A9DED4A4FA341C2A5D985A260650F232"
# Change Directory
cd 13-EKS-IRSA/01-ekscluster-terraform-manifests
# Terraform Init
terraform init
# Terraform Validate
terraform validate
# Terraform Plan
terraform plan
# Terraform Apply
terraform apply -auto-approve
# Configure kubeconfig for kubectl
aws eks --region <region-code> update-kubeconfig --name <cluster_name>
aws eks --region us-east-1 update-kubeconfig --name hr-dev-eksdemo1
# Verify Kubernetes Worker Nodes using kubectl
kubectl get nodes
kubectl get nodes -o wide
# Stop EC2 Bastion Host
Go to Services -> EC2 -> Instances -> hr-dev-BastionHost -> Instance State -> Stop
- We can also call it as
OpenID Connect Discovery URL
- Discovery: Defines how Clients dynamically discover information about OpenID Providers
# Get OpenID Connect provider URL for EKS Cluster
Go to Services -> EKS -> hr-dev-eksdemo1 -> Configuration -> Details -> OpenID Connect provider URL
# EKS OpenID Connect Well Known Configuration URL
<EKS OpenID Connect provider URL>/.well-known/openid-configuration
# Sample
https://oidc.eks.us-east-1.amazonaws.com/id/EC973221A6C1BC248C79CFD5455EEECC/.well-known/openid-configuration
- Sample Output from EKS OpenID Connect Well Known Configuration URL
// 20220106104407
// https://oidc.eks.us-east-1.amazonaws.com/id/EC973221A6C1BC248C79CFD5455EEECC/.well-known/openid-configuration
{
"issuer": "https://oidc.eks.us-east-1.amazonaws.com/id/EC973221A6C1BC248C79CFD5455EEECC",
"jwks_uri": "https://oidc.eks.us-east-1.amazonaws.com/id/EC973221A6C1BC248C79CFD5455EEECC/keys",
"authorization_endpoint": "urn:kubernetes:programmatic_authorization",
"response_types_supported": [
"id_token"
],
"subject_types_supported": [
"public"
],
"claims_supported": [
"sub",
"iss"
],
"id_token_signing_alg_values_supported": [
"RS256"
]
}
- This step is optional, Terraform can create this folder
dev/ebs-storage
duringterraform apply
but to maintain consistency we create it. - Go to Services -> S3 ->
- Bucket name: terraform-on-aws-eks
- Create Folder
- Folder Name: dev/eks-irsa-demo
- Click on Create Folder
- Create Dynamo DB Table for EKS IRSA Demo
- Table Name: dev-eks-irsa-demo
- Partition key (Primary Key): LockID (Type as String)
- Table settings: Use default settings (checked)
- Click on Create
- Folder: 02-eks-irsa-demo-terraform-manifests
# Terraform Settings Block
terraform {
required_version = ">= 1.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.70"
}
}
# Adding Backend as S3 for Remote State Storage
backend "s3" {
bucket = "terraform-on-aws-eks"
key = "dev/eks-irsa-demo/terraform.tfstate"
region = "us-east-1"
# For State Locking
dynamodb_table = "dev-eks-irsa-demo"
}
}
- Folder: 02-eks-irsa-demo-terraform-manifests
# Terraform Remote State Datasource - Remote Backend AWS S3
data "terraform_remote_state" "eks" {
backend = "s3"
config = {
bucket = "terraform-on-aws-eks"
key = "dev/eks-cluster/terraform.tfstate"
region = var.aws_region
}
}
- Folder: 02-eks-irsa-demo-terraform-manifests
# Input Variables - Placeholder file
# AWS Region
variable "aws_region" {
description = "Region in which AWS Resources to be created"
type = string
default = "us-east-1"
}
# Environment Variable
variable "environment" {
description = "Environment Variable used as a prefix"
type = string
default = "dev"
}
# Business Division
variable "business_divsion" {
description = "Business Division in the large organization this Infrastructure belongs"
type = string
default = "SAP"
}
- Folder: 02-eks-irsa-demo-terraform-manifests
# Define Local Values in Terraform
locals {
owners = var.business_divsion
environment = var.environment
name = "${var.business_divsion}-${var.environment}"
common_tags = {
owners = local.owners
environment = local.environment
}
eks_cluster_name = "${local.name}-${data.terraform_remote_state.eks.outputs.cluster_id}"
}
- Folder: 02-eks-irsa-demo-terraform-manifests
# Terraform AWS Provider Block
provider "aws" {
region = "us-east-1"
}
data "aws_eks_cluster" "cluster" {
name = data.terraform_remote_state.eks.outputs.cluster_id
}
data "aws_eks_cluster_auth" "cluster" {
name = data.terraform_remote_state.eks.outputs.cluster_id
}
# Terraform Kubernetes Provider
provider "kubernetes" {
host = data.terraform_remote_state.eks.outputs.cluster_endpoint
cluster_ca_certificate = base64decode(data.terraform_remote_state.eks.outputs.cluster_certificate_authority_data)
token = data.aws_eks_cluster_auth.cluster.token
}
- Folder: 02-eks-irsa-demo-terraform-manifests
#data.terraform_remote_state.eks.outputs.aws_iam_openid_connect_provider_arn
#data.terraform_remote_state.eks.outputs.aws_iam_openid_connect_provider_extract_from_arn
# Resource: Create IAM Role and associate the EBS IAM Policy to it
resource "aws_iam_role" "irsa_iam_role" {
name = "${local.name}-irsa-iam-role"
# Terraform's "jsonencode" function converts a Terraform expression result to valid JSON syntax.
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRoleWithWebIdentity"
Effect = "Allow"
Sid = ""
Principal = {
Federated = "${data.terraform_remote_state.eks.outputs.aws_iam_openid_connect_provider_arn}"
}
Condition = {
StringEquals = {
"${data.terraform_remote_state.eks.outputs.aws_iam_openid_connect_provider_extract_from_arn}:sub": "system:serviceaccount:default:irsa-demo-sa"
}
}
},
]
})
tags = {
tag-key = "${local.name}-irsa-iam-role"
}
}
# Associate IAM Role and Policy
resource "aws_iam_role_policy_attachment" "irsa_iam_role_policy_attach" {
policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
role = aws_iam_role.irsa_iam_role.name
}
output "irsa_iam_role_arn" {
description = "IRSA Demo IAM Role ARN"
value = aws_iam_role.irsa_iam_role.arn
}
- Folder: 02-eks-irsa-demo-terraform-manifests
# Resource: Kubernetes Service Account
resource "kubernetes_service_account_v1" "irsa_demo_sa" {
depends_on = [ aws_iam_role_policy_attachment.irsa_iam_role_policy_attach ]
metadata {
name = "irsa-demo-sa"
annotations = {
"eks.amazonaws.com/role-arn" = aws_iam_role.irsa_iam_role.arn
}
}
}
- Folder: 02-eks-irsa-demo-terraform-manifests
# Resource: Kubernetes Job
resource "kubernetes_job_v1" "irsa_demo" {
metadata {
name = "irsa-demo"
}
spec {
template {
metadata {
labels = {
app = "irsa-demo"
}
}
spec {
service_account_name = kubernetes_service_account_v1.irsa_demo_sa.metadata.0.name
container {
name = "irsa-demo"
image = "amazon/aws-cli:latest"
args = ["s3", "ls"]
#args = ["ec2", "describe-instances", "--region", "${var.aws_region}"] # Should fail as we don't have access to EC2 Describe Instances for IAM Role
}
restart_policy = "Never"
}
}
}
}
- Folder: 02-eks-irsa-demo-terraform-manifests
# Generic Variables
aws_region = "us-east-1"
environment = "dev"
business_divsion = "hr"
- Folder: 02-eks-irsa-demo-terraform-manifests
# Change Directory
cd 13-EKS-IRSA/02-eks-irsa-demo-terraform-manifests
# Terraform Init
terraform init
# Terraform Validate
terraform validate
# Terraform Plan
terraform plan
# Terraform Apply
terraform apply -auto-approve
# Verify Kubernetes Service Account
kubectl get sa
kubectl describe sa irsa-demo-sa
Observation:
1. We can see that IAM Role ARN is associated in Annotations field of Kubernetes Service Account
# List & Describe Kubernetes Jobs
kubectl get job
kubectl describe job irsa-demo
Observation:
1. You should see COMPLETIONS 1/1
2. You should see when you describe Pods Statuses: 0 Running / 1 Succeeded / 0 Failed
# Verify Logs (by giving job label app=irsa-demo)
kubectl logs -f -l app=irsa-demo
Observation:
1. You can see all the S3 buckets from your AWS account listed
- Our core focus here is to learn terraform taint command
# Change Directory
cd 13-EKS-IRSA/02-eks-irsa-demo-terraform-manifests
# List Terraform Resources
terraform state list
# Taint Kubernetes Job
terraform taint kubernetes_job_v1.irsa_demo
Observation:
1. Terraform taint will ensure the resource will get destroyed and recreated during next terraform apply.
# Terraform Plan
terraform plan
Observation:
1. We should see a message "kubernetes_job_v1.irsa_demo is tainted, so must be replaced"
# Terraform Plan
terraform apply -auto-approve
Observation:
1. Resource kubernetes_job_v1.irsa_demo should be destroyed and recreated.
# Verify Kubernetes Job
kubectl get job
kubectl describe job irsa-demo
kubectl logs -f -l app=irsa-demo
Observation:
1. k8s Job should run successfully.
- Our core focus here is to learn terraform apply -replace command
- Instead of
terraform taint
we can also useterraform apply -replace
command
# Change Directory
cd 13-EKS-IRSA/02-eks-irsa-demo-terraform-manifests
# List Terraform Resources
terraform state list
# Verify AGE column of job before running replace
kubectl get job irsa-demo
# Terraform Apply with "-replace" option for Kubernetes Job
terraform apply -replace kubernetes_job_v1.irsa_demo
Observation:
1. Terraform apply with "replace" option will ensure the existing resource will get destroyed and recreated with single command.
# Verify Kubernetes Job
kubectl get job
kubectl describe job irsa-demo
kubectl logs -f -l app=irsa-demo
Observation:
1. k8s Job should run successfully.
- Delete the k8s Job with
kubectl
and create it withterraform apply
# Delete the Job with kubectl
kubectl delete job irsa-demo
# Run terraform plan
terraform plan
Observation:
1. It will show that 1 resource to be created which is irsa-demo job
# Run terraform apply
terraform apply -auto-approve
Observation:
1. We should see that irsa-demo job created succesfully
# Verify Kubernetes Job
kubectl get job
kubectl describe job irsa-demo
kubectl logs -f -l app=irsa-demo
Observation:
1. k8s Job should run successfully.
# Change Directory
cd 13-EKS-IRSA/02-eks-irsa-demo-terraform-manifests
# Terraform Destroy
terraform destroy -auto-approve
# Delete Terraform Provider Plugins
rm -rf .terraform*
- If you are moving to next section now, don't destroy the EKS Cluster
# Change Directory
cd 13-EKS-IRSA/01-ekscluster-terraform-manifests
# Terraform Destroy
terraform destroy -auto-approve
# Delete Terraform Provider Plugins
rm -rf .terraform*