├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md └── terraform ├── README.md ├── cloudwatch.tf ├── configs ├── amazon-cloudwatch-observability.json ├── core-dns.json ├── kube-state-metrics.json └── metrics-server.json ├── data-vpce.tf ├── data.tf ├── eks-access-entry.tf ├── eks-addon-coredns.tf ├── eks-addons.tf ├── eks-cluster.tf ├── eks-managed-node-group-al2023.tf ├── eks-nodes-karpenter.tf ├── kms.tf ├── locals.tf ├── oidc-iam-policies.tf ├── oidc-iam-roles.tf ├── openid-connect.tf ├── policies └── oidc_assume_role_policy.json ├── sqs.tf ├── terraform.tfvars ├── userdata_al2023.tpl ├── variables.tf ├── versions.tf └── vpc.tf /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [marcincuber] 2 | custom: https://www.paypal.me/marcincube -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | 11 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 12 | # .tfvars files are managed as part of configuration and so should be included in 13 | # version control. 14 | # 15 | # example.tfvars 16 | 17 | # Ignore override files as they are usually used to override resources locally and so 18 | # are not checked in 19 | override.tf 20 | override.tf.json 21 | *_override.tf 22 | *_override.tf.json 23 | 24 | # Include override files you do wish to add to version control using negated pattern 25 | # 26 | # !example_override.tf 27 | 28 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 29 | # example: *tfplan* 30 | 31 | .terraform.lock.hcl 32 | 33 | .DS_Store 34 | .idea/ 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EKS 2 | 3 | Implementation of EKS setup using Terraform. Terraform module located in [terraform](./terraform/) directory supports deployment to different AWS partitions. I have tested it with `commercial` and `china` partitions. I am actively using this configuration to run EKS setup in Ireland(eu-west-1), London(eu-west-2), North Virginia(us-east-1) and Beijing(cn-north-1). 4 | 5 | ## Module details 6 | 7 | Module creates: 8 | 9 | * VPC 10 | * VPC Endpoints- S3, ECR, STS, APS, GuardDuty 11 | * EKS Cluster 12 | * EKS Node Group to run cluster critical services 13 | * EKS Addons- coredns, kube-proxy, guardduty, aws-ebs-csi-driver, adot (requires cert-manger to be installed), kubecost, cloudwatch observability, snapshot-controller, identity agent, metrics server and kube-state-metrics 14 | * IAM Roles for worker nodes and Karpenter nodes 15 | * Additional IAM Roles for operators- load-balancer-controller, external-dns, cert-manager, adot-collector 16 | * SQS queue configuration to be used with Karpeneter while utlising Spot Instances. 17 | * CloudWatch log groups used by container insights. 18 | 19 | ## Kubernetes addons and operators 20 | 21 | I am utilising Flux2 to deploy all additional configurations. You can find them at https://github.com/marcincuber/kubernetes-fluxv2 22 | I have built this as a separate repository to show how to develop a successful configuration for your own cluster using GitOps FluxV2 and Helm. 23 | 24 | You will find configurations for: 25 | 26 | * AWS Load Balancer controller 27 | * AWS node termination handler 28 | * Cert Manager 29 | * External-DNS 30 | * External Secrets Operator 31 | * Metrics server 32 | * Reloader 33 | * VPC CNI Plugin 34 | * EBS CSI Driver 35 | * and more :) 36 | 37 | ## Docs and other additional resources 38 | 39 | Check out my stories on Medium if you want to learn more about specific topics. 40 | 41 | ### Amazon EKS upgrade 1.31 to 1.32 42 | 43 | [Amazon EKS upgrade journey from 1.31 to 1.32](https://medium.com/@marcincuber/amazon-eks-upgrade-journey-from-1-31-to-1-32-hi-to-penelope-dfd7f6820e80) 44 | 45 | ### Amazon EKS upgrade 1.30 to 1.31 46 | 47 | [Amazon EKS upgrade journey from 1.30 to 1.31](https://medium.com/@marcincuber/amazon-eks-upgrade-journey-from-1-30-to-1-31-say-hello-to-another-cutee-elli-d488fd6521eb) 48 | 49 | ### Amazon EKS upgrade 1.29 to 1.30 50 | 51 | [Amazon EKS upgrade journey from 1.29 to 1.30](https://medium.com/@marcincuber/amazon-eks-upgrade-journey-from-1-29-to-1-30-say-hello-to-cute-uwubernetes-eba082199cc4) 52 | 53 | ### Amazon EKS upgrade 1.28 to 1.29 54 | 55 | [Amazon EKS upgrade journey from 1.28 to 1.29](https://marcincuber.medium.com/amazon-eks-upgrade-journey-from-1-28-to-1-29-say-hello-to-mandala-858ae0579f4f) 56 | 57 | ### Amazon EKS upgrade 1.27 to 1.28 58 | 59 | [Amazon EKS upgrade journey from 1.27 to 1.28](https://marcincuber.medium.com/amazon-eks-upgrade-journey-from-1-27-to-1-28-welcoming-planternetes-44985e11463a) 60 | 61 | ### Amazon EKS upgrade 1.26 to 1.27 62 | 63 | [Amazon EKS upgrade journey from 1.26 to 1.27](https://marcincuber.medium.com/amazon-eks-upgrade-journey-from-1-26-to-1-27-chill-vibes-46f3f979afac) 64 | 65 | ### Amazon EKS upgrade 1.25 to 1.26 66 | 67 | [Amazon EKS upgrade journey from 1.25 to 1.26](https://medium.com/@marcincuber/amazon-eks-upgrade-journey-from-1-25-to-1-26-electrifying-79b287084eef) 68 | 69 | ### Recover image from EKS node and push it back to ECR 70 | 71 | [Recovering Images from EKS node back to ECR](https://medium.com/@marcincuber/amazon-eks-recover-deleted-images-from-eks-node-to-ecr-repository-15e37b661733) 72 | 73 | ### Karpenter Upgrade guide from Beta to v1 API version 74 | 75 | [Karpenter Upgrade guide from Beta to v1 API version](https://medium.com/@marcincuber/amazon-eks-migrating-karpenter-resources-from-beta-to-v1-api-version-1a84e175a999) 76 | 77 | ### Karpenter Upgrade guide from alpha to beta API version 78 | 79 | [Migrate Karpenter resources from alpha to beta API version](https://medium.com/@marcincuber/amazon-eks-migrating-karpenter-resources-from-alpha-to-beta-api-version-7bf320bbecb5) 80 | 81 | ### Amazon EKS Addons 82 | [Amazon EKS Addons](https://marcincuber.medium.com/amazon-eks-add-ons-implemented-with-terraform-66a49fad4174) 83 | 84 | ### EKS + Kube-bench 85 | 86 | [Kube-bench implementation with EKS](https://itnext.io/aws-eks-and-kube-bench-a7ae840f0f1) 87 | 88 | ### EKS and ECR Pull-through cache implementation using Terraform 89 | 90 | [ECR pull through cache for DockerHub, Github, Quay, ECR public and kubernetes](https://medium.com/@marcincuber/implementing-aws-ecr-pull-through-cache-for-eks-cluster-most-in-depth-implementation-details-e51395568034) 91 | 92 | ### Amazon EKS design, use of spot instances, and cluster scaling 93 | 94 | More about my configuration can be found in the blog post I have written recently -> [EKS design](https://medium.com/@marcincuber/amazon-eks-design-use-of-spot-instances-and-cluster-scaling-da7f3a72d061) 95 | 96 | ### IAM Roles for specific namespaces 97 | 98 | [Amazon EKS- RBAC with IAM access](https://medium.com/@marcincuber/amazon-eks-rbac-and-iam-access-f124f1164de7) 99 | 100 | ### IAM Roles for service accounts using OpenID Connect 101 | 102 | [Using OIDC provider to allow service accounts to assume IAM role](https://medium.com/@marcincuber/amazon-eks-with-oidc-provider-iam-roles-for-kubernetes-services-accounts-59015d15cb0c) 103 | 104 | ### External DNS 105 | 106 | [Amazon EKS, setup external DNS with OIDC provider and kube2iam](https://medium.com/swlh/amazon-eks-setup-external-dns-with-oidc-provider-and-kube2iam-f2487c77b2a1) 107 | 108 | ### EKS Managed Node Groups 109 | 110 | [Amazon EKS + managed node groups](https://itnext.io/amazon-eks-managed-node-groups-87943e3f3360) 111 | 112 | Terraform module written by me can be found in -> https://registry.terraform.io/modules/umotif-public/eks-node-group 113 | 114 | ### Gitlab runners on EKS 115 | 116 | [Kubernetes GitLab Runners on Amazon EKS](https://medium.com/@marcincuber/kubernetes-gitlab-runners-on-amazon-eks-5ba7f0bff30e) 117 | 118 | ### Useful resources 119 | 120 | [EKS platforms information](https://docs.aws.amazon.com/eks/latest/userguide/platform-versions.html) 121 | [Worker nodes upgrades](https://docs.aws.amazon.com/eks/latest/userguide/update-stack.html) 122 | 123 | ## Generate kubeconfig file 124 | 125 | On user's machine who has been added to EKS, they can configure .kube/config file using the following command: 126 | 127 | ```bash 128 | $ aws eks list-clusters 129 | $ aws eks update-kubeconfig --name ${cluster_name} 130 | ``` 131 | -------------------------------------------------------------------------------- /terraform/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | To run this module you need to execute: 4 | 5 | ```bash 6 | $ terraform init 7 | $ terraform plan 8 | $ terraform apply 9 | ``` 10 | 11 | 12 | ## Requirements 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [terraform](#requirement\_terraform) | >= 1.6.0 | 17 | | [aws](#requirement\_aws) | >= 5.74 | 18 | | [tls](#requirement\_tls) | >= 4.0 | 19 | 20 | ## Providers 21 | 22 | | Name | Version | 23 | |------|---------| 24 | | [aws](#provider\_aws) | 5.98.0 | 25 | | [tls](#provider\_tls) | 4.1.0 | 26 | 27 | ## Modules 28 | 29 | | Name | Source | Version | 30 | |------|--------|---------| 31 | | [eks\_cluster](#module\_eks\_cluster) | native-cube/kms/aws | ~> 1.0.0 | 32 | | [eks\_node\_group\_al2023](#module\_eks\_node\_group\_al2023) | native-cube/eks-node-group/aws | ~> 1.1.0 | 33 | | [eks\_vpc\_flow\_logs](#module\_eks\_vpc\_flow\_logs) | native-cube/vpc-flow-logs/aws | ~> 2.2.0 | 34 | | [vpc\_eks](#module\_vpc\_eks) | terraform-aws-modules/vpc/aws | 5.18.1 | 35 | 36 | ## Resources 37 | 38 | | Name | Type | 39 | |------|------| 40 | | [aws_cloudwatch_event_rule.karpenter_spot_interruption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource | 41 | | [aws_cloudwatch_event_target.karpenter_spot_interruption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource | 42 | | [aws_cloudwatch_log_group.cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | 43 | | [aws_cloudwatch_log_group.cluster_application](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | 44 | | [aws_cloudwatch_log_group.cluster_dataplane](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | 45 | | [aws_cloudwatch_log_group.cluster_host](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | 46 | | [aws_cloudwatch_log_group.cluster_performance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | 47 | | [aws_eks_access_entry.cluster_admin_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_access_entry) | resource | 48 | | [aws_eks_access_entry.roles](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_access_entry) | resource | 49 | | [aws_eks_access_policy_association.cluster_admin_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_access_policy_association) | resource | 50 | | [aws_eks_addon.adot](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 51 | | [aws_eks_addon.amazon_cloudwatch_observability](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 52 | | [aws_eks_addon.aws_ebs_csi_driver](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 53 | | [aws_eks_addon.cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 54 | | [aws_eks_addon.core_dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 55 | | [aws_eks_addon.guardduty](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 56 | | [aws_eks_addon.identity_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 57 | | [aws_eks_addon.kube_proxy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 58 | | [aws_eks_addon.kube_state_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 59 | | [aws_eks_addon.kubecost](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 60 | | [aws_eks_addon.metrics_server](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 61 | | [aws_eks_addon.snapshot_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | 62 | | [aws_eks_cluster.cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster) | resource | 63 | | [aws_iam_instance_profile.eks_node_karpenter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | 64 | | [aws_iam_openid_connect_provider.cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider) | resource | 65 | | [aws_iam_role.adot_collector](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 66 | | [aws_iam_role.cert_manager](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 67 | | [aws_iam_role.cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 68 | | [aws_iam_role.ebs_csi_controller_sa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 69 | | [aws_iam_role.eks_node_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 70 | | [aws_iam_role.eks_node_karpenter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 71 | | [aws_iam_role.external_dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 72 | | [aws_iam_role.karpenter_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 73 | | [aws_iam_role.load_balancer_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 74 | | [aws_iam_role_policy.cert_manager](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | 75 | | [aws_iam_role_policy.eks_node_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | 76 | | [aws_iam_role_policy.eks_node_karpenter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | 77 | | [aws_iam_role_policy.external_dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | 78 | | [aws_iam_role_policy.karpenter_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | 79 | | [aws_iam_role_policy.load_balancer_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | 80 | | [aws_iam_role_policy_attachments_exclusive.adot_collector](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachments_exclusive) | resource | 81 | | [aws_iam_role_policy_attachments_exclusive.cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachments_exclusive) | resource | 82 | | [aws_iam_role_policy_attachments_exclusive.ebs_csi_controller_sa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachments_exclusive) | resource | 83 | | [aws_iam_role_policy_attachments_exclusive.eks_node_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachments_exclusive) | resource | 84 | | [aws_iam_role_policy_attachments_exclusive.eks_node_karpenter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachments_exclusive) | resource | 85 | | [aws_launch_template.cluster_al2023](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template) | resource | 86 | | [aws_security_group.core_dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | 87 | | [aws_security_group.eks_vpc_endpoint](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | 88 | | [aws_security_group.eks_vpc_endpoint_guardduty](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | 89 | | [aws_security_group.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | 90 | | [aws_security_group_rule.eks_vpc_endpoint_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | 91 | | [aws_security_group_rule.eks_vpc_endpoint_self_ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | 92 | | [aws_sqs_queue.karpenter_spot_interruption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue) | resource | 93 | | [aws_sqs_queue_policy.karpenter_spot_interruption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy) | resource | 94 | | [aws_vpc_endpoint.eks_vpc_aps_workspaces](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource | 95 | | [aws_vpc_endpoint.eks_vpc_ecr_dkr](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource | 96 | | [aws_vpc_endpoint.eks_vpc_guardduty](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource | 97 | | [aws_vpc_endpoint.eks_vpc_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource | 98 | | [aws_vpc_endpoint.eks_vpc_s3_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource | 99 | | [aws_vpc_endpoint.eks_vpc_sts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource | 100 | | [aws_vpc_security_group_egress_rule.cluster_to_karpenter_nodes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | 101 | | [aws_vpc_security_group_egress_rule.core_dns_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | 102 | | [aws_vpc_security_group_egress_rule.core_dns_udp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | 103 | | [aws_vpc_security_group_egress_rule.node_to_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | 104 | | [aws_vpc_security_group_egress_rule.node_to_internet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | 105 | | [aws_vpc_security_group_ingress_rule.all_allow_access_from_control_plane](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 106 | | [aws_vpc_security_group_ingress_rule.all_allow_access_from_control_plane_to_core_dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 107 | | [aws_vpc_security_group_ingress_rule.all_allow_access_from_karpenter_nodes_to_core_dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 108 | | [aws_vpc_security_group_ingress_rule.allow_ingress_from_coredns_to_cluster_nodes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 109 | | [aws_vpc_security_group_ingress_rule.allow_ingress_from_coredns_to_karpenter_nodes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 110 | | [aws_vpc_security_group_ingress_rule.cluster_to_nodes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 111 | | [aws_vpc_security_group_ingress_rule.cluster_to_vpc_endpoints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 112 | | [aws_vpc_security_group_ingress_rule.eks_vpc_guardduty](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 113 | | [aws_vpc_security_group_ingress_rule.node_to_vpc_endpoints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 114 | | [aws_vpc_security_group_ingress_rule.self](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 115 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 116 | | [aws_iam_policy_document.cert_manager](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 117 | | [aws_iam_policy_document.cluster_role_assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 118 | | [aws_iam_policy_document.eks_node_custom_inline_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 119 | | [aws_iam_policy_document.eks_node_group_assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 120 | | [aws_iam_policy_document.eks_node_karpenter_assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 121 | | [aws_iam_policy_document.eks_vpc_aps_workspaces](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 122 | | [aws_iam_policy_document.eks_vpc_guardduty](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 123 | | [aws_iam_policy_document.external_dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 124 | | [aws_iam_policy_document.karpenter_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 125 | | [aws_iam_policy_document.karpenter_spot_interruption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 126 | | [aws_iam_policy_document.kms_policy_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 127 | | [aws_iam_policy_document.load_balancer_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 128 | | [aws_iam_session_context.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_session_context) | data source | 129 | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | 130 | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | 131 | | [aws_ssm_parameter.eks_al2023](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | 132 | | [aws_vpc_endpoint_service.aps_workspaces](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc_endpoint_service) | data source | 133 | | [aws_vpc_endpoint_service.ecr_dkr](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc_endpoint_service) | data source | 134 | | [aws_vpc_endpoint_service.guardduty](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc_endpoint_service) | data source | 135 | | [aws_vpc_endpoint_service.s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc_endpoint_service) | data source | 136 | | [aws_vpc_endpoint_service.s3_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc_endpoint_service) | data source | 137 | | [aws_vpc_endpoint_service.sts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc_endpoint_service) | data source | 138 | | [tls_certificate.cluster](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/data-sources/certificate) | data source | 139 | 140 | ## Inputs 141 | 142 | | Name | Description | Type | Default | Required | 143 | |------|-------------|------|---------|:--------:| 144 | | [azs](#input\_azs) | A list of availability zones names or ids in the region | `list(string)` | `[]` | no | 145 | | [ebs\_delete\_on\_termination](#input\_ebs\_delete\_on\_termination) | Whether the volume should be destroyed on instance termination. | `bool` | `true` | no | 146 | | [ebs\_encrypted](#input\_ebs\_encrypted) | Enables EBS encryption on the volume. | `bool` | `true` | no | 147 | | [ebs\_volume\_size](#input\_ebs\_volume\_size) | The size of the volume in gigabytes. | `number` | `100` | no | 148 | | [ebs\_volume\_type](#input\_ebs\_volume\_type) | The volume type. | `string` | `"gp3"` | no | 149 | | [eks\_access\_entry\_roles](#input\_eks\_access\_entry\_roles) | List of maps with kubernetes\_groups, principal\_arn, and user\_name. | `list(any)` | `[]` | no | 150 | | [eks\_addon\_version\_adot](#input\_eks\_addon\_version\_adot) | ADOT EKS addon version. | `string` | `null` | no | 151 | | [eks\_addon\_version\_amazon\_cloudwatch\_observability](#input\_eks\_addon\_version\_amazon\_cloudwatch\_observability) | Amazon cloudwatch observability addon version. | `string` | `null` | no | 152 | | [eks\_addon\_version\_cloudwatch](#input\_eks\_addon\_version\_cloudwatch) | Cloudwatch EKS addon version. | `string` | `null` | no | 153 | | [eks\_addon\_version\_core\_dns](#input\_eks\_addon\_version\_core\_dns) | Core DNS managed EKS addon version. | `string` | `null` | no | 154 | | [eks\_addon\_version\_ebs\_csi\_driver](#input\_eks\_addon\_version\_ebs\_csi\_driver) | AWS ebs csi driver managed EKS addon version. | `string` | `null` | no | 155 | | [eks\_addon\_version\_guardduty](#input\_eks\_addon\_version\_guardduty) | Guardduty agent EKS addon version. | `string` | `null` | no | 156 | | [eks\_addon\_version\_identity\_agent](#input\_eks\_addon\_version\_identity\_agent) | Pod Identity Agent EKS addon version. | `string` | `null` | no | 157 | | [eks\_addon\_version\_kube\_proxy](#input\_eks\_addon\_version\_kube\_proxy) | Kube proxy managed EKS addon version. | `string` | `null` | no | 158 | | [eks\_addon\_version\_kube\_state\_metrics](#input\_eks\_addon\_version\_kube\_state\_metrics) | Kube State Metrics addon version. | `string` | `null` | no | 159 | | [eks\_addon\_version\_kubecost](#input\_eks\_addon\_version\_kubecost) | KubeCost EKS addon version. | `string` | `null` | no | 160 | | [eks\_addon\_version\_metrics\_server](#input\_eks\_addon\_version\_metrics\_server) | Metrics server addon version. | `string` | `null` | no | 161 | | [eks\_addon\_version\_snapshot\_controller](#input\_eks\_addon\_version\_snapshot\_controller) | CSI Snapshot Controller EKS addon version. | `string` | `null` | no | 162 | | [eks\_enabled\_log\_types](#input\_eks\_enabled\_log\_types) | List of the desired control plane logging to enable. | `list(string)` | `[]` | no | 163 | | [eks\_endpoint\_private\_access](#input\_eks\_endpoint\_private\_access) | Whether the Amazon EKS private API server endpoint is enabled. | `bool` | `true` | no | 164 | | [eks\_endpoint\_public\_access](#input\_eks\_endpoint\_public\_access) | Whether the Amazon EKS public API server endpoint is enabled. | `bool` | `true` | no | 165 | | [eks\_public\_access\_cidrs](#input\_eks\_public\_access\_cidrs) | List of CIDR blocks. Indicates which CIDR blocks can access the Amazon EKS public API server endpoint when enabled. | `list(string)` |
[| no | 166 | | [eks\_security\_group\_ids](#input\_eks\_security\_group\_ids) | List of security group IDs for the cross-account elastic network interfaces that Amazon EKS creates to use to allow communication between your worker nodes and the Kubernetes control plane. | `list(string)` | `[]` | no | 167 | | [eks\_service\_ipv4\_cidr](#input\_eks\_service\_ipv4\_cidr) | The CIDR block to assign Kubernetes service IP addresses from. | `string` | `null` | no | 168 | | [eks\_version](#input\_eks\_version) | EKS controlplane version. | `string` | n/a | yes | 169 | | [instance\_types](#input\_instance\_types) | List of instance types associated with the EKS Node Group. | `list(string)` |
"0.0.0.0/0"
]
[| no | 170 | | [name\_prefix](#input\_name\_prefix) | Name prefix used across resources created by this module. | `string` | n/a | yes | 171 | | [private\_subnets\_cidrs](#input\_private\_subnets\_cidrs) | Classless Inter-Domain Routing ranges for private subnets. | `list(string)` | n/a | yes | 172 | | [public\_subnets\_cidrs](#input\_public\_subnets\_cidrs) | Classless Inter-Domain Routing ranges for public subnets. | `list(string)` | n/a | yes | 173 | | [vpc\_cidr](#input\_vpc\_cidr) | Amazon Virtual Private Cloud Classless Inter-Domain Routing range. | `string` | n/a | yes | 174 | 175 | ## Outputs 176 | 177 | | Name | Description | 178 | |------|-------------| 179 | | [eks\_arn](#output\_eks\_arn) | EKS cluster ARN. | 180 | | [eks\_id](#output\_eks\_id) | EKS cluster name. | 181 | | [eks\_network\_config](#output\_eks\_network\_config) | EKS cluster network configuration. | 182 | | [private\_subnet\_ids](#output\_private\_subnet\_ids) | Private subnet IDs. | 183 | | [public\_subnet\_ids](#output\_public\_subnet\_ids) | Public subnet IDs. | 184 | | [vpc\_id](#output\_vpc\_id) | VPC ID. | 185 | 186 | 187 | 188 | ## License 189 | 190 | See LICENSE file for full details. 191 | 192 | ## Maintainers 193 | 194 | * [Marcin Cuber](https://github.com/marcincuber) 195 | 196 | ## Pre-commit hooks 197 | 198 | ### Install dependencies 199 | 200 | * [`pre-commit`](https://pre-commit.com/#install) 201 | * [`terraform-docs`](https://github.com/segmentio/terraform-docs) required for `terraform_docs` hooks. 202 | * [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. 203 | 204 | ### Generate terraform-docs 205 | 206 | ``` 207 | terraform-docs markdown table --output-file README.md --output-mode inject . 208 | ``` 209 | 210 | #### MacOS 211 | 212 | ```bash 213 | brew install pre-commit terraform-docs tflint 214 | 215 | brew tap git-chglog/git-chglog 216 | brew install git-chglog 217 | ``` -------------------------------------------------------------------------------- /terraform/cloudwatch.tf: -------------------------------------------------------------------------------- 1 | resource "aws_cloudwatch_event_rule" "karpenter_spot_interruption" { 2 | for_each = { for k, v in local.events : k => v } 3 | 4 | name_prefix = "${var.name_prefix}-" 5 | description = each.value.description 6 | event_pattern = jsonencode(each.value.event_pattern) 7 | } 8 | 9 | resource "aws_cloudwatch_event_target" "karpenter_spot_interruption" { 10 | for_each = { for k, v in local.events : k => v } 11 | 12 | rule = aws_cloudwatch_event_rule.karpenter_spot_interruption[each.key].name 13 | target_id = "KarpenterInterruptionQueueTarget" 14 | arn = aws_sqs_queue.karpenter_spot_interruption.arn 15 | } 16 | 17 | # CloudWatch Log Groups for container insights 18 | resource "aws_cloudwatch_log_group" "cluster_performance" { 19 | count = var.eks_addon_version_cloudwatch != null ? 1 : 0 20 | 21 | name = "/aws/containerinsights/${var.name_prefix}/performance" 22 | 23 | retention_in_days = 180 24 | } 25 | 26 | resource "aws_cloudwatch_log_group" "cluster_application" { 27 | count = var.eks_addon_version_cloudwatch != null ? 1 : 0 28 | 29 | name = "/aws/containerinsights/${var.name_prefix}/application" 30 | 31 | retention_in_days = 180 32 | } 33 | 34 | resource "aws_cloudwatch_log_group" "cluster_dataplane" { 35 | count = var.eks_addon_version_cloudwatch != null ? 1 : 0 36 | 37 | name = "/aws/containerinsights/${var.name_prefix}/dataplane" 38 | 39 | retention_in_days = 180 40 | } 41 | 42 | resource "aws_cloudwatch_log_group" "cluster_host" { 43 | count = var.eks_addon_version_cloudwatch != null ? 1 : 0 44 | 45 | name = "/aws/containerinsights/${var.name_prefix}/host" 46 | 47 | retention_in_days = 180 48 | } 49 | -------------------------------------------------------------------------------- /terraform/configs/amazon-cloudwatch-observability.json: -------------------------------------------------------------------------------- 1 | { 2 | "agent": { 3 | "config": { 4 | "logs": { 5 | "metrics_collected": { 6 | "kubernetes": { 7 | "enhanced_container_insights": true 8 | } 9 | } 10 | } 11 | } 12 | }, 13 | "containerLogs": { 14 | "enabled": false 15 | } 16 | } -------------------------------------------------------------------------------- /terraform/configs/core-dns.json: -------------------------------------------------------------------------------- 1 | { 2 | "replicaCount": 3, 3 | "resources": 4 | { 5 | "limits": 6 | { 7 | "cpu": "100m", 8 | "memory": "200Mi" 9 | }, 10 | "requests": 11 | { 12 | "cpu": "100m", 13 | "memory": "200Mi" 14 | } 15 | }, 16 | "topologySpreadConstraints": [ 17 | { 18 | "maxSkew": 1, 19 | "topologyKey": "topology.kubernetes.io/zone", 20 | "whenUnsatisfiable": "ScheduleAnyway", 21 | "labelSelector": { 22 | "matchLabels": { 23 | "eks.amazonaws.com/component": "coredns", 24 | "k8s-app": "kube-dns" 25 | } 26 | } 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /terraform/configs/kube-state-metrics.json: -------------------------------------------------------------------------------- 1 | { 2 | "replicas": 3, 3 | "resources": { 4 | "limits": { 5 | "cpu": "256m", 6 | "memory": "512Mi" 7 | }, 8 | "requests": { 9 | "cpu": "256m", 10 | "memory": "512Mi" 11 | } 12 | }, 13 | "nodeSelector": { 14 | "kubernetes.io/os": "linux" 15 | } 16 | } -------------------------------------------------------------------------------- /terraform/configs/metrics-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "replicas": 3 3 | } 4 | -------------------------------------------------------------------------------- /terraform/data-vpce.tf: -------------------------------------------------------------------------------- 1 | data "aws_vpc_endpoint_service" "ecr_dkr" { 2 | service_type = "Interface" 3 | filter { 4 | name = "service-name" 5 | values = ["${data.aws_partition.current.reverse_dns_prefix}.${data.aws_region.current.name}.ecr.dkr"] 6 | } 7 | } 8 | 9 | data "aws_vpc_endpoint_service" "sts" { 10 | service_type = "Interface" 11 | filter { 12 | name = "service-name" 13 | values = ["${data.aws_partition.current.reverse_dns_prefix}.${data.aws_region.current.name}.sts"] 14 | } 15 | } 16 | 17 | data "aws_vpc_endpoint_service" "s3" { 18 | service_type = "Interface" 19 | filter { 20 | name = "service-name" 21 | values = ["${data.aws_partition.current.reverse_dns_prefix}.${data.aws_region.current.name}.s3"] 22 | } 23 | } 24 | 25 | data "aws_vpc_endpoint_service" "s3_gateway" { 26 | service_type = "Gateway" 27 | filter { 28 | name = "service-name" 29 | values = ["${data.aws_partition.current.reverse_dns_prefix}.${data.aws_region.current.name}.s3"] 30 | } 31 | } 32 | 33 | data "aws_vpc_endpoint_service" "guardduty" { 34 | service_type = "Interface" 35 | filter { 36 | name = "service-name" 37 | values = ["${data.aws_partition.current.reverse_dns_prefix}.${data.aws_region.current.name}.guardduty-data"] 38 | } 39 | } 40 | 41 | data "aws_iam_policy_document" "eks_vpc_aps_workspaces" { 42 | statement { 43 | actions = [ 44 | "aps:*", 45 | ] 46 | 47 | resources = ["arn:${data.aws_partition.current.partition}:aps:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:/workspaces*"] 48 | 49 | principals { 50 | type = "AWS" 51 | identifiers = ["*"] 52 | } 53 | } 54 | } 55 | 56 | data "aws_vpc_endpoint_service" "aps_workspaces" { 57 | service_type = "Interface" 58 | filter { 59 | name = "service-name" 60 | values = ["${data.aws_partition.current.reverse_dns_prefix}.${data.aws_region.current.name}.aps-workspaces"] 61 | } 62 | } 63 | 64 | data "aws_iam_policy_document" "eks_vpc_guardduty" { 65 | statement { 66 | actions = ["*"] 67 | 68 | effect = "Allow" 69 | 70 | resources = ["*"] 71 | 72 | principals { 73 | type = "AWS" 74 | identifiers = ["*"] 75 | } 76 | } 77 | 78 | statement { 79 | actions = ["*"] 80 | 81 | effect = "Deny" 82 | 83 | resources = ["*"] 84 | 85 | principals { 86 | type = "AWS" 87 | identifiers = ["*"] 88 | } 89 | 90 | condition { 91 | test = "StringNotEquals" 92 | variable = "aws:PrincipalAccount" 93 | 94 | values = [data.aws_caller_identity.current.account_id] 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /terraform/data.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "current" {} 2 | data "aws_partition" "current" {} 3 | data "aws_region" "current" {} 4 | 5 | data "aws_iam_session_context" "current" { 6 | arn = data.aws_caller_identity.current.arn 7 | } 8 | 9 | data "aws_ssm_parameter" "eks_al2023" { 10 | name = "/aws/service/eks/optimized-ami/${var.eks_version}/amazon-linux-2023/x86_64/standard/recommended/image_id" 11 | with_decryption = true 12 | } 13 | 14 | data "tls_certificate" "cluster" { 15 | url = aws_eks_cluster.cluster.identity[0].oidc[0].issuer 16 | } 17 | 18 | data "aws_iam_policy_document" "cluster_role_assume_role_policy" { 19 | statement { 20 | actions = ["sts:AssumeRole", "sts:TagSession"] 21 | 22 | principals { 23 | type = "Service" 24 | identifiers = ["eks.${data.aws_partition.current.dns_suffix}"] 25 | } 26 | } 27 | } 28 | 29 | data "aws_iam_policy_document" "eks_node_group_assume_role_policy" { 30 | statement { 31 | actions = ["sts:AssumeRole"] 32 | 33 | principals { 34 | type = "Service" 35 | identifiers = ["ec2.${data.aws_partition.current.dns_suffix}"] 36 | } 37 | } 38 | } 39 | 40 | data "aws_iam_policy_document" "eks_node_karpenter_assume_role_policy" { 41 | statement { 42 | actions = ["sts:AssumeRole"] 43 | 44 | principals { 45 | type = "Service" 46 | identifiers = ["ec2.${data.aws_partition.current.dns_suffix}"] 47 | } 48 | } 49 | } 50 | 51 | data "aws_iam_policy_document" "eks_node_custom_inline_policy" { 52 | statement { 53 | actions = [ 54 | "ecr:CreateRepository", 55 | "ecr:ReplicateImage", 56 | "ecr:BatchImportUpstreamImage" 57 | ] 58 | 59 | resources = ["*"] 60 | } 61 | } 62 | 63 | data "aws_iam_policy_document" "kms_policy_cluster" { 64 | statement { 65 | actions = [ 66 | "kms:*" 67 | ] 68 | 69 | resources = ["*"] 70 | 71 | principals { 72 | type = "AWS" 73 | identifiers = ["arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root"] 74 | } 75 | } 76 | 77 | statement { 78 | actions = [ 79 | "kms:Encrypt*", 80 | "kms:Decrypt*", 81 | "kms:ReEncrypt*", 82 | "kms:GenerateDataKey*", 83 | "kms:Describe*" 84 | ] 85 | 86 | resources = ["*"] 87 | 88 | principals { 89 | type = "Service" 90 | identifiers = ["logs.${data.aws_region.current.name}.${data.aws_partition.current.dns_suffix}"] 91 | } 92 | } 93 | 94 | statement { 95 | actions = [ 96 | "kms:Encrypt", 97 | "kms:Decrypt", 98 | "kms:ReEncrypt*", 99 | "kms:GenerateDataKey*", 100 | "kms:CreateGrant", 101 | "kms:DescribeKey", 102 | "kms:RetireGrant" 103 | ] 104 | 105 | resources = ["*"] 106 | 107 | principals { 108 | type = "Service" 109 | identifiers = ["events.${data.aws_partition.current.dns_suffix}"] 110 | } 111 | } 112 | } 113 | 114 | data "aws_iam_policy_document" "karpenter_spot_interruption" { 115 | statement { 116 | actions = [ 117 | "sqs:SendMessage" 118 | ] 119 | 120 | resources = [aws_sqs_queue.karpenter_spot_interruption.arn] 121 | 122 | principals { 123 | type = "Service" 124 | identifiers = [ 125 | "events.${data.aws_partition.current.dns_suffix}", 126 | "sqs.${data.aws_partition.current.dns_suffix}" 127 | ] 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /terraform/eks-access-entry.tf: -------------------------------------------------------------------------------- 1 | resource "aws_eks_access_entry" "roles" { 2 | for_each = { for role in var.eks_access_entry_roles : role.rolearn => role } 3 | 4 | cluster_name = aws_eks_cluster.cluster.id 5 | principal_arn = each.value.principal_arn 6 | kubernetes_groups = try(each.value.kubernetes_groups, null) 7 | user_name = try(each.value.user_name, null) 8 | type = try(each.value.type, "STANDARD") 9 | } 10 | 11 | resource "aws_eks_access_entry" "cluster_admin_role" { 12 | cluster_name = aws_eks_cluster.cluster.id 13 | 14 | principal_arn = try(data.aws_iam_session_context.current.issuer_arn, "") 15 | type = "STANDARD" 16 | } 17 | 18 | resource "aws_eks_access_policy_association" "cluster_admin_role" { 19 | cluster_name = aws_eks_cluster.cluster.id 20 | 21 | policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy" 22 | principal_arn = try(data.aws_iam_session_context.current.issuer_arn, "") 23 | access_scope { 24 | type = "cluster" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /terraform/eks-addon-coredns.tf: -------------------------------------------------------------------------------- 1 | ##### 2 | # CoreDNS Security Group 3 | ##### 4 | resource "aws_security_group" "core_dns" { 5 | name_prefix = "${var.name_prefix}-coredns-sg-" 6 | description = "EKS CoreDNS security group." 7 | 8 | vpc_id = module.vpc_eks.vpc_id 9 | 10 | tags = { 11 | "Name" = "${var.name_prefix}-coredns-sg" 12 | "kubernetes.io/cluster/${var.name_prefix}" = "owned" 13 | } 14 | 15 | lifecycle { 16 | create_before_destroy = true 17 | } 18 | } 19 | 20 | resource "aws_vpc_security_group_ingress_rule" "all_allow_access_from_control_plane_to_core_dns" { 21 | security_group_id = aws_security_group.core_dns.id 22 | description = "Allow communication from control plan to CoreDNS security group." 23 | 24 | referenced_security_group_id = aws_eks_cluster.cluster.vpc_config[0].cluster_security_group_id 25 | ip_protocol = "-1" 26 | } 27 | 28 | resource "aws_vpc_security_group_ingress_rule" "all_allow_access_from_karpenter_nodes_to_core_dns" { 29 | security_group_id = aws_security_group.core_dns.id 30 | description = "Allow communication from karpenter nodes to CoreDNS security group." 31 | 32 | referenced_security_group_id = aws_security_group.node.id 33 | ip_protocol = "-1" 34 | } 35 | 36 | resource "aws_vpc_security_group_egress_rule" "core_dns_udp" { 37 | security_group_id = aws_security_group.core_dns.id 38 | description = "Allow udp egress." 39 | 40 | from_port = "53" 41 | to_port = "53" 42 | ip_protocol = "udp" 43 | 44 | cidr_ipv4 = "0.0.0.0/0" 45 | } 46 | 47 | resource "aws_vpc_security_group_egress_rule" "core_dns_tcp" { 48 | security_group_id = aws_security_group.core_dns.id 49 | description = "Allow udp egress." 50 | 51 | from_port = "53" 52 | to_port = "53" 53 | ip_protocol = "tcp" 54 | 55 | cidr_ipv4 = "0.0.0.0/0" 56 | } 57 | -------------------------------------------------------------------------------- /terraform/eks-addons.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | core_dns_config = trimspace(file("${path.module}/configs/core-dns.json")) 3 | amazon_cloudwatch_observability_config = trimspace(file("${path.module}/configs/amazon-cloudwatch-observability.json")) 4 | metrics_server_config = trimspace(file("${path.module}/configs/metrics-server.json")) 5 | kube_state_metrics_config = trimspace(file("${path.module}/configs/kube-state-metrics.json")) 6 | } 7 | 8 | resource "aws_eks_addon" "kube_proxy" { 9 | count = var.eks_addon_version_kube_proxy != null ? 1 : 0 10 | 11 | cluster_name = aws_eks_cluster.cluster.name 12 | addon_name = "kube-proxy" 13 | addon_version = var.eks_addon_version_kube_proxy 14 | 15 | resolve_conflicts_on_create = "OVERWRITE" 16 | resolve_conflicts_on_update = "OVERWRITE" 17 | 18 | preserve = true 19 | 20 | tags = { 21 | "eks_addon" = "kube-proxy" 22 | } 23 | } 24 | 25 | resource "aws_eks_addon" "core_dns" { 26 | count = var.eks_addon_version_core_dns != null ? 1 : 0 27 | 28 | cluster_name = aws_eks_cluster.cluster.name 29 | addon_name = "coredns" 30 | addon_version = var.eks_addon_version_core_dns 31 | 32 | resolve_conflicts_on_create = "OVERWRITE" 33 | resolve_conflicts_on_update = "OVERWRITE" 34 | 35 | configuration_values = local.core_dns_config 36 | 37 | preserve = true 38 | 39 | tags = { 40 | "eks_addon" = "coredns" 41 | } 42 | } 43 | 44 | resource "aws_eks_addon" "aws_ebs_csi_driver" { 45 | count = var.eks_addon_version_ebs_csi_driver != null ? 1 : 0 46 | 47 | cluster_name = aws_eks_cluster.cluster.name 48 | addon_name = "aws-ebs-csi-driver" 49 | addon_version = var.eks_addon_version_ebs_csi_driver 50 | 51 | resolve_conflicts_on_create = "OVERWRITE" 52 | resolve_conflicts_on_update = "OVERWRITE" 53 | 54 | service_account_role_arn = aws_iam_role.ebs_csi_controller_sa.arn 55 | 56 | preserve = true 57 | 58 | tags = { 59 | "eks_addon" = "aws-ebs-csi-driver" 60 | } 61 | } 62 | 63 | resource "aws_iam_role" "ebs_csi_controller_sa" { 64 | name = "ebs-csi-controller-sa" 65 | 66 | assume_role_policy = templatefile("policies/oidc_assume_role_policy.json", { 67 | OIDC_ARN = aws_iam_openid_connect_provider.cluster.arn, 68 | OIDC_URL = replace(aws_iam_openid_connect_provider.cluster.url, "https://", ""), 69 | NAMESPACE = "kube-system", 70 | SA_NAME = "ebs-csi-controller-sa" 71 | }) 72 | } 73 | 74 | resource "aws_iam_role_policy_attachments_exclusive" "ebs_csi_controller_sa" { 75 | role_name = aws_iam_role.ebs_csi_controller_sa.name 76 | policy_arns = ["arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"] 77 | } 78 | 79 | resource "aws_eks_addon" "kubecost" { 80 | count = var.eks_addon_version_kubecost != null ? 1 : 0 81 | 82 | cluster_name = aws_eks_cluster.cluster.name 83 | addon_name = "kubecost_kubecost" 84 | addon_version = var.eks_addon_version_kubecost 85 | 86 | resolve_conflicts_on_create = "OVERWRITE" 87 | resolve_conflicts_on_update = "OVERWRITE" 88 | 89 | preserve = true 90 | 91 | tags = { 92 | "eks_addon" = "kubecost" 93 | } 94 | } 95 | 96 | resource "aws_eks_addon" "guardduty" { 97 | count = var.eks_addon_version_guardduty != null ? 1 : 0 98 | 99 | cluster_name = aws_eks_cluster.cluster.name 100 | addon_name = "aws-guardduty-agent" 101 | addon_version = var.eks_addon_version_guardduty 102 | 103 | resolve_conflicts_on_create = "OVERWRITE" 104 | resolve_conflicts_on_update = "OVERWRITE" 105 | 106 | preserve = true 107 | 108 | tags = { 109 | "eks_addon" = "guardduty" 110 | } 111 | } 112 | 113 | resource "aws_eks_addon" "adot" { 114 | count = var.eks_addon_version_adot != null ? 1 : 0 115 | 116 | cluster_name = aws_eks_cluster.cluster.name 117 | addon_name = "adot" 118 | addon_version = var.eks_addon_version_adot 119 | 120 | resolve_conflicts_on_create = "OVERWRITE" 121 | resolve_conflicts_on_update = "OVERWRITE" 122 | 123 | service_account_role_arn = aws_iam_role.adot_collector.arn 124 | 125 | preserve = true 126 | 127 | tags = { 128 | "eks_addon" = "adot" 129 | } 130 | } 131 | 132 | resource "aws_eks_addon" "cloudwatch" { 133 | count = var.eks_addon_version_cloudwatch != null ? 1 : 0 134 | 135 | cluster_name = aws_eks_cluster.cluster.name 136 | addon_name = "amazon-cloudwatch-observability" 137 | addon_version = var.eks_addon_version_cloudwatch 138 | 139 | resolve_conflicts_on_create = "OVERWRITE" 140 | resolve_conflicts_on_update = "OVERWRITE" 141 | 142 | preserve = true 143 | 144 | tags = { 145 | "eks_addon" = "amazon-cloudwatch-observability" 146 | } 147 | } 148 | 149 | resource "aws_eks_addon" "snapshot_controller" { 150 | count = var.eks_addon_version_snapshot_controller != null ? 1 : 0 151 | 152 | cluster_name = aws_eks_cluster.cluster.name 153 | addon_name = "snapshot-controller" 154 | addon_version = var.eks_addon_version_snapshot_controller 155 | 156 | resolve_conflicts_on_create = "OVERWRITE" 157 | resolve_conflicts_on_update = "OVERWRITE" 158 | 159 | preserve = true 160 | 161 | tags = { 162 | "eks_addon" = "snapshot-controller" 163 | } 164 | } 165 | 166 | resource "aws_eks_addon" "identity_agent" { 167 | count = var.eks_addon_version_identity_agent != null ? 1 : 0 168 | 169 | cluster_name = aws_eks_cluster.cluster.name 170 | addon_name = "eks-pod-identity-agent" 171 | addon_version = var.eks_addon_version_identity_agent 172 | 173 | resolve_conflicts_on_create = "OVERWRITE" 174 | resolve_conflicts_on_update = "OVERWRITE" 175 | 176 | preserve = true 177 | 178 | tags = { 179 | "eks_addon" = "eks-pod-identity-agent" 180 | } 181 | } 182 | 183 | resource "aws_eks_addon" "amazon_cloudwatch_observability" { 184 | count = var.eks_addon_version_amazon_cloudwatch_observability != null ? 1 : 0 185 | 186 | cluster_name = aws_eks_cluster.cluster.name 187 | addon_name = "amazon-cloudwatch-observability" 188 | addon_version = var.eks_addon_version_amazon_cloudwatch_observability 189 | 190 | resolve_conflicts_on_create = "OVERWRITE" 191 | resolve_conflicts_on_update = "OVERWRITE" 192 | 193 | configuration_values = local.amazon_cloudwatch_observability_config 194 | 195 | preserve = true 196 | 197 | tags = { 198 | "eks_addon" = "amazon-cloudwatch-observability" 199 | } 200 | 201 | depends_on = [ 202 | aws_cloudwatch_log_group.cluster_performance 203 | ] 204 | } 205 | 206 | resource "aws_eks_addon" "metrics_server" { 207 | count = var.eks_addon_version_metrics_server != null ? 1 : 0 208 | 209 | cluster_name = aws_eks_cluster.cluster.name 210 | addon_name = "metrics-server" 211 | addon_version = var.eks_addon_version_metrics_server 212 | 213 | configuration_values = local.metrics_server_config 214 | 215 | resolve_conflicts_on_create = "OVERWRITE" 216 | resolve_conflicts_on_update = "OVERWRITE" 217 | 218 | preserve = true 219 | 220 | tags = { 221 | "eks_addon" = "metrics-server" 222 | } 223 | } 224 | 225 | resource "aws_eks_addon" "kube_state_metrics" { 226 | count = var.eks_addon_version_kube_state_metrics != null ? 1 : 0 227 | 228 | cluster_name = aws_eks_cluster.cluster.name 229 | addon_name = "kube-state-metrics" 230 | addon_version = var.eks_addon_version_kube_state_metrics 231 | 232 | configuration_values = local.kube_state_metrics_config 233 | 234 | resolve_conflicts_on_create = "OVERWRITE" 235 | resolve_conflicts_on_update = "OVERWRITE" 236 | 237 | preserve = true 238 | 239 | tags = { 240 | "eks_addon" = "kube-state-metrics" 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /terraform/eks-cluster.tf: -------------------------------------------------------------------------------- 1 | ##### 2 | # EKS Cluster 3 | ##### 4 | resource "aws_eks_cluster" "cluster" { 5 | name = var.name_prefix 6 | 7 | version = var.eks_version 8 | role_arn = aws_iam_role.cluster.arn 9 | 10 | enabled_cluster_log_types = var.eks_enabled_log_types 11 | 12 | vpc_config { 13 | subnet_ids = module.vpc_eks.private_subnets 14 | security_group_ids = var.eks_security_group_ids 15 | endpoint_private_access = var.eks_endpoint_private_access 16 | endpoint_public_access = var.eks_endpoint_public_access 17 | public_access_cidrs = var.eks_public_access_cidrs 18 | } 19 | 20 | encryption_config { 21 | resources = ["secrets"] 22 | provider { 23 | key_arn = module.eks_cluster.key_arn 24 | } 25 | } 26 | 27 | access_config { 28 | authentication_mode = "API" 29 | bootstrap_cluster_creator_admin_permissions = false 30 | } 31 | 32 | kubernetes_network_config { 33 | service_ipv4_cidr = var.eks_service_ipv4_cidr 34 | } 35 | 36 | zonal_shift_config { 37 | enabled = true 38 | } 39 | } 40 | 41 | resource "aws_cloudwatch_log_group" "cluster" { 42 | name = "/aws/eks/${var.name_prefix}/cluster" 43 | retention_in_days = 7 44 | 45 | kms_key_id = module.eks_cluster.key_arn 46 | } 47 | 48 | resource "aws_iam_role" "cluster" { 49 | name = "${var.name_prefix}-cluster-role" 50 | 51 | assume_role_policy = data.aws_iam_policy_document.cluster_role_assume_role_policy.json 52 | } 53 | 54 | resource "aws_iam_role_policy_attachments_exclusive" "cluster" { 55 | role_name = aws_iam_role.cluster.name 56 | policy_arns = [ 57 | "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy", 58 | "arn:aws:iam::aws:policy/AmazonEKSComputePolicy", 59 | "arn:aws:iam::aws:policy/AmazonEKSBlockStoragePolicy", 60 | "arn:aws:iam::aws:policy/AmazonEKSLoadBalancingPolicy", 61 | "arn:aws:iam::aws:policy/AmazonEKSNetworkingPolicy", 62 | "arn:aws:iam::aws:policy/AmazonEKSServicePolicy", 63 | "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController" 64 | ] 65 | } 66 | 67 | ##### 68 | # Outputs 69 | ##### 70 | output "eks_id" { 71 | value = aws_eks_cluster.cluster.id 72 | description = "EKS cluster name." 73 | } 74 | 75 | output "eks_arn" { 76 | value = aws_eks_cluster.cluster.arn 77 | description = "EKS cluster ARN." 78 | } 79 | 80 | output "eks_network_config" { 81 | value = aws_eks_cluster.cluster.kubernetes_network_config 82 | description = "EKS cluster network configuration." 83 | } 84 | -------------------------------------------------------------------------------- /terraform/eks-managed-node-group-al2023.tf: -------------------------------------------------------------------------------- 1 | ##### 2 | # Launch Template with AL2023 AMI 3 | ##### 4 | resource "aws_launch_template" "cluster_al2023" { 5 | name = "${var.name_prefix}-node-group-launch-template-al2023" 6 | 7 | image_id = data.aws_ssm_parameter.eks_al2023.value 8 | update_default_version = true 9 | 10 | block_device_mappings { 11 | device_name = "/dev/xvda" 12 | 13 | ebs { 14 | delete_on_termination = var.ebs_delete_on_termination 15 | encrypted = var.ebs_encrypted 16 | volume_size = var.ebs_volume_size 17 | volume_type = var.ebs_volume_type 18 | } 19 | } 20 | 21 | tag_specifications { 22 | resource_type = "instance" 23 | 24 | tags = { 25 | Name = "${var.name_prefix}-worker-node" 26 | } 27 | } 28 | 29 | metadata_options { 30 | http_endpoint = "enabled" 31 | http_tokens = "required" 32 | instance_metadata_tags = "enabled" 33 | http_put_response_hop_limit = 2 # required by aws-load-balancer controller 34 | } 35 | 36 | user_data = base64encode(templatefile("userdata_al2023.tpl", { 37 | CLUSTER_NAME = aws_eks_cluster.cluster.name, 38 | B64_CLUSTER_CA = aws_eks_cluster.cluster.certificate_authority[0].data, 39 | API_SERVER_URL = aws_eks_cluster.cluster.endpoint, 40 | CLUSTER_SERVICE_CIDR = var.eks_service_ipv4_cidr 41 | })) 42 | } 43 | 44 | ##### 45 | # EKS AL2023 Node Group 46 | ##### 47 | module "eks_node_group_al2023" { 48 | source = "native-cube/eks-node-group/aws" 49 | version = "~> 1.1.0" 50 | 51 | node_group_name_prefix = "${var.name_prefix}-node-group-al2023-" 52 | 53 | cluster_name = aws_eks_cluster.cluster.id 54 | 55 | create_iam_role = false 56 | node_role_arn = aws_iam_role.eks_node_group.arn 57 | 58 | instance_types = var.instance_types 59 | 60 | subnet_ids = module.vpc_eks.private_subnets 61 | 62 | desired_size = 3 63 | min_size = 3 64 | max_size = 4 65 | 66 | labels = { 67 | "workload" = "system-critical" 68 | "ami-family" = "AL2023" 69 | } 70 | 71 | update_config = { 72 | max_unavailable = 1 73 | } 74 | 75 | launch_template = { 76 | name = aws_launch_template.cluster_al2023.name 77 | version = aws_launch_template.cluster_al2023.latest_version 78 | } 79 | 80 | capacity_type = "ON_DEMAND" 81 | 82 | tags = { 83 | "kubernetes.io/cluster/${var.name_prefix}" = "owned" 84 | } 85 | 86 | create_before_destroy = true 87 | } 88 | 89 | ##### 90 | # Worker IAM Role 91 | ##### 92 | resource "aws_iam_role" "eks_node_group" { 93 | name = "${var.name_prefix}-node-group" 94 | 95 | assume_role_policy = data.aws_iam_policy_document.eks_node_group_assume_role_policy.json 96 | } 97 | 98 | resource "aws_iam_role_policy_attachments_exclusive" "eks_node_group" { 99 | role_name = aws_iam_role.eks_node_group.name 100 | policy_arns = [ 101 | "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", 102 | "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", 103 | "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly", 104 | "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore", 105 | "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess", 106 | "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy", 107 | "arn:aws:iam::aws:policy/CloudWatchApplicationInsightsFullAccess" 108 | ] 109 | } 110 | 111 | resource "aws_iam_role_policy" "eks_node_group" { 112 | name = "custom-policy" 113 | role = aws_iam_role.eks_node_group.id 114 | policy = data.aws_iam_policy_document.eks_node_custom_inline_policy.json 115 | } 116 | 117 | ##### 118 | # Worker Security Group rules 119 | ##### 120 | resource "aws_vpc_security_group_ingress_rule" "cluster_to_nodes" { 121 | security_group_id = aws_eks_cluster.cluster.vpc_config[0].cluster_security_group_id 122 | description = "Allow controlplane to communicate with worker nodes." 123 | 124 | ip_protocol = "-1" 125 | referenced_security_group_id = aws_security_group.node.id 126 | } 127 | 128 | resource "aws_vpc_security_group_ingress_rule" "allow_ingress_from_coredns_to_cluster_nodes" { 129 | security_group_id = aws_eks_cluster.cluster.vpc_config[0].cluster_security_group_id 130 | description = "All traffic from CoreDNS." 131 | 132 | referenced_security_group_id = aws_security_group.core_dns.id 133 | ip_protocol = "-1" 134 | } 135 | 136 | resource "aws_vpc_security_group_egress_rule" "cluster_to_karpenter_nodes" { 137 | security_group_id = aws_eks_cluster.cluster.vpc_config[0].cluster_security_group_id 138 | description = "Allow manager worker nodes and cluster all outbound to karpenter nodes." 139 | 140 | ip_protocol = "-1" 141 | referenced_security_group_id = aws_security_group.node.id 142 | } 143 | 144 | # Access to vpc endpoint sg 145 | resource "aws_vpc_security_group_ingress_rule" "cluster_to_vpc_endpoints" { 146 | security_group_id = aws_security_group.eks_vpc_endpoint.id 147 | description = "Allow EKS controlplane and nodes access to VPC endpoints." 148 | 149 | ip_protocol = "-1" 150 | referenced_security_group_id = aws_eks_cluster.cluster.vpc_config[0].cluster_security_group_id 151 | } 152 | -------------------------------------------------------------------------------- /terraform/eks-nodes-karpenter.tf: -------------------------------------------------------------------------------- 1 | ##### 2 | # Karpenter Node IAM Role 3 | ##### 4 | resource "aws_iam_role" "eks_node_karpenter" { 5 | name = "${var.name_prefix}-node-karpenter" 6 | 7 | assume_role_policy = data.aws_iam_policy_document.eks_node_karpenter_assume_role_policy.json 8 | } 9 | 10 | resource "aws_iam_role_policy_attachments_exclusive" "eks_node_karpenter" { 11 | role_name = aws_iam_role.eks_node_karpenter.name 12 | policy_arns = [ 13 | "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", 14 | "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", 15 | "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly", 16 | "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore", 17 | "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess", 18 | "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy", 19 | "arn:aws:iam::aws:policy/CloudWatchApplicationInsightsFullAccess" 20 | ] 21 | } 22 | 23 | resource "aws_iam_role_policy" "eks_node_karpenter" { 24 | name = "custom-policy" 25 | role = aws_iam_role.eks_node_karpenter.id 26 | policy = data.aws_iam_policy_document.eks_node_custom_inline_policy.json 27 | } 28 | 29 | resource "aws_iam_instance_profile" "eks_node_karpenter" { 30 | name = "${var.name_prefix}-node-karpenter" 31 | role = aws_iam_role.eks_node_karpenter.name 32 | } 33 | 34 | ##### 35 | # Karpenter Node Security Group 36 | ##### 37 | resource "aws_security_group" "node" { 38 | name_prefix = "${var.name_prefix}-node-sg-" 39 | 40 | description = "EKS Karpenter Node security group." 41 | vpc_id = module.vpc_eks.vpc_id 42 | 43 | tags = { 44 | "Name" = "${var.name_prefix}-node-sg" 45 | "kubernetes.io/cluster/${var.name_prefix}" = "owned" 46 | "karpenter.sh/discovery" = var.name_prefix 47 | } 48 | 49 | lifecycle { 50 | create_before_destroy = true 51 | } 52 | } 53 | 54 | resource "aws_vpc_security_group_ingress_rule" "all_allow_access_from_control_plane" { 55 | security_group_id = aws_security_group.node.id 56 | description = "Allow communication from control plan security group." 57 | 58 | referenced_security_group_id = aws_eks_cluster.cluster.vpc_config[0].cluster_security_group_id 59 | ip_protocol = "-1" 60 | } 61 | 62 | resource "aws_vpc_security_group_ingress_rule" "self" { 63 | security_group_id = aws_security_group.node.id 64 | description = "Self Reference all traffic." 65 | 66 | referenced_security_group_id = aws_security_group.node.id 67 | ip_protocol = "-1" 68 | } 69 | 70 | resource "aws_vpc_security_group_ingress_rule" "allow_ingress_from_coredns_to_karpenter_nodes" { 71 | security_group_id = aws_security_group.node.id 72 | description = "All traffic from CoreDNS." 73 | 74 | referenced_security_group_id = aws_security_group.core_dns.id 75 | ip_protocol = "-1" 76 | } 77 | 78 | resource "aws_vpc_security_group_egress_rule" "node_to_cluster" { 79 | security_group_id = aws_security_group.node.id 80 | description = "Nodes access to cluster API." 81 | 82 | referenced_security_group_id = aws_eks_cluster.cluster.vpc_config[0].cluster_security_group_id 83 | ip_protocol = "-1" 84 | } 85 | 86 | resource "aws_vpc_security_group_egress_rule" "node_to_internet" { 87 | security_group_id = aws_security_group.node.id 88 | description = "Allow all egress." 89 | 90 | cidr_ipv4 = "0.0.0.0/0" 91 | ip_protocol = "-1" 92 | } 93 | 94 | # Access to vpc endpoint sg 95 | resource "aws_vpc_security_group_ingress_rule" "node_to_vpc_endpoints" { 96 | security_group_id = aws_security_group.eks_vpc_endpoint.id 97 | description = "Allow EKS Karpenter nodes access to VPC endpoints." 98 | 99 | ip_protocol = "-1" 100 | referenced_security_group_id = aws_security_group.node.id 101 | } 102 | -------------------------------------------------------------------------------- /terraform/kms.tf: -------------------------------------------------------------------------------- 1 | module "eks_cluster" { 2 | source = "native-cube/kms/aws" 3 | version = "~> 1.0.0" 4 | 5 | alias_name = var.name_prefix 6 | 7 | policy = data.aws_iam_policy_document.kms_policy_cluster.json 8 | } 9 | -------------------------------------------------------------------------------- /terraform/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | eks_cluster_name = var.name_prefix 3 | 4 | events = { 5 | health_event = { 6 | name = "HealthEvent" 7 | description = "Karpenter interrupt - AWS health event" 8 | event_pattern = { 9 | source = ["aws.health"] 10 | detail-type = ["AWS Health Event"] 11 | } 12 | } 13 | spot_interupt = { 14 | name = "SpotInterrupt" 15 | description = "Karpenter interrupt - EC2 spot instance interruption warning" 16 | event_pattern = { 17 | source = ["aws.ec2"] 18 | detail-type = ["EC2 Spot Instance Interruption Warning"] 19 | } 20 | } 21 | instance_rebalance = { 22 | name = "InstanceRebalance" 23 | description = "Karpenter interrupt - EC2 instance rebalance recommendation" 24 | event_pattern = { 25 | source = ["aws.ec2"] 26 | detail-type = ["EC2 Instance Rebalance Recommendation"] 27 | } 28 | } 29 | instance_state_change = { 30 | name = "InstanceStateChange" 31 | description = "Karpenter interrupt - EC2 instance state-change notification" 32 | event_pattern = { 33 | source = ["aws.ec2"] 34 | detail-type = ["EC2 Instance State-change Notification"] 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /terraform/oidc-iam-policies.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "load_balancer_controller" { 2 | statement { 3 | actions = [ 4 | "iam:CreateServiceLinkedRole", 5 | "ec2:DescribeAccountAttributes", 6 | "ec2:DescribeAddresses", 7 | "ec2:DescribeAvailabilityZones", 8 | "ec2:DescribeInternetGateways", 9 | "ec2:DescribeVpcs", 10 | "ec2:DescribeSubnets", 11 | "ec2:DescribeSecurityGroups", 12 | "ec2:DescribeInstances", 13 | "ec2:DescribeNetworkInterfaces", 14 | "ec2:DescribeTags", 15 | "ec2:GetCoipPoolUsage", 16 | "ec2:DescribeCoipPools", 17 | "ec2:DescribeIpamPools", 18 | "ec2:GetSecurityGroupsForVpc", 19 | "elasticloadbalancing:DescribeLoadBalancers", 20 | "elasticloadbalancing:DescribeLoadBalancerAttributes", 21 | "elasticloadbalancing:DescribeListeners", 22 | "elasticloadbalancing:DescribeListenerCertificates", 23 | "elasticloadbalancing:DescribeListenerAttributes", 24 | "elasticloadbalancing:DescribeSSLPolicies", 25 | "elasticloadbalancing:DescribeRules", 26 | "elasticloadbalancing:DescribeTargetGroups", 27 | "elasticloadbalancing:DescribeTargetGroupAttributes", 28 | "elasticloadbalancing:DescribeTargetHealth", 29 | "elasticloadbalancing:DescribeTags", 30 | "elasticloadbalancing:DescribeTrustStores", 31 | "tag:GetResources" 32 | ] 33 | 34 | resources = ["*"] 35 | } 36 | 37 | statement { 38 | actions = [ 39 | "cognito-idp:DescribeUserPoolClient", 40 | "acm:ListCertificates", 41 | "acm:DescribeCertificate", 42 | "acm:GetCertificate", 43 | "iam:ListServerCertificates", 44 | "iam:GetServerCertificate", 45 | "waf-regional:GetWebACL", 46 | "waf-regional:GetWebACLForResource", 47 | "waf-regional:AssociateWebACL", 48 | "waf-regional:DisassociateWebACL", 49 | "wafv2:GetWebACL", 50 | "wafv2:GetWebACLForResource", 51 | "wafv2:AssociateWebACL", 52 | "wafv2:DisassociateWebACL", 53 | "shield:GetSubscriptionState", 54 | "shield:DescribeProtection", 55 | "shield:CreateProtection", 56 | "shield:DeleteProtection" 57 | ] 58 | 59 | resources = ["*"] 60 | } 61 | 62 | statement { 63 | actions = [ 64 | "ec2:AuthorizeSecurityGroupIngress", 65 | "ec2:RevokeSecurityGroupIngress" 66 | ] 67 | resources = ["*"] 68 | } 69 | 70 | statement { 71 | actions = [ 72 | "ec2:CreateSecurityGroup" 73 | ] 74 | 75 | resources = ["*"] 76 | } 77 | 78 | statement { 79 | actions = [ 80 | "ec2:CreateTags" 81 | ] 82 | 83 | resources = ["arn:${data.aws_partition.current.partition}:ec2:*:*:security-group/*"] 84 | 85 | condition { 86 | test = "StringEquals" 87 | variable = "ec2:CreateAction" 88 | 89 | values = [ 90 | "CreateSecurityGroup", 91 | ] 92 | } 93 | 94 | condition { 95 | test = "Null" 96 | variable = "aws:RequestTag/elbv2.k8s.aws/cluster" 97 | 98 | values = [ 99 | "false", 100 | ] 101 | } 102 | } 103 | 104 | statement { 105 | actions = [ 106 | "ec2:CreateTags", 107 | "ec2:DeleteTags" 108 | ] 109 | 110 | resources = ["arn:${data.aws_partition.current.partition}:ec2:*:*:security-group/*"] 111 | 112 | condition { 113 | test = "Null" 114 | variable = "aws:RequestTag/elbv2.k8s.aws/cluster" 115 | 116 | values = [ 117 | "true", 118 | ] 119 | } 120 | 121 | condition { 122 | test = "Null" 123 | variable = "aws:ResourceTag/elbv2.k8s.aws/cluster" 124 | 125 | values = [ 126 | "false", 127 | ] 128 | } 129 | } 130 | 131 | statement { 132 | actions = [ 133 | "ec2:AuthorizeSecurityGroupIngress", 134 | "ec2:RevokeSecurityGroupIngress", 135 | "ec2:DeleteSecurityGroup" 136 | ] 137 | 138 | resources = ["*"] 139 | 140 | condition { 141 | test = "Null" 142 | variable = "aws:ResourceTag/elbv2.k8s.aws/cluster" 143 | 144 | values = [ 145 | "false", 146 | ] 147 | } 148 | } 149 | 150 | statement { 151 | actions = [ 152 | "elasticloadbalancing:CreateLoadBalancer", 153 | "elasticloadbalancing:CreateTargetGroup" 154 | ] 155 | 156 | resources = ["*"] 157 | 158 | condition { 159 | test = "Null" 160 | variable = "aws:RequestTag/elbv2.k8s.aws/cluster" 161 | 162 | values = [ 163 | "false", 164 | ] 165 | } 166 | } 167 | 168 | statement { 169 | actions = [ 170 | "elasticloadbalancing:CreateListener", 171 | "elasticloadbalancing:DeleteListener", 172 | "elasticloadbalancing:CreateRule", 173 | "elasticloadbalancing:DeleteRule" 174 | ] 175 | resources = ["*"] 176 | } 177 | 178 | statement { 179 | actions = [ 180 | "elasticloadbalancing:AddTags", 181 | "elasticloadbalancing:RemoveTags" 182 | ] 183 | 184 | resources = [ 185 | "arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:targetgroup/*/*", 186 | "arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:loadbalancer/net/*/*", 187 | "arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:loadbalancer/app/*/*" 188 | ] 189 | 190 | condition { 191 | test = "Null" 192 | variable = "aws:RequestTag/elbv2.k8s.aws/cluster" 193 | 194 | values = [ 195 | "true", 196 | ] 197 | } 198 | 199 | condition { 200 | test = "Null" 201 | variable = "aws:ResourceTag/elbv2.k8s.aws/cluster" 202 | 203 | values = [ 204 | "false", 205 | ] 206 | } 207 | } 208 | 209 | statement { 210 | actions = [ 211 | "elasticloadbalancing:AddTags", 212 | "elasticloadbalancing:RemoveTags" 213 | ] 214 | 215 | resources = [ 216 | "arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:listener/net/*/*/*", 217 | "arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:listener/app/*/*/*", 218 | "arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:listener-rule/net/*/*/*", 219 | "arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:listener-rule/app/*/*/*" 220 | ] 221 | } 222 | 223 | statement { 224 | actions = [ 225 | "elasticloadbalancing:AddTags", 226 | ] 227 | 228 | resources = [ 229 | "arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:targetgroup/*/*", 230 | "arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:loadbalancer/net/*/*", 231 | "arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:loadbalancer/app/*/*" 232 | ] 233 | 234 | condition { 235 | test = "StringEquals" 236 | variable = "elasticloadbalancing:CreateAction" 237 | 238 | values = [ 239 | "CreateTargetGroup", 240 | "CreateLoadBalancer" 241 | ] 242 | } 243 | 244 | condition { 245 | test = "Null" 246 | variable = "aws:RequestTag/elbv2.k8s.aws/cluster" 247 | 248 | values = [ 249 | "false", 250 | ] 251 | } 252 | } 253 | 254 | statement { 255 | actions = [ 256 | "elasticloadbalancing:ModifyLoadBalancerAttributes", 257 | "elasticloadbalancing:SetIpAddressType", 258 | "elasticloadbalancing:SetSecurityGroups", 259 | "elasticloadbalancing:SetSubnets", 260 | "elasticloadbalancing:DeleteLoadBalancer", 261 | "elasticloadbalancing:ModifyTargetGroup", 262 | "elasticloadbalancing:ModifyTargetGroupAttributes", 263 | "elasticloadbalancing:DeleteTargetGroup", 264 | "elasticloadbalancing:ModifyListenerAttributes", 265 | "elasticloadbalancing:ModifyIpPools" 266 | ] 267 | 268 | resources = ["*"] 269 | 270 | condition { 271 | test = "Null" 272 | variable = "aws:ResourceTag/elbv2.k8s.aws/cluster" 273 | 274 | values = [ 275 | "false", 276 | ] 277 | } 278 | } 279 | 280 | statement { 281 | actions = [ 282 | "elasticloadbalancing:RegisterTargets", 283 | "elasticloadbalancing:DeregisterTargets" 284 | ] 285 | 286 | resources = ["arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:targetgroup/*/*"] 287 | } 288 | 289 | statement { 290 | actions = [ 291 | "elasticloadbalancing:SetWebAcl", 292 | "elasticloadbalancing:ModifyListener", 293 | "elasticloadbalancing:AddListenerCertificates", 294 | "elasticloadbalancing:RemoveListenerCertificates", 295 | "elasticloadbalancing:ModifyRule", 296 | "elasticloadbalancing:SetRulePriorities" 297 | ] 298 | 299 | resources = ["*"] 300 | } 301 | } 302 | 303 | data "aws_iam_policy_document" "external_dns" { 304 | statement { 305 | actions = [ 306 | "route53:ChangeResourceRecordSets", 307 | "route53:ListHostedZones", 308 | "route53:ListResourceRecordSets" 309 | ] 310 | 311 | resources = ["*"] 312 | } 313 | } 314 | 315 | data "aws_iam_policy_document" "cert_manager" { 316 | statement { 317 | actions = ["route53:GetChange"] 318 | 319 | resources = ["arn:aws:route53:::change/*"] 320 | } 321 | 322 | statement { 323 | actions = [ 324 | "route53:ChangeResourceRecordSets", 325 | "route53:ListResourceRecordSets", 326 | ] 327 | 328 | resources = ["arn:aws:route53:::hostedzone/*"] 329 | } 330 | 331 | statement { 332 | actions = [ 333 | "route53:ListHostedZones", 334 | "route53:ListHostedZonesByName" 335 | ] 336 | 337 | resources = ["*"] 338 | } 339 | } 340 | 341 | data "aws_iam_policy_document" "karpenter_controller" { 342 | statement { 343 | sid = "AllowScopedEC2InstanceActions" 344 | actions = [ 345 | "ec2:RunInstances", 346 | "ec2:CreateFleet" 347 | ] 348 | 349 | resources = [ 350 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}::image/*", 351 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}::snapshot/*", 352 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:spot-instances-request/*", 353 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:security-group/*", 354 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:subnet/*", 355 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:launch-template/*" 356 | ] 357 | } 358 | 359 | statement { 360 | sid = "AllowScopedEC2InstanceActionsWithTags" 361 | actions = [ 362 | "ec2:RunInstances", 363 | "ec2:CreateFleet", 364 | "ec2:CreateLaunchTemplate" 365 | ] 366 | 367 | resources = [ 368 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:fleet/*", 369 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:instance/*", 370 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:volume/*", 371 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:network-interface/*", 372 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:launch-template/*" 373 | ] 374 | 375 | condition { 376 | test = "StringEquals" 377 | variable = "aws:RequestTag/kubernetes.io/cluster/${var.name_prefix}" 378 | 379 | values = [ 380 | "owned" 381 | ] 382 | } 383 | 384 | condition { 385 | test = "StringLike" 386 | variable = "aws:RequestTag/karpenter.sh/nodepool" 387 | 388 | values = [ 389 | "*" 390 | ] 391 | } 392 | } 393 | 394 | statement { 395 | sid = "AllowScopedResourceCreationTagging" 396 | actions = [ 397 | "ec2:CreateTags" 398 | ] 399 | 400 | resources = [ 401 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:fleet/*", 402 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:instance/*", 403 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:spot-instances-request/*", 404 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:volume/*", 405 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:network-interface/*", 406 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:launch-template/*" 407 | ] 408 | 409 | condition { 410 | test = "StringEquals" 411 | variable = "aws:RequestTag/kubernetes.io/cluster/${var.name_prefix}" 412 | 413 | values = [ 414 | "owned" 415 | ] 416 | } 417 | 418 | condition { 419 | test = "StringEquals" 420 | variable = "aws:RequestTag/eks:eks-cluster-name" 421 | 422 | values = [ 423 | local.eks_cluster_name 424 | ] 425 | } 426 | 427 | condition { 428 | test = "StringEquals" 429 | variable = "ec2:CreateAction" 430 | 431 | values = [ 432 | "RunInstances", 433 | "CreateFleet", 434 | "CreateLaunchTemplate" 435 | ] 436 | } 437 | 438 | condition { 439 | test = "StringLike" 440 | variable = "aws:RequestTag/karpenter.sh/nodepool" 441 | 442 | values = [ 443 | "*" 444 | ] 445 | } 446 | } 447 | 448 | statement { 449 | sid = "AllowScopedResourceTagging" 450 | actions = [ 451 | "ec2:CreateTags" 452 | ] 453 | 454 | resources = [ 455 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:instance/*", 456 | ] 457 | 458 | condition { 459 | test = "StringEquals" 460 | variable = "aws:ResourceTag/kubernetes.io/cluster/${var.name_prefix}" 461 | 462 | values = [ 463 | "owned" 464 | ] 465 | } 466 | 467 | condition { 468 | test = "StringLike" 469 | variable = "aws:ResourceTag/karpenter.sh/nodepool" 470 | 471 | values = [ 472 | "*" 473 | ] 474 | } 475 | 476 | condition { 477 | test = "StringEqualsIfExists" 478 | variable = "aws:RequestTag/eks:eks-cluster-name" 479 | 480 | values = [ 481 | local.eks_cluster_name 482 | ] 483 | } 484 | 485 | condition { 486 | test = "ForAllValues:StringEquals" 487 | variable = "aws:TagKeys" 488 | 489 | values = [ 490 | "eks:eks-cluster-name", 491 | "karpenter.sh/nodeclaim", 492 | "Name" 493 | ] 494 | } 495 | } 496 | 497 | statement { 498 | sid = "AllowScopedDeletion" 499 | actions = [ 500 | "ec2:TerminateInstances", 501 | "ec2:DeleteLaunchTemplate" 502 | ] 503 | 504 | resources = [ 505 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:instance/*", 506 | "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:*:launch-template/*" 507 | ] 508 | 509 | condition { 510 | test = "StringEquals" 511 | variable = "aws:ResourceTag/kubernetes.io/cluster/${var.name_prefix}" 512 | 513 | values = [ 514 | "owned" 515 | ] 516 | } 517 | 518 | condition { 519 | test = "StringLike" 520 | variable = "aws:ResourceTag/karpenter.sh/nodepool" 521 | 522 | values = [ 523 | "*" 524 | ] 525 | } 526 | } 527 | 528 | statement { 529 | sid = "AllowRegionalReadActions" 530 | actions = [ 531 | "ec2:DescribeAvailabilityZones", 532 | "ec2:DescribeImages", 533 | "ec2:DescribeInstances", 534 | "ec2:DescribeInstanceTypeOfferings", 535 | "ec2:DescribeInstanceTypes", 536 | "ec2:DescribeLaunchTemplates", 537 | "ec2:DescribeSecurityGroups", 538 | "ec2:DescribeSpotPriceHistory", 539 | "ec2:DescribeSubnets" 540 | ] 541 | 542 | resources = [ 543 | "*" 544 | ] 545 | 546 | condition { 547 | test = "StringEquals" 548 | variable = "aws:RequestedRegion" 549 | 550 | values = [ 551 | data.aws_region.current.name 552 | ] 553 | } 554 | } 555 | 556 | statement { 557 | sid = "AllowSSMReadActions" 558 | actions = [ 559 | "ssm:GetParameter" 560 | ] 561 | 562 | resources = [ 563 | "arn:${data.aws_partition.current.partition}:ssm:${data.aws_region.current.name}::parameter/aws/service/*" 564 | ] 565 | } 566 | 567 | statement { 568 | sid = "AllowPricingReadActions" 569 | actions = [ 570 | "pricing:GetProducts" 571 | ] 572 | 573 | resources = [ 574 | "*" 575 | ] 576 | } 577 | 578 | statement { 579 | sid = "AllowInterruptionQueueActions" 580 | actions = [ 581 | "sqs:DeleteMessage", 582 | "sqs:GetQueueUrl", 583 | "sqs:GetQueueAttributes", 584 | "sqs:ReceiveMessage" 585 | ] 586 | 587 | resources = [aws_sqs_queue.karpenter_spot_interruption.arn] 588 | } 589 | 590 | statement { 591 | sid = "AllowPassingInstanceRole" 592 | actions = [ 593 | "iam:PassRole", 594 | ] 595 | 596 | resources = [aws_iam_role.eks_node_karpenter.arn] 597 | 598 | condition { 599 | test = "StringEquals" 600 | variable = "iam:PassedToService" 601 | 602 | values = [ 603 | "ec2.amazonaws.com" 604 | ] 605 | } 606 | } 607 | 608 | statement { 609 | sid = "AllowScopedInstanceProfileCreationActions" 610 | actions = [ 611 | "iam:CreateInstanceProfile" 612 | ] 613 | 614 | resources = [ 615 | "*" 616 | ] 617 | 618 | condition { 619 | test = "StringEquals" 620 | variable = "aws:RequestTag/kubernetes.io/cluster/${var.name_prefix}" 621 | 622 | values = [ 623 | "owned" 624 | ] 625 | } 626 | 627 | condition { 628 | test = "StringEquals" 629 | variable = "aws:RequestTag/topology.kubernetes.io/region" 630 | 631 | values = [ 632 | data.aws_region.current.name 633 | ] 634 | } 635 | 636 | 637 | condition { 638 | test = "StringLike" 639 | variable = "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass" 640 | 641 | values = [ 642 | "*" 643 | ] 644 | } 645 | } 646 | 647 | statement { 648 | sid = "AllowScopedInstanceProfileTagActions" 649 | actions = [ 650 | "iam:TagInstanceProfile" 651 | ] 652 | 653 | resources = [ 654 | "*" 655 | ] 656 | 657 | condition { 658 | test = "StringEquals" 659 | variable = "aws:ResourceTag/kubernetes.io/cluster/${var.name_prefix}" 660 | 661 | values = [ 662 | "owned" 663 | ] 664 | } 665 | 666 | condition { 667 | test = "StringEquals" 668 | variable = "aws:ResourceTag/topology.kubernetes.io/region" 669 | 670 | values = [ 671 | data.aws_region.current.name 672 | ] 673 | } 674 | 675 | condition { 676 | test = "StringEquals" 677 | variable = "aws:RequestTag/kubernetes.io/cluster/${var.name_prefix}" 678 | 679 | values = [ 680 | "owned" 681 | ] 682 | } 683 | 684 | condition { 685 | test = "StringEquals" 686 | variable = "aws:RequestTag/topology.kubernetes.io/region" 687 | 688 | values = [ 689 | data.aws_region.current.name 690 | ] 691 | } 692 | 693 | 694 | condition { 695 | test = "StringLike" 696 | variable = "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass" 697 | 698 | values = [ 699 | "*" 700 | ] 701 | } 702 | 703 | condition { 704 | test = "StringLike" 705 | variable = "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass" 706 | 707 | values = [ 708 | "*" 709 | ] 710 | } 711 | } 712 | 713 | statement { 714 | sid = "AllowScopedInstanceProfileActions" 715 | actions = [ 716 | "iam:AddRoleToInstanceProfile", 717 | "iam:RemoveRoleFromInstanceProfile", 718 | "iam:DeleteInstanceProfile" 719 | ] 720 | 721 | resources = [ 722 | "*" 723 | ] 724 | 725 | condition { 726 | test = "StringEquals" 727 | variable = "aws:ResourceTag/kubernetes.io/cluster/${var.name_prefix}" 728 | 729 | values = [ 730 | "owned" 731 | ] 732 | } 733 | 734 | condition { 735 | test = "StringEquals" 736 | variable = "aws:ResourceTag/topology.kubernetes.io/region" 737 | 738 | values = [ 739 | data.aws_region.current.name 740 | ] 741 | } 742 | 743 | 744 | condition { 745 | test = "StringLike" 746 | variable = "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass" 747 | 748 | values = [ 749 | "*" 750 | ] 751 | } 752 | } 753 | 754 | statement { 755 | sid = "AllowInstanceProfileReadActions" 756 | actions = [ 757 | "iam:GetInstanceProfile" 758 | ] 759 | 760 | resources = [ 761 | "*" 762 | ] 763 | } 764 | 765 | statement { 766 | actions = [ 767 | "eks:DescribeCluster", 768 | ] 769 | 770 | resources = [ 771 | "arn:${data.aws_partition.current.partition}:eks:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:cluster/${var.name_prefix}", 772 | ] 773 | } 774 | 775 | statement { 776 | actions = [ 777 | "kms:GenerateDataKey", 778 | "kms:Decrypt", 779 | ] 780 | 781 | resources = [module.eks_cluster.key_arn] 782 | } 783 | } 784 | -------------------------------------------------------------------------------- /terraform/oidc-iam-roles.tf: -------------------------------------------------------------------------------- 1 | # Used by Load Balancer Controller service account 2 | resource "aws_iam_role" "load_balancer_controller" { 3 | name = "${var.name_prefix}-load-balancer-controller" 4 | 5 | description = "Allow load-balancer-controller to manage ALBs and NLBs." 6 | 7 | assume_role_policy = templatefile("policies/oidc_assume_role_policy.json", { 8 | OIDC_ARN = aws_iam_openid_connect_provider.cluster.arn, 9 | OIDC_URL = replace(aws_iam_openid_connect_provider.cluster.url, "https://", ""), 10 | NAMESPACE = "kube-system", 11 | SA_NAME = "aws-load-balancer-controller" 12 | }) 13 | 14 | force_detach_policies = true 15 | 16 | tags = { 17 | "ServiceAccountName" = "aws-load-balancer-controller" 18 | "ServiceAccountNamespace" = "kube-system" 19 | } 20 | } 21 | 22 | resource "aws_iam_role_policy" "load_balancer_controller" { 23 | name = "CustomPolicy" 24 | role = aws_iam_role.load_balancer_controller.id 25 | 26 | policy = data.aws_iam_policy_document.load_balancer_controller.json 27 | } 28 | 29 | # Used by external-dns service account 30 | resource "aws_iam_role" "external_dns" { 31 | name = "${var.name_prefix}-external-dns" 32 | 33 | description = "Allow external-dns to upsert DNS records." 34 | 35 | assume_role_policy = templatefile("policies/oidc_assume_role_policy.json", { 36 | OIDC_ARN = aws_iam_openid_connect_provider.cluster.arn, 37 | OIDC_URL = replace(aws_iam_openid_connect_provider.cluster.url, "https://", ""), 38 | NAMESPACE = "kube-system", 39 | SA_NAME = "external-dns" 40 | }) 41 | 42 | force_detach_policies = true 43 | 44 | tags = { 45 | "ServiceAccountName" = "external-dns" 46 | "ServiceAccountNamespace" = "kube-system" 47 | } 48 | } 49 | 50 | resource "aws_iam_role_policy" "external_dns" { 51 | name = "CustomPolicy" 52 | role = aws_iam_role.external_dns.id 53 | 54 | policy = data.aws_iam_policy_document.external_dns.json 55 | } 56 | 57 | # Used by cert-manager 58 | resource "aws_iam_role" "cert_manager" { 59 | name = "${var.name_prefix}-cert-manager" 60 | description = "Allow cert-manager to manage DNS records for LetsEncrypt validation purposes." 61 | 62 | assume_role_policy = templatefile("policies/oidc_assume_role_policy.json", { 63 | OIDC_ARN = aws_iam_openid_connect_provider.cluster.arn, 64 | OIDC_URL = replace(aws_iam_openid_connect_provider.cluster.url, "https://", ""), 65 | NAMESPACE = "cert-manager", 66 | SA_NAME = "cert-manager" 67 | }) 68 | 69 | force_detach_policies = true 70 | 71 | tags = { 72 | "ServiceAccountName" = "cert-manager" 73 | "ServiceAccountNamespace" = "cert-manager" 74 | } 75 | } 76 | 77 | resource "aws_iam_role_policy" "cert_manager" { 78 | name = "CustomPolicy" 79 | role = aws_iam_role.cert_manager.id 80 | 81 | policy = data.aws_iam_policy_document.cert_manager.json 82 | } 83 | 84 | # Used by karpenter-controller 85 | resource "aws_iam_role" "karpenter_controller" { 86 | name = "${var.name_prefix}-karpenter-controller" 87 | description = "Allow karpenter-controller EC2 read and write operations." 88 | 89 | assume_role_policy = templatefile("policies/oidc_assume_role_policy.json", { 90 | OIDC_ARN = aws_iam_openid_connect_provider.cluster.arn, 91 | OIDC_URL = replace(aws_iam_openid_connect_provider.cluster.url, "https://", ""), 92 | NAMESPACE = "karpenter", 93 | SA_NAME = "karpenter" 94 | }) 95 | 96 | force_detach_policies = true 97 | 98 | tags = { 99 | "ServiceAccountName" = "karpenter" 100 | "ServiceAccountNamespace" = "karpenter" 101 | } 102 | } 103 | 104 | resource "aws_iam_role_policy" "karpenter_controller" { 105 | name = "KarpenterControllerPolicy" 106 | role = aws_iam_role.karpenter_controller.id 107 | 108 | policy = data.aws_iam_policy_document.karpenter_controller.json 109 | } 110 | 111 | # Used by adot-addon 112 | resource "aws_iam_role" "adot_collector" { 113 | name = "${var.name_prefix}-adot-collector" 114 | description = "Allow ADOT to write to Prometheus, X-ray and Cloudwatch." 115 | 116 | assume_role_policy = templatefile("policies/oidc_assume_role_policy.json", { 117 | OIDC_ARN = aws_iam_openid_connect_provider.cluster.arn, 118 | OIDC_URL = replace(aws_iam_openid_connect_provider.cluster.url, "https://", ""), 119 | NAMESPACE = "opentelemetry-operator-system", 120 | SA_NAME = "adot-collector" 121 | }) 122 | 123 | force_detach_policies = true 124 | 125 | tags = { 126 | "ServiceAccountName" = "adot-collector" 127 | "ServiceAccountNamespace" = "opentelemetry-operator-system" 128 | } 129 | } 130 | 131 | resource "aws_iam_role_policy_attachments_exclusive" "adot_collector" { 132 | role_name = aws_iam_role.adot_collector.name 133 | policy_arns = [ 134 | "arn:aws:iam::aws:policy/AmazonPrometheusRemoteWriteAccess", 135 | "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess", 136 | "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy", 137 | ] 138 | } 139 | -------------------------------------------------------------------------------- /terraform/openid-connect.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_openid_connect_provider" "cluster" { 2 | client_id_list = ["sts.amazonaws.com"] 3 | thumbprint_list = [data.tls_certificate.cluster.certificates[0].sha1_fingerprint] 4 | url = aws_eks_cluster.cluster.identity[0].oidc[0].issuer 5 | } 6 | -------------------------------------------------------------------------------- /terraform/policies/oidc_assume_role_policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Federated": "${OIDC_ARN}" 8 | }, 9 | "Action": "sts:AssumeRoleWithWebIdentity", 10 | "Condition": { 11 | "StringEquals": { 12 | "${OIDC_URL}:sub": "system:serviceaccount:${NAMESPACE}:${SA_NAME}" 13 | } 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /terraform/sqs.tf: -------------------------------------------------------------------------------- 1 | resource "aws_sqs_queue" "karpenter_spot_interruption" { 2 | name = "${var.name_prefix}-karpenter-spot-interruption" 3 | 4 | message_retention_seconds = 86400 5 | receive_wait_time_seconds = 5 6 | kms_master_key_id = module.eks_cluster.key_arn 7 | } 8 | 9 | resource "aws_sqs_queue_policy" "karpenter_spot_interruption" { 10 | queue_url = aws_sqs_queue.karpenter_spot_interruption.url 11 | policy = data.aws_iam_policy_document.karpenter_spot_interruption.json 12 | } 13 | -------------------------------------------------------------------------------- /terraform/terraform.tfvars: -------------------------------------------------------------------------------- 1 | name_prefix = "eks-eu-dev" 2 | 3 | vpc_cidr = "10.100.0.0/16" 4 | private_subnets_cidrs = ["10.100.0.0/18", "10.100.64.0/18", "10.100.128.0/18"] 5 | public_subnets_cidrs = ["10.100.192.0/20", "10.100.208.0/20", "10.100.224.0/20"] 6 | azs = ["eu-west-2a", "eu-west-2b", "eu-west-2c"] 7 | 8 | eks_enabled_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"] 9 | eks_service_ipv4_cidr = "10.190.0.0/16" 10 | 11 | instance_types = ["m7i.2xlarge"] 12 | 13 | eks_public_access_cidrs = [ 14 | "0.0.0.0/0" 15 | ] 16 | 17 | eks_version = "1.33" 18 | 19 | eks_addon_version_kube_proxy = "v1.33.0-eksbuild.2" 20 | eks_addon_version_core_dns = "v1.12.1-eksbuild.2" 21 | eks_addon_version_ebs_csi_driver = "v1.44.0-eksbuild.1" 22 | eks_addon_version_guardduty = "v1.10.0-eksbuild.2" 23 | eks_addon_version_snapshot_controller = "v8.2.0-eksbuild.1" 24 | eks_addon_version_amazon_cloudwatch_observability = "v4.0.1-eksbuild.1" 25 | eks_addon_version_metrics_server = "v0.7.2-eksbuild.3" 26 | eks_addon_version_kube_state_metrics = "v2.15.0-eksbuild.4" 27 | eks_addon_version_identity_agent = "v1.3.7-eksbuild.2" 28 | -------------------------------------------------------------------------------- /terraform/userdata_al2023.tpl: -------------------------------------------------------------------------------- 1 | MIME-Version: 1.0 2 | Content-Type: multipart/mixed; boundary="BOUNDARY" 3 | 4 | --BOUNDARY 5 | Content-Type: application/node.eks.aws 6 | 7 | --- 8 | apiVersion: node.eks.aws/v1alpha1 9 | kind: NodeConfig 10 | spec: 11 | cluster: 12 | name: ${CLUSTER_NAME} 13 | apiServerEndpoint: ${API_SERVER_URL} 14 | certificateAuthority: ${B64_CLUSTER_CA} 15 | cidr: ${CLUSTER_SERVICE_CIDR} 16 | 17 | --BOUNDARY-- -------------------------------------------------------------------------------- /terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name_prefix" { 2 | type = string 3 | description = "Name prefix used across resources created by this module." 4 | } 5 | 6 | ##### 7 | # VPC 8 | ##### 9 | variable "vpc_cidr" { 10 | type = string 11 | description = "Amazon Virtual Private Cloud Classless Inter-Domain Routing range." 12 | } 13 | 14 | variable "private_subnets_cidrs" { 15 | type = list(string) 16 | description = "Classless Inter-Domain Routing ranges for private subnets." 17 | } 18 | 19 | variable "public_subnets_cidrs" { 20 | type = list(string) 21 | description = "Classless Inter-Domain Routing ranges for public subnets." 22 | } 23 | 24 | variable "azs" { 25 | description = "A list of availability zones names or ids in the region" 26 | type = list(string) 27 | default = [] 28 | } 29 | 30 | ##### 31 | # EKS 32 | ##### 33 | variable "eks_version" { 34 | type = string 35 | description = "EKS controlplane version." 36 | } 37 | 38 | variable "eks_enabled_log_types" { 39 | description = "List of the desired control plane logging to enable." 40 | type = list(string) 41 | default = [] 42 | } 43 | 44 | variable "instance_types" { 45 | type = list(string) 46 | description = "List of instance types associated with the EKS Node Group." 47 | default = ["m6i.large"] 48 | } 49 | 50 | variable "eks_service_ipv4_cidr" { 51 | type = string 52 | description = "The CIDR block to assign Kubernetes service IP addresses from." 53 | default = null 54 | } 55 | 56 | variable "eks_public_access_cidrs" { 57 | type = list(string) 58 | description = "List of CIDR blocks. Indicates which CIDR blocks can access the Amazon EKS public API server endpoint when enabled." 59 | default = ["0.0.0.0/0"] 60 | } 61 | 62 | variable "eks_security_group_ids" { 63 | type = list(string) 64 | description = "List of security group IDs for the cross-account elastic network interfaces that Amazon EKS creates to use to allow communication between your worker nodes and the Kubernetes control plane." 65 | default = [] 66 | } 67 | 68 | variable "eks_endpoint_private_access" { 69 | type = bool 70 | description = "Whether the Amazon EKS private API server endpoint is enabled." 71 | default = true 72 | } 73 | 74 | variable "eks_endpoint_public_access" { 75 | type = bool 76 | description = "Whether the Amazon EKS public API server endpoint is enabled." 77 | default = true 78 | } 79 | 80 | variable "eks_access_entry_roles" { 81 | description = "List of maps with kubernetes_groups, principal_arn, and user_name." 82 | 83 | type = list(any) 84 | 85 | # Example: 86 | # 87 | # [ 88 | # { 89 | # principal_arn = "some-role-arn", 90 | # user_name = "system:node:{{EC2PrivateDNSName}}", 91 | # kubernetes_groups = [ 92 | # "system:nodes" 93 | # ], 94 | # } 95 | # ] 96 | 97 | default = [] 98 | } 99 | 100 | ##### 101 | # EKS Addons 102 | ##### 103 | variable "eks_addon_version_kube_proxy" { 104 | type = string 105 | description = "Kube proxy managed EKS addon version." 106 | default = null 107 | } 108 | 109 | variable "eks_addon_version_core_dns" { 110 | type = string 111 | description = "Core DNS managed EKS addon version." 112 | default = null 113 | } 114 | 115 | variable "eks_addon_version_ebs_csi_driver" { 116 | type = string 117 | description = "AWS ebs csi driver managed EKS addon version." 118 | default = null 119 | } 120 | 121 | variable "eks_addon_version_kubecost" { 122 | type = string 123 | description = "KubeCost EKS addon version." 124 | default = null 125 | } 126 | 127 | variable "eks_addon_version_guardduty" { 128 | type = string 129 | description = "Guardduty agent EKS addon version." 130 | default = null 131 | } 132 | 133 | variable "eks_addon_version_adot" { 134 | type = string 135 | description = "ADOT EKS addon version." 136 | default = null 137 | } 138 | 139 | variable "eks_addon_version_cloudwatch" { 140 | type = string 141 | description = "Cloudwatch EKS addon version." 142 | default = null 143 | } 144 | 145 | variable "eks_addon_version_snapshot_controller" { 146 | type = string 147 | description = "CSI Snapshot Controller EKS addon version." 148 | default = null 149 | } 150 | 151 | variable "eks_addon_version_identity_agent" { 152 | type = string 153 | description = "Pod Identity Agent EKS addon version." 154 | default = null 155 | } 156 | 157 | variable "eks_addon_version_amazon_cloudwatch_observability" { 158 | type = string 159 | description = "Amazon cloudwatch observability addon version." 160 | default = null 161 | } 162 | 163 | variable "eks_addon_version_metrics_server" { 164 | type = string 165 | description = "Metrics server addon version." 166 | default = null 167 | } 168 | 169 | variable "eks_addon_version_kube_state_metrics" { 170 | type = string 171 | description = "Kube State Metrics addon version." 172 | default = null 173 | } 174 | 175 | ##### 176 | # EKS Default Managed Node Group 177 | ##### 178 | variable "ebs_delete_on_termination" { 179 | type = bool 180 | description = "Whether the volume should be destroyed on instance termination." 181 | default = true 182 | } 183 | 184 | variable "ebs_volume_size" { 185 | type = number 186 | description = "The size of the volume in gigabytes." 187 | default = 100 188 | } 189 | 190 | variable "ebs_volume_type" { 191 | type = string 192 | description = "The volume type." 193 | default = "gp3" 194 | } 195 | 196 | variable "ebs_encrypted" { 197 | type = bool 198 | description = "Enables EBS encryption on the volume." 199 | default = true 200 | } 201 | -------------------------------------------------------------------------------- /terraform/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.6.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.74" 8 | } 9 | 10 | tls = { 11 | source = "hashicorp/tls" 12 | version = ">= 4.0" 13 | } 14 | } 15 | } 16 | 17 | provider "aws" { 18 | region = "eu-west-2" 19 | 20 | default_tags { 21 | tags = { 22 | Environment = "dev" 23 | Team = "MC" 24 | Repository = "https://github.com/marcincuber/eks" 25 | Service = "eks" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /terraform/vpc.tf: -------------------------------------------------------------------------------- 1 | module "vpc_eks" { 2 | source = "terraform-aws-modules/vpc/aws" 3 | version = "5.18.1" 4 | 5 | name = var.name_prefix 6 | 7 | azs = var.azs 8 | 9 | cidr = var.vpc_cidr 10 | private_subnets = var.private_subnets_cidrs 11 | public_subnets = var.public_subnets_cidrs 12 | 13 | enable_nat_gateway = true 14 | single_nat_gateway = false 15 | one_nat_gateway_per_az = true 16 | 17 | enable_vpn_gateway = true 18 | 19 | enable_dns_hostnames = true 20 | enable_dns_support = true 21 | 22 | propagate_private_route_tables_vgw = true 23 | propagate_public_route_tables_vgw = true 24 | 25 | private_subnet_tags = { 26 | "kubernetes.io/role/internal-elb" = "1", 27 | "mapPublicIpOnLaunch" = "FALSE" 28 | "karpenter.sh/discovery" = var.name_prefix 29 | } 30 | 31 | public_subnet_tags = { 32 | "kubernetes.io/role/elb" = "1", 33 | "mapPublicIpOnLaunch" = "TRUE" 34 | } 35 | 36 | tags = { 37 | "kubernetes.io/cluster/${var.name_prefix}" = "shared" 38 | } 39 | } 40 | 41 | resource "aws_vpc_endpoint" "eks_vpc_ecr_dkr" { 42 | vpc_id = module.vpc_eks.vpc_id 43 | service_name = data.aws_vpc_endpoint_service.ecr_dkr.service_name 44 | vpc_endpoint_type = "Interface" 45 | 46 | security_group_ids = [aws_security_group.eks_vpc_endpoint.id] 47 | subnet_ids = module.vpc_eks.private_subnets 48 | private_dns_enabled = true 49 | 50 | tags = { 51 | Name = "${var.name_prefix}-ecr-dkr" 52 | } 53 | } 54 | 55 | resource "aws_vpc_endpoint" "eks_vpc_sts" { 56 | vpc_id = module.vpc_eks.vpc_id 57 | service_name = data.aws_vpc_endpoint_service.sts.service_name 58 | vpc_endpoint_type = "Interface" 59 | 60 | security_group_ids = [aws_security_group.eks_vpc_endpoint.id] 61 | subnet_ids = module.vpc_eks.private_subnets 62 | private_dns_enabled = true 63 | 64 | tags = { 65 | Name = "${var.name_prefix}-sts" 66 | } 67 | } 68 | 69 | resource "aws_vpc_endpoint" "eks_vpc_s3" { 70 | vpc_id = module.vpc_eks.vpc_id 71 | service_name = data.aws_vpc_endpoint_service.s3.service_name 72 | vpc_endpoint_type = "Interface" 73 | 74 | security_group_ids = [aws_security_group.eks_vpc_endpoint.id] 75 | subnet_ids = module.vpc_eks.private_subnets 76 | private_dns_enabled = true 77 | 78 | tags = { 79 | Name = "${var.name_prefix}-s3-int" 80 | } 81 | 82 | depends_on = [aws_vpc_endpoint.eks_vpc_s3_gateway] 83 | } 84 | 85 | resource "aws_vpc_endpoint" "eks_vpc_s3_gateway" { 86 | vpc_id = module.vpc_eks.vpc_id 87 | service_name = data.aws_vpc_endpoint_service.s3_gateway.service_name 88 | route_table_ids = module.vpc_eks.private_route_table_ids 89 | vpc_endpoint_type = "Gateway" 90 | 91 | tags = { 92 | Name = "${var.name_prefix}-s3-gateway" 93 | } 94 | } 95 | 96 | resource "aws_vpc_endpoint" "eks_vpc_aps_workspaces" { 97 | vpc_id = module.vpc_eks.vpc_id 98 | service_name = data.aws_vpc_endpoint_service.aps_workspaces.service_name 99 | vpc_endpoint_type = "Interface" 100 | 101 | security_group_ids = [aws_security_group.eks_vpc_endpoint.id] 102 | subnet_ids = module.vpc_eks.private_subnets 103 | private_dns_enabled = true 104 | 105 | policy = data.aws_iam_policy_document.eks_vpc_aps_workspaces.json 106 | 107 | tags = { 108 | Name = "${var.name_prefix}-aps-workspaces" 109 | } 110 | } 111 | 112 | resource "aws_security_group" "eks_vpc_endpoint" { 113 | name_prefix = "${var.name_prefix}-vpc-endpoint-sg-" 114 | description = "Security Group used by VPC Endpoints." 115 | vpc_id = module.vpc_eks.vpc_id 116 | 117 | tags = { 118 | "Name" = "${var.name_prefix}-vpc-endpoint-sg" 119 | } 120 | 121 | lifecycle { 122 | create_before_destroy = true 123 | } 124 | } 125 | 126 | resource "aws_security_group_rule" "eks_vpc_endpoint_egress" { 127 | description = "Allow all egress." 128 | security_group_id = aws_security_group.eks_vpc_endpoint.id 129 | type = "egress" 130 | protocol = "-1" 131 | from_port = 0 132 | to_port = 0 133 | cidr_blocks = ["0.0.0.0/0"] 134 | } 135 | 136 | resource "aws_security_group_rule" "eks_vpc_endpoint_self_ingress" { 137 | description = "Self-ingress for all ports." 138 | security_group_id = aws_security_group.eks_vpc_endpoint.id 139 | type = "ingress" 140 | protocol = "-1" 141 | from_port = 0 142 | to_port = 0 143 | source_security_group_id = aws_security_group.eks_vpc_endpoint.id 144 | } 145 | 146 | resource "aws_vpc_endpoint" "eks_vpc_guardduty" { 147 | vpc_id = module.vpc_eks.vpc_id 148 | service_name = data.aws_vpc_endpoint_service.guardduty.service_name 149 | vpc_endpoint_type = "Interface" 150 | 151 | policy = data.aws_iam_policy_document.eks_vpc_guardduty.json 152 | 153 | security_group_ids = [aws_security_group.eks_vpc_endpoint_guardduty.id] 154 | subnet_ids = module.vpc_eks.private_subnets 155 | private_dns_enabled = true 156 | 157 | tags = { 158 | Name = "${var.name_prefix}-guardduty-data" 159 | } 160 | } 161 | 162 | resource "aws_security_group" "eks_vpc_endpoint_guardduty" { 163 | name_prefix = "${var.name_prefix}-vpc-endpoint-guardduty-sg-" 164 | description = "Security Group used by VPC Endpoints." 165 | vpc_id = module.vpc_eks.vpc_id 166 | 167 | tags = { 168 | "Name" = "${var.name_prefix}-vpc-endpoint-guardduty-sg" 169 | "GuardDutyManaged" = "false" 170 | } 171 | 172 | lifecycle { 173 | create_before_destroy = true 174 | } 175 | } 176 | 177 | resource "aws_vpc_security_group_ingress_rule" "eks_vpc_guardduty" { 178 | security_group_id = aws_security_group.eks_vpc_endpoint_guardduty.id 179 | description = "Ingress for port 443." 180 | 181 | cidr_ipv4 = "0.0.0.0/0" 182 | from_port = 443 183 | ip_protocol = "tcp" 184 | to_port = 443 185 | } 186 | 187 | ##### 188 | # VPC Flow logs 189 | ##### 190 | module "eks_vpc_flow_logs" { 191 | source = "native-cube/vpc-flow-logs/aws" 192 | version = "~> 2.2.0" 193 | 194 | name_prefix = "${var.name_prefix}-vpc-" 195 | 196 | cloudwatch_log_group_name = "/vpc-flow-logs/${var.name_prefix}" 197 | 198 | vpc_id = module.vpc_eks.vpc_id 199 | 200 | retention_in_days = 30 201 | 202 | traffic_type = "ALL" 203 | } 204 | 205 | ##### 206 | # Outputs 207 | ##### 208 | output "vpc_id" { 209 | value = module.vpc_eks.vpc_id 210 | description = "VPC ID." 211 | } 212 | 213 | output "public_subnet_ids" { 214 | value = module.vpc_eks.public_subnets 215 | description = "Public subnet IDs." 216 | } 217 | 218 | output "private_subnet_ids" { 219 | value = module.vpc_eks.private_subnets 220 | description = "Private subnet IDs." 221 | } 222 | --------------------------------------------------------------------------------
"m6i.large"
]