From 6917166c4c973d6f56b22bc73d9adfe79712c693 Mon Sep 17 00:00:00 2001
From: Ishan Sharma <38211728+ishuar@users.noreply.github.com>
Date: Fri, 3 Feb 2023 02:08:21 +0100
Subject: [PATCH] Submodules "IRSA" and "Helm-add-on" (#29)
* added irsa submodule
* added helm-add-on submodule
* relative links in readme for module IRSA
* example IRSA added
* chore: removed coment
* added cluster-autoscaler-helm-add-on example
* optional inputs from irsa submodule for helm-add-on module
* set minimum version of submodule in example and readme updated
* minimum version of module in example
* removed duplicate local block , similar condition is used for resource creation
* added descriptive comment on the helm-addon main.tf irsa reference
* chore: typo
* docs: updated readme for the submodules irsa and helmaddon
* modules examples tested with minimum eks version `1.3`
* updated `CHANGELOG.md`
---
CHANGELOG.md | 19 +-
.../.terraform.lock.hcl | 83 +++++
.../cluster-autoscaler-helm-add-on/README.md | 39 ++
.../cluster-autoscaler-values.yaml | 11 +
.../dependencies.tf | 205 ++++++++++
.../cluster-autoscaler-helm-add-on/eks.tf | 38 ++
.../cluster-autoscaler-helm-add-on/local.tf | 33 ++
.../cluster-autoscaler-helm-add-on/main.tf | 32 ++
.../versions.tf | 34 ++
examples/irsa/.terraform.lock.hcl | 65 ++++
examples/irsa/README.md | 34 ++
examples/irsa/dependencies.tf | 171 +++++++++
examples/irsa/eks.tf | 38 ++
examples/irsa/local.tf | 37 ++
examples/irsa/main.tf | 43 +++
examples/irsa/versions.tf | 23 ++
.../helm-add-on/.config/.terraform-docs.yml | 11 +
modules/helm-add-on/.config/footer.md | 3 +
modules/helm-add-on/.config/header.md | 19 +
modules/helm-add-on/README.md | 116 ++++++
modules/helm-add-on/main.tf | 91 +++++
modules/helm-add-on/outputs.tf | 9 +
modules/helm-add-on/variables.tf | 350 ++++++++++++++++++
modules/helm-add-on/versions.tf | 15 +
modules/irsa/.config/.terraform-docs.yml | 11 +
modules/irsa/.config/footer.md | 3 +
modules/irsa/.config/header.md | 23 ++
modules/irsa/README.md | 88 +++++
modules/irsa/main.tf | 72 ++++
modules/irsa/outputs.tf | 14 +
modules/irsa/service_account.tf | 28 ++
modules/irsa/variables.tf | 116 ++++++
modules/irsa/versions.tf | 15 +
33 files changed, 1885 insertions(+), 4 deletions(-)
create mode 100644 examples/cluster-autoscaler-helm-add-on/.terraform.lock.hcl
create mode 100644 examples/cluster-autoscaler-helm-add-on/README.md
create mode 100644 examples/cluster-autoscaler-helm-add-on/cluster-autoscaler-values.yaml
create mode 100644 examples/cluster-autoscaler-helm-add-on/dependencies.tf
create mode 100644 examples/cluster-autoscaler-helm-add-on/eks.tf
create mode 100644 examples/cluster-autoscaler-helm-add-on/local.tf
create mode 100644 examples/cluster-autoscaler-helm-add-on/main.tf
create mode 100644 examples/cluster-autoscaler-helm-add-on/versions.tf
create mode 100644 examples/irsa/.terraform.lock.hcl
create mode 100644 examples/irsa/README.md
create mode 100644 examples/irsa/dependencies.tf
create mode 100644 examples/irsa/eks.tf
create mode 100644 examples/irsa/local.tf
create mode 100644 examples/irsa/main.tf
create mode 100644 examples/irsa/versions.tf
create mode 100644 modules/helm-add-on/.config/.terraform-docs.yml
create mode 100644 modules/helm-add-on/.config/footer.md
create mode 100644 modules/helm-add-on/.config/header.md
create mode 100644 modules/helm-add-on/README.md
create mode 100644 modules/helm-add-on/main.tf
create mode 100644 modules/helm-add-on/outputs.tf
create mode 100644 modules/helm-add-on/variables.tf
create mode 100644 modules/helm-add-on/versions.tf
create mode 100644 modules/irsa/.config/.terraform-docs.yml
create mode 100644 modules/irsa/.config/footer.md
create mode 100644 modules/irsa/.config/header.md
create mode 100644 modules/irsa/README.md
create mode 100644 modules/irsa/main.tf
create mode 100644 modules/irsa/outputs.tf
create mode 100644 modules/irsa/service_account.tf
create mode 100644 modules/irsa/variables.tf
create mode 100644 modules/irsa/versions.tf
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f67d64..d5984c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,13 +2,24 @@
All notable changes to this project will be documented in this file.
-### [v1.3.0](https://github.com/ishuar/terraform-aws-eks/compare/v1.2.0...v1.3.0)
+## [v1.4.0](https://github.com/ishuar/terraform-aws-eks/compare/v1.3.0...v1.4.0)
+
+### Features
+
+- Added Submodules
+ - irsa
+ - helm-add-on
+- Added Examples for submodules.
+- Added [AWS EKS Cluster Autoscaler](https://docs.aws.amazon.com/eks/latest/userguide/autoscaling.html) as an Helm Add-on.
+
+
+## [v1.3.0](https://github.com/ishuar/terraform-aws-eks/compare/v1.2.0...v1.3.0)
### Features
- Optional Use of Created KMS key for encryption in module resources.
-### [v1.2.0](https://github.com/ishuar/terraform-eks/compare/v1.1.0...v1.2.0)
+## [v1.2.0](https://github.com/ishuar/terraform-eks/compare/v1.1.0...v1.2.0)
### Features
@@ -19,7 +30,7 @@ All notable changes to this project will be documented in this file.
- Fix Usage in Readme.
- Fix misleading variables descriptions.
-### [v1.1.0](https://github.com/ishuar/terraform-eks/compare/v1.0.0...v1.1.0)
+## [v1.1.0](https://github.com/ishuar/terraform-eks/compare/v1.0.0...v1.1.0)
### Features
@@ -27,7 +38,7 @@ All notable changes to this project will be documented in this file.
- Example for ALB with aws-alb-controller and external-dns
- Improved Docs
-### [v1.0.0](https://github.com/ishuar/terraform-eks/commits/v1.0.0)
+## [v1.0.0](https://github.com/ishuar/terraform-eks/commits/v1.0.0)
### Features
diff --git a/examples/cluster-autoscaler-helm-add-on/.terraform.lock.hcl b/examples/cluster-autoscaler-helm-add-on/.terraform.lock.hcl
new file mode 100644
index 0000000..69e0d7c
--- /dev/null
+++ b/examples/cluster-autoscaler-helm-add-on/.terraform.lock.hcl
@@ -0,0 +1,83 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+ version = "4.52.0"
+ constraints = "~> 4.0"
+ hashes = [
+ "h1:pTH20eNlkwO3vxYb3f/Dm9QXjhVeidpvrn5YcvydM7Y=",
+ "zh:00c865de3a0e7643f4e2e5c8d4ba91eee94a46d41090eb134baca6b58c107172",
+ "zh:1430682e26eba25d8ace19fa780361187f474153e455545235b4fe30637fdcc2",
+ "zh:1b9a4e5c889bd2022bd59fb924dc78e189f1b7a4fd718fcacda0f0a4cb74d6eb",
+ "zh:2485260141608f1d386d0f68934092bbf68a27d96f0d83c73222d0382aee02f5",
+ "zh:2fe67ee94e2df7dabee7e474356f8e907e7c8011533f9d71df8702d59f9060b2",
+ "zh:37babd1b7ff96ff1f42aa56d7575cacabda6f9f460ff651d70662bfd90076341",
+ "zh:54aa8d39f22ecab6613169f49d37d2ccfaf417e59dd7a8c8fc6bf92600c3384f",
+ "zh:5bf4a84b962a8d2da8f4ccf2a7de56fb6c7a1f566e8393b563977fc7872a8740",
+ "zh:8cb4a51f209a3cc497e53f09188c15c6675697587fe2ea14a6c7fff10c8c8476",
+ "zh:91f6bdcbb1e36471140982e9048b7ced437d3290b2cc21079e5429cc84fed2fd",
+ "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+ "zh:9f8c01c3f677bc64ddefa41e59c6fc98860c11875d7f148af55969d3e3847f77",
+ "zh:b6b4fc0bd6f3c0adcd9531da3ccf8c25787ccd6ccc568f13ebbff1336d71a9e1",
+ "zh:d52a428bd92cc319088685ecac63b9f7d12d4cd6725604edb20d0c4f37a9936e",
+ "zh:e20252a851a0d38548a3c01a006bfc59ee1fc84217bf9eb95b22724769601b2b",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/helm" {
+ version = "2.8.0"
+ hashes = [
+ "h1:a98mBNghv9odh5PVmgdXapgyYJmO/ncAWkwLWdXLuY4=",
+ "zh:1e42d1a04c07d4006844e477ca32b5f45b04f6525dbbbe00b6be6e6ec5a11c54",
+ "zh:2f87187cb48ccfb18d12e2c4332e7e822923b659e7339b954b7db78aff91529f",
+ "zh:391fe49b4d2dc07bc717248a3fc6952189cfc49c596c514ad72a29c9a9f9d575",
+ "zh:89272048e1e63f3edc3e83dfddd5a9fd4bd2a4ead104e67de1e14319294dedf1",
+ "zh:a5a057c3435a854389ce8a1d98a54aaa7cbab68aca7baa436a605897aa70ff7e",
+ "zh:b1098e53e1a8a3afcd325ecd0328662156b3d9c3d80948f19ba3a4eb870cee2b",
+ "zh:b676f949e8274a2b6c3fa41f5428ea597125579c7b93bb50bb73a5e295a7a447",
+ "zh:cdf7e9460f28c2dbfe49a79a5022bd0d474ff18120d340738aa35456ba77ebca",
+ "zh:e24b59b4ed1c593facbf8051ec58550917991e2e017f3085dac5fb902d9908cb",
+ "zh:e3b5e1f5543cac9d9031a028f1c1be4858fb80fae69f181f21e9465e366ebfa2",
+ "zh:e9fddc0bcdb28503078456f0088851d45451600d229975fd9990ee92c7489a10",
+ "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/kubernetes" {
+ version = "2.17.0"
+ hashes = [
+ "h1:Dq/EHg8mKP9wDDTJx5CzZ+w44wutIZJGfQLrAIznAqY=",
+ "zh:1cbafea8c404195d8ad2490d75dbeebef131563d3e38dec87231ceb3923a3012",
+ "zh:26d9584423ee77e607999b082de7d9dc3e937934aa83341e0832e7253caf4f51",
+ "zh:333527fc15fb43bbf1898a2f058598c596468a01d88c415627bb617878dc4d4d",
+ "zh:391b8c80e3115af485977d6e949d7260b7fc0b641089b884256bfd36a7077db2",
+ "zh:4d18ba55247486181759d60195777945bcd68e17ccd980820ca18e8a8b94aeb5",
+ "zh:607ae94d85d1c1ed3845bd71095daadea4b2468e16f57fa05c98eab0de6b14ae",
+ "zh:95c6cf22f8ef14e7a4f85e33cff5d6f11056c7880041b71d425d1b5ebbe246e7",
+ "zh:b077edcedb46a313b461ac1e49317872063b3871f2acbe1a50498612cefff387",
+ "zh:c6a7891683e44148b0c928fd4748b7abac727266ab551d679015f5fe8b72d1e6",
+ "zh:e5cebfdf873770c37a4304362003d3fea8d6c2fd819663ad121bc65bb81e4738",
+ "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+ "zh:feb19269e7c0de473ad412b37818b48da0cc91e5c93dd4c77a72676ca97a16b1",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/tls" {
+ version = "4.0.4"
+ constraints = "~> 4.0"
+ hashes = [
+ "h1:GZcFizg5ZT2VrpwvxGBHQ/hO9r6g0vYdQqx3bFD3anY=",
+ "zh:23671ed83e1fcf79745534841e10291bbf34046b27d6e68a5d0aab77206f4a55",
+ "zh:45292421211ffd9e8e3eb3655677700e3c5047f71d8f7650d2ce30242335f848",
+ "zh:59fedb519f4433c0fdb1d58b27c210b27415fddd0cd73c5312530b4309c088be",
+ "zh:5a8eec2409a9ff7cd0758a9d818c74bcba92a240e6c5e54b99df68fff312bbd5",
+ "zh:5e6a4b39f3171f53292ab88058a59e64825f2b842760a4869e64dc1dc093d1fe",
+ "zh:810547d0bf9311d21c81cc306126d3547e7bd3f194fc295836acf164b9f8424e",
+ "zh:824a5f3617624243bed0259d7dd37d76017097dc3193dac669be342b90b2ab48",
+ "zh:9361ccc7048be5dcbc2fafe2d8216939765b3160bd52734f7a9fd917a39ecbd8",
+ "zh:aa02ea625aaf672e649296bce7580f62d724268189fe9ad7c1b36bb0fa12fa60",
+ "zh:c71b4cd40d6ec7815dfeefd57d88bc592c0c42f5e5858dcc88245d371b4b8b1e",
+ "zh:dabcd52f36b43d250a3d71ad7abfa07b5622c69068d989e60b79b2bb4f220316",
+ "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+ ]
+}
diff --git a/examples/cluster-autoscaler-helm-add-on/README.md b/examples/cluster-autoscaler-helm-add-on/README.md
new file mode 100644
index 0000000..e44a95c
--- /dev/null
+++ b/examples/cluster-autoscaler-helm-add-on/README.md
@@ -0,0 +1,39 @@
+# AWS EKS Cluster Autoscaler Helm Addon
+
+## Overview
+
+Configuration in this directory creates an AWS EKS cluster with the underlying network infrastructure. On top of that, an EKS cluster-autoscaler is deployed which utilizes Amazon EC2 Auto Scaling Groups to manage node groups with a service account having access to AWS using AWS IAM roles.
+
+### Resources created with this configuration
+
+- Network Infrastrucutre and policies for Cluster Autoscaler with [dependencies.tf](dependencies.tf)
+- EKS Cluster with [eks.tf](eks.tf)
+- Cluster Autoscaler helm release and IRSA config with [main.tf](main.tf)
+
+
+### Documentation for More Insights
+
+- [AWS Autoscaling](https://docs.aws.amazon.com/eks/latest/userguide/autoscaling.html)
+- [EKS Best Practices Guide](https://aws.github.io/aws-eks-best-practices/cluster-autoscaling/)
+- [Cluster Autoscaler on AWS](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/aws/README.md)
+- [IAM Roles For Service Account](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)
+
+
+## Applying the Configuration
+
+To run this example you need to execute:
+
+```bash
+terraform init
+terraform plan
+terraform apply
+```
+
+## Destroying Resources
+
+To destroy the resources created by this Terraform configuration, run the following command.
+
+```bash
+terraform destroy -auto-approve # ignore "-auto-approve" if you don't want to autoapprove.
+```
+
diff --git a/examples/cluster-autoscaler-helm-add-on/cluster-autoscaler-values.yaml b/examples/cluster-autoscaler-helm-add-on/cluster-autoscaler-values.yaml
new file mode 100644
index 0000000..20916e6
--- /dev/null
+++ b/examples/cluster-autoscaler-helm-add-on/cluster-autoscaler-values.yaml
@@ -0,0 +1,11 @@
+autoDiscovery:
+ clusterName: ${clusterName}
+ tags:
+ - k8s.io/cluster-autoscaler/enabled
+ - k8s.io/cluster-autoscaler/{{ .Values.autoDiscovery.clusterName }}
+ - kubernetes.io/cluster/{{ .Values.autoDiscovery.clusterName }}
+cloudProvider: aws
+rbac:
+ serviceAccount:
+ create: false
+ name: ${serviceAccountName}
\ No newline at end of file
diff --git a/examples/cluster-autoscaler-helm-add-on/dependencies.tf b/examples/cluster-autoscaler-helm-add-on/dependencies.tf
new file mode 100644
index 0000000..7268d34
--- /dev/null
+++ b/examples/cluster-autoscaler-helm-add-on/dependencies.tf
@@ -0,0 +1,205 @@
+resource "aws_vpc" "eks" {
+ cidr_block = "10.0.0.0/16"
+ tags = merge(local.tags, { Name = "vpc-eks-${local.aws_region}-01" })
+ enable_dns_hostnames = true
+}
+
+resource "aws_subnet" "private_subnets" {
+ for_each = local.private_subnets
+
+ cidr_block = each.key
+ vpc_id = aws_vpc.eks.id
+ availability_zone = each.value.az
+ tags = merge(local.tags, { Name = "${each.value.name}" })
+
+}
+resource "aws_subnet" "public_subnets" {
+ for_each = local.public_subnets
+
+ cidr_block = each.key
+ vpc_id = aws_vpc.eks.id
+ availability_zone = each.value.az
+ map_public_ip_on_launch = true
+ tags = merge(local.tags, { Name = "${each.value.name}" })
+}
+
+## Security Group for Endpoints.
+resource "aws_security_group" "eks_endpoints" {
+ name = "allow-endpoints-eks"
+ description = "Security group for allowing Endpoints within VPC"
+ vpc_id = aws_vpc.eks.id
+
+ tags = merge(
+ {
+ Name = "allow-endpoints-eks"
+ }
+ , local.tags)
+}
+
+resource "aws_security_group_rule" "eks_endpoints" {
+ type = "ingress"
+ from_port = 443
+ to_port = 443
+ protocol = "tcp"
+ description = "To allow https VPC internal traffic"
+ cidr_blocks = [aws_vpc.eks.cidr_block]
+ security_group_id = aws_security_group.eks_endpoints.id
+}
+
+## Additional Security Group for EKS API and NodeGroup Access.
+
+resource "aws_security_group" "eks_additional" {
+ name = "addditional-eks-cluster-access"
+ description = "Additional Security group for allowing access to EKS API and Node group communication."
+ vpc_id = aws_vpc.eks.id
+
+ tags = merge(
+ {
+ Name = "addditional-eks-cluster-access"
+ }
+ , local.tags)
+}
+
+resource "aws_security_group_rule" "eks_additional" {
+ for_each = local.private_subnets
+ type = "ingress"
+ from_port = 443
+ to_port = 443
+ protocol = "tcp"
+ description = "To allow https access from only private subnets ${aws_subnet.private_subnets[each.key].cidr_block}"
+ cidr_blocks = [aws_subnet.private_subnets[each.key].cidr_block]
+ security_group_id = aws_security_group.eks_additional.id
+}
+
+## Public Routing
+resource "aws_route_table" "public" {
+ vpc_id = aws_vpc.eks.id
+
+ tags = merge(
+ { "Name" = "rt-eks-public" },
+ local.tags,
+ )
+}
+
+resource "aws_internet_gateway" "this" {
+ vpc_id = aws_vpc.eks.id
+
+ tags = merge(
+ { "Name" = "vpc-eks-${local.aws_region}-01" },
+ local.tags,
+ )
+}
+
+resource "aws_route" "public" {
+ route_table_id = aws_route_table.public.id
+ destination_cidr_block = "0.0.0.0/0"
+ gateway_id = aws_internet_gateway.this.id
+
+ timeouts {
+ create = "5m"
+ }
+}
+
+resource "aws_route_table_association" "public" {
+ for_each = local.public_subnets
+
+ subnet_id = aws_subnet.public_subnets[each.key].id
+ route_table_id = aws_route_table.public.id
+}
+
+## Private Routing
+
+resource "aws_eip" "eks_nat_eip" {
+ vpc = true
+ tags = merge(
+ {
+ Name = "pip-eks-natgw"
+ },
+ local.tags)
+}
+
+resource "aws_route_table" "private" {
+ vpc_id = aws_vpc.eks.id
+
+ tags = merge(
+ {
+ Name = "rt-eks-private"
+ },
+ local.tags, )
+}
+
+resource "aws_nat_gateway" "this" {
+
+ allocation_id = aws_eip.eks_nat_eip.allocation_id
+ subnet_id = aws_subnet.public_subnets["10.0.2.0/24"].id
+
+ tags = merge(
+ {
+ Name = "vpc-eks-${local.aws_region}-01"
+ },
+ local.tags)
+
+ depends_on = [aws_internet_gateway.this]
+}
+
+resource "aws_route" "private" {
+ route_table_id = aws_route_table.private.id
+ destination_cidr_block = "0.0.0.0/0"
+ nat_gateway_id = aws_nat_gateway.this.id
+
+}
+resource "aws_route_table_association" "private" {
+ for_each = local.private_subnets
+
+ subnet_id = aws_subnet.private_subnets[each.key].id
+ route_table_id = aws_route_table.private.id
+}
+
+## Policies
+
+resource "aws_iam_policy" "cluster_autoscaler_policy" {
+ name = "AmazonEKSClusterAutoscalerPolicy-01"
+ description = "cluster autoscaler policy for AWS autoscaler service account and role"
+ policy = data.aws_iam_policy_document.cluster_autoscaler_policy.json
+}
+
+# https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/aws/README.md#full-cluster-autoscaler-features-policy-recommended
+#tfsec:ignore:aws-iam-no-policy-wildcards
+data "aws_iam_policy_document" "cluster_autoscaler_policy" {
+ version = "2012-10-17"
+
+ statement {
+ sid = "AllowAutoscaling1"
+ effect = "Allow"
+ actions = [
+ "autoscaling:DescribeAutoScalingGroups",
+ "autoscaling:DescribeAutoScalingInstances",
+ "autoscaling:DescribeLaunchConfigurations",
+ "autoscaling:DescribeScalingActivities",
+ "autoscaling:DescribeTags",
+ "ec2:DescribeInstanceTypes",
+ "ec2:DescribeLaunchTemplateVersions"
+ ]
+ resources = ["*"]
+ }
+
+ statement {
+ sid = "AllowAutoscaling2"
+ effect = "Allow"
+ actions = [
+ "autoscaling:SetDesiredCapacity",
+ "autoscaling:TerminateInstanceInAutoScalingGroup",
+ "ec2:DescribeImages",
+ "ec2:GetInstanceTypesFromInstanceRequirements",
+ "eks:DescribeNodegroup"
+ ]
+ condition {
+ test = "StringEquals"
+ variable = "aws:ResourceTag/k8s.io/cluster-autoscaler/${local.cluster_name}"
+ values = [
+ "owned"
+ ]
+ }
+ resources = ["*"]
+ }
+}
diff --git a/examples/cluster-autoscaler-helm-add-on/eks.tf b/examples/cluster-autoscaler-helm-add-on/eks.tf
new file mode 100644
index 0000000..fec3b09
--- /dev/null
+++ b/examples/cluster-autoscaler-helm-add-on/eks.tf
@@ -0,0 +1,38 @@
+module "helm_add_on_eks" {
+ source = "ishuar/eks/aws"
+ version = "~> 1.3"
+
+ name = "${local.tags["github_repo"]}-helm-add-on-example"
+ cluster_iam_role_name = "${local.tags["github_repo"]}-helm-add-on-example"
+ subnet_ids = [aws_subnet.private_subnets["10.0.1.0/24"].id, aws_subnet.private_subnets["10.0.3.0/24"].id]
+ vpc_id = aws_vpc.eks.id
+ cluster_additional_security_group_ids = [aws_security_group.eks_additional.id]
+ create_encryption_kms_key = true
+ use_launch_template = true
+ node_group_iam_role_name = "${local.tags["github_repo"]}-repo-helm-add-on-example-nodegroup-role"
+
+ block_device_mappings = {
+ xvda = {
+ device_name = "/dev/xvda"
+ ebs = {
+ encrypted = true
+ delete_on_termination = true
+ volume_size = 80
+ volume_type = "gp3"
+ }
+ }
+ }
+
+ node_groups = {
+ node_group_001 = {
+ min_size = 0
+ max_size = 2
+ desired_size = 1
+ ami_type = "AL2_x86_64"
+ instance_types = ["t3.medium"]
+ }
+ }
+
+ tags = local.tags
+
+}
diff --git a/examples/cluster-autoscaler-helm-add-on/local.tf b/examples/cluster-autoscaler-helm-add-on/local.tf
new file mode 100644
index 0000000..9dc4f32
--- /dev/null
+++ b/examples/cluster-autoscaler-helm-add-on/local.tf
@@ -0,0 +1,33 @@
+locals {
+
+ aws_region = "eu-central-1"
+
+ private_subnets = {
+ "10.0.1.0/24" = {
+ az = "eu-central-1a"
+ name = "snet-eks-alb-example-private-${local.aws_region}-01"
+ }
+ "10.0.3.0/24" = {
+ az = "eu-central-1b"
+ name = "snet-eks-alb-example-private-${local.aws_region}-02"
+ }
+ }
+ public_subnets = {
+ "10.0.2.0/24" = {
+ az = "eu-central-1a"
+ name = "snet-eks-alb-example-public-${local.aws_region}-01"
+ }
+ "10.0.4.0/24" = {
+ az = "eu-central-1b"
+ name = "snet-eks-alb-example-public-${local.aws_region}-02"
+ }
+ }
+
+ tags = {
+ github_repo = "terraform-aws-eks"
+ example = "helm-add-on"
+ }
+
+ cluster_name = module.helm_add_on_eks.eks_cluster_name
+
+}
diff --git a/examples/cluster-autoscaler-helm-add-on/main.tf b/examples/cluster-autoscaler-helm-add-on/main.tf
new file mode 100644
index 0000000..6af8966
--- /dev/null
+++ b/examples/cluster-autoscaler-helm-add-on/main.tf
@@ -0,0 +1,32 @@
+module "autoscaler_add_on" {
+ source = "ishuar/eks/aws//modules/helm-add-on"
+ version = "~> 1.4"
+
+ name = "cluster-autoscaler-01"
+ repository = "https://kubernetes.github.io/autoscaler"
+ chart = "cluster-autoscaler"
+ chart_version = "9.21.1"
+ namespace = "kube-system"
+
+ values = [
+ templatefile("${path.module}/cluster-autoscaler-values.yaml",
+ {
+ clusterName = "${local.cluster_name}"
+ serviceAccountName = "aws-cluster-autoscaler"
+ }
+ )
+ ]
+ set = {
+ "awsRegion" = local.aws_region
+ }
+
+ # IRSA Attributes
+ enable_irsa = true
+ oidc_issuer_url = module.helm_add_on_eks.eks_cluster_oidc_issuer
+ service_account_name = "aws-cluster-autoscaler"
+ irsa_role_name = "AmazonEKSClusterAutoscalerRole"
+ create_service_account = true # as helm relase values has disabled the creation of service account
+ role_policy_arns = { cluster_autoscaler_policy = aws_iam_policy.cluster_autoscaler_policy.arn }
+
+}
+
diff --git a/examples/cluster-autoscaler-helm-add-on/versions.tf b/examples/cluster-autoscaler-helm-add-on/versions.tf
new file mode 100644
index 0000000..3e920c7
--- /dev/null
+++ b/examples/cluster-autoscaler-helm-add-on/versions.tf
@@ -0,0 +1,34 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 4.0"
+ }
+ }
+}
+
+provider "aws" {
+ region = local.aws_region
+}
+
+provider "helm" {
+ kubernetes {
+ host = module.helm_add_on_eks.eks_cluster_endpoint
+ cluster_ca_certificate = base64decode(module.helm_add_on_eks.eks_cluster_certificate_authority[0].data)
+ exec {
+ api_version = "client.authentication.k8s.io/v1beta1"
+ args = ["eks", "get-token", "--cluster-name", local.cluster_name]
+ command = "aws"
+ }
+ }
+}
+provider "kubernetes" {
+ host = module.helm_add_on_eks.eks_cluster_endpoint
+ cluster_ca_certificate = base64decode(module.helm_add_on_eks.eks_cluster_certificate_authority[0].data)
+ exec {
+ api_version = "client.authentication.k8s.io/v1beta1"
+ args = ["eks", "get-token", "--cluster-name", local.cluster_name]
+ command = "aws"
+ }
+}
+
diff --git a/examples/irsa/.terraform.lock.hcl b/examples/irsa/.terraform.lock.hcl
new file mode 100644
index 0000000..da2d4bc
--- /dev/null
+++ b/examples/irsa/.terraform.lock.hcl
@@ -0,0 +1,65 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+ version = "4.52.0"
+ constraints = "~> 4.0"
+ hashes = [
+ "h1:pTH20eNlkwO3vxYb3f/Dm9QXjhVeidpvrn5YcvydM7Y=",
+ "zh:00c865de3a0e7643f4e2e5c8d4ba91eee94a46d41090eb134baca6b58c107172",
+ "zh:1430682e26eba25d8ace19fa780361187f474153e455545235b4fe30637fdcc2",
+ "zh:1b9a4e5c889bd2022bd59fb924dc78e189f1b7a4fd718fcacda0f0a4cb74d6eb",
+ "zh:2485260141608f1d386d0f68934092bbf68a27d96f0d83c73222d0382aee02f5",
+ "zh:2fe67ee94e2df7dabee7e474356f8e907e7c8011533f9d71df8702d59f9060b2",
+ "zh:37babd1b7ff96ff1f42aa56d7575cacabda6f9f460ff651d70662bfd90076341",
+ "zh:54aa8d39f22ecab6613169f49d37d2ccfaf417e59dd7a8c8fc6bf92600c3384f",
+ "zh:5bf4a84b962a8d2da8f4ccf2a7de56fb6c7a1f566e8393b563977fc7872a8740",
+ "zh:8cb4a51f209a3cc497e53f09188c15c6675697587fe2ea14a6c7fff10c8c8476",
+ "zh:91f6bdcbb1e36471140982e9048b7ced437d3290b2cc21079e5429cc84fed2fd",
+ "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+ "zh:9f8c01c3f677bc64ddefa41e59c6fc98860c11875d7f148af55969d3e3847f77",
+ "zh:b6b4fc0bd6f3c0adcd9531da3ccf8c25787ccd6ccc568f13ebbff1336d71a9e1",
+ "zh:d52a428bd92cc319088685ecac63b9f7d12d4cd6725604edb20d0c4f37a9936e",
+ "zh:e20252a851a0d38548a3c01a006bfc59ee1fc84217bf9eb95b22724769601b2b",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/kubernetes" {
+ version = "2.17.0"
+ constraints = "~> 2.0"
+ hashes = [
+ "h1:Dq/EHg8mKP9wDDTJx5CzZ+w44wutIZJGfQLrAIznAqY=",
+ "zh:1cbafea8c404195d8ad2490d75dbeebef131563d3e38dec87231ceb3923a3012",
+ "zh:26d9584423ee77e607999b082de7d9dc3e937934aa83341e0832e7253caf4f51",
+ "zh:333527fc15fb43bbf1898a2f058598c596468a01d88c415627bb617878dc4d4d",
+ "zh:391b8c80e3115af485977d6e949d7260b7fc0b641089b884256bfd36a7077db2",
+ "zh:4d18ba55247486181759d60195777945bcd68e17ccd980820ca18e8a8b94aeb5",
+ "zh:607ae94d85d1c1ed3845bd71095daadea4b2468e16f57fa05c98eab0de6b14ae",
+ "zh:95c6cf22f8ef14e7a4f85e33cff5d6f11056c7880041b71d425d1b5ebbe246e7",
+ "zh:b077edcedb46a313b461ac1e49317872063b3871f2acbe1a50498612cefff387",
+ "zh:c6a7891683e44148b0c928fd4748b7abac727266ab551d679015f5fe8b72d1e6",
+ "zh:e5cebfdf873770c37a4304362003d3fea8d6c2fd819663ad121bc65bb81e4738",
+ "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+ "zh:feb19269e7c0de473ad412b37818b48da0cc91e5c93dd4c77a72676ca97a16b1",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/tls" {
+ version = "4.0.4"
+ constraints = "~> 4.0"
+ hashes = [
+ "h1:GZcFizg5ZT2VrpwvxGBHQ/hO9r6g0vYdQqx3bFD3anY=",
+ "zh:23671ed83e1fcf79745534841e10291bbf34046b27d6e68a5d0aab77206f4a55",
+ "zh:45292421211ffd9e8e3eb3655677700e3c5047f71d8f7650d2ce30242335f848",
+ "zh:59fedb519f4433c0fdb1d58b27c210b27415fddd0cd73c5312530b4309c088be",
+ "zh:5a8eec2409a9ff7cd0758a9d818c74bcba92a240e6c5e54b99df68fff312bbd5",
+ "zh:5e6a4b39f3171f53292ab88058a59e64825f2b842760a4869e64dc1dc093d1fe",
+ "zh:810547d0bf9311d21c81cc306126d3547e7bd3f194fc295836acf164b9f8424e",
+ "zh:824a5f3617624243bed0259d7dd37d76017097dc3193dac669be342b90b2ab48",
+ "zh:9361ccc7048be5dcbc2fafe2d8216939765b3160bd52734f7a9fd917a39ecbd8",
+ "zh:aa02ea625aaf672e649296bce7580f62d724268189fe9ad7c1b36bb0fa12fa60",
+ "zh:c71b4cd40d6ec7815dfeefd57d88bc592c0c42f5e5858dcc88245d371b4b8b1e",
+ "zh:dabcd52f36b43d250a3d71ad7abfa07b5622c69068d989e60b79b2bb4f220316",
+ "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+ ]
+}
diff --git a/examples/irsa/README.md b/examples/irsa/README.md
new file mode 100644
index 0000000..5c7b002
--- /dev/null
+++ b/examples/irsa/README.md
@@ -0,0 +1,34 @@
+# IAM Roles for Service Account
+
+Configuration in this directory creates an AWS EKS cluster with the underlying network infrastructure. On top of that its creating a example service account which has a role assigned to it with example policy to access AWS.
+
+> The policy arn used in example can be replaced with any sensible policy which can be utilised for some function.
+>> Treat this Policy as an example only.
+
+Refer to [IAM Roles For Service Account](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) for more details.
+
+### Resources created with this configuration
+
+- Network Infrastrucutre and policies for Cluster Autoscaler with [dependencies.tf](dependencies.tf)
+- EKS Cluster with [eks.tf](eks.tf)
+- Service account and IRSA with [main.tf](main.tf)
+
+In the `main.tf` there are two examples `complete_irsa` and `minimal_irsa` which shows maximum(all) and minimum (required) attributes with the module.
+
+## Applying the Configuration
+
+To run this example you need to execute:
+
+```bash
+terraform init
+terraform plan
+terraform apply
+```
+
+## Destroying Resources
+
+To destroy the resources created by this Terraform configuration, run the following command.
+
+```bash
+terraform destroy -auto-approve # ignore "-auto-approve" if you don't want to autoapprove.
+```
\ No newline at end of file
diff --git a/examples/irsa/dependencies.tf b/examples/irsa/dependencies.tf
new file mode 100644
index 0000000..ed7cd9b
--- /dev/null
+++ b/examples/irsa/dependencies.tf
@@ -0,0 +1,171 @@
+resource "aws_vpc" "eks" {
+ cidr_block = "10.0.0.0/16"
+ tags = merge(local.tags, { Name = "vpc-eks-${local.aws_region}-01" })
+ enable_dns_hostnames = true
+}
+
+resource "aws_subnet" "private_subnets" {
+ for_each = local.private_subnets
+
+ cidr_block = each.key
+ vpc_id = aws_vpc.eks.id
+ availability_zone = each.value.az
+ tags = merge(local.tags, { Name = "${each.value.name}" })
+
+}
+resource "aws_subnet" "public_subnets" {
+ for_each = local.public_subnets
+
+ cidr_block = each.key
+ vpc_id = aws_vpc.eks.id
+ availability_zone = each.value.az
+ map_public_ip_on_launch = true
+ tags = merge(local.tags, { Name = "${each.value.name}" })
+}
+
+## Security Group for Endpoints.
+resource "aws_security_group" "eks_endpoints" {
+ name = "allow-endpoints-eks"
+ description = "Security group for allowing Endpoints within VPC"
+ vpc_id = aws_vpc.eks.id
+
+ tags = merge(
+ {
+ Name = "allow-endpoints-eks"
+ }
+ , local.tags)
+}
+resource "aws_security_group_rule" "eks_endpoints" {
+ type = "ingress"
+ from_port = 443
+ to_port = 443
+ protocol = "tcp"
+ description = "To allow https VPC internal traffic"
+ cidr_blocks = [aws_vpc.eks.cidr_block]
+ security_group_id = aws_security_group.eks_endpoints.id
+}
+
+## Additional Security Group for EKS API and NodeGroup Access.
+
+resource "aws_security_group" "eks_additional" {
+ name = "addditional-eks-cluster-access"
+ description = "Additional Security group for allowing access to EKS API and Node group communication."
+ vpc_id = aws_vpc.eks.id
+
+ tags = merge(
+ {
+ Name = "addditional-eks-cluster-access"
+ }
+ , local.tags)
+}
+resource "aws_security_group_rule" "eks_additional" {
+ for_each = local.private_subnets
+ type = "ingress"
+ from_port = 443
+ to_port = 443
+ protocol = "tcp"
+ description = "To allow https access from only private subnets ${aws_subnet.private_subnets[each.key].cidr_block}"
+ cidr_blocks = [aws_subnet.private_subnets[each.key].cidr_block]
+ security_group_id = aws_security_group.eks_additional.id
+}
+
+## Public Routing
+resource "aws_route_table" "public" {
+ vpc_id = aws_vpc.eks.id
+
+ tags = merge(
+ { "Name" = "rt-eks-public" },
+ local.tags,
+ )
+}
+resource "aws_internet_gateway" "this" {
+ vpc_id = aws_vpc.eks.id
+
+ tags = merge(
+ { "Name" = "vpc-eks-${local.aws_region}-01" },
+ local.tags,
+ )
+}
+
+resource "aws_route" "public" {
+ route_table_id = aws_route_table.public.id
+ destination_cidr_block = "0.0.0.0/0"
+ gateway_id = aws_internet_gateway.this.id
+
+ timeouts {
+ create = "5m"
+ }
+}
+resource "aws_route_table_association" "public" {
+ for_each = local.public_subnets
+
+ subnet_id = aws_subnet.public_subnets[each.key].id
+ route_table_id = aws_route_table.public.id
+}
+
+## Private Routing
+
+resource "aws_eip" "eks_nat_eip" {
+ vpc = true
+ tags = merge(
+ {
+ Name = "pip-eks-natgw"
+ },
+ local.tags)
+}
+resource "aws_route_table" "private" {
+ vpc_id = aws_vpc.eks.id
+
+ tags = merge(
+ {
+ Name = "rt-eks-private"
+ },
+ local.tags, )
+}
+resource "aws_nat_gateway" "this" {
+
+ allocation_id = aws_eip.eks_nat_eip.allocation_id
+ subnet_id = aws_subnet.public_subnets["10.0.2.0/24"].id
+
+ tags = merge(
+ {
+ Name = "vpc-eks-${local.aws_region}-01"
+ },
+ local.tags)
+
+ depends_on = [aws_internet_gateway.this]
+}
+
+resource "aws_route" "private" {
+ route_table_id = aws_route_table.private.id
+ destination_cidr_block = "0.0.0.0/0"
+ nat_gateway_id = aws_nat_gateway.this.id
+
+}
+resource "aws_route_table_association" "private" {
+ for_each = local.private_subnets
+
+ subnet_id = aws_subnet.private_subnets[each.key].id
+ route_table_id = aws_route_table.private.id
+}
+
+## Policies
+
+data "aws_iam_policy_document" "example_irsa" {
+ version = "2012-10-17"
+
+ statement {
+ sid = "01"
+ effect = "Allow"
+ actions = [
+ "ec2:DescribeInstanceTypes",
+ ]
+ resources = ["*"]
+ }
+}
+resource "aws_iam_policy" "example_irsa_policy" {
+ for_each = local.service_account_names
+ name = "${each.value}-Policy"
+ description = "Example policy for IRSA service account and role"
+ policy = data.aws_iam_policy_document.example_irsa.json
+}
diff --git a/examples/irsa/eks.tf b/examples/irsa/eks.tf
new file mode 100644
index 0000000..c1a1c9d
--- /dev/null
+++ b/examples/irsa/eks.tf
@@ -0,0 +1,38 @@
+module "irsa_eks" {
+ source = "ishuar/eks/aws"
+ version = "~> 1.3"
+
+ name = "${local.tags["github_repo"]}-irsa-example"
+ cluster_iam_role_name = "${local.tags["github_repo"]}-irsa-example"
+ subnet_ids = [aws_subnet.private_subnets["10.0.1.0/24"].id, aws_subnet.private_subnets["10.0.3.0/24"].id]
+ vpc_id = aws_vpc.eks.id
+ cluster_additional_security_group_ids = [aws_security_group.eks_additional.id]
+ create_encryption_kms_key = true
+ use_launch_template = true
+ node_group_iam_role_name = "${local.tags["github_repo"]}-repo-irsa-example-nodegroup-role"
+
+ block_device_mappings = {
+ xvda = {
+ device_name = "/dev/xvda"
+ ebs = {
+ encrypted = true
+ delete_on_termination = true
+ volume_size = 80
+ volume_type = "gp3"
+ }
+ }
+ }
+
+ node_groups = {
+ node_group_001 = {
+ min_size = 0
+ max_size = 2
+ desired_size = 1
+ ami_type = "AL2_x86_64"
+ instance_types = ["t3.medium"]
+ }
+ }
+
+ tags = local.tags
+
+}
diff --git a/examples/irsa/local.tf b/examples/irsa/local.tf
new file mode 100644
index 0000000..27ee25f
--- /dev/null
+++ b/examples/irsa/local.tf
@@ -0,0 +1,37 @@
+locals {
+
+ aws_region = "eu-central-1"
+
+ private_subnets = {
+ "10.0.1.0/24" = {
+ az = "eu-central-1a"
+ name = "snet-eks-alb-example-private-${local.aws_region}-01"
+ }
+ "10.0.3.0/24" = {
+ az = "eu-central-1b"
+ name = "snet-eks-alb-example-private-${local.aws_region}-02"
+ }
+ }
+ public_subnets = {
+ "10.0.2.0/24" = {
+ az = "eu-central-1a"
+ name = "snet-eks-alb-example-public-${local.aws_region}-01"
+ }
+ "10.0.4.0/24" = {
+ az = "eu-central-1b"
+ name = "snet-eks-alb-example-public-${local.aws_region}-02"
+ }
+ }
+
+ tags = {
+ github_repo = "terraform-aws-eks"
+ example = "irsa"
+ }
+
+ service_account_names = {
+ complete_service_account_name = "irsa-service-account-complete"
+ minimal_service_account_name = "irsa-service-account-minimal"
+ }
+ cluster_name = module.irsa_eks.eks_cluster_name
+
+}
diff --git a/examples/irsa/main.tf b/examples/irsa/main.tf
new file mode 100644
index 0000000..3e198f5
--- /dev/null
+++ b/examples/irsa/main.tf
@@ -0,0 +1,43 @@
+data "aws_caller_identity" "current" {}
+
+module "complete_irsa" {
+ source = "ishuar/eks/aws//modules/irsa"
+ version = "~> 1.4"
+
+ enable_irsa = true
+ oidc_issuer_url = module.irsa_eks.eks_cluster_oidc_issuer
+ irsa_role_name = "${local.service_account_names.complete_service_account_name}-Role"
+ role_path = "/"
+ force_detach_policies = true
+ permissions_boundary = null
+ role_policy_arns = { example_policy = aws_iam_policy.example_irsa_policy["complete_service_account_name"].arn }
+ use_wildcard_service_account_policy = false
+ use_wildcard_namespace_policy = false
+ aws_account_id = data.aws_caller_identity.current.account_id
+ create_kubernetes_namespace = true
+ namespace = "irsa"
+ namespace_labels = { "provisioner" = "terraform" }
+ namespace_annotations = { "environment" = "dev" }
+ create_service_account = true
+ service_account_name = local.service_account_names.complete_service_account_name
+ service_account_annotations = { "provisioner" = "terraform", "environment" = "dev" }
+ automount_service_account_token = true
+
+ depends_on = [module.irsa_eks]
+}
+
+module "minimal_irsa" {
+ source = "ishuar/eks/aws//modules/irsa"
+ version = "~> 1.4"
+
+ oidc_issuer_url = module.irsa_eks.eks_cluster_oidc_issuer
+ service_account_name = local.service_account_names.minimal_service_account_name
+ namespace = "irsa"
+
+
+ depends_on = [module.irsa_eks]
+
+ ## This ROLE on Service Account won't have any policies attached. for attaching policies use variable role_policy_arns
+ ## map of strings is acceptable where key can be any random string and value as a valid policy ARN.
+ # role_policy_arns = {example_policy = aws_iam_policy.example_irsa_policy["minimal_service_account_name"].arn }
+}
diff --git a/examples/irsa/versions.tf b/examples/irsa/versions.tf
new file mode 100644
index 0000000..bc8a737
--- /dev/null
+++ b/examples/irsa/versions.tf
@@ -0,0 +1,23 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 4.0"
+ }
+ }
+}
+
+provider "aws" {
+ region = local.aws_region
+}
+
+provider "kubernetes" {
+ host = module.irsa_eks.eks_cluster_endpoint
+ cluster_ca_certificate = base64decode(module.irsa_eks.eks_cluster_certificate_authority[0].data)
+ exec {
+ api_version = "client.authentication.k8s.io/v1beta1"
+ args = ["eks", "get-token", "--cluster-name", local.cluster_name]
+ command = "aws"
+ }
+}
+
diff --git a/modules/helm-add-on/.config/.terraform-docs.yml b/modules/helm-add-on/.config/.terraform-docs.yml
new file mode 100644
index 0000000..d0b71ba
--- /dev/null
+++ b/modules/helm-add-on/.config/.terraform-docs.yml
@@ -0,0 +1,11 @@
+formatter: md
+header-from: "./.config/header.md"
+footer-from: "./.config/footer.md"
+output:
+ file: README.md
+ mode: replace
+ template: |-
+ {{ .Content }}
+sort:
+ enabled: true
+ by: required
diff --git a/modules/helm-add-on/.config/footer.md b/modules/helm-add-on/.config/footer.md
new file mode 100644
index 0000000..cc8eb76
--- /dev/null
+++ b/modules/helm-add-on/.config/footer.md
@@ -0,0 +1,3 @@
+## License
+
+MIT License. See [LICENSE](https://github.com/ishuar/terraform-aws-eks/blob/main/LICENSE) for full details.
\ No newline at end of file
diff --git a/modules/helm-add-on/.config/header.md b/modules/helm-add-on/.config/header.md
new file mode 100644
index 0000000..43b9da7
--- /dev/null
+++ b/modules/helm-add-on/.config/header.md
@@ -0,0 +1,19 @@
+# Introduction
+This Terraform submodule is designed to create a Helm release and an IAM role for a service account in the Terraform EKS module. The submodule uses Terraform to manage the deployment of the Helm chart and the IAM role.
+
+## Prerequisites
+
+Before using this submodule, you will need to have the following:
+
+- EKS cluster deployed/running.
+- The Helm chart that you want to deploy, either stored locally or in a repository.
+
+
+## Available Features
+
+- Helm Release with all arguments available at [helm_release Terraform resource](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release)
+- [IAM roles For Service Account](../irsa/README.md)
+
+## Examples
+
+- [AWS EKS Cluster Autoscaler](../../examples/cluster-autoscaler-helm-add-on/) as helm add-on.
\ No newline at end of file
diff --git a/modules/helm-add-on/README.md b/modules/helm-add-on/README.md
new file mode 100644
index 0000000..c549350
--- /dev/null
+++ b/modules/helm-add-on/README.md
@@ -0,0 +1,116 @@
+# Introduction
+This Terraform submodule is designed to create a Helm release and an IAM role for a service account in the Terraform EKS module. The submodule uses Terraform to manage the deployment of the Helm chart and the IAM role.
+
+## Prerequisites
+
+Before using this submodule, you will need to have the following:
+
+- EKS cluster deployed/running.
+- The Helm chart that you want to deploy, either stored locally or in a repository.
+
+## Available Features
+
+- Helm Release with all arguments available at [helm\_release Terraform resource](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release)
+- [IAM roles For Service Account](../irsa/README.md)
+
+## Examples
+
+- [AWS EKS Cluster Autoscaler](../../examples/cluster-autoscaler-helm-add-on/) as helm add-on.
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.0.0 |
+| [aws](#requirement\_aws) | ~> 4.0 |
+| [helm](#requirement\_helm) | ~> 2.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [helm](#provider\_helm) | ~> 2.0 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [irsa](#module\_irsa) | ../irsa | n/a |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [helm_release.this](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [chart](#input\_chart) | (Required) Chart name to be installed. The chart name can be local path, a URL to a chart, or the name of the chart if repository is specified. It is also possible to use the / format here if you are running Terraform on a system that the repository has been added to with helm repo add but this is not recommended. | `string` | n/a | yes |
+| [name](#input\_name) | (Required) Release name. | `string` | n/a | yes |
+| [atomic](#input\_atomic) | (Optional) If set, installation process purges chart on fail. The wait flag will be set automatically if atomic is used. Defaults to false. | `bool` | `true` | no |
+| [automount\_service\_account\_token](#input\_automount\_service\_account\_token) | (Optional) To enable automatic mounting of the service account token. Defaults to true | `bool` | `null` | no |
+| [aws\_account\_id](#input\_aws\_account\_id) | (optional) Account ID from where EKS cluster will assume role, could be used in cross account scenarios. | `string` | `""` | no |
+| [chart\_version](#input\_chart\_version) | (Optional) Specify the exact chart version to install. If this is not specified, the latest version is installed. helm\_release will not automatically grab the latest release, version must explicitly upgraded when upgrading an installed chart. | `string` | `null` | no |
+| [cleanup\_on\_fail](#input\_cleanup\_on\_fail) | (Optional) Allow deletion of new resources created in this upgrade when upgrade fails. Defaults to false. | `bool` | `true` | no |
+| [create\_kubernetes\_namespace](#input\_create\_kubernetes\_namespace) | (optional) Whether or not to create kubernetes namespace via terraform-kubernetes-provider resource? Set to true if need to create a new namespace and helm release attribute 'create\_namespace' is set to false | `bool` | `false` | no |
+| [create\_namespace](#input\_create\_namespace) | (Optional) Create the namespace if it does not yet exist. Defaults to false. | `bool` | `false` | no |
+| [create\_service\_account](#input\_create\_service\_account) | (optional) Whether or not to create service account for the helm release? | `bool` | `false` | no |
+| [dependency\_update](#input\_dependency\_update) | (Optional) Runs helm dependency update before installing the chart. Defaults to false. | `bool` | `null` | no |
+| [description](#input\_description) | (Optional) Set release description attibute (visible in the history). | `string` | `null` | no |
+| [devel](#input\_devel) | (Optional) Use chart development versions, too. Equivalent to version '>0.0.0-0'. If version is set, this is ignored. | `string` | `null` | no |
+| [disable\_openapi\_validation](#input\_disable\_openapi\_validation) | (Optional) If set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema. Defaults to false. | `bool` | `null` | no |
+| [disable\_webhooks](#input\_disable\_webhooks) | (Optional) Prevent hooks from running. Defaults to false. | `bool` | `null` | no |
+| [enable\_irsa](#input\_enable\_irsa) | (optional) Whether to use IRSA module for helm release deployment or not? If set to false then all IRSA module resources are disabled. | `bool` | `false` | no |
+| [enable\_postrender](#input\_enable\_postrender) | (Optional) Whether or not to configure a command to run after helm renders the manifest which can alter the manifest contents? | `bool` | `false` | no |
+| [force\_detach\_policies](#input\_force\_detach\_policies) | (optional) Whether to force detaching any policies the role has before destroying it, refer to [TOP NOTE Section](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) for default value. | `bool` | `true` | no |
+| [force\_update](#input\_force\_update) | (Optional) Force resource update through delete/recreate if needed. Defaults to false. | `bool` | `null` | no |
+| [irsa\_role\_name](#input\_irsa\_role\_name) | (optional) Role name created with the IRSA policy | `string` | `""` | no |
+| [keyring](#input\_keyring) | (Optional) Location of public keys used for verification. Used only if verify is true. Defaults to /.gnupg/pubring.gpg in the location set by home | `string` | `null` | no |
+| [lint](#input\_lint) | (Optional) Run the helm chart linter during the plan. Defaults to false. | `bool` | `true` | no |
+| [max\_history](#input\_max\_history) | (Optional) Maximum number of release versions stored per release. Defaults to 0 (no limit). | `number` | `null` | no |
+| [namespace](#input\_namespace) | (Optional) The namespace to install the release into. Defaults to default.This name is also used for creating new namespace via kubernetes-terraform-provider resource in this module when variable `create_namespace` is set to false and 'create\_kubernetes\_namespace' is set to true | `string` | `"default"` | no |
+| [namespace\_annotations](#input\_namespace\_annotations) | (optional) Annotations for namespace created via terraform-kubernetes-provider resource. | `map(string)` | `{}` | no |
+| [namespace\_labels](#input\_namespace\_labels) | (optional)Labels for namespace created via terraform-kubernetes-provider resource. | `map(string)` | `{}` | no |
+| [oidc\_issuer\_url](#input\_oidc\_issuer\_url) | (optional) **Required if enable\_irsa is set to true.**. Issuer URL for the OpenID Connect identity provider. | `string` | `""` | no |
+| [pass\_credentials](#input\_pass\_credentials) | (Optional) Pass credentials to all domains. Defaults to false. | `bool` | `null` | no |
+| [permissions\_boundary](#input\_permissions\_boundary) | (optional) ARN of the policy that is used to set the permissions boundary for the role. | `string` | `null` | no |
+| [postrender\_args](#input\_postrender\_args) | (optional) A list of arguments to supply to the post-renderer | `list(string)` | `[]` | no |
+| [postrender\_binary\_path](#input\_postrender\_binary\_path) | (optional) Relative or full path to command binary.(Required) if enable\_postrender is set to true | `string` | `null` | no |
+| [recreate\_pods](#input\_recreate\_pods) | (Optional) Perform pods restart during upgrade/rollback. Defaults to false. | `bool` | `null` | no |
+| [render\_subchart\_notes](#input\_render\_subchart\_notes) | (Optional) If set, render subchart notes along with the parent. Defaults to true. | `bool` | `null` | no |
+| [replace](#input\_replace) | (Optional) Re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production. Defaults to false. | `bool` | `null` | no |
+| [repository](#input\_repository) | (Optional) Repository URL where to locate the requested chart. | `string` | `null` | no |
+| [repository\_ca\_file](#input\_repository\_ca\_file) | (Optional) The Repositories CA File. | `string` | `null` | no |
+| [repository\_cert\_file](#input\_repository\_cert\_file) | (Optional) The repositories cert file | `string` | `null` | no |
+| [repository\_key\_file](#input\_repository\_key\_file) | (Optional) The repositories cert key file | `string` | `null` | no |
+| [repository\_password](#input\_repository\_password) | (Optional) Password for HTTP basic authentication against the repository. | `string` | `null` | no |
+| [repository\_username](#input\_repository\_username) | (Optional) Username for HTTP basic authentication against the repository. | `string` | `null` | no |
+| [reset\_values](#input\_reset\_values) | (Optional) When upgrading, reset the values to the ones built into the chart. Defaults to false. | `bool` | `null` | no |
+| [reuse\_values](#input\_reuse\_values) | (Optional) When upgrading, reuse the last release's values and merge in any overrides. If 'reset\_values' is specified, this is ignored. Defaults to false. | `bool` | `null` | no |
+| [role\_path](#input\_role\_path) | (optional) Path to the role | `string` | `null` | no |
+| [role\_policy\_arns](#input\_role\_policy\_arns) | (optional) Policies ARNs attached with the IRSA IAM role. Map where key can be any random string and value as a valid policy ARN. | `map(string)` | `{}` | no |
+| [service\_account\_annotations](#input\_service\_account\_annotations) | (optional) Additional Annotations for the new service account created. | `map(string)` | `{}` | no |
+| [service\_account\_name](#input\_service\_account\_name) | (optional) **Required if enable\_irsa is set to true.** .Service Account Name used when created via terraform or from the helm chart/release created service account name for IRSA assume policy. | `string` | `""` | no |
+| [set](#input\_set) | (Optional) Value block with custom values to be merged with the values yaml. | `map(string)` | `{}` | no |
+| [set\_sensitive](#input\_set\_sensitive) | (Optional) Value block with custom sensitive values to be merged with the values yaml that won't be exposed in the plan's diff. | `map(string)` | `{}` | no |
+| [skip\_crds](#input\_skip\_crds) | (Optional) If set, no CRDs will be installed. By default, CRDs are installed if not already present. Defaults to false. | `bool` | `null` | no |
+| [timeout](#input\_timeout) | (Optional) Time in seconds to wait for any individual kubernetes operation (like Jobs for hooks). Defaults to 300 seconds. | `number` | `200` | no |
+| [use\_wildcard\_namespace\_policy](#input\_use\_wildcard\_namespace\_policy) | (optional) Whether to use the wildcard namespace name or not? If set to true then created role can be used by any service account in any namespace. Use it with caution !! | `bool` | `false` | no |
+| [use\_wildcard\_service\_account\_policy](#input\_use\_wildcard\_service\_account\_policy) | (optional) Whether to use the wildcard service account name or not? The created role can be used by any service account, if use\_wildcard\_namespace is set to false then restricts to namespace variable otherwise otherwise globally. Use it with caution !! | `bool` | `false` | no |
+| [values](#input\_values) | (Optional) List of values in raw yaml to pass to helm. Values will be merged, in order, as Helm does with multiple -f options. | `list(string)` | `null` | no |
+| [verify](#input\_verify) | (Optional) Verify the package before installing it. Helm uses a provenance file to verify the integrity of the chart; this must be hosted alongside the chart. For more information see the Helm Documentation. Defaults to false. | `bool` | `null` | no |
+| [wait](#input\_wait) | (Optional) Will wait until all resources are in a ready state before marking the release as successful. It will wait for as long as timeout. Defaults to true. | `bool` | `true` | no |
+| [wait\_for\_jobs](#input\_wait\_for\_jobs) | (Optional) If wait is enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as timeout. Defaults to false. | `bool` | `null` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [metadata](#output\_metadata) | Helm Release metadata block |
+| [name](#output\_name) | Helm release name. |
+
+## License
+
+MIT License. See [LICENSE](https://github.com/ishuar/terraform-aws-eks/blob/main/LICENSE) for full details.
\ No newline at end of file
diff --git a/modules/helm-add-on/main.tf b/modules/helm-add-on/main.tf
new file mode 100644
index 0000000..3f4fee3
--- /dev/null
+++ b/modules/helm-add-on/main.tf
@@ -0,0 +1,91 @@
+resource "helm_release" "this" {
+ name = var.name
+ repository = var.repository
+ chart = var.chart
+ version = var.chart_version
+ timeout = var.timeout
+ values = var.values
+ create_namespace = var.create_namespace
+ namespace = var.namespace
+ lint = var.lint
+ description = var.description
+ repository_key_file = var.repository_key_file
+ repository_cert_file = var.repository_cert_file
+ repository_username = var.repository_username
+ repository_password = var.repository_password
+ verify = var.verify
+ keyring = var.keyring
+ disable_webhooks = var.disable_webhooks
+ reuse_values = var.reuse_values
+ reset_values = var.reset_values
+ force_update = var.force_update
+ recreate_pods = var.recreate_pods
+ cleanup_on_fail = var.cleanup_on_fail
+ max_history = var.max_history
+ atomic = var.atomic
+ skip_crds = var.skip_crds
+ render_subchart_notes = var.render_subchart_notes
+ disable_openapi_validation = var.disable_openapi_validation
+ wait = var.wait
+ wait_for_jobs = var.wait_for_jobs
+ dependency_update = var.dependency_update
+ replace = var.replace
+
+
+ dynamic "postrender" {
+ for_each = var.enable_postrender ? ["postrender_command"] : []
+ content {
+ binary_path = var.postrender_binary_path
+ args = var.postrender_args
+ }
+ }
+
+ dynamic "set" {
+ for_each = try(var.set, {})
+
+ content {
+ name = set.key
+ value = set.value
+ type = "auto"
+ }
+ }
+
+ dynamic "set_sensitive" {
+ for_each = try(var.set_sensitive, {})
+
+ content {
+ name = set_sensitive.key
+ value = set_sensitive.value
+ type = "auto"
+ }
+ }
+ depends_on = [module.irsa]
+}
+
+module "irsa" {
+ source = "../irsa"
+
+ # Required if var.enable_irsa is set to true
+ oidc_issuer_url = var.oidc_issuer_url
+ service_account_name = var.service_account_name
+
+ # global controller, only enables this module resources if set to true. default value is false.
+ enable_irsa = var.enable_irsa
+
+ # optional
+ irsa_role_name = var.irsa_role_name
+ role_path = var.role_path
+ force_detach_policies = var.force_detach_policies
+ permissions_boundary = var.permissions_boundary
+ role_policy_arns = var.role_policy_arns
+ create_service_account = var.create_service_account
+ use_wildcard_service_account_policy = var.use_wildcard_service_account_policy
+ aws_account_id = var.aws_account_id
+ use_wildcard_namespace_policy = var.use_wildcard_namespace_policy # USE THIS WITH CAUTION !!
+ service_account_annotations = var.service_account_annotations
+ automount_service_account_token = var.automount_service_account_token
+ namespace = var.namespace
+ create_kubernetes_namespace = var.create_namespace ? false : var.create_kubernetes_namespace
+ namespace_labels = var.namespace_labels
+ namespace_annotations = var.namespace_annotations
+}
diff --git a/modules/helm-add-on/outputs.tf b/modules/helm-add-on/outputs.tf
new file mode 100644
index 0000000..ee243a9
--- /dev/null
+++ b/modules/helm-add-on/outputs.tf
@@ -0,0 +1,9 @@
+output "metadata" {
+ value = helm_release.this.metadata
+ description = "Helm Release metadata block"
+}
+
+output "name" {
+ value = helm_release.this.name
+ description = "Helm release name."
+}
diff --git a/modules/helm-add-on/variables.tf b/modules/helm-add-on/variables.tf
new file mode 100644
index 0000000..aa9b12b
--- /dev/null
+++ b/modules/helm-add-on/variables.tf
@@ -0,0 +1,350 @@
+############################################
+######## Helm Release variables #############
+############################################
+
+variable "name" {
+ description = "(Required) Release name."
+ type = string
+}
+
+variable "chart" {
+ description = "(Required) Chart name to be installed. The chart name can be local path, a URL to a chart, or the name of the chart if repository is specified. It is also possible to use the / format here if you are running Terraform on a system that the repository has been added to with helm repo add but this is not recommended."
+ type = string
+}
+
+variable "repository" {
+ description = "(Optional) Repository URL where to locate the requested chart."
+ type = string
+ default = null
+}
+
+variable "repository_key_file" {
+ description = "(Optional) The repositories cert key file"
+ type = string
+ default = null
+ sensitive = true
+}
+
+variable "repository_cert_file" {
+ description = "(Optional) The repositories cert file"
+ type = string
+ default = null
+ sensitive = true
+
+}
+
+variable "repository_ca_file" {
+ description = "(Optional) The Repositories CA File."
+ type = string
+ default = null
+ sensitive = true
+}
+
+variable "repository_username" {
+ description = "(Optional) Username for HTTP basic authentication against the repository."
+ type = string
+ default = null
+}
+
+variable "repository_password" {
+ description = "(Optional) Password for HTTP basic authentication against the repository."
+ type = string
+ sensitive = true
+ default = null
+}
+
+variable "devel" {
+ description = "(Optional) Use chart development versions, too. Equivalent to version '>0.0.0-0'. If version is set, this is ignored."
+ type = string
+ default = null
+}
+
+variable "chart_version" {
+ description = "(Optional) Specify the exact chart version to install. If this is not specified, the latest version is installed. helm_release will not automatically grab the latest release, version must explicitly upgraded when upgrading an installed chart."
+ type = string
+ default = null
+}
+
+variable "namespace" {
+ description = "(Optional) The namespace to install the release into. Defaults to default.This name is also used for creating new namespace via kubernetes-terraform-provider resource in this module when variable `create_namespace` is set to false and 'create_kubernetes_namespace' is set to true"
+ type = string
+ default = "default"
+}
+
+variable "verify" {
+ description = "(Optional) Verify the package before installing it. Helm uses a provenance file to verify the integrity of the chart; this must be hosted alongside the chart. For more information see the Helm Documentation. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "keyring" {
+ description = "(Optional) Location of public keys used for verification. Used only if verify is true. Defaults to /.gnupg/pubring.gpg in the location set by home"
+ type = string
+ default = null
+}
+
+variable "timeout" {
+ description = "(Optional) Time in seconds to wait for any individual kubernetes operation (like Jobs for hooks). Defaults to 300 seconds."
+ type = number
+ default = 200
+}
+
+variable "disable_webhooks" {
+ description = "(Optional) Prevent hooks from running. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "reuse_values" {
+ description = "(Optional) When upgrading, reuse the last release's values and merge in any overrides. If 'reset_values' is specified, this is ignored. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "reset_values" {
+ description = "(Optional) When upgrading, reset the values to the ones built into the chart. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "force_update" {
+ description = "(Optional) Force resource update through delete/recreate if needed. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "recreate_pods" {
+ description = "(Optional) Perform pods restart during upgrade/rollback. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "cleanup_on_fail" {
+ description = "(Optional) Allow deletion of new resources created in this upgrade when upgrade fails. Defaults to false."
+ type = bool
+ default = true
+}
+
+variable "max_history" {
+ description = "(Optional) Maximum number of release versions stored per release. Defaults to 0 (no limit)."
+ type = number
+ default = null
+}
+
+variable "atomic" {
+ description = "(Optional) If set, installation process purges chart on fail. The wait flag will be set automatically if atomic is used. Defaults to false."
+ type = bool
+ default = true
+}
+
+variable "skip_crds" {
+ description = "(Optional) If set, no CRDs will be installed. By default, CRDs are installed if not already present. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "render_subchart_notes" {
+ description = "(Optional) If set, render subchart notes along with the parent. Defaults to true."
+ type = bool
+ default = null
+}
+
+variable "disable_openapi_validation" {
+ description = "(Optional) If set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "wait" {
+ description = "(Optional) Will wait until all resources are in a ready state before marking the release as successful. It will wait for as long as timeout. Defaults to true."
+ type = bool
+ default = true
+}
+
+variable "wait_for_jobs" {
+ description = "(Optional) If wait is enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as timeout. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "values" {
+ description = "(Optional) List of values in raw yaml to pass to helm. Values will be merged, in order, as Helm does with multiple -f options."
+ type = list(string)
+ default = null
+}
+
+variable "set" {
+ description = "(Optional) Value block with custom values to be merged with the values yaml."
+ type = map(string)
+ default = {}
+}
+
+variable "set_sensitive" {
+ description = "(Optional) Value block with custom sensitive values to be merged with the values yaml that won't be exposed in the plan's diff."
+ type = map(string)
+ default = {}
+}
+
+variable "dependency_update" {
+ description = "(Optional) Runs helm dependency update before installing the chart. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "replace" {
+ description = "(Optional) Re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "description" {
+ type = string
+ description = "(Optional) Set release description attibute (visible in the history)."
+ default = null
+}
+
+variable "enable_postrender" {
+ description = "(Optional) Whether or not to configure a command to run after helm renders the manifest which can alter the manifest contents?"
+ type = bool
+ default = false
+}
+
+variable "postrender_binary_path" {
+ type = string
+ description = "(optional) Relative or full path to command binary.(Required) if enable_postrender is set to true"
+ default = null
+}
+
+variable "postrender_args" {
+ type = list(string)
+ description = "(optional) A list of arguments to supply to the post-renderer"
+ default = []
+}
+
+variable "pass_credentials" {
+ description = "(Optional) Pass credentials to all domains. Defaults to false."
+ type = bool
+ default = null
+}
+
+variable "lint" {
+ description = "(Optional) Run the helm chart linter during the plan. Defaults to false."
+ type = bool
+ default = true
+}
+
+variable "create_namespace" {
+ description = "(Optional) Create the namespace if it does not yet exist. Defaults to false."
+ type = bool
+ default = false
+}
+
+##########################
+######## IRSA #############
+#########################
+
+variable "enable_irsa" {
+ type = bool
+ description = "(optional) Whether to use IRSA module for helm release deployment or not? If set to false then all IRSA module resources are disabled."
+ default = false
+}
+
+variable "oidc_issuer_url" {
+ type = string
+ description = "(optional) **Required if enable_irsa is set to true.**. Issuer URL for the OpenID Connect identity provider."
+ default = ""
+}
+
+variable "irsa_role_name" {
+ type = string
+ description = "(optional) Role name created with the IRSA policy"
+ default = ""
+}
+
+variable "role_path" {
+ type = string
+ description = "(optional) Path to the role"
+ default = null
+}
+
+variable "force_detach_policies" {
+ type = bool
+ description = "(optional) Whether to force detaching any policies the role has before destroying it, refer to [TOP NOTE Section](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) for default value."
+ default = true
+}
+
+variable "permissions_boundary" {
+ type = string
+ description = "(optional) ARN of the policy that is used to set the permissions boundary for the role."
+ default = null
+}
+
+/*
+ * To avoid the "Terraform cannot predict how many instances will be created error message used a map(strings) is used to create dynamic variable values.
+ * key can be any human readable string to explain policy type.(technically key can be any string)
+*/
+variable "role_policy_arns" {
+ type = map(string)
+ description = "(optional) Policies ARNs attached with the IRSA IAM role. Map where key can be any random string and value as a valid policy ARN."
+ default = {}
+}
+
+variable "use_wildcard_service_account_policy" {
+ type = bool
+ description = "(optional) Whether to use the wildcard service account name or not? The created role can be used by any service account, if use_wildcard_namespace is set to false then restricts to namespace variable otherwise otherwise globally. Use it with caution !!"
+ default = false
+}
+
+variable "use_wildcard_namespace_policy" {
+ type = bool
+ description = "(optional) Whether to use the wildcard namespace name or not? If set to true then created role can be used by any service account in any namespace. Use it with caution !!"
+ default = false
+}
+
+variable "aws_account_id" {
+ type = string
+ description = "(optional) Account ID from where EKS cluster will assume role, could be used in cross account scenarios."
+ default = ""
+}
+
+variable "create_kubernetes_namespace" {
+ type = bool
+ description = "(optional) Whether or not to create kubernetes namespace via terraform-kubernetes-provider resource? Set to true if need to create a new namespace and helm release attribute 'create_namespace' is set to false"
+ default = false
+}
+
+variable "namespace_labels" {
+ type = map(string)
+ description = "(optional)Labels for namespace created via terraform-kubernetes-provider resource."
+ default = {}
+}
+
+variable "namespace_annotations" {
+ type = map(string)
+ description = "(optional) Annotations for namespace created via terraform-kubernetes-provider resource."
+ default = {}
+}
+
+variable "create_service_account" {
+ type = bool
+ description = "(optional) Whether or not to create service account for the helm release?"
+ default = false
+}
+
+variable "service_account_annotations" {
+ type = map(string)
+ description = "(optional) Additional Annotations for the new service account created."
+ default = {}
+}
+
+variable "service_account_name" {
+ type = string
+ description = "(optional) **Required if enable_irsa is set to true.** .Service Account Name used when created via terraform or from the helm chart/release created service account name for IRSA assume policy."
+ default = ""
+}
+
+variable "automount_service_account_token" {
+ type = bool
+ description = "(Optional) To enable automatic mounting of the service account token. Defaults to true"
+ default = null
+}
diff --git a/modules/helm-add-on/versions.tf b/modules/helm-add-on/versions.tf
new file mode 100644
index 0000000..7ecce9c
--- /dev/null
+++ b/modules/helm-add-on/versions.tf
@@ -0,0 +1,15 @@
+terraform {
+
+ required_version = ">= 1.0.0"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 4.0"
+ }
+ helm = {
+ source = "hashicorp/helm"
+ version = "~> 2.0"
+ }
+ }
+}
diff --git a/modules/irsa/.config/.terraform-docs.yml b/modules/irsa/.config/.terraform-docs.yml
new file mode 100644
index 0000000..d0b71ba
--- /dev/null
+++ b/modules/irsa/.config/.terraform-docs.yml
@@ -0,0 +1,11 @@
+formatter: md
+header-from: "./.config/header.md"
+footer-from: "./.config/footer.md"
+output:
+ file: README.md
+ mode: replace
+ template: |-
+ {{ .Content }}
+sort:
+ enabled: true
+ by: required
diff --git a/modules/irsa/.config/footer.md b/modules/irsa/.config/footer.md
new file mode 100644
index 0000000..cc8eb76
--- /dev/null
+++ b/modules/irsa/.config/footer.md
@@ -0,0 +1,3 @@
+## License
+
+MIT License. See [LICENSE](https://github.com/ishuar/terraform-aws-eks/blob/main/LICENSE) for full details.
\ No newline at end of file
diff --git a/modules/irsa/.config/header.md b/modules/irsa/.config/header.md
new file mode 100644
index 0000000..009e1c0
--- /dev/null
+++ b/modules/irsa/.config/header.md
@@ -0,0 +1,23 @@
+# Introduction
+
+IRSA stands for IAM Roles for Service Accounts. It provides the ability to manage credentials for your applications, similar to the way that AmazonEC2 instance profiles provide credentials to Amazon EC2 instances. Instead of creating and distributing your AWS credentialsto the containers or using the Amazon EC2 instance's role, you associate an IAM role with a Kubernetes service account andconfigure your pods to use the service account. You can't use IAM roles for service accounts with local clusters for AmazonEKS on AWS Outposts.
+
+AWS Official Documentation on [IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)
+
+## Prerequisites
+
+Before using this submodule, you will need to have the following:
+
+- EKS cluster deployed/running.
+
+## Available Features
+
+- Can create a new kubernetes service account.
+- Can create a new kubernetes namespace.
+- Can create a role with Assumable policy by the kubernetes service account in the respective EKS cluster.
+- Attach input `role_policy_arns` with the Role for Kubernetes Service Account.
+- Wildcard Service Account and Wildcard Namespace assumable policies. (:warning: Use With Caution :warning:)
+
+### Examples
+- [Minimal required options](../../examples/irsa/main.tf)
+- [Complete options](../../examples/irsa/main.tf)
diff --git a/modules/irsa/README.md b/modules/irsa/README.md
new file mode 100644
index 0000000..5af8aa3
--- /dev/null
+++ b/modules/irsa/README.md
@@ -0,0 +1,88 @@
+# Introduction
+
+IRSA stands for IAM Roles for Service Accounts. It provides the ability to manage credentials for your applications, similar to the way that AmazonEC2 instance profiles provide credentials to Amazon EC2 instances. Instead of creating and distributing your AWS credentialsto the containers or using the Amazon EC2 instance's role, you associate an IAM role with a Kubernetes service account andconfigure your pods to use the service account. You can't use IAM roles for service accounts with local clusters for AmazonEKS on AWS Outposts.
+
+AWS Official Documentation on [IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)
+
+## Prerequisites
+
+Before using this submodule, you will need to have the following:
+
+- EKS cluster deployed/running.
+
+## Available Features
+
+- Can create a new kubernetes service account.
+- Can create a new kubernetes namespace.
+- Can create a role with Assumable policy by the kubernetes service account in the respective EKS cluster.
+- Attach input `role_policy_arns` with the Role for Kubernetes Service Account.
+- Wildcard Service Account and Wildcard Namespace assumable policies. (:warning: Use With Caution :warning:)
+
+### Examples
+- [Minimal required options](../../examples/irsa/main.tf)
+- [Complete options](../../examples/irsa/main.tf)
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.0.0 |
+| [aws](#requirement\_aws) | ~> 4.0 |
+| [kubernetes](#requirement\_kubernetes) | ~> 2.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | ~> 4.0 |
+| [kubernetes](#provider\_kubernetes) | ~> 2.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [kubernetes_namespace_v1.this](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace_v1) | resource |
+| [kubernetes_service_account_v1.this](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service_account_v1) | resource |
+| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
+| [aws_iam_policy_document.irsa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [namespace](#input\_namespace) | (Required) The namespace where service account will be created. New will be created if value is not equeal to kube-sytem and default | `string` | n/a | yes |
+| [oidc\_issuer\_url](#input\_oidc\_issuer\_url) | (Required)Issuer URL for the OpenID Connect identity provider. | `string` | n/a | yes |
+| [service\_account\_name](#input\_service\_account\_name) | (Required) Service Account Name if needs to create new via terraform or from the helm chart/release created service account name for IRSA assume policy. | `string` | n/a | yes |
+| [automount\_service\_account\_token](#input\_automount\_service\_account\_token) | (Optional) To enable automatic mounting of the service account token. Defaults to true | `bool` | `null` | no |
+| [aws\_account\_id](#input\_aws\_account\_id) | (optional) Account ID from where EKS cluster will assume role, could be used in cross account scenarios. | `string` | `""` | no |
+| [create\_kubernetes\_namespace](#input\_create\_kubernetes\_namespace) | (optional) Whether or not to create kubernetes namespace via terraform-kubernetes-provider resource? Set to true if need to create a new namespace and helm release attribute 'create\_namespace' is set to false | `bool` | `false` | no |
+| [create\_service\_account](#input\_create\_service\_account) | (optional) Whether or not to create service account for the helm release? | `bool` | `false` | no |
+| [enable\_irsa](#input\_enable\_irsa) | Whether to use IRSA for helm release deployment or not? | `bool` | `true` | no |
+| [force\_detach\_policies](#input\_force\_detach\_policies) | (optional) Whether to force detaching any policies the role has before destroying it, refer to [TOP NOTE Section](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) for default value. | `bool` | `true` | no |
+| [irsa\_role\_name](#input\_irsa\_role\_name) | (optional) Role name created with the IRSA policy | `string` | `""` | no |
+| [namespace\_annotations](#input\_namespace\_annotations) | (optional) Annotations for namespace created via terraform-kubernetes-provider resource. | `map(string)` | `{}` | no |
+| [namespace\_labels](#input\_namespace\_labels) | (optional)Labels for namespace created via terraform-kubernetes-provider resource. | `map(string)` | `{}` | no |
+| [permissions\_boundary](#input\_permissions\_boundary) | (optional) ARN of the policy that is used to set the permissions boundary for the role. | `string` | `null` | no |
+| [role\_path](#input\_role\_path) | (optional) Path to the role | `string` | `null` | no |
+| [role\_policy\_arns](#input\_role\_policy\_arns) | (optional) Policies ARNs attached with the IRSA IAM role. Map where key can be any random string and value as a valid policy ARN. | `map(string)` | `{}` | no |
+| [service\_account\_annotations](#input\_service\_account\_annotations) | (optional) Additional Annotations for the new service account created. | `map(string)` | `{}` | no |
+| [use\_wildcard\_namespace\_policy](#input\_use\_wildcard\_namespace\_policy) | (optional) Whether to use the wildcard namespace name or not? If set to true then created role can be used by any service account in any namespace. Use it with caution !! | `bool` | `false` | no |
+| [use\_wildcard\_service\_account\_policy](#input\_use\_wildcard\_service\_account\_policy) | (optional) Whether to use the wildcard service account name or not? The created role can be used by any service account, if use\_wildcard\_namespace is set to false then restricts to namespace variable otherwise otherwise globally. Use it with caution !! | `bool` | `false` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [irsa\_role\_name](#output\_irsa\_role\_name) | IAM role for the EKS Service Account |
+| [namespace](#output\_namespace) | Name of kubernetes namespace |
+| [service\_account\_name](#output\_service\_account\_name) | Name of kubernetes Service Account |
+
+## License
+
+MIT License. See [LICENSE](https://github.com/ishuar/terraform-aws-eks/blob/main/LICENSE) for full details.
\ No newline at end of file
diff --git a/modules/irsa/main.tf b/modules/irsa/main.tf
new file mode 100644
index 0000000..a8c1123
--- /dev/null
+++ b/modules/irsa/main.tf
@@ -0,0 +1,72 @@
+data "aws_caller_identity" "current" {}
+
+locals {
+ wildcard_service_account_namespace = var.use_wildcard_service_account_policy && var.use_wildcard_namespace_policy ? "*" : var.namespace
+ oidc_issuer_url_without_http = replace(var.oidc_issuer_url, "https://", "")
+ aws_account_id = var.aws_account_id != "" ? var.aws_account_id : data.aws_caller_identity.current.account_id
+}
+
+data "aws_iam_policy_document" "irsa" {
+ count = var.enable_irsa ? 1 : 0
+
+ statement {
+ effect = "Allow"
+ actions = ["sts:AssumeRoleWithWebIdentity"]
+ principals {
+ type = "Federated"
+ identifiers = [
+ "arn:aws:iam::${local.aws_account_id}:oidc-provider/${local.oidc_issuer_url_without_http}"
+ ]
+ }
+
+ dynamic "condition" {
+ for_each = var.use_wildcard_service_account_policy ? [] : ["noWildcardServiceaccount"]
+
+ content {
+ test = "StringEquals"
+ variable = "${local.oidc_issuer_url_without_http}:sub"
+ values = [
+ "system:serviceaccount:${var.namespace}:${var.service_account_name}"
+ ]
+ }
+ }
+
+ dynamic "condition" {
+ for_each = var.use_wildcard_service_account_policy ? ["wildcardServiceaccount"] : []
+
+ content {
+ test = "StringLike"
+ variable = "${local.oidc_issuer_url_without_http}:sub"
+ values = [
+ "system:serviceaccount:${local.wildcard_service_account_namespace}:*"
+ ]
+ }
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "${local.oidc_issuer_url_without_http}:aud"
+ values = [
+ "sts.amazonaws.com"
+ ]
+ }
+ }
+}
+
+resource "aws_iam_role" "this" {
+ count = var.enable_irsa ? 1 : 0
+
+ name = var.irsa_role_name == "" ? "${var.service_account_name}-role" : var.irsa_role_name
+ path = var.role_path
+ force_detach_policies = var.force_detach_policies
+ description = "IAM role for the EKS Service Account: ${var.service_account_name}"
+ assume_role_policy = data.aws_iam_policy_document.irsa[0].json
+ permissions_boundary = var.permissions_boundary
+}
+
+resource "aws_iam_role_policy_attachment" "this" {
+ for_each = var.enable_irsa ? var.role_policy_arns : {}
+
+ role = aws_iam_role.this[0].name
+ policy_arn = each.value
+}
diff --git a/modules/irsa/outputs.tf b/modules/irsa/outputs.tf
new file mode 100644
index 0000000..1e946d9
--- /dev/null
+++ b/modules/irsa/outputs.tf
@@ -0,0 +1,14 @@
+output "service_account_name" {
+ value = try(kubernetes_service_account_v1.this[0].metadata[0].name, var.service_account_name)
+ description = "Name of kubernetes Service Account"
+}
+
+output "namespace" {
+ value = try(kubernetes_namespace_v1.this[0].metadata[0].name, var.namespace)
+ description = "Name of kubernetes namespace"
+}
+
+output "irsa_role_name" {
+ value = try(aws_iam_role.this[0].name, var.irsa_role_name)
+ description = "IAM role for the EKS Service Account"
+}
diff --git a/modules/irsa/service_account.tf b/modules/irsa/service_account.tf
new file mode 100644
index 0000000..3b699f3
--- /dev/null
+++ b/modules/irsa/service_account.tf
@@ -0,0 +1,28 @@
+locals {
+ create_namespace_via_this_module = var.enable_irsa && var.create_kubernetes_namespace && var.namespace != "kube-system" && var.namespace != "default" ? 1 : 0
+}
+
+resource "kubernetes_namespace_v1" "this" {
+ count = local.create_namespace_via_this_module
+
+ metadata {
+ labels = var.namespace_labels
+ name = var.namespace
+ annotations = var.namespace_annotations
+ }
+
+ timeouts {
+ delete = "15m"
+ }
+}
+
+resource "kubernetes_service_account_v1" "this" {
+ count = var.create_service_account && var.enable_irsa ? 1 : 0
+
+ automount_service_account_token = var.automount_service_account_token
+ metadata {
+ name = var.service_account_name
+ namespace = var.namespace
+ annotations = merge({ "eks.amazonaws.com/role-arn" = aws_iam_role.this[0].arn }, var.service_account_annotations)
+ }
+}
diff --git a/modules/irsa/variables.tf b/modules/irsa/variables.tf
new file mode 100644
index 0000000..1cb38fb
--- /dev/null
+++ b/modules/irsa/variables.tf
@@ -0,0 +1,116 @@
+
+##########################
+######## IRSA #############
+#########################
+
+variable "enable_irsa" {
+ type = bool
+ description = "Whether to use IRSA for helm release deployment or not?"
+ default = true
+}
+
+variable "oidc_issuer_url" {
+ type = string
+ description = "(Required)Issuer URL for the OpenID Connect identity provider."
+}
+
+variable "irsa_role_name" {
+ type = string
+ description = "(optional) Role name created with the IRSA policy"
+ default = ""
+}
+
+variable "role_path" {
+ type = string
+ description = "(optional) Path to the role"
+ default = null
+}
+
+variable "force_detach_policies" {
+ type = bool
+ description = "(optional) Whether to force detaching any policies the role has before destroying it, refer to [TOP NOTE Section](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) for default value."
+ default = true
+}
+
+variable "permissions_boundary" {
+ type = string
+ description = "(optional) ARN of the policy that is used to set the permissions boundary for the role."
+ default = null
+}
+
+/*
+ * To avoid the "Terraform cannot predict how many instances will be created error message used a map(strings) is used to create dynamic variable values.
+ * key can be any human readable string to explain policy type.(technically key can be any string)
+*/
+variable "role_policy_arns" {
+ type = map(string)
+ description = "(optional) Policies ARNs attached with the IRSA IAM role. Map where key can be any random string and value as a valid policy ARN."
+ default = {}
+}
+
+variable "use_wildcard_service_account_policy" {
+ type = bool
+ description = "(optional) Whether to use the wildcard service account name or not? The created role can be used by any service account, if use_wildcard_namespace is set to false then restricts to namespace variable otherwise otherwise globally. Use it with caution !!"
+ default = false
+}
+
+variable "use_wildcard_namespace_policy" {
+ type = bool
+ description = "(optional) Whether to use the wildcard namespace name or not? If set to true then created role can be used by any service account in any namespace. Use it with caution !!"
+ default = false
+}
+
+variable "aws_account_id" {
+ type = string
+ description = "(optional) Account ID from where EKS cluster will assume role, could be used in cross account scenarios."
+ default = ""
+}
+
+############################################
+######## Service Account variables ##########
+############################################
+
+variable "create_kubernetes_namespace" {
+ type = bool
+ description = "(optional) Whether or not to create kubernetes namespace via terraform-kubernetes-provider resource? Set to true if need to create a new namespace and helm release attribute 'create_namespace' is set to false"
+ default = false
+}
+variable "namespace" {
+ description = "(Required) The namespace where service account will be created. New will be created if value is not equeal to kube-sytem and default"
+ type = string
+}
+
+variable "namespace_labels" {
+ type = map(string)
+ description = "(optional)Labels for namespace created via terraform-kubernetes-provider resource."
+ default = {}
+}
+
+variable "namespace_annotations" {
+ type = map(string)
+ description = "(optional) Annotations for namespace created via terraform-kubernetes-provider resource."
+ default = {}
+}
+
+variable "create_service_account" {
+ type = bool
+ description = "(optional) Whether or not to create service account for the helm release?"
+ default = false
+}
+
+variable "service_account_annotations" {
+ type = map(string)
+ description = "(optional) Additional Annotations for the new service account created."
+ default = {}
+}
+
+variable "service_account_name" {
+ type = string
+ description = "(Required) Service Account Name if needs to create new via terraform or from the helm chart/release created service account name for IRSA assume policy."
+}
+
+variable "automount_service_account_token" {
+ type = bool
+ description = "(Optional) To enable automatic mounting of the service account token. Defaults to true"
+ default = null
+}
diff --git a/modules/irsa/versions.tf b/modules/irsa/versions.tf
new file mode 100644
index 0000000..c315b20
--- /dev/null
+++ b/modules/irsa/versions.tf
@@ -0,0 +1,15 @@
+terraform {
+
+ required_version = ">= 1.0.0"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 4.0"
+ }
+ kubernetes = {
+ source = "hashicorp/kubernetes"
+ version = "~> 2.0"
+ }
+ }
+}