commit 188cf2679c67f5adfddd2db0cf0622d65fa7c390 Author: Patrick MARIE Date: Fri Jul 1 14:12:11 2022 +0200 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9eaaf5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.terraform/ +.terragrunt-cache/ +.vscode/ + +terraform.tfstate diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3c63184 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +format: + @echo + @echo "Running terraform fmt & terragrunt hclfmt..." + @echo + @terraform fmt -recursive + @terragrunt hclfmt + +validate: + @echo + @echo "Validating infrastructure with Terragrunt..." + @echo + @terragrunt run-all --terragrunt-non-interactive --terragrunt-working-dir infra validate + +plan: + @echo + @echo "Planning infrastructure" + @echo + @terragrunt run-all --terragrunt-non-interactive --terragrunt-working-dir infra plan + +apply: + @echo + @echo "Planning infrastructure" + @echo + @terragrunt run-all --terragrunt-non-interactive --terragrunt-working-dir infra apply + +destroy: + @echo + @echo "Destroying infrastructure with Terragrunt..." + @echo + @terragrunt run-all --terragrunt-non-interactive --terragrunt-working-dir infra destroy + +.PHONY: format validate plan apply \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e17525 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# avx-pmarie-aws-infra + +## Initialization + +Run `terraform` into the `init-state` directory to create initial s3 & dynamodb. + +## Validate, plan & apply + +There is a `Makefile` for this: + +```sh +$ make validate +$ make plan +$ make apply +``` + +## Extract EKS configuration + +``` +$ aws eks update-kubeconfig --name avx-pmarie-eks --region eu-west-3 +$ kubectl config rename-context arn:aws:eks:eu-west-3:563042046245:cluster/avx-pmarie-eks avx-pmarie-eks +``` + + +## Notes + +### aws-auth + +It can be required to import existing aws-auth configmap: + +``` +$ cd infra/eks-aws-auth +$ terragrunt import kubernetes_config_map.aws_auth kube-system/aws-auth +... + +``` \ No newline at end of file diff --git a/backend.tf b/backend.tf new file mode 100644 index 0000000..3f12cf2 --- /dev/null +++ b/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "avx-pmarie-aws-infra-state" + dynamodb_table = "avx-pmarie-aws-infra-state-locks" + encrypt = true + key = "avx-pmarie-aws-infra/./terraform.tfstate" + region = "eu-west-3" + } +} diff --git a/infra/aws-load-balancer-controller/.terraform.lock.hcl b/infra/aws-load-balancer-controller/.terraform.lock.hcl new file mode 100644 index 0000000..0c0330c --- /dev/null +++ b/infra/aws-load-balancer-controller/.terraform.lock.hcl @@ -0,0 +1,40 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "4.21.0" + hashes = [ + "h1:eeuRCgJ2aEsVvCl0UOU99Rx58L2NMpM4Q5XdsfO4sr8=", + "zh:16529a8ac663845da9214a75f5a32a2d0daf393612e46259b6dff10f1b8b50ed", + "zh:1ae36386d4862a489a3981a482a537c16f8a1588a445b60f173d1f13fcc3552e", + "zh:5ab0f63784f7216528855272b341d3cbfbf378dc6ee23796debead505aff58a2", + "zh:5f28fec15d2e58623b0cdb610e36703b3035fb3a61289c6d8a4705fca5144cb8", + "zh:60b664b6d34b27609b3b4273dffa41ff2c6d15bb01e326bcd6a40944f9cc9839", + "zh:6a9010783b1c4574956e047d9981e96f8d4bbdd7057496ad35bb81acc0efa862", + "zh:8631ceb0187605305e2045f1f6aded046ba17e0cad64663011dd55c8a20330ec", + "zh:891ac1b0053c435b939462b1872ab383e72a8de05454164def2b96a362f7a729", + "zh:92bccfd7517abeda2ac6ddb78f1819742cafdba87af2074929d57cd7f2256c22", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:ad169953f8b9441624064815bd4b82b12ab20ba3e2f033ecf019d6a25ae42175", + "zh:b46eccb3bec96ace8863cd0302de475dd22e4bdd2176ddb82e76f998424e7ac3", + ] +} + +provider "registry.terraform.io/hashicorp/helm" { + version = "2.6.0" + hashes = [ + "h1:rGVucCeYAqklKupwoLVG5VPQTIkUhO7WGcw3WuHYrm8=", + "zh:0ac248c28acc1a4fd11bd26a85e48ab78dd6abf0f7ac842bf1cd7edd05ac6cf8", + "zh:3d32c8deae3740d8c5310136cc11c8afeffc350fbf88afaca0c34a223a5246f5", + "zh:4055a27489733d19ca7fa2dfce14d323fe99ae9dede7d0fea21ee6db0b9ca74b", + "zh:58a8ed39653fd4c874a2ecb128eccfa24c94266a00e349fd7fb13e22ad81f381", + "zh:6c81508044913f25083de132d0ff81d083732aba07c506cc2db05aa0cefcde2c", + "zh:7db5d18093047bfc4fe597f79610c0a281b21db0d61b0bacb3800585e976f814", + "zh:8269207b7422db99e7be80a5352d111966c3dfc7eb98511f11c8ff7b2e813456", + "zh:b1d7ababfb2374e72532308ff442cc906b79256b66b3fe7a98d42c68c4ddf9c5", + "zh:ca63e226cbdc964a5d63ef21189f059ce45c3fa4a5e972204d6916a9177d2b44", + "zh:d205a72d60e8cc362943d66f5bcdd6b6aaaa9aab2b89fd83bf6f1978ac0b1e4c", + "zh:db47dc579a0e68e5bfe3a61f2e950e6e2af82b1f388d1069de014a937962b56a", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/infra/aws-load-balancer-controller/terragrunt.hcl b/infra/aws-load-balancer-controller/terragrunt.hcl new file mode 100644 index 0000000..34b6c21 --- /dev/null +++ b/infra/aws-load-balancer-controller/terragrunt.hcl @@ -0,0 +1,53 @@ +include "root" { + path = find_in_parent_folders() +} + +terraform { + source = "${get_repo_root()}//modules/eks-aws-load-balancer-controller" +} + +dependency "eks" { + config_path = "../eks" + + # Configure mock outputs for the `validate` and `plan` commands that are returned when there are no outputs available + # (e.g the module hasn't been applied yet) + mock_outputs_allowed_terraform_commands = ["validate", "plan"] + mock_outputs = { + cluster_id = "fake-cluster-id" + cluster_endpoint = "https://fake-cluster-endpoint.eks.amazonaws.com" + cluster_certificate_authority_data = "ZmFrZS1jYS1jZXJ0LWRhdGE=" + } +} + +dependency "iam" { + config_path = "../aws-load-balancer-iam-aws" + + mock_outputs_allowed_terraform_commands = ["validate", "plan"] + mock_outputs = { + aws_iam_roles_arns = { + "${local.iam_role_prefix}Dev" = "arn:aws:iam::505686222560:role/${local.iam_role_prefix}Dev" + "${local.iam_role_prefix}Staging" = "arn:aws:iam::505686222560:role/${local.iam_role_prefix}Staging" + "${local.iam_role_prefix}Prod" = "arn:aws:iam::505686222560:role/${local.iam_role_prefix}Prod" + } + } +} + +locals { + config_vars = read_terragrunt_config(find_in_parent_folders("config.hcl")) + + env = local.config_vars.locals.environment + namespace = local.config_vars.locals.aws_load_balancer_namespace + iam_role_prefix = local.config_vars.locals.aws_load_balancer_iam_role_prefix + service_account_name = local.config_vars.locals.aws_load_balancer_service_account_name +} + +inputs = { + cluster_id = dependency.eks.outputs.cluster_id + cluster_endpoint = dependency.eks.outputs.cluster_endpoint + cluster_certificate_authority_data = dependency.eks.outputs.cluster_certificate_authority_data + + environment = local.env + iam_role_arn = dependency.iam.outputs.aws_iam_roles_arns["${local.iam_role_prefix}${title(local.env)}"] + namespace = local.namespace + service_account_name = local.service_account_name +} diff --git a/infra/aws-load-balancer-iam-aws/.terraform.lock.hcl b/infra/aws-load-balancer-iam-aws/.terraform.lock.hcl new file mode 100644 index 0000000..47f9247 --- /dev/null +++ b/infra/aws-load-balancer-iam-aws/.terraform.lock.hcl @@ -0,0 +1,21 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "4.21.0" + hashes = [ + "h1:eeuRCgJ2aEsVvCl0UOU99Rx58L2NMpM4Q5XdsfO4sr8=", + "zh:16529a8ac663845da9214a75f5a32a2d0daf393612e46259b6dff10f1b8b50ed", + "zh:1ae36386d4862a489a3981a482a537c16f8a1588a445b60f173d1f13fcc3552e", + "zh:5ab0f63784f7216528855272b341d3cbfbf378dc6ee23796debead505aff58a2", + "zh:5f28fec15d2e58623b0cdb610e36703b3035fb3a61289c6d8a4705fca5144cb8", + "zh:60b664b6d34b27609b3b4273dffa41ff2c6d15bb01e326bcd6a40944f9cc9839", + "zh:6a9010783b1c4574956e047d9981e96f8d4bbdd7057496ad35bb81acc0efa862", + "zh:8631ceb0187605305e2045f1f6aded046ba17e0cad64663011dd55c8a20330ec", + "zh:891ac1b0053c435b939462b1872ab383e72a8de05454164def2b96a362f7a729", + "zh:92bccfd7517abeda2ac6ddb78f1819742cafdba87af2074929d57cd7f2256c22", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:ad169953f8b9441624064815bd4b82b12ab20ba3e2f033ecf019d6a25ae42175", + "zh:b46eccb3bec96ace8863cd0302de475dd22e4bdd2176ddb82e76f998424e7ac3", + ] +} diff --git a/infra/aws-load-balancer-iam-aws/policy.json b/infra/aws-load-balancer-iam-aws/policy.json new file mode 100644 index 0000000..a8d47c8 --- /dev/null +++ b/infra/aws-load-balancer-iam-aws/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": "*" + } + ] +} diff --git a/infra/aws-load-balancer-iam-aws/terragrunt.hcl b/infra/aws-load-balancer-iam-aws/terragrunt.hcl new file mode 100644 index 0000000..886f2ba --- /dev/null +++ b/infra/aws-load-balancer-iam-aws/terragrunt.hcl @@ -0,0 +1,53 @@ +include "root" { + path = find_in_parent_folders() +} + +terraform { + source = "${get_repo_root()}//modules/aws-iam" +} + +dependency "eks" { + config_path = "../eks" + + mock_outputs_allowed_terraform_commands = ["validate", "plan"] + mock_outputs = { + cluster_oidc_issuer_url = "https://oidc.eks.us-east-2.amazonaws.com/id/FAKEIDENTIFIERXXXXXXXXXXXXXXXXXX" + } +} + +locals { + config_vars = read_terragrunt_config(find_in_parent_folders("config.hcl")) + + env = local.config_vars.locals.environment + service_account_name = local.config_vars.locals.aws_load_balancer_service_account_name + namespace = local.config_vars.locals.aws_load_balancer_namespace + iam_role_prefix = local.config_vars.locals.aws_load_balancer_iam_role_prefix +} + +generate = local.config_vars.generate + +inputs = { + iam_roles = { + "${local.iam_role_prefix}${title(local.env)}" = { + assume_role_policy = { + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Principal = { + Federated = "arn:aws:iam::${get_aws_account_id()}:oidc-provider/${replace("${dependency.eks.outputs.cluster_oidc_issuer_url}", "https://", "")}" + }, + Action = "sts:AssumeRoleWithWebIdentity", + Condition = { + StringEquals = { + "${replace("${dependency.eks.outputs.cluster_oidc_issuer_url}", "https://", "")}:aud" : "sts.amazonaws.com", + "${replace("${dependency.eks.outputs.cluster_oidc_issuer_url}", "https://", "")}:sub" : "system:serviceaccount:${local.namespace}:${local.service_account_name}" + } + } + }] + } + policy = jsondecode(file("policy.json")) + tags = {} + } + } +} diff --git a/infra/config.hcl b/infra/config.hcl new file mode 100644 index 0000000..fc6f287 --- /dev/null +++ b/infra/config.hcl @@ -0,0 +1,165 @@ +locals { + # Common tags + environment = "dev" + source = "terraform" + repository = "avx-pmarie-aws-infra" + + # VPC + vpc_name = "avx-pmarie-aws-infra-vpc" + cidr = "10.88.0.0/16" + + azs = ["eu-west-3a", "eu-west-3b"] + private_subnets = ["10.88.0.0/24", "10.88.1.0/24"] + public_subnets = ["10.88.128.0/24", "10.88.129.0/24"] + + enable_nat_gateway = true + enable_vpn_gateway = false + + # EKS + eks_cluster_name = "avx-pmarie-eks" + eks_cluster_version = "1.22" + enable_irsa = true + + node_group_defaults = { + disk_size = 50 + instance_types = ["t3.small"] + } + + node_groups = { + "eks-${local.environment}" = { + min_size = 1 + max_size = 10 + desired_size = 3 + + bootstrap_extra_args = "--container-runtime containerd" + + instance_types = ["t3.small"] + tags = { + environment = local.environment + repository = local.repository + source = local.source + } + } + } + + /* + ⋮ 22 │ eks_cluster_security_group_additional_rules = { + ⋮ 23 │ egress_nodes_ephemeral_ports_tcp = { + ⋮ 24 │ description = "Nginx validation webhook" + ⋮ 25 │ protocol = "tcp" + ⋮ 26 │ from_port = 8443 + ⋮ 27 │ to_port = 8443 + ⋮ 28 │ type = "egress" + ⋮ 29 │ source_node_security_group = true + ⋮ 30 │ } + ⋮ 31 │ } + ⋮ 32 │ + 22 ⋮ 33 │ eks_node_security_group_additional_rules = { + ⋮ 34 │ ingress_cluster_api_validation_webhooks = { + ⋮ 35 │ description = "Control Plane to validation nginx webhook" + ⋮ 36 │ protocol = "tcp" + ⋮ 37 │ from_port = 8443 + ⋮ 38 │ to_port = 8443 + ⋮ 39 │ type = "ingress" + ⋮ 40 │ source_cluster_security_group = true + ⋮ 41 │ } + + */ + + eks_cluster_security_group_additional_rules = { + egress_nodes_ephemeral_ports_tcp = { + description = "Validation webhooks" + protocol = "tcp" + from_port = 1024 + to_port = 65535 + type = "egress" + source_node_security_group = true + } + } + + eks_node_security_group_additional_rules = { + ingress_cluster_api_validation_webhooks = { + description = "Control Plane to webhooks" + protocol = "tcp" + from_port = 1024 + to_port = 65535 + type = "ingress" + source_cluster_security_group = true + } + ingress_self_all = { + description = "Node to node all ports/protocols" + protocol = "-1" + from_port = 0 + to_port = 0 + type = "ingress" + self = true + } + egress_all = { + description = "Node all egress" + protocol = "-1" + from_port = 0 + to_port = 0 + type = "egress" + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] + } + } + + # EKS aws auth + eks_aws_auth_additional_roles = [{ + rolearn = "arn:aws:iam::563042046245:role/AWSReservedSSO_SubAccountAdmin_6abfb8362d0dfbc0" + username = "sre-admins" + groups = ["system:masters"] + }, { + rolearn = "arn:aws:iam::${get_aws_account_id()}:role/k8s_admin" + username = "admins" + groups = ["system:masters"] + }] + + # EKS addon CSI EBS + eks_addon_versions = { + aws-ebs-csi-driver = "v1.7.0-eksbuild.0" + } + + # AWS load balancer + aws_load_balancer_service_account_name = "aws-load-balancer" + aws_load_balancer_namespace = "aws-load-balancer" + aws_load_balancer_iam_role_prefix = "AWSLoadBalancerController" + + # DNS + dns_public_domain = "aws.mkz.me" + dns_private_domain = "aws-priv.mkz.me" + + # Ingress + eks_ingress_controller = { + namespace = "nginx-ingress" + create_namespace = true + enable_internal_lb = true + ingress_config = { + use-proxy-protocol = false + proxy-real-ip-cidr = "0.0.0.0/0" + use-forwarded-headers = false + compute-full-forwarded-for = false + } + load_balancer_config = { + public = { + dns_record = "*" + backend-protocol = "tcp" + connection-idle-timeout = "60" + cross-zone-load-balancing-enabled = true + type = "nlb" + proxy-protocol = "" + nlb-target-type = "" + } + internal = { + dns_record = "*" + backend-protocol = "tcp" + connection-idle-timeout = "60" + cross-zone-load-balancing-enabled = true + type = "nlb" + proxy-protocol = "" + nlb-target-type = "" + } + } + } +} diff --git a/infra/dns-private/.terraform.lock.hcl b/infra/dns-private/.terraform.lock.hcl new file mode 100644 index 0000000..47f9247 --- /dev/null +++ b/infra/dns-private/.terraform.lock.hcl @@ -0,0 +1,21 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "4.21.0" + hashes = [ + "h1:eeuRCgJ2aEsVvCl0UOU99Rx58L2NMpM4Q5XdsfO4sr8=", + "zh:16529a8ac663845da9214a75f5a32a2d0daf393612e46259b6dff10f1b8b50ed", + "zh:1ae36386d4862a489a3981a482a537c16f8a1588a445b60f173d1f13fcc3552e", + "zh:5ab0f63784f7216528855272b341d3cbfbf378dc6ee23796debead505aff58a2", + "zh:5f28fec15d2e58623b0cdb610e36703b3035fb3a61289c6d8a4705fca5144cb8", + "zh:60b664b6d34b27609b3b4273dffa41ff2c6d15bb01e326bcd6a40944f9cc9839", + "zh:6a9010783b1c4574956e047d9981e96f8d4bbdd7057496ad35bb81acc0efa862", + "zh:8631ceb0187605305e2045f1f6aded046ba17e0cad64663011dd55c8a20330ec", + "zh:891ac1b0053c435b939462b1872ab383e72a8de05454164def2b96a362f7a729", + "zh:92bccfd7517abeda2ac6ddb78f1819742cafdba87af2074929d57cd7f2256c22", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:ad169953f8b9441624064815bd4b82b12ab20ba3e2f033ecf019d6a25ae42175", + "zh:b46eccb3bec96ace8863cd0302de475dd22e4bdd2176ddb82e76f998424e7ac3", + ] +} diff --git a/infra/dns-private/terragrunt.hcl b/infra/dns-private/terragrunt.hcl new file mode 100644 index 0000000..5d6df0e --- /dev/null +++ b/infra/dns-private/terragrunt.hcl @@ -0,0 +1,18 @@ +include "root" { + path = find_in_parent_folders() +} + +locals { + config_vars = read_terragrunt_config(find_in_parent_folders("config.hcl")) + domain = local.config_vars.locals.dns_private_domain +} + +generate = local.config_vars.generate + +terraform { + source = "${get_repo_root()}//modules/dns" +} + +inputs = { + dns_zone_name = local.domain +} diff --git a/infra/dns-public/.terraform.lock.hcl b/infra/dns-public/.terraform.lock.hcl new file mode 100644 index 0000000..47f9247 --- /dev/null +++ b/infra/dns-public/.terraform.lock.hcl @@ -0,0 +1,21 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "4.21.0" + hashes = [ + "h1:eeuRCgJ2aEsVvCl0UOU99Rx58L2NMpM4Q5XdsfO4sr8=", + "zh:16529a8ac663845da9214a75f5a32a2d0daf393612e46259b6dff10f1b8b50ed", + "zh:1ae36386d4862a489a3981a482a537c16f8a1588a445b60f173d1f13fcc3552e", + "zh:5ab0f63784f7216528855272b341d3cbfbf378dc6ee23796debead505aff58a2", + "zh:5f28fec15d2e58623b0cdb610e36703b3035fb3a61289c6d8a4705fca5144cb8", + "zh:60b664b6d34b27609b3b4273dffa41ff2c6d15bb01e326bcd6a40944f9cc9839", + "zh:6a9010783b1c4574956e047d9981e96f8d4bbdd7057496ad35bb81acc0efa862", + "zh:8631ceb0187605305e2045f1f6aded046ba17e0cad64663011dd55c8a20330ec", + "zh:891ac1b0053c435b939462b1872ab383e72a8de05454164def2b96a362f7a729", + "zh:92bccfd7517abeda2ac6ddb78f1819742cafdba87af2074929d57cd7f2256c22", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:ad169953f8b9441624064815bd4b82b12ab20ba3e2f033ecf019d6a25ae42175", + "zh:b46eccb3bec96ace8863cd0302de475dd22e4bdd2176ddb82e76f998424e7ac3", + ] +} diff --git a/infra/dns-public/terragrunt.hcl b/infra/dns-public/terragrunt.hcl new file mode 100644 index 0000000..70604d3 --- /dev/null +++ b/infra/dns-public/terragrunt.hcl @@ -0,0 +1,18 @@ +include "root" { + path = find_in_parent_folders() +} + +locals { + config_vars = read_terragrunt_config(find_in_parent_folders("config.hcl")) + domain = local.config_vars.locals.dns_public_domain +} + +generate = local.config_vars.generate + +terraform { + source = "${get_repo_root()}//modules/dns" +} + +inputs = { + dns_zone_name = local.domain +} diff --git a/infra/eks-addon-csi-ebs/.terraform.lock.hcl b/infra/eks-addon-csi-ebs/.terraform.lock.hcl new file mode 100644 index 0000000..722fea1 --- /dev/null +++ b/infra/eks-addon-csi-ebs/.terraform.lock.hcl @@ -0,0 +1,40 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "4.21.0" + hashes = [ + "h1:eeuRCgJ2aEsVvCl0UOU99Rx58L2NMpM4Q5XdsfO4sr8=", + "zh:16529a8ac663845da9214a75f5a32a2d0daf393612e46259b6dff10f1b8b50ed", + "zh:1ae36386d4862a489a3981a482a537c16f8a1588a445b60f173d1f13fcc3552e", + "zh:5ab0f63784f7216528855272b341d3cbfbf378dc6ee23796debead505aff58a2", + "zh:5f28fec15d2e58623b0cdb610e36703b3035fb3a61289c6d8a4705fca5144cb8", + "zh:60b664b6d34b27609b3b4273dffa41ff2c6d15bb01e326bcd6a40944f9cc9839", + "zh:6a9010783b1c4574956e047d9981e96f8d4bbdd7057496ad35bb81acc0efa862", + "zh:8631ceb0187605305e2045f1f6aded046ba17e0cad64663011dd55c8a20330ec", + "zh:891ac1b0053c435b939462b1872ab383e72a8de05454164def2b96a362f7a729", + "zh:92bccfd7517abeda2ac6ddb78f1819742cafdba87af2074929d57cd7f2256c22", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:ad169953f8b9441624064815bd4b82b12ab20ba3e2f033ecf019d6a25ae42175", + "zh:b46eccb3bec96ace8863cd0302de475dd22e4bdd2176ddb82e76f998424e7ac3", + ] +} + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.12.0" + hashes = [ + "h1:d9e/U+LvNNR4WNgVUevYqS3MOg0SnhTysJIxb84313c=", + "zh:0b9495de4cde4bff48941409cab65ef66ea0d260fcf3d6919fb7f915911fb8da", + "zh:31b8817e521d4928f2d19735bf54cbedb91cf70b93a16f4edf5495eed3f9ea33", + "zh:3ec7dc2d3d432f8f7b44a108a21bea44de310c37f2414eb2913762dbd01c112a", + "zh:4949e4cbc9cd1a633b621d217552b3254a439178cf562886c09fe386119ecf66", + "zh:5a4451328e4940bb0b235e17944b48e75017eb37dce6470142dd899c07512ae1", + "zh:8123b3fdc7a46239dfdc49392aeacafb532d32724e85b00fc84b7ba77a337856", + "zh:8d7229be22e0534c7568a3b60e9ed0b4f468794521cdcaf32c4f6ca727436ede", + "zh:9548f2ce05a5de59bd71a0770cae3b80095145c96754f3698c608a44fdd5d0de", + "zh:c245f1bafc8c2c6adb8dc38ca7ba43ce35134c1f9de3a3370187dec701aa38a1", + "zh:d0496e2fed1787186bd4cfb821db8cbb8dedb3c13e78195e3572ec7ae01ed100", + "zh:f2d515a13e40a2d3757230b6bd0ba469877d6a103cf6ac6d9b06655efc99808d", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/infra/eks-addon-csi-ebs/terragrunt.hcl b/infra/eks-addon-csi-ebs/terragrunt.hcl new file mode 100644 index 0000000..dc1594c --- /dev/null +++ b/infra/eks-addon-csi-ebs/terragrunt.hcl @@ -0,0 +1,37 @@ +include "root" { + path = find_in_parent_folders() +} + +terraform { + source = "${get_repo_root()}//modules/eks-addon-csi-ebs" +} + +dependency "eks" { + config_path = "../eks" + + mock_outputs_allowed_terraform_commands = ["validate", "plan"] + mock_outputs = { + cluster_id = "fake-cluster-id" + cluster_endpoint = "https://fake-cluster-endpoint.eks.amazonaws.com" + cluster_oidc_issuer_url = "https://oidc.eks.us-east-2.amazonaws.com/id/FAKEIDENTIFIERXXXXXXXXXXXXXXXXXX" + cluster_certificate_authority_data = "ZmFrZS1jYS1jZXJ0LWRhdGE=" + oidc_provider_arn = "arn:aws:iam::999999999999:role/FakeRoleName" + } +} + +locals { + config_vars = read_terragrunt_config(find_in_parent_folders("config.hcl")) + + addon_version = local.config_vars.locals.eks_addon_versions["aws-ebs-csi-driver"] +} + +generate = local.config_vars.generate + +inputs = { + eks_cluster_id = dependency.eks.outputs.cluster_id + eks_cluster_oidc_issuer_url = dependency.eks.outputs.cluster_oidc_issuer_url + eks_oidc_provider_arn = dependency.eks.outputs.oidc_provider_arn + eks_cluster_endpoint = dependency.eks.outputs.cluster_endpoint + eks_cluster_certificate_authority_data = dependency.eks.outputs.cluster_certificate_authority_data + addon_version = local.addon_version +} diff --git a/infra/eks-aws-auth/.terraform.lock.hcl b/infra/eks-aws-auth/.terraform.lock.hcl new file mode 100644 index 0000000..b0f9eac --- /dev/null +++ b/infra/eks-aws-auth/.terraform.lock.hcl @@ -0,0 +1,40 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "4.4.0" + constraints = "~> 4.4.0" + hashes = [ + "h1:xUXge6/Bn/CzSjZpmQIr7/FwANKj+3cIEnxYlgS1xFo=", + "zh:087e8e1b9c3d2c9d547181aa88f75fd42d9800eea6d37c0276b1208c427113ff", + "zh:25c3deac14f06a7da5d4d8b56dd5e25a24b5c3bb6bb7a585145d7df1a6e5bc3f", + "zh:5bd23fc03cd51eca3f1e4e4414624dcc4f075eca5cf5aabf06b54b4edded5c50", + "zh:8399507975a422a84b93b24c07db34cc9342f54aa693eace1b451c6b1ab54b87", + "zh:9618bed0832433fee57579d4a001479b08e2092d0c08539edb897f57f6ea0114", + "zh:b0b9060bc367c5fb6175c7ae59382fd6107ab0c0bad6e40cd3205127d8e6717d", + "zh:b160122057659cceb72f78a86483f71d59742502dad23b770dc4248b8e94edd4", + "zh:cb927f4622ef9bf439b867aef760c948839e1cec2ddb8bdba7abfc5183124360", + "zh:e37ce5054a5838eda190f286a62eeb7146087863e38b1a205aa0eb12a5e765b9", + "zh:e38856fd703b2f6e08a35cbe5ddab9a734c9608d2372411bfa6ef1b05ffeb758", + "zh:f342e638d9672d969ed3946b9f0650cf327690b35e0812b2ddae97bd32c2d946", + ] +} + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.8.0" + constraints = "2.8.0" + hashes = [ + "h1:UZCCMTH49ziz6YDV5oCCoOHypOxZWvzc59IfZxVdWeI=", + "zh:0cf42c17c05ae5f0f5eb4b2c375dd2068960b97392e50823e47b2cee7b5e01be", + "zh:29e3751eceae92c7400a17fe3a5394ed761627bcadfda66e7ac91d6485c37927", + "zh:2d95584504c651e1e2e49fbb5fae1736e32a505102c3dbd2c319b26884a7d3d5", + "zh:4a5f1d915c19e7c7b4f04d7d68f82db2c872dad75b9e6f33a6ddce43aa160405", + "zh:4b959187fd2c884a4c6606e1c4edc7b506ec4cadb2742831f37aca1463eb349d", + "zh:5e76a2b81c93d9904d50c2a703845f79d2b080c2f87c07ef8f168592033d638f", + "zh:c5aa21a7168f96afa4b4776cbd7eefd3e1f47d48430dce75c7f761f2d2fac77b", + "zh:d45e8bd98fc6752ea087e744efdafb209e7ec5a4224f9affee0a24fb51d26bb9", + "zh:d4739255076ed7f3ac2a06aef89e8e48a87667f3e470c514ce2185c0569cc1fb", + "zh:dbd2f11529a422ffd17040a70c0cc2802b7f1be2499e976dc22f1138d022b1b4", + "zh:dbd5357082b2485bb9978bce5b6d508d6b431d15c53bfa1fcc2781131826b5d8", + ] +} diff --git a/infra/eks-aws-auth/terragrunt.hcl b/infra/eks-aws-auth/terragrunt.hcl new file mode 100644 index 0000000..a9eb043 --- /dev/null +++ b/infra/eks-aws-auth/terragrunt.hcl @@ -0,0 +1,49 @@ +include "root" { + path = find_in_parent_folders() +} + +terraform { + source = "${get_repo_root()}//modules/eks-auth" +} + +dependency "eks" { + config_path = "../eks" + + # Configure mock outputs for the `validate` and `plan` commands that are returned when there are no outputs available + # (e.g the module hasn't been applied yet) + mock_outputs_allowed_terraform_commands = ["validate", "plan"] + mock_outputs = { + aws_auth_configmap_yaml = < v.arn } +} diff --git a/modules/aws-iam/role.tf b/modules/aws-iam/role.tf new file mode 100644 index 0000000..c311e12 --- /dev/null +++ b/modules/aws-iam/role.tf @@ -0,0 +1,18 @@ +locals { + iam_roles = { for name, role in var.iam_roles : name => merge(var.default_iam_role, role) } +} + +resource "aws_iam_role" "self" { + for_each = local.iam_roles + name = each.key + assume_role_policy = jsonencode(each.value.assume_role_policy) + permissions_boundary = each.value.permissions_boundary +} + +resource "aws_iam_role_policy" "self" { + for_each = { for role, role_config in local.iam_roles : role => role_config if length(role_config.policy) > 0 } + name = each.key + role = each.key + policy = jsonencode(each.value.policy) + depends_on = [aws_iam_role.self] +} diff --git a/modules/aws-iam/variables.tf b/modules/aws-iam/variables.tf new file mode 100644 index 0000000..bb13f81 --- /dev/null +++ b/modules/aws-iam/variables.tf @@ -0,0 +1,21 @@ +variable "default_iam_role" { + description = "The default parameters for and IAM Role definition" + type = object({ + assume_role_policy = any + permissions_boundary = any + policy = any + tags = map(string) + }) + default = { + assume_role_policy = null + permissions_boundary = null + policy = null + tags = {} + } +} + +variable "iam_roles" { + type = map(any) + default = {} + description = "The list of IAM roles and their permissions. See `default_iam_role` for the list of available params" +} diff --git a/modules/dns/main.tf b/modules/dns/main.tf new file mode 100644 index 0000000..495c7e8 --- /dev/null +++ b/modules/dns/main.tf @@ -0,0 +1,3 @@ +resource "aws_route53_zone" "self" { + name = var.dns_zone_name +} diff --git a/modules/dns/outputs.tf b/modules/dns/outputs.tf new file mode 100644 index 0000000..6f49ff7 --- /dev/null +++ b/modules/dns/outputs.tf @@ -0,0 +1,3 @@ +output "dns_zone" { + value = aws_route53_zone.self +} diff --git a/modules/dns/variables.tf b/modules/dns/variables.tf new file mode 100644 index 0000000..87090f1 --- /dev/null +++ b/modules/dns/variables.tf @@ -0,0 +1,4 @@ +variable "dns_zone_name" { + type = string + description = "The name of the main DNS zone to create" +} diff --git a/modules/eks-addon-csi-ebs/data.tf b/modules/eks-addon-csi-ebs/data.tf new file mode 100644 index 0000000..a883bfa --- /dev/null +++ b/modules/eks-addon-csi-ebs/data.tf @@ -0,0 +1,28 @@ +data "aws_eks_cluster_auth" "self" { + name = var.eks_cluster_id +} + +data "aws_iam_policy_document" "self_assume_role_policy" { + statement { + actions = ["sts:AssumeRoleWithWebIdentity"] + effect = "Allow" + + condition { + test = "StringEquals" + variable = "${replace(var.eks_cluster_oidc_issuer_url, "https://", "")}:sub" + values = ["system:serviceaccount:kube-system:ebs-csi-controller-sa"] + } + condition { + test = "StringEquals" + variable = "${replace(var.eks_cluster_oidc_issuer_url, "https://", "")}:aud" + values = ["sts.amazonaws.com"] + } + + principals { + identifiers = [ + var.eks_oidc_provider_arn + ] + type = "Federated" + } + } +} diff --git a/modules/eks-addon-csi-ebs/eks.tf b/modules/eks-addon-csi-ebs/eks.tf new file mode 100644 index 0000000..7374d74 --- /dev/null +++ b/modules/eks-addon-csi-ebs/eks.tf @@ -0,0 +1,15 @@ +resource "kubernetes_storage_class" "self_encrypted" { + metadata { + name = "gp3-encrypted" + } + storage_provisioner = "ebs.csi.aws.com" + parameters = { + type = "gp3" + encrypted = "true" + } + + reclaim_policy = "Retain" + + allow_volume_expansion = "true" + volume_binding_mode = "WaitForFirstConsumer" +} diff --git a/modules/eks-addon-csi-ebs/main.tf b/modules/eks-addon-csi-ebs/main.tf new file mode 100644 index 0000000..7a6e2ff --- /dev/null +++ b/modules/eks-addon-csi-ebs/main.tf @@ -0,0 +1,17 @@ +resource "aws_iam_role" "self" { + assume_role_policy = data.aws_iam_policy_document.self_assume_role_policy.json + name = "EksCsiEbsRole-${var.eks_cluster_id}" +} + +resource "aws_iam_role_policy_attachment" "self" { + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy" + role = aws_iam_role.self.name +} + +resource "aws_eks_addon" "self" { + cluster_name = var.eks_cluster_id + addon_name = "aws-ebs-csi-driver" + addon_version = var.addon_version + + service_account_role_arn = aws_iam_role.self.arn +} diff --git a/modules/eks-addon-csi-ebs/provider.tf b/modules/eks-addon-csi-ebs/provider.tf new file mode 100644 index 0000000..2e715c8 --- /dev/null +++ b/modules/eks-addon-csi-ebs/provider.tf @@ -0,0 +1,5 @@ +provider "kubernetes" { + host = var.eks_cluster_endpoint + cluster_ca_certificate = base64decode(var.eks_cluster_certificate_authority_data) + token = data.aws_eks_cluster_auth.self.token +} diff --git a/modules/eks-addon-csi-ebs/variables.tf b/modules/eks-addon-csi-ebs/variables.tf new file mode 100644 index 0000000..0dd9317 --- /dev/null +++ b/modules/eks-addon-csi-ebs/variables.tf @@ -0,0 +1,30 @@ +variable "eks_cluster_id" { + type = string + description = "The name/id of the EKS cluster. Will block on cluster creation until the cluster is really ready" +} + +variable "eks_cluster_oidc_issuer_url" { + type = string + description = "The EKS cluster OIDC issuer url" +} + +variable "eks_oidc_provider_arn" { + type = string + description = "The EKS cluster OIDC provider arn" +} + +variable "eks_cluster_endpoint" { + type = string + description = "Endpoint for your Kubernetes API server" +} + +variable "eks_cluster_certificate_authority_data" { + type = string + description = "Base64 encoded certificate data required to communicate with the cluster" +} + +variable "addon_version" { + type = string + description = "The addon version" + default = "v1.5.2-eksbuild.1" +} diff --git a/modules/eks-auth/data.tf b/modules/eks-auth/data.tf new file mode 100644 index 0000000..e3cc1be --- /dev/null +++ b/modules/eks-auth/data.tf @@ -0,0 +1,3 @@ +data "aws_eks_cluster_auth" "self" { + name = var.cluster_id +} diff --git a/modules/eks-auth/main.tf b/modules/eks-auth/main.tf new file mode 100644 index 0000000..c9b5d9d --- /dev/null +++ b/modules/eks-auth/main.tf @@ -0,0 +1,14 @@ +locals { + current_roles = yamldecode(yamldecode(var.aws_auth_configmap_yaml).data.mapRoles) +} + +resource "kubernetes_config_map" "aws_auth" { + metadata { + name = "aws-auth" + namespace = "kube-system" + } + + data = { + mapRoles = yamlencode(concat(local.current_roles, var.aws_auth_additional_roles)) + } +} diff --git a/modules/eks-auth/provider.tf b/modules/eks-auth/provider.tf new file mode 100644 index 0000000..89b578e --- /dev/null +++ b/modules/eks-auth/provider.tf @@ -0,0 +1,5 @@ +provider "kubernetes" { + host = var.cluster_endpoint + cluster_ca_certificate = base64decode(var.cluster_certificate_authority_data) + token = data.aws_eks_cluster_auth.self.token +} diff --git a/modules/eks-auth/variables.tf b/modules/eks-auth/variables.tf new file mode 100644 index 0000000..7852f66 --- /dev/null +++ b/modules/eks-auth/variables.tf @@ -0,0 +1,28 @@ +variable "aws_auth_configmap_yaml" { + description = "Formatted yaml for base aws-auth configmap containing roles used in cluster node groups/fargate profiles" + type = string +} + +variable "aws_auth_additional_roles" { + type = list(object({ + rolearn = string + groups = list(string) + username = string + } + )) +} + +variable "cluster_id" { + type = string + description = "The name/id of the EKS cluster. Will block on cluster creation until the cluster is really ready" +} + +variable "cluster_endpoint" { + type = string + description = "Endpoint for your Kubernetes API server" +} + +variable "cluster_certificate_authority_data" { + type = string + description = "Base64 encoded certificate data required to communicate with the cluster" +} diff --git a/modules/eks-auth/versions.tf b/modules/eks-auth/versions.tf new file mode 100644 index 0000000..4f51592 --- /dev/null +++ b/modules/eks-auth/versions.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.8.0" + } + aws = { + source = "hashicorp/aws" + version = "~> 4.4.0" + } + } +} diff --git a/modules/eks-aws-load-balancer-controller/data.tf b/modules/eks-aws-load-balancer-controller/data.tf new file mode 100644 index 0000000..e3cc1be --- /dev/null +++ b/modules/eks-aws-load-balancer-controller/data.tf @@ -0,0 +1,3 @@ +data "aws_eks_cluster_auth" "self" { + name = var.cluster_id +} diff --git a/modules/eks-aws-load-balancer-controller/main.tf b/modules/eks-aws-load-balancer-controller/main.tf new file mode 100644 index 0000000..2af48ed --- /dev/null +++ b/modules/eks-aws-load-balancer-controller/main.tf @@ -0,0 +1,16 @@ +resource "helm_release" "aws_load_balancer_controller" { + name = "aws-load-balancer-controller" + repository = "https://aws.github.io/eks-charts" + chart = "aws-load-balancer-controller" + version = "1.4.2" + namespace = var.namespace + create_namespace = var.create_namespace + + values = [ + templatefile("values.yaml", { + cluster_name = var.cluster_id + service_account_name = var.service_account_name + iam_role_arn = var.iam_role_arn + }) + ] +} diff --git a/modules/eks-aws-load-balancer-controller/provider.tf b/modules/eks-aws-load-balancer-controller/provider.tf new file mode 100644 index 0000000..3dd1f2c --- /dev/null +++ b/modules/eks-aws-load-balancer-controller/provider.tf @@ -0,0 +1,7 @@ +provider "helm" { + kubernetes { + host = var.cluster_endpoint + cluster_ca_certificate = base64decode(var.cluster_certificate_authority_data) + token = data.aws_eks_cluster_auth.self.token + } +} diff --git a/modules/eks-aws-load-balancer-controller/values.yaml b/modules/eks-aws-load-balancer-controller/values.yaml new file mode 100644 index 0000000..b6b3129 --- /dev/null +++ b/modules/eks-aws-load-balancer-controller/values.yaml @@ -0,0 +1,8 @@ +clusterName: "${cluster_name}" + +serviceAccount: + create: true + annotations: + eks.amazonaws.com/role-arn: "${iam_role_arn}" + name: "${service_account_name}" + automountServiceAccountToken: true diff --git a/modules/eks-aws-load-balancer-controller/variables.tf b/modules/eks-aws-load-balancer-controller/variables.tf new file mode 100644 index 0000000..e73f08e --- /dev/null +++ b/modules/eks-aws-load-balancer-controller/variables.tf @@ -0,0 +1,41 @@ +variable "environment" { + description = "Environment name, used for secret naming convention" + type = string +} + +variable "cluster_id" { + type = string + description = "The name/id of the EKS cluster. Will block on cluster creation until the cluster is really ready" +} + +variable "cluster_endpoint" { + type = string + description = "Endpoint for your Kubernetes API server" +} + +variable "cluster_certificate_authority_data" { + type = string + description = "Base64 encoded certificate data required to communicate with the cluster" +} + +variable "namespace" { + type = string + description = "The namespace where the helm chart is deployed" + default = "aws-local-balancer" +} + +variable "create_namespace" { + type = bool + description = "Flag allowing to create the namespace if it does not exists" + default = true +} + +variable "service_account_name" { + type = string + description = "The name of the service account used by the controller" +} + +variable "iam_role_arn" { + type = string + description = "The arn of the IAM Role that have permissions required by the controller" +} diff --git a/modules/eks-ingress-controller/data.tf b/modules/eks-ingress-controller/data.tf new file mode 100644 index 0000000..6fe78a7 --- /dev/null +++ b/modules/eks-ingress-controller/data.tf @@ -0,0 +1,15 @@ +data "aws_lb" "public" { + depends_on = [helm_release.nginx_ingress_contoller] + + tags = merge(var.tags, { scheme = "internet-facing" }) +} + +data "aws_lb" "internal" { + depends_on = [helm_release.nginx_ingress_contoller] + + tags = merge(var.tags, { scheme = "internal" }) +} + +data "aws_eks_cluster_auth" "self" { + name = var.cluster_id +} diff --git a/modules/eks-ingress-controller/main.tf b/modules/eks-ingress-controller/main.tf new file mode 100644 index 0000000..05a9011 --- /dev/null +++ b/modules/eks-ingress-controller/main.tf @@ -0,0 +1,52 @@ +locals { + string_list_tags = join(",", [for key, value in var.tags : "${key}=${value}"]) + template_vars = merge(var.load_balancer_config, var.ingress_config, { "tags" = local.string_list_tags }) +} + +resource "helm_release" "nginx_ingress_contoller" { + name = "nginx-ingress-controller" + repository = "https://kubernetes.github.io/ingress-nginx" + chart = "ingress-nginx" + version = "4.0.18" + namespace = var.namespace + create_namespace = var.create_namespace + + values = [ + "${templatefile("values.yaml", local.template_vars)}" + ] + + set { + name = "controller.ingressClass" + value = var.ingress_class + } + + set { + name = "controller.service.internal.enabled" + value = var.enable_internal_lb + } + + set { + name = "controller.ingressClassResource.default" + value = var.is_default_ingress_class + } +} + +resource "aws_route53_record" "public" { + zone_id = var.public_dns_record.zone_id + name = var.public_dns_record.name + type = "CNAME" + ttl = "60" + records = [ + data.aws_lb.public.dns_name + ] +} + +resource "aws_route53_record" "internal" { + zone_id = var.internal_dns_record.zone_id + name = var.internal_dns_record.name + type = "CNAME" + ttl = "60" + records = [ + data.aws_lb.internal.dns_name + ] +} diff --git a/modules/eks-ingress-controller/outputs.tf b/modules/eks-ingress-controller/outputs.tf new file mode 100644 index 0000000..47ac769 --- /dev/null +++ b/modules/eks-ingress-controller/outputs.tf @@ -0,0 +1,9 @@ +output "public_ingress_load_balancer" { + description = "The internet facing Load Balancer object created by the ingress controller deployment" + value = data.aws_lb.public +} + +output "internal_ingress_load_balancer" { + description = "The internal Load Balancer object created by the ingress controller deployment" + value = data.aws_lb.internal +} diff --git a/modules/eks-ingress-controller/provider.tf b/modules/eks-ingress-controller/provider.tf new file mode 100644 index 0000000..3dd1f2c --- /dev/null +++ b/modules/eks-ingress-controller/provider.tf @@ -0,0 +1,7 @@ +provider "helm" { + kubernetes { + host = var.cluster_endpoint + cluster_ca_certificate = base64decode(var.cluster_certificate_authority_data) + token = data.aws_eks_cluster_auth.self.token + } +} diff --git a/modules/eks-ingress-controller/values.yaml b/modules/eks-ingress-controller/values.yaml new file mode 100644 index 0000000..b634ea2 --- /dev/null +++ b/modules/eks-ingress-controller/values.yaml @@ -0,0 +1,39 @@ +--- +controller: + config: + use-proxy-protocol: ${use-proxy-protocol} + proxy-real-ip-cidr: ${proxy-real-ip-cidr} + use-forwarded-headers: ${use-forwarded-headers} + compute-full-forwarded-for: ${compute-full-forwarded-for} + service: + annotations: + service.beta.kubernetes.io/aws-load-balancer-backend-protocol: ${public.backend-protocol} + service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '${public.connection-idle-timeout}' + service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: '${public.cross-zone-load-balancing-enabled}' + service.beta.kubernetes.io/aws-load-balancer-type: ${public.type} + service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: "scheme=internet-facing,${tags}" + service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "${public.proxy-protocol}" + service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "${public.nlb-target-type}" + service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing" + internal: + annotations: + service.beta.kubernetes.io/aws-load-balancer-internal: "true" + service.beta.kubernetes.io/aws-load-balancer-backend-protocol: ${internal.backend-protocol} + service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '${internal.connection-idle-timeout}' + service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: '${internal.cross-zone-load-balancing-enabled}' + service.beta.kubernetes.io/aws-load-balancer-type: ${internal.type} + service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: "scheme=internal,${tags}" + service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "${internal.proxy-protocol}" + service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "${public.nlb-target-type}" + service.beta.kubernetes.io/aws-load-balancer-scheme: "internal" + service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: preserve_client_ip.enabled=false + +# metrics: +# enabled: true +# serviceMonitor: +# enabled: true +# additionalLabels: +# release: prometheus-community +# namespaceSelector: +# any: true +... diff --git a/modules/eks-ingress-controller/variables.tf b/modules/eks-ingress-controller/variables.tf new file mode 100644 index 0000000..fc3b9dd --- /dev/null +++ b/modules/eks-ingress-controller/variables.tf @@ -0,0 +1,101 @@ +variable "load_balancer_config" { + type = map(object({ + backend-protocol = string + connection-idle-timeout = string + cross-zone-load-balancing-enabled = bool + type = string + dns_record = string + proxy-protocol = string + nlb-target-type = string + })) + description = "The AWS Load Balancer(s) configuration. Map keys shall be 'public' and/or 'internal'" +} + +variable "ingress_config" { + type = object({ + use-proxy-protocol = bool + proxy-real-ip-cidr = string + use-forwarded-headers = bool + compute-full-forwarded-for = bool + }) + description = "Ingress level configuration" + default = { + use-proxy-protocol = false + proxy-real-ip-cidr = "0.0.0.0/0" + use-forwarded-headers = false + compute-full-forwarded-for = false + } +} + +variable "cluster_id" { + type = string + description = "The name/id of the EKS cluster. Will block on cluster creation until the cluster is really ready" +} + +variable "cluster_endpoint" { + type = string + description = "Endpoint for your Kubernetes API server" +} + +variable "cluster_certificate_authority_data" { + type = string + description = "Base64 encoded certificate data required to communicate with the cluster" +} + +variable "ingress_class" { + type = string + default = "nginx" + description = "A class for the Ingress controller" +} + +variable "is_default_ingress_class" { + type = bool + description = "Flag allowing to define this ingress controller ingress class as the default one" + default = true +} + +variable "enable_internal_lb" { + type = bool + default = false +} + +variable "namespace" { + type = string + description = "The namespace where the ingress controller is deployed" + default = "nginx-ingress" +} + +variable "create_namespace" { + type = bool + description = "Flag allowing to create the namespace if it does not exists" + default = true +} + +variable "public_dns_record" { + type = object({ + zone_id = string + name = string + }) + default = { + zone_id = "" + name = "" + } + description = "Public DNS zone and record name where to register the ingress load balancer" +} + +variable "internal_dns_record" { + type = object({ + zone_id = string + name = string + }) + default = { + zone_id = "" + name = "" + } + description = "Private DNS zone and record name where to register the ingress load balancer" +} + +variable "tags" { + type = map(string) + default = {} +} \ No newline at end of file diff --git a/modules/eks-ingress-controller/versions.tf b/modules/eks-ingress-controller/versions.tf new file mode 100644 index 0000000..bd5f11b --- /dev/null +++ b/modules/eks-ingress-controller/versions.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + helm = { + source = "hashicorp/helm" + version = "2.4.1" + } + aws = { + source = "hashicorp/aws" + version = "~> 4.4.0" + } + } +} diff --git a/terragrunt.hcl b/terragrunt.hcl new file mode 100644 index 0000000..1e52e7b --- /dev/null +++ b/terragrunt.hcl @@ -0,0 +1,14 @@ +remote_state { + backend = "s3" + config = { + encrypt = true + bucket = "avx-pmarie-aws-infra-state" + key = "terraform-plans/${path_relative_to_include()}/terraform.tfstate" + region = "eu-west-3" + dynamodb_table = "avx-pmarie-aws-infra-state-locks" + } + generate = { + path = "tg-backend.tf" + if_exists = "overwrite_terragrunt" + } +}