From 9b22e38d450abafd16afe86220d6a502b1a3b4fa Mon Sep 17 00:00:00 2001 From: campaand Date: Thu, 7 Apr 2022 20:35:53 +0200 Subject: [PATCH] Add all file for terraform module alb-ingress-controller --- .gitignore | 2 + AUTHORS.md | 5 + CHANGELOG.md | 18 +++ README.md | 69 +++++++- load-balancer-controller-policy.json | 219 ++++++++++++++++++++++++++ load-balancer-role-trust-policy.tftpl | 18 +++ main.tf | 87 ++++++++++ outputs.tf | 6 + variables.tf | 33 ++++ versions.tf | 19 +++ 10 files changed, 474 insertions(+), 2 deletions(-) create mode 100644 AUTHORS.md create mode 100644 CHANGELOG.md create mode 100644 load-balancer-controller-policy.json create mode 100644 load-balancer-role-trust-policy.tftpl create mode 100644 main.tf create mode 100644 outputs.tf create mode 100644 variables.tf create mode 100644 versions.tf diff --git a/.gitignore b/.gitignore index 7a3e2fd..dbf89f6 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ override.tf.json # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan # example: *tfplan* + +examples/* \ No newline at end of file diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..b8987c9 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,5 @@ +# Authors + +## Maintainer + +- Andrea Campice <a.campice@gmail.com> \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..712e158 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +# 1.0.0 - (07 April 2022) + +## Breaking Changes + +* First Release + +# 0.1.0 - (07 April 2022) + +## Breaking Changes + +* Test terraform module release \ No newline at end of file diff --git a/README.md b/README.md index d41cef2..41f24ff 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,67 @@ -# terraform-aws-alb-ingress-controller -Terraform module to ease deployment of the AWS ALB Ingress Controller, based on best practices. +# Terraform module: AWS ALB Ingress Controller + +This Terraform module can be used to install the [AWS ALB Ingress Controller](https://github.com/kubernetes-sigs/aws-alb-ingress-controller) +into a Kubernetes cluster. + +IMPORTANT WARNING: considering that the kubernetes provider, in order to run the plan and apply, must be able to access the cluster api, it is not possible to insert the module within the same file and / or terraform configuration that creates the EKS cluster as it is not the cluster would exist. + +For this reason, I recommend separating the terraform configurations and launching the one containing this module and other modules like this one at a later time. [HERE THE ISSUE DIRECTLY FROM KUBERNETES PROVIDER](https://github.com/hashicorp/terraform-provider-kubernetes-alpha/issues/199#issuecomment-832614387) + +## Examples + +### EKS Basic Deployment + +To deploy the AWS ALB Ingress Controller into an EKS cluster. + +```hcl +module "alb_controller" { + source = "campaand/alb-controller/aws" + version = "0.1.0" + + cluster_name = var.cluster_name + vpc_id = module.vpc.vpc_id + oidc_provider = module.eks.odic_provider +} +``` + +### EKS Deployment with Different Namespace + +To deploy the AWS ALB Ingress Controller into an EKS cluster using different custom namespace, if not exist, the namespace will be created. + +```hcl +module "alb_controller" { + source = "campaand/alb-controller/aws" + version = "0.1.0" + + namespace = "your-custom-namespace" + cluster_name = var.cluster_name + vpc_id = module.vpc.vpc_id + oidc_provider = module.eks.odic_provider +} +``` + +### EKS Deployment with Different Helm Settings + +To deploy the AWS ALB Ingress Controller into an EKS cluster using different helm parameters based on [Helm Chart Values](https://github.com/kubernetes-sigs/aws-alb-ingress-controller). + +If you need to insert custom annotations for Ingresses and Services, consult [ALB Controller Annotations](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.2/guide/ingress/annotations/). + +```hcl +module "alb_controller" { + source = "campaand/alb-controller/aws" + version = "0.1.0" + + cluster_name = var.cluster_name + vpc_id = module.vpc.vpc_id + oidc_provider = module.eks.odic_provider + + helm_chart_version = "1.4.1" + + settings = { + key1 = value1, + key2 = value2, + key3 = value3, + key4 = value4 + } +} +``` \ No newline at end of file diff --git a/load-balancer-controller-policy.json b/load-balancer-controller-policy.json new file mode 100644 index 0000000..a9061bd --- /dev/null +++ b/load-balancer-controller-policy.json @@ -0,0 +1,219 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "iam:CreateServiceLinkedRole" + ], + "Resource": "*", + "Condition": { + "StringEquals": { + "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DescribeAccountAttributes", + "ec2:DescribeAddresses", + "ec2:DescribeAvailabilityZones", + "ec2:DescribeInternetGateways", + "ec2:DescribeVpcs", + "ec2:DescribeVpcPeeringConnections", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeInstances", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeTags", + "ec2:GetCoipPoolUsage", + "ec2:DescribeCoipPools", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:DescribeLoadBalancerAttributes", + "elasticloadbalancing:DescribeListeners", + "elasticloadbalancing:DescribeListenerCertificates", + "elasticloadbalancing:DescribeSSLPolicies", + "elasticloadbalancing:DescribeRules", + "elasticloadbalancing:DescribeTargetGroups", + "elasticloadbalancing:DescribeTargetGroupAttributes", + "elasticloadbalancing:DescribeTargetHealth", + "elasticloadbalancing:DescribeTags" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "cognito-idp:DescribeUserPoolClient", + "acm:ListCertificates", + "acm:DescribeCertificate", + "iam:ListServerCertificates", + "iam:GetServerCertificate", + "waf-regional:GetWebACL", + "waf-regional:GetWebACLForResource", + "waf-regional:AssociateWebACL", + "waf-regional:DisassociateWebACL", + "wafv2:GetWebACL", + "wafv2:GetWebACLForResource", + "wafv2:AssociateWebACL", + "wafv2:DisassociateWebACL", + "shield:GetSubscriptionState", + "shield:DescribeProtection", + "shield:CreateProtection", + "shield:DeleteProtection" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateSecurityGroup" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateTags" + ], + "Resource": "arn:aws:ec2:*:*:security-group/*", + "Condition": { + "StringEquals": { + "ec2:CreateAction": "CreateSecurityGroup" + }, + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateTags", + "ec2:DeleteTags" + ], + "Resource": "arn:aws:ec2:*:*:security-group/*", + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress", + "ec2:DeleteSecurityGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:CreateLoadBalancer", + "elasticloadbalancing:CreateTargetGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:CreateListener", + "elasticloadbalancing:DeleteListener", + "elasticloadbalancing:CreateRule", + "elasticloadbalancing:DeleteRule" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" + ], + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:ModifyLoadBalancerAttributes", + "elasticloadbalancing:SetIpAddressType", + "elasticloadbalancing:SetSecurityGroups", + "elasticloadbalancing:SetSubnets", + "elasticloadbalancing:DeleteLoadBalancer", + "elasticloadbalancing:ModifyTargetGroup", + "elasticloadbalancing:ModifyTargetGroupAttributes", + "elasticloadbalancing:DeleteTargetGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:RegisterTargets", + "elasticloadbalancing:DeregisterTargets" + ], + "Resource": "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*" + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:SetWebAcl", + "elasticloadbalancing:ModifyListener", + "elasticloadbalancing:AddListenerCertificates", + "elasticloadbalancing:RemoveListenerCertificates", + "elasticloadbalancing:ModifyRule" + ], + "Resource": "*" + } + ] +} \ No newline at end of file diff --git a/load-balancer-role-trust-policy.tftpl b/load-balancer-role-trust-policy.tftpl new file mode 100644 index 0000000..f7c6c37 --- /dev/null +++ b/load-balancer-role-trust-policy.tftpl @@ -0,0 +1,18 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam::${customer_account_id}:oidc-provider/${oidc_provider}" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "${oidc_provider}:aud": "sts.amazonaws.com", + "${oidc_provider}:sub": "system:serviceaccount:${namespace}:aws-load-balancer-controller" + } + } + } + ] +} \ No newline at end of file diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..8402f50 --- /dev/null +++ b/main.tf @@ -0,0 +1,87 @@ +# Created by Andrea Campice + +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} + +resource "aws_iam_policy" "this" { + name = "AWSLoadBalancerControllerIAMPolicy" + description = "AWS Load Balancer Controller Policy" + policy = file("${path.module}/load-balancer-controller-policy.json") +} + +resource "aws_iam_role" "this" { + name = "AWSEKSLoadBalancerControllerRole" + description = "AWS EKS Load Balancer Controller Role" + assume_role_policy = templatefile("${path.module}/load-balancer-role-trust-policy.tftpl", { "customer_account_id" = "${data.aws_caller_identity.current.account_id}", "oidc_provider" = "${var.oidc_provider}", "namespace" = "${var.namespace}" }) +} + +resource "aws_iam_role_policy_attachment" "this" { + role = aws_iam_role.this.name + policy_arn = aws_iam_policy.this.arn +} + +resource "kubernetes_namespace_v1" "this" { + count = var.namespace != "kube-system" ? 1 : 0 + metadata { + name = var.namespace + } +} + +resource "kubernetes_manifest" "this" { + manifest = { + "apiVersion" = "v1" + "kind" = "ServiceAccount" + "metadata" = { + "annotations" = { + "eks.amazonaws.com/role-arn" = "${aws_iam_role.this.arn}" + } + "labels" = { + "app.kubernetes.io/component" = "controller" + "app.kubernetes.io/name" = "aws-load-balancer-controller" + } + "name" = "aws-load-balancer-controller" + "namespace" = "${var.namespace}" + } + } + depends_on = [ + kubernetes_namespace_v1.this + ] +} + +resource "helm_release" "alb_ingress_controller" { + chart = "aws-load-balancer-controller" + create_namespace = true + namespace = var.namespace + name = "aws-load-balancer-controller" + repository = "https://aws.github.io/eks-charts" + version = var.helm_chart_version + cleanup_on_fail = true + recreate_pods = true + replace = true + set { + name = "image.repository" + value = "docker.io/amazon/aws-alb-ingress-controller" + } + set { + name = "clusterName" + value = var.cluster_name + } + set { + name = "serviceAccount.create" + value = "false" + } + set { + name = "serviceAccount.name" + value = "aws-load-balancer-controller" + } + dynamic "set" { + for_each = var.settings + content { + name = set.key + value = set.value + } + } + depends_on = [ + kubernetes_manifest.this + ] +} \ No newline at end of file diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..8dd83f2 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,6 @@ +# Created by Andrea Campice + +output "aws_iam_role_arn" { + description = "ARN of the IAM role that is being utilized by the deployed controller" + value = aws_iam_role.this.arn +} \ No newline at end of file diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..9bdd957 --- /dev/null +++ b/variables.tf @@ -0,0 +1,33 @@ +# Created by Andrea Campice + +variable "oidc_provider" { + type = string + description = "Additional settings which will be passed to the Helm chart values" +} + +variable "vpc_id" { + type = string + description = "Additional settings which will be passed to the Helm chart values" +} + +variable "cluster_name" { + type = string + description = "Additional settings which will be passed to the Helm chart values" +} + +variable "namespace" { + type = string + default = "kube-system" +} + +variable "helm_chart_version" { + type = string + default = "1.4.1" + description = "Version for Helm Chart, not Application Version, https://artifacthub.io/packages/helm/aws/aws-load-balancer-controller" +} + +variable "settings" { + type = map(any) + default = {} + description = "Additional settings which will be passed to the Helm chart values" +} \ No newline at end of file diff --git a/versions.tf b/versions.tf new file mode 100644 index 0000000..c5eff14 --- /dev/null +++ b/versions.tf @@ -0,0 +1,19 @@ +# Created by Andrea Campice + +terraform { + required_version = "~> 1.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "~> 2.9.0" + } + helm = { + source = "hashicorp/helm" + version = "~> 2.5.0" + } + } +} \ No newline at end of file