diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..88cb251903 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,30 @@ +# EditorConfig is awesome: http://EditorConfig.org +# Uses editorconfig to maintain consistent coding styles + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 80 +trim_trailing_whitespace = true + +[*.{tf,tfvars}] +indent_size = 2 +indent_style = space + +[*.md] +max_line_length = 0 +trim_trailing_whitespace = false + +[Makefile] +tab_width = 2 +indent_style = tab + +[COMMIT_EDITMSG] +max_line_length = 0 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..176a458f94 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 8243a0028a..0000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,30 +0,0 @@ -# I have issues - -## I'm submitting a... - -* [ ] bug report -* [ ] feature request -* [ ] support request -* [ ] kudos, thank you, warm fuzzy - -## What is the current behavior? - - - -## If this is a bug, how to reproduce? Please include a code sample if relevant. - - - -## What's the expected behavior? - - - -## Are you able to fix this problem and submit a PR? Link here if you have already. - -## Environment details - -* Affected module version: -* OS: -* Terraform version: - -## Any other relevant info diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 5a32376b08..0000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,13 +0,0 @@ -# PR o'clock - -## Description - -Please explain the changes you made here and link to any relevant issues. - -### Checklist - -- [ ] `terraform fmt` and `terraform validate` both work from the root and `examples/eks_test_fixture` directories (look in CI for an example) -- [ ] Tests for the changes have been added and passing (for bug fixes/features) -- [ ] Test results are pasted in this PR (in lieu of CI) -- [ ] I've added my change to CHANGELOG.md -- [ ] Any breaking changes are highlighted above diff --git a/.github/images/security_groups.svg b/.github/images/security_groups.svg new file mode 100644 index 0000000000..6012962597 --- /dev/null +++ b/.github/images/security_groups.svg @@ -0,0 +1 @@ + diff --git a/.github/images/user_data.svg b/.github/images/user_data.svg new file mode 100644 index 0000000000..a36b8051e4 --- /dev/null +++ b/.github/images/user_data.svg @@ -0,0 +1 @@ + diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 0000000000..bd5f2df7cb --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,21 @@ +name: 'Lock Threads' + +on: + schedule: + - cron: '50 1 * * *' + +jobs: + lock: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v5 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + issue-comment: > + I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. + If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. + issue-inactive-days: '30' + pr-comment: > + I'm going to lock this pull request because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. + If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. + pr-inactive-days: '30' diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000000..6419f3aa90 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,52 @@ +name: 'Validate PR title' + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + steps: + # Please look up the latest version from + # https://github.com/amannn/action-semantic-pull-request/releases + - uses: amannn/action-semantic-pull-request@v6.1.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + # Configure which types are allowed. + # Default: https://github.com/commitizen/conventional-commit-types + types: | + fix + feat + docs + ci + chore + # Configure that a scope must always be provided. + requireScope: false + # Configure additional validation for the subject based on a regex. + # This example ensures the subject starts with an uppercase character. + subjectPattern: ^[A-Z].+$ + # If `subjectPattern` is configured, you can use this property to override + # the default error message that is shown when the pattern doesn't match. + # The variables `subject` and `title` can be used within the message. + subjectPatternError: | + The subject "{subject}" found in the pull request title "{title}" + didn't match the configured pattern. Please ensure that the subject + starts with an uppercase character. + # For work-in-progress PRs you can typically use draft pull requests + # from Github. However, private repositories on the free plan don't have + # this option and therefore this action allows you to opt-in to using the + # special "[WIP]" prefix to indicate this state. This will avoid the + # validation of the PR title and the pull request checks remain pending. + # Note that a second check will be reported if this is enabled. + wip: true + # When using "Squash and merge" on a PR with only one commit, GitHub + # will suggest using that commit message instead of the PR title for the + # merge commit, and it's easy to commit this by mistake. Enable this option + # to also validate the commit message for one commit PRs. + validateSingleCommit: false diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000000..057b9c42e2 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,168 @@ +name: Pre-Commit + +on: + pull_request: + branches: + - main + - master + +env: + TERRAFORM_DOCS_VERSION: v0.20.0 + TFLINT_VERSION: v0.59.1 + +jobs: + collectInputs: + name: Collect workflow inputs + runs-on: ubuntu-latest + outputs: + directories: ${{ steps.dirs.outputs.directories }} + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Get root directories + id: dirs + uses: clowdhaus/terraform-composite-actions/directories@v1.14.0 + + preCommitMinVersions: + name: Min TF pre-commit + needs: collectInputs + runs-on: ubuntu-latest + strategy: + matrix: + directory: ${{ fromJson(needs.collectInputs.outputs.directories) }} + steps: + - name: Install rmz + uses: jaxxstorm/action-install-gh-release@v2.1.0 + with: + repo: SUPERCILEX/fuc + asset-name: x86_64-unknown-linux-gnu-rmz + rename-to: rmz + chmod: 0755 + extension-matching: disable + + # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 + - name: Delete unnecessary files + run: | + formatByteCount() { echo $(numfmt --to=iec-i --suffix=B --padding=7 $1'000'); } + getAvailableSpace() { echo $(df -a $1 | awk 'NR > 1 {avail+=$4} END {print avail}'); } + + BEFORE=$(getAvailableSpace) + + ln -s /opt/hostedtoolcache/SUPERCILEX/x86_64-unknown-linux-gnu-rmz/latest/linux-x64/rmz /usr/local/bin/rmz + rmz -f /opt/hostedtoolcache/CodeQL & + rmz -f /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk & + rmz -f /opt/hostedtoolcache/PyPy & + rmz -f /opt/hostedtoolcache/Ruby & + rmz -f /opt/hostedtoolcache/go & + + wait + + AFTER=$(getAvailableSpace) + SAVED=$((AFTER-BEFORE)) + echo "=> Saved $(formatByteCount $SAVED)" + + - name: Checkout + uses: actions/checkout@v5 + + - name: Terraform min/max versions + id: minMax + uses: clowdhaus/terraform-min-max@v2.1.0 + with: + directory: ${{ matrix.directory }} + + - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} + # Run only validate pre-commit check on min version supported + if: ${{ matrix.directory != '.' }} + uses: clowdhaus/terraform-composite-actions/pre-commit@v1.14.0 + with: + terraform-version: ${{ steps.minMax.outputs.minVersion }} + tflint-version: ${{ env.TFLINT_VERSION }} + args: 'terraform_validate --color=always --show-diff-on-failure --files ${{ matrix.directory }}/*' + + - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} + # Run only validate pre-commit check on min version supported + if: ${{ matrix.directory == '.' }} + uses: clowdhaus/terraform-composite-actions/pre-commit@v1.14.0 + with: + terraform-version: ${{ steps.minMax.outputs.minVersion }} + tflint-version: ${{ env.TFLINT_VERSION }} + args: 'terraform_validate --color=always --show-diff-on-failure --files $(ls *.tf)' + + preCommitMaxVersion: + name: Max TF pre-commit + runs-on: ubuntu-latest + needs: collectInputs + steps: + - name: Install rmz + uses: jaxxstorm/action-install-gh-release@v2.1.0 + with: + repo: SUPERCILEX/fuc + asset-name: x86_64-unknown-linux-gnu-rmz + rename-to: rmz + chmod: 0755 + extension-matching: disable + + # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 + - name: Delete unnecessary files + run: | + formatByteCount() { echo $(numfmt --to=iec-i --suffix=B --padding=7 $1'000'); } + getAvailableSpace() { echo $(df -a $1 | awk 'NR > 1 {avail+=$4} END {print avail}'); } + + BEFORE=$(getAvailableSpace) + + ln -s /opt/hostedtoolcache/SUPERCILEX/x86_64-unknown-linux-gnu-rmz/latest/linux-x64/rmz /usr/local/bin/rmz + rmz -f /opt/hostedtoolcache/CodeQL & + rmz -f /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk & + rmz -f /opt/hostedtoolcache/PyPy & + rmz -f /opt/hostedtoolcache/Ruby & + rmz -f /opt/hostedtoolcache/go & + sudo rmz -f /usr/local/lib/android & + + if [[ ${{ github.repository }} == terraform-aws-modules/terraform-aws-security-group ]]; then + sudo rmz -f /usr/share/dotnet & + sudo rmz -f /usr/local/.ghcup & + sudo apt-get -qq remove -y 'azure-.*' + sudo apt-get -qq remove -y 'cpp-.*' + sudo apt-get -qq remove -y 'dotnet-runtime-.*' + sudo apt-get -qq remove -y 'google-.*' + sudo apt-get -qq remove -y 'libclang-.*' + sudo apt-get -qq remove -y 'libllvm.*' + sudo apt-get -qq remove -y 'llvm-.*' + sudo apt-get -qq remove -y 'mysql-.*' + sudo apt-get -qq remove -y 'postgresql-.*' + sudo apt-get -qq remove -y 'php.*' + sudo apt-get -qq remove -y 'temurin-.*' + sudo apt-get -qq remove -y kubectl firefox mono-devel + sudo apt-get -qq autoremove -y + sudo apt-get -qq clean + fi + + wait + + AFTER=$(getAvailableSpace) + SAVED=$((AFTER-BEFORE)) + echo "=> Saved $(formatByteCount $SAVED)" + + - name: Checkout + uses: actions/checkout@v5 + with: + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Terraform min/max versions + id: minMax + uses: clowdhaus/terraform-min-max@v2.1.0 + + - name: Hide template dir + # Special to this repo, we don't want to check this dir + if: ${{ github.repository == 'terraform-aws-modules/terraform-aws-security-group' }} + run: rm -rf modules/_templates + + - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }} + uses: clowdhaus/terraform-composite-actions/pre-commit@v1.14.0 + with: + terraform-version: ${{ steps.minMax.outputs.maxVersion }} + tflint-version: ${{ env.TFLINT_VERSION }} + terraform-docs-version: ${{ env.TERRAFORM_DOCS_VERSION }} + install-hcledit: true diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml new file mode 100644 index 0000000000..918601d915 --- /dev/null +++ b/.github/workflows/publish-docs.yml @@ -0,0 +1,41 @@ +name: Publish docs via GitHub Pages +on: + workflow_dispatch: + push: + branches: + - main + - master + +permissions: + contents: read + +jobs: + build: + name: Deploy docs + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout main + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.x + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install mkdocs-material==9.5.26 \ + mkdocs-include-markdown-plugin==6.2.0 \ + mkdocs-awesome-pages-plugin==2.9.2 + + - name: git config + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + + - run: mkdocs gh-deploy --force diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..e739b790db --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,45 @@ +name: Release + +on: + workflow_dispatch: + push: + branches: + - main + - master + paths: + - '**/*.tpl' + - '**/*.py' + - '**/*.tf' + - '.github/workflows/release.yml' + +jobs: + release: + name: Release + runs-on: ubuntu-latest + # Skip running release workflow on forks + if: github.repository_owner == 'terraform-aws-modules' + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Set correct Node.js version + uses: actions/setup-node@v6 + with: + node-version: 24 + + - name: Install dependencies + run: | + npm install \ + @semantic-release/changelog@6.0.3 \ + @semantic-release/git@10.0.1 \ + conventional-changelog-conventionalcommits@9.1.0 + + - name: Release + uses: cycjimmy/semantic-release-action@v5 + with: + semantic_version: 25.0.0 + env: + GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }} diff --git a/.github/workflows/stale-actions.yaml b/.github/workflows/stale-actions.yaml new file mode 100644 index 0000000000..3e826dcfa3 --- /dev/null +++ b/.github/workflows/stale-actions.yaml @@ -0,0 +1,32 @@ +name: 'Mark or close stale issues and PRs' +on: + schedule: + - cron: '0 0 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v10 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + # Staling issues and PR's + days-before-stale: 30 + stale-issue-label: stale + stale-pr-label: stale + stale-issue-message: | + This issue has been automatically marked as stale because it has been open 30 days + with no activity. Remove stale label or comment or this issue will be closed in 10 days + stale-pr-message: | + This PR has been automatically marked as stale because it has been open 30 days + with no activity. Remove stale label or comment or this PR will be closed in 10 days + # Not stale if have this labels or part of milestone + exempt-issue-labels: bug,wip,on-hold + exempt-pr-labels: bug,wip,on-hold + exempt-all-milestones: true + # Close issue operations + # Label will be automatically removed if the issues are no longer closed nor locked. + days-before-close: 10 + delete-branch: true + close-issue-message: This issue was automatically closed because of stale in 10 days + close-pr-message: This PR was automatically closed because of stale in 10 days diff --git a/.gitignore b/.gitignore index 6128b4ba23..fd39819e6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,39 @@ +# Local .terraform directories +**/.terraform/* + +# Terraform lockfile +.terraform.lock.hcl + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Exclude all .tfvars files, which are likely to contain sentitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. *.tfvars -*.tfstate* -.terraform/ -**/inspec.lock -*.gem -.kitchen/ -.kitchen.local.yml -Gemfile.lock -terraform.tfstate.d/ -eks-admin-cluster-role-binding.yaml -eks-admin-service-account.yaml -.idea/ -*.iml -config-map-aws-auth*.yaml -kubeconfig_* + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Ignore CLI configuration files +.terraformrc +terraform.rc + +# Lambda build artifacts +builds/ +__pycache__/ +*.zip +.tox + +# Local editors/macos files +.DS_Store +.idea diff --git a/.kitchen.yml b/.kitchen.yml deleted file mode 100644 index 9f73ea39dd..0000000000 --- a/.kitchen.yml +++ /dev/null @@ -1,20 +0,0 @@ ---- -driver: - name: "terraform" - root_module_directory: "examples/eks_test_fixture" - -provisioner: - name: "terraform" - -platforms: - - name: "aws" - -verifier: - name: "awspec" - -suites: - - name: "default" - verifier: - name: "awspec" - patterns: - - "test/integration/default/test_eks.rb" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3a7d2a9e49..fc16b01682 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,32 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.7.4 + rev: v1.105.0 hooks: - id: terraform_fmt - id: terraform_docs + args: + - '--args=--lockfile=false' + - id: terraform_tflint + args: + - '--args=--only=terraform_deprecated_interpolation' + - '--args=--only=terraform_deprecated_index' + - '--args=--only=terraform_unused_declarations' + - '--args=--only=terraform_comment_syntax' + - '--args=--only=terraform_documented_outputs' + - '--args=--only=terraform_documented_variables' + - '--args=--only=terraform_typed_variables' + - '--args=--only=terraform_module_pinned_source' + - '--args=--only=terraform_naming_convention' + - '--args=--only=terraform_required_version' + - '--args=--only=terraform_required_providers' + - '--args=--only=terraform_standard_module_structure' + - '--args=--only=terraform_workspace_remote' + - id: terraform_validate - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.0.0 + rev: v6.0.0 hooks: - id: check-merge-conflict + - id: end-of-file-fixer - id: trailing-whitespace - - id: check-yaml - - id: check-added-large-files + - id: mixed-line-ending + args: [--fix=lf] diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 08ce6f3485..0000000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -**/*.* diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000000..66b3eefd65 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,45 @@ +{ + "branches": [ + "main", + "master" + ], + "ci": false, + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits" + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits" + } + ], + [ + "@semantic-release/github", + { + "successComment": "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:", + "labels": false, + "releasedLabels": false + } + ], + [ + "@semantic-release/changelog", + { + "changelogFile": "CHANGELOG.md", + "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file." + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md" + ], + "message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ] + ] +} diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 79a614418f..0000000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.4.4 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index cdcc24b4c8..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,56 +0,0 @@ -language: ruby -sudo: required -dist: trusty - -services: -- docker - -rvm: -- 2.4.4 - -env: - global: - - AWS_REGION='us-east-1' - - TF_VAR_region=${AWS_REGION} - - TF_WARN_OUTPUT_ERRORS=1 - -before_install: -- echo "before_install" - -install: -- echo "install" -- gem install bundler --no-rdoc --no-ri -- bundle install - -before_script: -- export TERRAFORM_VERSION=$(curl -s https://checkpoint-api.hashicorp.com/v1/check/terraform | jq -r -M '.current_version') -- curl --silent --output terraform.zip "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip" -- unzip terraform.zip ; rm -f terraform.zip; chmod +x terraform -- mkdir -p ${HOME}/bin ; export PATH=${PATH}:${HOME}/bin; mv terraform ${HOME}/bin/ -- terraform -v - -script: -- echo 'script' -- terraform init -- terraform fmt -check=true -- terraform validate -var "region=${AWS_REGION}" -var "vpc_id=vpc-123456" -var "subnets=[\"subnet-12345a\"]" -var "workers_ami_id=ami-123456" -var "cluster_ingress_cidrs=[]" -var "cluster_name=test_cluster" -# - docker run --rm -v $(pwd):/app/ --workdir=/app/ -t wata727/tflint --error-with-issues -- cd examples/eks_test_fixture -- terraform init -- terraform fmt -check=true -- terraform validate -- cd - -- terraform -v -# - bundle exec kitchen test --destroy always -# deploy: -# provider: script -# script: ci/deploy.sh -# on: -# branch: master - -notifications: - email: - recipients: - - brandon@atscale.run - on_success: change - on_failure: change diff --git a/CHANGELOG.md b/CHANGELOG.md index 6459488cce..5583c68217 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,270 +1,2209 @@ -# Change Log +# Changelog All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/) and this -project adheres to [Semantic Versioning](http://semver.org/). +## [21.15.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.15.0...v21.15.1) (2026-01-20) -## Next release +### Bug Fixes -## [[v3.?.?](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v3.0.0...HEAD)] - 2019-05-??] +* Move EKS workshop banner up the README ([#3633](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3633)) ([8a83380](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/8a833809b9314a57d93b08597679fd4b2ea2af65)) -### Added +## [21.15.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.14.0...v21.15.0) (2026-01-20) -- Added support for custom service linked role for Auto Scaling group (by @voanhduy1512) -- Write your awesome addition here (by @you) +### Features -### Changed +* Added link to AWS EKS workshops ([#3631](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3631)) ([c2d3b48](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c2d3b488ce9c63fabeaaded3f369b26a0902126a)) - - Add .prettierignore file (by @rothandrew) - - Switch to https for the pre-commit repos (by @rothandrew) - - Add instructions on how to enable the docker bridge network (by @rothandrew) - - Write your awesome change here (by @you) +## [21.14.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.13.0...v21.14.0) (2026-01-13) -# History +### Features -## [[v3.0.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v2.3.1...v3.0.0)] - 2019-04-15] +* Add support for EKS managed node group `update_config.update_strategy` ([#3626](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3626)) ([617dba6](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/617dba6267162edcd0bf007e1c5fd23e4b43584f)) -### Added +## [21.13.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.12.0...v21.13.0) (2026-01-13) -- Fixed: Ability to destroy clusters due to security groups being attached to ENI's (by @whiskeyjimbo) -- Added outputs for worker IAM instance profile(s) (by @soapergem) -- Added support for cluster logging via the `cluster_enabled_log_types` variable (by @sc250024) +### Features -### Changed +* Add support for EKS Capabilities ([#3624](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3624)) ([990050b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/990050b224f92c1470f1fa2014bf5bb3b6b2f021)) - - Updated vpc module version and aws provider version. (by @chenrui333) - - Upgraded default kubernetes version from 1.11 to 1.12 (by @stijndehaes) +## [21.12.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.11.0...v21.12.0) (2026-01-08) -## [[v2.3.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v2.3.0...v2.3.1)] - 2019-03-26] +### Features -### Added +* Add provider meta user-agent, replacing static tag ([#3614](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3614)) ([391b11e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/391b11e0411212c362147e8da37bd2436f250e6d)) -- Added support for eks public and private endpoints (by @stijndehaes) -- Added minimum inbound traffic rule to the cluster worker security group as per the [EKS security group requirements](https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html) (by @sc250024) +## [21.11.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.10.1...v21.11.0) (2026-01-05) -### Changed +### Features -- (Breaking Change) Replaced `enable_docker_bridge` with a generic option called `bootstrap_extra_args` to resolve [310](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/310) (by @max-rocket-internet) +* Update Hybrid Node IAM role permissions ([#3620](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3620)) ([60dcc45](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/60dcc4506399000610e8f2b592f7a68a5131ac33)) -## [[v2.3.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v2.2.1...v2.3.0)] - 2019-03-20] +## [21.10.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.10.0...v21.10.1) (2025-11-28) -### Added +### Bug Fixes -- Allow additional policies to be attached to worker nodes (by @rottenbytes) -- Ability to specify a placement group for each worker group (by @matheuss) -- "k8s.io/cluster-autoscaler/{cluster-name}" and "k8s.io/cluster-autoscaler/node-template/resources/ephemeral-storage" tags for autoscaling groups (by @tbarrella) -- Added "ec2:DescribeLaunchTemplateVersions" action to worker instance role (by @skang0601) -- Adding ebs encryption for workers launched using workers_launch_template (by @russki) -- Added output for generated kubeconfig filename (by @syst0m) -- Added outputs for cluster role ARN and name (by @spingel) -- Added optional name filter variable to be able to pin worker AMI to a release (by @max-rocket-internet) -- Added `--enable-docker-bridge` option for bootstrap.sh in AMI (by @michaelmccord) +* Update minimum required version of AWS provider for provisioned control plane ([#3603](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3603)) ([dc4de4f](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/dc4de4fec0d1c50669ac957fbedc26cac0ebe940)) -## [[v2.2.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v2.2.1...v2.2.2)] - 2019-02-25] +## [21.10.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.9.0...v21.10.0) (2025-11-27) -### Added +### Features -- Ability to specify a path for IAM roles (by @tekn0ir) +* Add support for Provisioned Control Plane ([#3597](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3597)) ([d3d6697](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/d3d6697b33dd3ce283626caf4d36fc9ba7a99827)) -## [[v2.2.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v2.2.0...v2.2.1)] - 2019-02-18] +## [21.9.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.8.0...v21.9.0) (2025-11-16) -## [[v2.2.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v2.1.0...v2.2.0)] - 2019-02-07] +### Features -### Added +* Add support for node repair configuration arguments ([#3585](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3585)) ([c0ed29b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c0ed29b922d22c5950161ddde9c458b1f60708da)) -- Ability to specify a permissions_boundary for IAM roles (by @dylanhellems) -- Ability to configure force_delete for the worker group ASG (by @stefansedich) -- Ability to configure worker group ASG tags (by @stefansedich) -- Added EBS optimized mapping for the g3s.xlarge instance type (by @stefansedich) -- `enabled_metrics` input (by @zanitete) -- write_aws_auth_config to input (by @yutachaos) +## [21.8.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.7.0...v21.8.0) (2025-10-27) -### Changed +### Features -- Change worker group ASG to use create_before_destroy (by @stefansedich) -- Fixed a bug where worker group defaults were being used for launch template user data (by @leonsodhi-lf) -- Managed_aws_auth option is true, the aws-auth configmap file is no longer created, and write_aws_auth_config must be set to true to generate config_map. (by @yutachaos) +* Allow using inline policy for Karpenter controller role to mitigate policy size `LimitExceeded` error ([#3563](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3563)) ([0659a8d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/0659a8d1cca0d62a7d79d4d4534bf9a2c78eed8a)), closes [#3512](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3512) -## [[v2.1.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v2.0.0...v2.1.0)] - 2019-01-15] +## [21.7.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.6.1...v21.7.0) (2025-10-27) -### Added +### Features -- Initial support for worker groups based on Launch Templates (by @skang0601) +* Add recommended security group rule for port `10251` to match EKS addon for `metrics-server` ([#3562](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3562)) ([de8c550](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/de8c550d5d10017eba4b2c0b492360511fe0c34b)) -### Changed +## [21.6.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.6.0...v21.6.1) (2025-10-21) -- Updated the `update_config_map_aws_auth` resource to trigger when the EKS cluster endpoint changes. This likely means that a new cluster was spun up so our ConfigMap won't exist (fixes #234) (by @elatt) -- Removed invalid action from worker_autoscaling iam policy (by @marcelloromani) -- Fixed zsh-specific syntax in retry loop for aws auth config map (by @marcelloromani) -- Fix: fail deployment if applying the aws auth config map still fails after 10 attempts (by @marcelloromani) +### Bug Fixes -## [[v2.0.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v1.8.0...v2.0.0)] - 2018-12-14] +* Update CI workflow versions to latest ([#3554](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3554)) ([e4e25b1](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e4e25b1c4883698a209b23155005fb2a5257c265)) -### Added +## [21.6.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.5.0...v21.6.0) (2025-10-20) -- (Breaking Change) New input variables `map_accounts_count`, `map_roles_count` and `map_users_count` to allow using computed values as part of `map_accounts`, `map_roles` and `map_users` configs (by @chili-man on behalf of OpenGov). -- (Breaking Change) New variables `cluster_create_security_group` and `worker_create_security_group` to stop `value of 'count' cannot be computed` error. -- Added ability to choose local-exec interpreter (by @rothandrew) -### Changed +### Features -- Added `--with-aggregate-type-defaults` option to terraform-docs (by @max-rocket-internet) -- Updated AMI ID filtering to only filter AMIs from current cluster k8s version (by @max-rocket-internet) -- Added `pre-commit-terraform` git hook to automatically create documentation of inputs/outputs (by @antonbabenko) -- Travis fixes (by @RothAndrew) -- Fixed some Windows compatibility issues (by @RothAndrew) +* Use `aws_service_principal` data source for deriving IAM service prinicpals ([#3539](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3539)) ([0b0ca66](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/0b0ca6601923e8542f2f692994d5cb0671823c46)) -## [[v1.8.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v1.7.0...v1.8.0)] - 2018-12-04] +## [21.5.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.4.0...v21.5.0) (2025-10-20) -### Added -- Support for using AWS Launch Templates to define autoscaling groups (by @skang0601) -- `suspended_processes` to `worker_groups` input (by @bkmeneguello) -- `target_group_arns` to `worker_groups` input (by @zihaoyu) -- `force_detach_policies` to `aws_iam_role` `cluster` and `workers` (by @marky-mark) -- Added sleep while trying to apply the kubernetes configurations if failed, up to 50 seconds (by @rmakram-ims) -- `cluster_create_security_group` and `worker_create_security_group`. This allows using computed cluster and worker security groups. (by @rmakram-ims) +### Features -### Changed +* Allow for additional policy statements on sqs queue policy ([#3543](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3543)) ([67557e8](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/67557e8fe866dafd318a9c1d79b08bd9615a839b)) -- new variables worker_groups_launch_template and worker_group_count_launch_template (by @skang0601) -- Remove aws_iam_service_linked_role (by @max-rocket-internet) -- Adjust the order and correct/update the ec2 instance type info. (@chenrui333) -- Removed providers from `main.tf`. (by @max-rocket-internet) -- Removed `configure_kubectl_session` references in documentation [#171](https://github.com/terraform-aws-modules/terraform-aws-eks/pull/171) (by @dominik-k) +## [21.4.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.3.2...v21.4.0) (2025-10-14) -## [[v1.7.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v1.6.0...v1.7.0)] - 2018-10-09] -### Added +### Features -- Worker groups can be created with a specified IAM profile. (from @laverya) -- exposed `aws_eks_cluster` create and destroy timeouts (by @RGPosadas) -- exposed `placement_tenancy` for autoscaling group (by @monsterxx03) -- Allow port 443 from EKS service to nodes to run `metrics-server`. (by @max-rocket-internet) +* Allow setting KMS key rotation period ([#3546](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3546)) ([fd490ea](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/fd490ea897117f3c9346c600cceece6b3fead7e7)) -### Changed +## [21.3.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.3.1...v21.3.2) (2025-10-06) -- fix default worker subnets not working (by @erks) -- fix default worker autoscaling_enabled not working (by @erks) -- Cosmetic syntax changes to improve readability. (by @max-rocket-internet) -- add `protect_from_scale_in` to solve issue #134 (by @kinghajj) -## [[v1.6.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v1.5.0...v1.6.0)] - 2018-09-04] +### Bug Fixes -### Added +* Incorporate AWS provider `v6.15` corrections for EKS Auto Mode to support enabling/disabling EKS Auto Mode without affecting non-Auto Mode users ([#3526](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3526)) ([f5f6dae](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f5f6dae50737137d8709b5fe2f4129a1251eacca)) -- add support for [`amazon-eks-node-*` AMI with bootstrap script](https://aws.amazon.com/blogs/opensource/improvements-eks-worker-node-provisioning/) (by @erks) -- expose `kubelet_extra_args` worker group option (replacing `kubelet_node_labels`) to allow specifying arbitrary kubelet options (e.g. taints and labels) (by @erks) -- add optional input `worker_additional_security_group_ids` to allow one or more additional security groups to be added to all worker launch configurations - #47 (by @hhobbsh @mr-joshua) -- add optional input `additional_security_group_ids` to allow one or more additional security groups to be added to a specific worker launch configuration - #47 (by @mr-joshua) +## [21.3.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.3.0...v21.3.1) (2025-09-16) -### Changed -- allow a custom AMI to be specified as a default (by @erks) -- bugfix for above change (by @max-rocket-internet) -- **Breaking change** Removed support for `eks-worker-*` AMI. The cluster specifying a custom AMI based off of `eks-worker-*` AMI will have to rebuild the AMI from `amazon-eks-node-*`. (by @erks) -- **Breaking change** Removed `kubelet_node_labels` worker group option in favor of `kubelet_extra_args`. (by @erks) +### Bug Fixes -## [[v1.5.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v1.4.0...v1.5.0)] - 2018-08-30] +* Sync Karpenter IAM permissions with upstream ([#3517](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3517)) ([c8bb152](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c8bb152839c411247321194531eadbd7dcdeced4)) -### Added +## [21.3.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.2.0...v21.3.0) (2025-09-16) -- add spot_price option to aws_launch_configuration -- add enable_monitoring option to aws_launch_configuration -- add t3 instance class settings -- add aws_iam_service_linked_role for elasticloadbalancing. (by @max-rocket-internet) -- Added autoscaling policies into module that are optionally attached when enabled for a worker group. (by @max-rocket-internet) -### Changed +### Features -- **Breaking change** Removed `workstation_cidr` variable, http callout and unnecessary security rule. (by @dpiddockcmp) - If you are upgrading from 1.4 you should fix state after upgrade: `terraform state rm module.eks.data.http.workstation_external_ip` -- Can now selectively override keys in `workers_group_defaults` variable rather than callers maintaining a duplicate of the whole map. (by @dpiddockcmp) +* Support EKS Auto Mode custom node pools only creation ([#3514](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3514)) ([165d7c8](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/165d7c8c3bb15b260c23bf07fa0443c0d3accd2f)) -## [[v1.4.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v1.3.0...v1.4.0)] - 2018-08-02] +## [21.2.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.1.5...v21.2.0) (2025-09-11) -### Added -- manage eks workers' root volume size and type. -- `workers_asg_names` added to outputs. (kudos to @laverya) -- New top level variable `worker_group_count` added to replace the use of `length(var.worker_groups)`. This allows using computed values as part of worker group configs. (complaints to @laverya) +### Features -## [[v1.3.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v1.2.0...v1.3.0)] - 2018-07-11] +* Update Karpenter controller policy and permissions to match upstream project ([#3510](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3510)) ([131db39](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/131db3973f7eaf539c33b73014058a94ac0d0528)) -### Added +## [21.1.5](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.1.4...v21.1.5) (2025-08-26) -- New variables `map_accounts`, `map_roles` and `map_users` in order to manage additional entries in the `aws-auth` configmap. (by @max-rocket-internet) -- kubelet_node_labels worker group option allows setting --node-labels= in kubelet. (Hat-tip, @bshelton229 👒) -- `worker_iam_role_arn` added to outputs. Sweet, @hatemosphere 🔥 -### Changed +### Bug Fixes -- Worker subnets able to be specified as a dedicated list per autoscaling group. (up top, @bshelton229 🙏) +* Ensure module created security group is included on any network interfaces created ([#3495](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3495)) ([fa1d422](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/fa1d4221c8fd346927e88d617181fdb75790ecf8)) -## [[v1.2.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v1.1.0...v1.2.0)] - 2018-07-01] +## [21.1.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.1.3...v21.1.4) (2025-08-25) -### Added -- new variable `pre_userdata` added to worker launch configuration allows to run scripts before the plugin does anything. (W00t, @jimbeck 🦉) +### Bug Fixes -### Changed +* Ensure module created security group is included on any network interfaces created ([#3493](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3493)) ([e5cff84](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e5cff842835f2bdede53db843c2b37b3d3534332)) -- kubeconfig made much more flexible. (Bang up job, @sdavids13 💥) -- ASG desired capacity is now ignored as ASG size is more effectively handed by k8s. (Thanks, @ozbillwang 💇♂️) -- Providing security groups didn't behave as expected. This has been fixed. (Good catch, @jimbeck 🔧) -- workstation cidr to be allowed by created security group is now more flexible. (A welcome addition, @jimbeck 🔐) +## [21.1.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.1.2...v21.1.3) (2025-08-24) -## [[v1.1.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v1.0.0...v1.1.0)] - 2018-06-25] -### Added +### Bug Fixes -- new variable `worker_sg_ingress_from_port` allows to change the minimum port number from which pods will accept communication (Thanks, @ilyasotkov 👏). -- expanded on worker example to show how multiple worker autoscaling groups can be created. -- IPv4 is used explicitly to resolve testing from IPv6 networks (thanks, @tsub 🙏). -- Configurable public IP attachment and ssh keys for worker groups. Defaults defined in `worker_group_defaults`. Nice, @hatemosphere 🌂 -- `worker_iam_role_name` now an output. Sweet, @artursmet 🕶️ +* Correct addon timeout lookup/override logic to support global and addon specific settings ([#3492](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3492)) ([b236208](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/b236208d5ce9ff14447f3d8d580b71790c8074e9)) -### Changed +## [21.1.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.1.1...v21.1.2) (2025-08-24) -- IAM test role repaired by @lcharkiewicz 💅 -- `kube-proxy` restart no longer needed in userdata. Good catch, @hatemosphere 🔥 -- worker ASG reattachment wasn't possible when using `name`. Moved to `name_prefix` to allow recreation of resources. Kudos again, @hatemosphere 🐧 -## [[v1.0.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v0.2.0...v1.0.0)] - 2018-06-11] +### Bug Fixes -### Added +* Remediate type mismatch for EFA interfaces and ensure correct (local) definition is used ([#3491](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3491)) ([3959b65](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/3959b65672286c84c03012e12a2e7c8630db6c11)) -- security group id can be provided for either/both of the cluster and the workers. If not provided, security groups will be created with sufficient rules to allow cluster-worker communication. - kudos to @tanmng on the idea ⭐ -- outputs of security group ids and worker ASG arns added for working with these resources outside the module. +## [21.1.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.1.0...v21.1.1) (2025-08-24) -### Changed -- Worker build out refactored to allow multiple autoscaling groups each having differing specs. If none are given, a single ASG is created with a set of sane defaults - big thanks to @kppullin 🥨 +### Bug Fixes -## [[v0.2.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v0.1.1...v0.2.0)] - 2018-06-08] +* Correct metadata options loop condition due to variable definition defaults ([#3490](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3490)) ([b40968a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/b40968a503f1134adcb986af9b4c7f3f3514b811)) -### Added +## [21.1.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.0.9...v21.1.0) (2025-08-15) -- ability to specify extra userdata code to execute following kubelet services start. -- EBS optimization used whenever possible for the given instance type. -- When `configure_kubectl_session` is set to true the current shell will be configured to talk to the kubernetes cluster using config files output from the module. -### Changed +### Features -- files rendered from dedicated templates to separate out raw code and config from `hcl` -- `workers_ami_id` is now made optional. If not specified, the module will source the latest AWS supported EKS AMI instead. +* Add support for deletion protection functionality in the cluster ([#3475](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3475)) ([83c9cd1](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/83c9cd187a36c10f46472e82a197212e897f7f0d)) -## [[v0.1.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v0.1.0...v0.1.1)] - 2018-06-07] +## [21.0.9](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.0.8...v21.0.9) (2025-08-13) -### Changed -- Pre-commit hooks fixed and working. -- Made progress on CI, advancing the build to the final `kitchen test` stage before failing. +### Bug Fixes -## [v0.1.0] - 2018-06-07 +* Allow disabling instance refresh on self-managed node groups (part deux) ([#3478](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3478)) ([ca8f37e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/ca8f37e8ce2a15d0b216ac30e431fa4ac03fc8bc)) -### Added +## [21.0.8](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.0.7...v21.0.8) (2025-08-07) -- Everything! Initial release of the module. -- added a local variable to do a lookup against for a dynamic value in userdata which was previously static. Kudos to @tanmng for finding and fixing bug #1! + +### Bug Fixes + +* Allow disabling instance refresh on self-managed node groups ([#3473](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3473)) ([6a887ad](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/6a887ad38686299c27333a83eb62310ed3106684)) + +## [21.0.7](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.0.6...v21.0.7) (2025-08-02) + + +### Bug Fixes + +* Correct access policy logic to support not providing a policy to associate ([#3464](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3464)) ([39be61d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/39be61d70232ba156fbf92ef90243b93fe5a9eee)) + +## [21.0.6](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.0.5...v21.0.6) (2025-07-30) + + +### Bug Fixes + +* Allow `instance_requirements` to be set in self-managed node groups ([#3455](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3455)) ([5322bf7](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/5322bf72fbbff4afb6a02ae283b21419d9de5b17)) + +## [21.0.5](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.0.4...v21.0.5) (2025-07-29) + + +### Bug Fixes + +* Correct addon logic lookup to pull latest addon version ([#3449](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3449)) ([55d7fa2](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/55d7fa23a356f518ae7b73ec2ddb0ab5947f9a42)) + +## [21.0.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.0.3...v21.0.4) (2025-07-25) + + +### Bug Fixes + +* Correct encryption configuration enable logic; avoid creating Auto Mode policy when Auto Mode is not enabled ([#3439](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3439)) ([6b8a3d9](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/6b8a3d94777346d79a64ccd8287c96b525348013)) + +## [21.0.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.0.2...v21.0.3) (2025-07-24) + + +### Bug Fixes + +* Correct variable defaults for `ami_id` and `kubernetes_version` ([#3437](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3437)) ([8807e0b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/8807e0bb55fdc49ed894b5b51c14131526dbfb91)) + +## [21.0.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.0.1...v21.0.2) (2025-07-24) + + +### Bug Fixes + +* Move `encryption_config` default for `resources` out of type definition and to default variable value to allow disabling encryption ([#3436](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3436)) ([b37368f](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/b37368fdbc608a026f9c17952d964467f5e44e8a)) + +## [21.0.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v21.0.0...v21.0.1) (2025-07-24) + + +### Bug Fixes + +* Correct logic to try to use module created IAM role before falli… ([#3433](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3433)) ([97d4ebb](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/97d4ebbe68a23aa431a534fd7ed56a76f9b37801)) + +## [21.0.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.37.2...v21.0.0) (2025-07-23) + + +### ⚠ BREAKING CHANGES + +* Upgrade min AWS provider and Terraform versions to `6.0` and `1.5.7` respectively (#3412) + +### Features + +* Upgrade min AWS provider and Terraform versions to `6.0` and `1.5.7` respectively ([#3412](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3412)) ([416515a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/416515a0da1ca96c539977d6460e2bc02f10b4d4)) + +## [20.37.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.37.1...v20.37.2) (2025-07-17) + + +### Bug Fixes + +* Allow for both `amazonaws.com.cn` and `amazonaws.com` conditions in PassRole as required for AWS CN ([#3422](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3422)) ([83b68fd](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/83b68fda2b0ea818fc980ab847dd8255a2d18334)) + +## [20.37.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.37.0...v20.37.1) (2025-06-18) + + +### Bug Fixes + +* Restrict AWS provider max version due to v6 provider breaking changes ([#3384](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3384)) ([681a868](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/681a868d624878474fd9f92d1b04d3fec0120db7)) + +## [20.37.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.36.1...v20.37.0) (2025-06-09) + + +### Features + +* Add AL2023 ARM64 NVIDIA variants ([#3369](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3369)) ([715d42b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/715d42bf146791cad911b0b6979c5ce67bc0d2f6)) + +## [20.36.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.36.0...v20.36.1) (2025-06-09) + + +### Bug Fixes + +* Ensure `additional_cluster_dns_ips` is passed through from root module ([#3376](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3376)) ([7a83b1b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/7a83b1b3db9c7475fe6ec46d1c300c0a18f19b2a)) + +## [20.36.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.35.0...v20.36.0) (2025-04-18) + + +### Features + +* Add support for cluster `force_update_version` ([#3345](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3345)) ([207d73f](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/207d73fbaa5eebe6e98b94e95b83fd0a5a13c307)) + +## [20.35.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.34.0...v20.35.0) (2025-03-29) + + +### Features + +* Default to not changing autoscaling schedule values at the scheduled time ([#3322](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3322)) ([abf76f6](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/abf76f60144fe645bbf500d98505377fd4a9da79)) + +## [20.34.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.33.1...v20.34.0) (2025-03-07) + + +### Features + +* Add capacity reservation permissions to Karpenter IAM policy ([#3318](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3318)) ([770ee99](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/770ee99d9c4b61c509d9988eac62de4db113af91)) + +## [20.33.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.33.0...v20.33.1) (2025-01-22) + + +### Bug Fixes + +* Allow `"EC2"` access entry type for EKS Auto Mode custom node pools ([#3281](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3281)) ([3e2ea83](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/3e2ea83267d7532cb66fa4de7f0d2a944b43c3d5)) + +## [20.33.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.32.0...v20.33.0) (2025-01-17) + + +### Features + +* Add node repair config to managed node group ([#3271](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3271)) ([edd7ef3](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/edd7ef36dd0f6b6801275cbecbb6780f03fc7aed)), closes [terraform-aws-modules/terraform-aws-eks#3249](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3249) + +## [20.32.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.31.6...v20.32.0) (2025-01-17) + + +### Features + +* Add Bottlerocket FIPS image variants ([#3275](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3275)) ([d876ac4](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/d876ac4ef1bb45e4f078d0928630033b659c9aa0)) + +## [20.31.6](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.31.5...v20.31.6) (2024-12-20) + + +### Bug Fixes + +* Revert changes to disabling auto mode [#3253](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3253) ([#3255](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3255)) ([1ac67b8](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/1ac67b8a60e336285c4dca03e550dfc78d64acce)) + +## [20.31.5](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.31.4...v20.31.5) (2024-12-20) + + +### Bug Fixes + +* Correct Auto Mode disable ([#3253](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3253)) ([2a6a57a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/2a6a57a9bb1c6563608985bbdbfb7f47eec971df)) + +## [20.31.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.31.3...v20.31.4) (2024-12-14) + + +### Bug Fixes + +* Auto Mode custom tag policy should apply to cluster role, not node role ([#3242](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3242)) ([a07013a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/a07013a1f4d4d56b56eb2e6265a6f38041a4540b)) + +## [20.31.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.31.2...v20.31.3) (2024-12-12) + + +### Bug Fixes + +* Update min provider version to remediate cluster replacement when enabling EKS Auto Mode ([#3240](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3240)) ([012e51c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/012e51c05551da48a7f380d4a7b75880b0c24fe1)) + +## [20.31.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.31.1...v20.31.2) (2024-12-12) + + +### Bug Fixes + +* Avoid trying to attach the node role when Auto Mode nodepools are not specified ([#3239](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3239)) ([ce34f1d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/ce34f1db3f7824167d9a766e6c90dee3a6dcf1c3)) + +## [20.31.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.31.0...v20.31.1) (2024-12-09) + + +### Bug Fixes + +* Create EKS Auto Mode role when Auto Mode is enabled, regardless of built-in node pool use ([#3234](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3234)) ([e2846be](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e2846be8b110e59d36d6f868b74531a6d8ca4987)) + +## [20.31.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.30.1...v20.31.0) (2024-12-04) + + +### Features + +* Add support for EKS Auto Mode and EKS Hybrid nodes ([#3225](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3225)) ([3b974d3](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/3b974d33ad79e142566dd7bcb4bf10472cc91899)) + +## [20.30.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.30.0...v20.30.1) (2024-11-26) + + +### Bug Fixes + +* Coalesce local `resolve_conflicts_on_create_default` value to a boolean since default is `null` ([#3221](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3221)) ([35388bb](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/35388bb8c4cfa0c351427c133490b914b9944b07)) + +## [20.30.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.29.0...v20.30.0) (2024-11-26) + + +### Features + +* Improve addon dependency chain and decrease time to provision addons (due to retries) ([#3218](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3218)) ([ab2207d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/ab2207d50949079d5dd97c976c6f7a8f5b668f0c)) + +## [20.29.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.28.0...v20.29.0) (2024-11-08) + + +### Features + +* Add support for pod identity association on EKS addons ([#3203](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3203)) ([a224334](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/a224334fc8000dc8728971dff8adad46ceb7a8a1)) + +## [20.28.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.27.0...v20.28.0) (2024-11-02) + + +### Features + +* Add support for creating `efa-only` network interfaces ([#3196](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3196)) ([c6da22c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c6da22c78f60a8643a6c76f97c93724f4e1f4e5a)) + +## [20.27.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.26.1...v20.27.0) (2024-11-01) + + +### Features + +* Add support for zonal shift ([#3195](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3195)) ([1b0ac83](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/1b0ac832647dcf0425aedba119fa8276008cbe28)) + +## [20.26.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.26.0...v20.26.1) (2024-10-27) + + +### Bug Fixes + +* Use dynamic partition data source to determine DNS suffix for Karpenter EC2 pass role permission ([#3193](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3193)) ([dea6c44](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/dea6c44b459a546b1386563dfd497bc9d766bfe1)) + +## [20.26.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.25.0...v20.26.0) (2024-10-12) + + +### Features + +* Add support for `desired_capacity_type` (named `desired_size_type`) on self-managed node group ([#3166](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3166)) ([6974a5e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/6974a5e1582a4ed2d8b1f9a07cdacd156ba5ffef)) + +## [20.25.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.24.3...v20.25.0) (2024-10-12) + + +### Features + +* Add support for newly released AL2023 accelerated AMI types ([#3177](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3177)) ([b2a8617](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/b2a8617794a782107399b26c1ff4503e0ea5ec3a)) + + +### Bug Fixes + +* Update CI workflow versions to latest ([#3176](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3176)) ([eb78240](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/eb78240617993845a2a85056655b16302ea9a02c)) + +## [20.24.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.24.2...v20.24.3) (2024-10-03) + + +### Bug Fixes + +* Add `primary_ipv6` parameter to self-managed-node-group ([#3169](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3169)) ([fef6555](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/fef655585b33d717c1665bf8151f0573a17dedc2)) + +## [20.24.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.24.1...v20.24.2) (2024-09-21) + + +### Bug Fixes + +* Remove deprecated `inline_policy` from cluster role ([#3163](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3163)) ([8b90872](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/8b90872983b9c349ff2e0a71678d687dc32ed626)) + +## [20.24.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.24.0...v20.24.1) (2024-09-16) + + +### Bug Fixes + +* Correct Karpenter EC2 service principal DNS suffix in non-commercial regions ([#3157](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3157)) ([47ab3eb](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/47ab3eb884ab243a99322998445127ea6802fcaf)) + +## [20.24.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.23.0...v20.24.0) (2024-08-19) + + +### Features + +* Add support for Karpenter v1 controller IAM role permissions ([#3126](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3126)) ([e317651](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e31765153570631c1978e11cfd1d28e5fc349d8f)) + +## [20.23.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.22.1...v20.23.0) (2024-08-09) + + +### Features + +* Add new output values for OIDC issuer URL and provider that are dual-stack compatible ([#3120](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3120)) ([72668ac](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/72668ac04a2879fd3294e6059238b4aed57278fa)) + +## [20.22.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.22.0...v20.22.1) (2024-08-09) + + +### Bug Fixes + +* Eliminates null check on tag values to fix for_each error about unknown *keys* ([#3119](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3119)) ([6124a08](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/6124a08578d6c6bca1851df4c82cb7e2126e460a)), closes [#3118](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3118) [#2760](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2760) [#2681](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2681) [#2337](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2337) + +## [20.22.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.21.0...v20.22.0) (2024-08-05) + + +### Features + +* Enable update in place for node groups with cluster placement group strategy ([#3045](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3045)) ([75db486](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/75db486530459a04ce6eb2e4ed44b29d062de1b3)) + +## [20.21.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.20.0...v20.21.0) (2024-08-05) + + +### Features + +* Add support for `upgrade_policy` ([#3112](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3112)) ([e12ab7a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e12ab7a5de4ac82968aaede419752ce2bbb6a93d)) + +## [20.20.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.19.0...v20.20.0) (2024-07-19) + + +### Features + +* Enable support for ignore_failed_scaling_activities ([#3104](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3104)) ([532226e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/532226e64e61328b25426cabc27e4009e085154f)) + +## [20.19.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.18.0...v20.19.0) (2024-07-15) + + +### Features + +* Pass the `primary_ipv6` argument to the AWS provider. ([#3098](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3098)) ([e1bb8b6](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e1bb8b66617299c6d9972139b1f9355322e7801e)) + +## [20.18.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.17.2...v20.18.0) (2024-07-15) + + +### Features + +* Support `bootstrap_self_managed_addons` ([#3099](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3099)) ([af88e7d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/af88e7d2f835b3dfde242157ba3dd98b749bbc0b)) + +## [20.17.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.17.1...v20.17.2) (2024-07-05) + + +### Bug Fixes + +* Revert [#3058](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3058) - fix: Invoke aws_iam_session_context data source only when required ([#3092](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3092)) ([93ffdfc](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/93ffdfc6fa380cb0b73df7380e7e62302ebb1a98)) + +## [20.17.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.17.0...v20.17.1) (2024-07-05) + + +### Bug Fixes + +* Invoke `aws_iam_session_context` data source only when required ([#3058](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3058)) ([f02df92](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f02df92b66a9776a689a2baf39e7474f3b703d89)) + +## [20.17.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.16.0...v20.17.0) (2024-07-05) + + +### Features + +* Add support for ML capacity block reservations with EKS managed node group(s) ([#3091](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3091)) ([ae3379e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/ae3379e92429ed842f1c1017fd6ee59ec9f297d4)) + +## [20.16.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.15.0...v20.16.0) (2024-07-02) + + +### Features + +* Add support for custom IAM role policy ([#3087](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3087)) ([1604c6c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/1604c6cdc8cedcd47b7357c5068dc11d0ed1d7e5)) + +## [20.15.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.14.0...v20.15.0) (2024-06-27) + + +### Features + +* Deny HTTP on Karpenter SQS policy ([#3080](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3080)) ([f6e071c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f6e071cd99faa56b988b63051b22df260e929b03)) + +## [20.14.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.13.1...v20.14.0) (2024-06-13) + + +### Features + +* Require users to supply OS via `ami_type` and not via `platform` which is unable to distinquish between the number of variants supported today ([#3068](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3068)) ([ef657bf](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/ef657bfcb51296841f14cf514ffefb1066f810ee)) + +## [20.13.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.13.0...v20.13.1) (2024-06-04) + + +### Bug Fixes + +* Correct syntax for correctly ignoring `bootstrap_cluster_creator_admin_permissions` and not all of `access_config` ([#3056](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3056)) ([1e31929](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/1e319290445a6eb50b53dfb89c9ae9f2949d38d7)) + +## [20.13.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.12.0...v20.13.0) (2024-05-31) + + +### Features + +* Starting with `1.30`, do not use the cluster OIDC issuer URL by default in the identity provider config ([#3055](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3055)) ([00f076a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/00f076ada4cd78c5c34b8be6e8eba44b628b629a)) + +## [20.12.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.11.1...v20.12.0) (2024-05-28) + + +### Features + +* Support additional cluster DNS IPs with Bottlerocket based AMIs ([#3051](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3051)) ([541dbb2](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/541dbb29f12bb763a34b32acdaea9cea12d7f543)) + +## [20.11.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.11.0...v20.11.1) (2024-05-21) + + +### Bug Fixes + +* Ignore changes to `bootstrap_cluster_creator_admin_permissions` which is disabled by default ([#3042](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3042)) ([c65d308](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c65d3085037d9c1c87f4fd3a5be1ca1d732dbf7a)) + +## [20.11.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.10.0...v20.11.0) (2024-05-16) + + +### Features + +* Add `SourceArn` condition to Fargate profile trust policy ([#3039](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3039)) ([a070d7b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/a070d7b2bd92866b91e0963a0f819eec9839ed03)) + +## [20.10.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.9.0...v20.10.0) (2024-05-09) + + +### Features + +* Add support for Pod Identity assocation on Karpenter sub-module ([#3031](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3031)) ([cfcaf27](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/cfcaf27ac78278916ebf3d51dc64a20fe0d7bf01)) + +## [20.9.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.8.5...v20.9.0) (2024-05-08) + + +### Features + +* Propagate `ami_type` to self-managed node group; allow using `ami_type` only ([#3030](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3030)) ([74d3918](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/74d39187d855932dd976da6180eda42dcfe09873)) + +## [20.8.5](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.8.4...v20.8.5) (2024-04-08) + + +### Bug Fixes + +* Forces cluster outputs to wait until access entries are complete ([#3000](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/3000)) ([e2a39c0](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e2a39c0f261d776e4e18a650aa9068429c4f5ef4)) + +## [20.8.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.8.3...v20.8.4) (2024-03-21) + + +### Bug Fixes + +* Pass nodeadm user data variables from root module down to nodegroup sub-modules ([#2981](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2981)) ([84effa0](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/84effa0e30f64ba2fceb7f89c2a822e92f1ee1ea)) + +## [20.8.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.8.2...v20.8.3) (2024-03-12) + + +### Bug Fixes + +* Ensure the correct service CIDR and IP family is used in the rendered user data ([#2963](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2963)) ([aeb9f0c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/aeb9f0c990b259320a6c3e5ff93be3f064bb9238)) + +## [20.8.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.8.1...v20.8.2) (2024-03-11) + + +### Bug Fixes + +* Ensure a default `ip_family` value is provided to guarantee a CNI policy is attached to nodes ([#2967](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2967)) ([29dcca3](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/29dcca335d80e248c57b8efa2c36aaef2e1b1bd2)) + +## [20.8.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.8.0...v20.8.1) (2024-03-10) + + +### Bug Fixes + +* Do not attach policy if Karpenter node role is not created by module ([#2964](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2964)) ([3ad19d7](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/3ad19d7435f34600e4872fd131e155583e498cd9)) + +## [20.8.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.7.0...v20.8.0) (2024-03-10) + + +### Features + +* Replace the use of `toset()` with static keys for node IAM role policy attachment ([#2962](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2962)) ([57f5130](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/57f5130132ca11fd3e478a61a8fc082a929540c2)) + +## [20.7.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.6.0...v20.7.0) (2024-03-09) + + +### Features + +* Add supprot for creating placement group for managed node group ([#2959](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2959)) ([3031631](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/30316312f33fe7fd09faf86fdb1b01ab2a377b2a)) + +## [20.6.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.5.3...v20.6.0) (2024-03-09) + + +### Features + +* Add support for tracking latest AMI release version on managed nodegroups ([#2951](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2951)) ([393da7e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/393da7ec0ed158cf783356ab10959d91430c1d80)) + +## [20.5.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.5.2...v20.5.3) (2024-03-08) + + +### Bug Fixes + +* Update AWS provider version to support `AL2023_*` AMI types; ensure AL2023 user data receives cluster service CIDR ([#2960](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2960)) ([dfe4114](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/dfe41141c2385db783d97494792c8f2e227cfc7c)) + +## [20.5.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.5.1...v20.5.2) (2024-03-07) + + +### Bug Fixes + +* Use the `launch_template_tags` on the launch template ([#2957](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2957)) ([0ed32d7](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/0ed32d7b291513f34775ca85b0aa33da085d09fa)) + +## [20.5.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.5.0...v20.5.1) (2024-03-07) + + +### Bug Fixes + +* Update CI workflow versions to remove deprecated runtime warnings ([#2956](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2956)) ([d14cc92](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/d14cc925c450451b023407d05a2516d7682d1617)) + +## [20.5.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.4.0...v20.5.0) (2024-03-01) + + +### Features + +* Add support for AL2023 `nodeadm` user data ([#2942](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2942)) ([7c99bb1](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/7c99bb19cdbf1eb4f4543f9b8e6d29c3a6734a55)) + +## [20.4.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.3.0...v20.4.0) (2024-02-23) + + +### Features + +* Add support for enabling EFA resources ([#2936](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2936)) ([7f472ec](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/7f472ec660049d4ca85de039cb3015c1b1d12fb8)) + +## [20.3.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.2.2...v20.3.0) (2024-02-21) + + +### Features + +* Add support for addon and identity provider custom tags ([#2938](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2938)) ([f6255c4](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f6255c49e47d44bd62bb2b4e1e448ac80ceb2b3a)) + +### [20.2.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.2.1...v20.2.2) (2024-02-21) + + +### Bug Fixes + +* Replace Karpenter SQS policy dynamic service princpal DNS suffixes with static `amazonaws.com` ([#2941](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2941)) ([081c762](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/081c7624a5a4f2b039370ae8eb9ee8e445d01c48)) + +### [20.2.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.2.0...v20.2.1) (2024-02-08) + + +### Bug Fixes + +* Karpenter `enable_spot_termination = false` should not result in an error ([#2907](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2907)) ([671fc6e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/671fc6e627d957ada47ef3f33068d715e79d25d6)) + +## [20.2.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.1.1...v20.2.0) (2024-02-06) + + +### Features + +* Allow enable/disable of EKS pod identity for the Karpenter controller ([#2902](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2902)) ([cc6919d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/cc6919de811f3972815d4ca26e5e0c8f64c2b894)) + +### [20.1.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.1.0...v20.1.1) (2024-02-06) + + +### Bug Fixes + +* Update access entries `kubernetes_groups` default value to `null` ([#2897](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2897)) ([1e32e6a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/1e32e6a9f8a389b1a4969dde697d34ba4e3c85ac)) + +## [20.1.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.0.1...v20.1.0) (2024-02-06) + + +### Features + +* Add output for `access_policy_associations` ([#2904](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2904)) ([0d2a4c2](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/0d2a4c2af3d7c8593226bbccbf8753950e741b15)) + +### [20.0.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v20.0.0...v20.0.1) (2024-02-03) + + +### Bug Fixes + +* Correct cluster access entry to create multiple policy associations per access entry ([#2892](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2892)) ([4177913](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/417791374cf72dfb673105359463398eb4a75d6e)) + +## [20.0.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.21.0...v20.0.0) (2024-02-02) + + +### ⚠ BREAKING CHANGES + +* Replace the use of `aws-auth` configmap with EKS cluster access entry (#2858) + +### Features + +* Replace the use of `aws-auth` configmap with EKS cluster access entry ([#2858](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2858)) ([6b40bdb](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/6b40bdbb1d283d9259f43b03d24dca99cc1eceff)) + +## [19.21.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.20.0...v19.21.0) (2023-12-11) + + +### Features + +* Add tags for CloudWatch log group only ([#2841](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2841)) ([4c5c97b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/4c5c97b5d404a4e46945e3b6228d469743669937)) + +## [19.20.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.19.1...v19.20.0) (2023-11-14) + + +### Features + +* Allow OIDC root CA thumbprint to be included/excluded ([#2778](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2778)) ([091c680](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/091c68051d9cbf24644121a24c715307f00c44b3)) + +### [19.19.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.19.0...v19.19.1) (2023-11-10) + + +### Bug Fixes + +* Remove additional conditional on Karpenter instance profile creation to support upgrading ([#2812](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2812)) ([c36c8dc](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c36c8dc825aa09e2ded20ff675905aa8857853cf)) + +## [19.19.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.18.0...v19.19.0) (2023-11-04) + + +### Features + +* Update KMS module to avoid calling data sources when `create_kms_key = false` ([#2804](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2804)) ([0732bea](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/0732bea85f46fd2629705f9ee5f87cb695ee95e5)) + +## [19.18.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.17.4...v19.18.0) (2023-11-01) + + +### Features + +* Add Karpenter v1beta1 compatibility ([#2800](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2800)) ([aec2bab](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/aec2bab1d8da89b65b84d11fef77cbc969fccc91)) + +### [19.17.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.17.3...v19.17.4) (2023-10-30) + + +### Bug Fixes + +* Updating license_specification result type ([#2798](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2798)) ([ba0ebeb](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/ba0ebeb11a64a6400a3666165509975d5cdfea43)) + +### [19.17.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.17.2...v19.17.3) (2023-10-30) + + +### Bug Fixes + +* Correct key used on `license_configuration_arn` ([#2796](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2796)) ([bd4bda2](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/bd4bda266e23635c7ca09b6e9d307b29ef6b8579)) + +### [19.17.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.17.1...v19.17.2) (2023-10-10) + + +### Bug Fixes + +* Karpenter node IAM role policies variable should be a map of strings, not list ([#2771](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2771)) ([f4766e5](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f4766e5c27f060e8c7f5950cf82d1fe59c3231af)) + +### [19.17.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.17.0...v19.17.1) (2023-10-06) + + +### Bug Fixes + +* Only include CA thumbprint in OIDC provider list ([#2769](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2769)) ([7e5de15](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/7e5de1566c7e1330c05c5e6c51f5ab4690001915)), closes [#2732](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2732) [#32847](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/32847) + +## [19.17.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.16.0...v19.17.0) (2023-10-06) + + +### Features + +* Add support for `allowed_instance_types` on self-managed nodegroup ASG ([#2757](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2757)) ([feee18d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/feee18dd423b1e76f8a5119206f23306e5879b26)) + +## [19.16.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.15.4...v19.16.0) (2023-08-03) + + +### Features + +* Add `node_iam_role_arns` local variable to check for Windows platform on EKS managed nodegroups ([#2477](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2477)) ([adb47f4](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/adb47f46dc53b1a0c18691a59dc58401c327c0be)) + +### [19.15.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.15.3...v19.15.4) (2023-07-27) + + +### Bug Fixes + +* Use `coalesce` when desired default value is not `null` ([#2696](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2696)) ([c86f8d4](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c86f8d4db3236e7dae59ef9142da4d7e496138c8)) + +### [19.15.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.15.2...v19.15.3) (2023-06-09) + + +### Bug Fixes + +* Snapshot permissions issue for Karpenter submodule ([#2649](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2649)) ([6217d0e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/6217d0eaab4c864ec4d40a31538e78a7fbcee5e3)) + +### [19.15.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.15.1...v19.15.2) (2023-05-30) + + +### Bug Fixes + +* Ensure `isra_tag_values` can be tried before defaulting to `cluster_name` on Karpenter module ([#2631](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2631)) ([6c56e2a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/6c56e2ad20057a5672526b5484df96806598a4e2)) + +### [19.15.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.15.0...v19.15.1) (2023-05-24) + + +### Bug Fixes + +* Revert changes to ignore `role_last_used` ([#2629](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2629)) ([e23139a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e23139ad2da0c31c8aa644ae0516ba9ee2a66399)) + +## [19.15.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.14.0...v19.15.0) (2023-05-24) + + +### Features + +* Ignore changes to *.aws_iam_role.*.role_last_used ([#2628](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2628)) ([f8ea3d0](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f8ea3d08adbc4abfb18a77ad44e30b93cd05c050)) + +## [19.14.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.13.1...v19.14.0) (2023-05-17) + + +### Features + +* Add irsa_tag_values variable ([#2584](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2584)) ([aa3bdf1](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/aa3bdf1c19747bca7067c6e49c071ae80a9ca5e5)) + +### [19.13.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.13.0...v19.13.1) (2023-04-18) + + +### Bug Fixes + +* SQS queue encryption types selection ([#2575](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2575)) ([969c7a7](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/969c7a7c4340c8ed327d18f86c5e00e18190a48b)) + +## [19.13.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.12.0...v19.13.0) (2023-04-12) + + +### Features + +* Add support for allowed_instance_type ([#2552](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2552)) ([54417d2](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/54417d244c06b459b399e84433343af6e9934bb3)) + +## [19.12.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.11.0...v19.12.0) (2023-03-31) + + +### Features + +* Add Autoscaling schedule for EKS managed node group ([#2504](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2504)) ([4a2523c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/4a2523cddd4498f3ece5aee2eedf618dd701eb59)) + +## [19.11.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.10.3...v19.11.0) (2023-03-28) + + +### Features + +* Add optional list of policy ARNs for attachment to Karpenter IRSA ([#2537](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2537)) ([bd387d6](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/bd387d69fac5a431a426e12de786ab80aea112a6)) + +### [19.10.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.10.2...v19.10.3) (2023-03-23) + + +### Bug Fixes + +* Add `aws_eks_addons.before_compute` to the `cluster_addons` output ([#2533](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2533)) ([f977d83](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f977d83500ac529b09918d4e78aa8887749a8cd1)) + +### [19.10.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.10.1...v19.10.2) (2023-03-23) + + +### Bug Fixes + +* Add Name tag for EKS cloudwatch log group ([#2500](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2500)) ([e64a490](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e64a490d8db4ebf495f42c542a40d7d763005873)) + +### [19.10.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.10.0...v19.10.1) (2023-03-17) + + +### Bug Fixes + +* Return correct status for mng ([#2524](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2524)) ([e257daf](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e257dafe94e11384caf210d9ff21c4d3e078cb17)) + +## [19.10.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.9.0...v19.10.0) (2023-02-17) + + +### Features + +* Allow setting custom IRSA policy name for karpenter ([#2480](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2480)) ([8954ff7](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/8954ff7bb433358ba99b77248e3aae377d3a580b)) + +## [19.9.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.8.0...v19.9.0) (2023-02-17) + + +### Features + +* Add support for enabling addons before data plane compute is created ([#2478](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2478)) ([78027f3](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/78027f37e43c79748cd7528d3803122cb8072ed7)) + +## [19.8.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.7.0...v19.8.0) (2023-02-15) + + +### Features + +* Add auto discovery permission of cluster endpoint to Karpenter role ([#2451](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2451)) ([c4a4b8a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c4a4b8afe3d1e89117573e9e04aea08871a069dc)) + +## [19.7.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.6.0...v19.7.0) (2023-02-07) + + +### Features + +* Allow to pass prefix for rule names ([#2437](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2437)) ([68fe60f](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/68fe60f1c4e975d7f6f2c22ae891a32fd80a0156)) + +## [19.6.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.5.1...v19.6.0) (2023-01-28) + + +### Features + +* Add prometheus-adapter port 6443 to recommended sec groups ([#2399](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2399)) ([059dc0c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/059dc0c67c2aebbf2c9a2f0a05856a823dd1b5a0)) + +### [19.5.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.5.0...v19.5.1) (2023-01-05) + + +### Bug Fixes + +* AMI lookup should only happen when launch template is created ([#2386](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2386)) ([3834935](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/383493538748f1df844d40068cdde62579b79476)) + +## [19.5.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.4.3...v19.5.0) (2023-01-05) + + +### Features + +* Ignore changes to labels and annotations on on `aws-auth` ConfigMap ([#2380](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2380)) ([5015b42](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/5015b429e656d927fb66f214c998713c6fc84755)) + +### [19.4.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.4.2...v19.4.3) (2023-01-05) + + +### Bug Fixes + +* Use a version for to avoid GitHub API rate limiting on CI workflows ([#2376](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2376)) ([460e43d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/460e43db77244ad3ca2e62514de712fb0cc2cd7a)) + +### [19.4.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.4.1...v19.4.2) (2022-12-20) + + +### Bug Fixes + +* Drop spot-instances-request from tag_specifications ([#2363](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2363)) ([e391a99](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e391a99a7bd8209618fdb65cc09460673fbaf1bc)) + +### [19.4.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.4.0...v19.4.1) (2022-12-20) + + +### Bug Fixes + +* Correct `eks_managed_*` to `self_managed_*` for `tag_specification` argument ([#2364](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2364)) ([df7c57c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/df7c57c199d9e9f54d9ed18fb7c1e3a47ad732ed)) + +## [19.4.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.3.1...v19.4.0) (2022-12-19) + + +### Features + +* Allow configuring which tags are passed on launch template tag specifications ([#2360](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2360)) ([094ed1d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/094ed1d5e461552a0a76bc019c36690fe0fc2dd5)) + +### [19.3.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.3.0...v19.3.1) (2022-12-18) + + +### Bug Fixes + +* Correct map name for security group rule 4443/tcp ([#2354](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2354)) ([13a9542](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/13a9542dadd29fa75fd76c2adcee9dd17dcffda4)) + +## [19.3.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.2.0...v19.3.0) (2022-12-18) + + +### Features + +* Add additional port for `metrics-server` to recommended rules ([#2353](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2353)) ([5a270b7](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/5a270b7bf8de8c5846e91d72ffd9f594cbd8b921)) + +## [19.2.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.1.1...v19.2.0) (2022-12-18) + + +### Features + +* Ensure all supported resources are tagged under `tag_specifications` on launch templates ([#2352](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2352)) ([0751a0c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/0751a0ca04d6303015e8a9c2f917956ea00d184b)) + +### [19.1.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.1.0...v19.1.1) (2022-12-17) + + +### Bug Fixes + +* Use IAM session context data source to resolve the identities role when using `assumed_role` ([#2347](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2347)) ([71b8eca](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/71b8ecaa87db89c454b2c9446ff3d7675e4dc5a7)) + +## [19.1.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.0.4...v19.1.0) (2022-12-16) + + +### Features + +* Add support for addon `configuration_values` ([#2345](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2345)) ([3b62f6c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/3b62f6c31604490fc19184e626e73873b296ecd1)) + +### [19.0.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.0.3...v19.0.4) (2022-12-07) + + +### Bug Fixes + +* Ensure that custom KMS key is not created if encryption is not enabled, support computed values in cluster name ([#2328](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2328)) ([b83f6d9](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/b83f6d98bfbca548012ea74e792fe14f04f0e6dc)) + +### [19.0.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.0.2...v19.0.3) (2022-12-07) + + +### Bug Fixes + +* Invalid value for "replace" parameter: argument must not be null. ([#2322](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2322)) ([9adc475](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/9adc475bc1f1a201648e37b26cefe9bdf6b3a2f7)) + +### [19.0.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.0.1...v19.0.2) (2022-12-06) + + +### Bug Fixes + +* `public_access_cidrs` require a value even if public endpoint is disabled ([#2320](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2320)) ([3f6d915](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/3f6d915eef6672440df8c82468c31ed2bc2fce54)) + +### [19.0.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v19.0.0...v19.0.1) (2022-12-06) + + +### Bug Fixes + +* Call to lookup() closed too early, breaks sg rule creation in cluster sg if custom source sg is defined. ([#2319](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2319)) ([7bc4a27](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/7bc4a2743f0cdf9c8556a2c067eeb82436aafb41)) + +## [19.0.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.31.2...v19.0.0) (2022-12-05) + + +### ⚠ BREAKING CHANGES + +* Add support for Outposts, remove node security group, add support for addon `preserve` and `most_recent` configurations (#2250) + +### Features + +* Add support for Outposts, remove node security group, add support for addon `preserve` and `most_recent` configurations ([#2250](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2250)) ([b2e97ca](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/b2e97ca3dcbcd76063f1c932aa5199b4f49a2aa1)) + +### [18.31.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.31.1...v18.31.2) (2022-11-23) + + +### Bug Fixes + +* Ensure that `var.create` is tied to all resources correctly ([#2308](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2308)) ([3fb28b3](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/3fb28b357f4fc9144340f94abe9dd520e89f49e2)) + +### [18.31.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.31.0...v18.31.1) (2022-11-22) + + +### Bug Fixes + +* Include all certificate fingerprints in the OIDC provider thumbprint list ([#2307](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2307)) ([7436178](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/7436178cc1a720a066c73f1de23b04b3c24ae608)) + +## [18.31.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.30.3...v18.31.0) (2022-11-21) + + +### Features + +* New Karpenter sub-module for easily enabling Karpenter on EKS ([#2303](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2303)) ([f24de33](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f24de3326d3c12ce61fbaefe1e3dbe7418d8bc85)) + +### [18.30.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.30.2...v18.30.3) (2022-11-07) + + +### Bug Fixes + +* Update CI configuration files to use latest version ([#2293](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2293)) ([364c60d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/364c60d572e85676adca8f6e62679de7d9551271)) + +### [18.30.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.30.1...v18.30.2) (2022-10-14) + + +### Bug Fixes + +* Disable creation of cluster security group rules that map to node security group when `create_node_security_group` = `false` ([#2274](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2274)) ([28ccece](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/28ccecefe22d81a3a7febbbc3efc17c6590f88e1)) + +### [18.30.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.30.0...v18.30.1) (2022-10-11) + + +### Bug Fixes + +* Update CloudWatch log group creation deny policy to use wildcard ([#2267](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2267)) ([ac4d549](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/ac4d549629aa64bbd92f80486bef904a9098e0fa)) + +## [18.30.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.29.1...v18.30.0) (2022-09-29) + + +### Features + +* Add output for cluster TLS certificate SHA1 fingerprint and provider tags to cluster primary security group ([#2249](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2249)) ([a74e980](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/a74e98017b5dc7ed396cf26bfaf98ff7951c9e2e)) + +### [18.29.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.29.0...v18.29.1) (2022-09-26) + + +### Bug Fixes + +* Set `image_id` to come from the launch template instead of data source for self-managed node groups ([#2239](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2239)) ([c5944e5](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c5944e5fb6ea07429ef79f5fe5592e7111567e1e)) + +## [18.29.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.28.0...v18.29.0) (2022-08-26) + + +### Features + +* Allow TLS provider to use versions 3.0+ (i.e. - `>= 3.0`) ([#2211](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2211)) ([f576a6f](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f576a6f9ea523c94a7bb5420d5ab3ed8c7d3fec7)) + +## [18.28.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.27.1...v18.28.0) (2022-08-17) + + +### Features + +* Add output for launch template name, and correct variable type value ([#2205](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2205)) ([0a52d69](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/0a52d690d54a7c39fd4e0d46db36d200f7ef679e)) + +### [18.27.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.27.0...v18.27.1) (2022-08-09) + + +### Bug Fixes + +* Remove empty `""` from node group names output when node group creation is disabled ([#2197](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2197)) ([d2f162b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/d2f162b190596756f1bc9d8f8061e68329c3e5c4)) + +## [18.27.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.26.6...v18.27.0) (2022-08-09) + + +### Features + +* Default to clusters OIDC issuer URL for `aws_eks_identity_provider_config` ([#2190](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2190)) ([93065fa](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/93065fabdf508267b399f677d561f18fd6d7b7f0)) + +### [18.26.6](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.26.5...v18.26.6) (2022-07-22) + + +### Bug Fixes + +* Pin TLS provider version to 3.x versions only ([#2174](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2174)) ([d990ea8](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/d990ea8aff682315828d7c177a309c71541e023c)) + +### [18.26.5](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.26.4...v18.26.5) (2022-07-20) + + +### Bug Fixes + +* Bump kms module to 1.0.2 to fix malformed policy document when not specifying key_owners ([#2163](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2163)) ([0fd1ab1](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/0fd1ab1db9b752e58211428e3c19f62655e5f97d)) + +### [18.26.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.26.3...v18.26.4) (2022-07-20) + + +### Bug Fixes + +* Use partition data source on VPC CNI IPv6 policy ([#2161](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2161)) ([f2d67ff](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f2d67ffa97cc0f9827f75673b1cd263e3a5062b6)) + +### [18.26.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.26.2...v18.26.3) (2022-07-05) + + +### Bug Fixes + +* Correct Fargate profiles additional IAM role policies default type to match variable ([#2143](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2143)) ([c4e6d28](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c4e6d28fc064435f6f05c6c57d7fff8576d9fbba)) + +### [18.26.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.26.1...v18.26.2) (2022-07-01) + + +### Bug Fixes + +* Correct variable types to improve dynamic check correctness ([#2133](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2133)) ([2d7701c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/2d7701c3b0f2c6dcc10f31fc1f703bfde31b2c5b)) + +### [18.26.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.26.0...v18.26.1) (2022-06-29) + + +### Bug Fixes + +* Update KMS module version which aligns on module version requirements ([#2127](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2127)) ([bc04cd3](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/bc04cd3a0a4286566ea56b20d9314115c6e489ab)) + +## [18.26.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.25.0...v18.26.0) (2022-06-28) + + +### Features + +* Add support for specifying NTP address to use private Amazon Time Sync Service ([#2125](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2125)) ([4543ab4](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/4543ab454bea80b64381b88a631d955a7cfae247)) + +## [18.25.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.24.1...v18.25.0) (2022-06-28) + + +### Features + +* Add support for creating KMS key for cluster secret encryption ([#2121](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2121)) ([75acb09](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/75acb09ec56c5ce8e5f74ebc7bf15468b272db8a)) + +### [18.24.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.24.0...v18.24.1) (2022-06-19) + + +### Bug Fixes + +* Remove `modified_at` from ignored changes on EKS addons ([#2114](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2114)) ([5a5a32e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/5a5a32ed1241ba3cc64abe37b37bcb5ad52d42c4)) + +## [18.24.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.23.0...v18.24.0) (2022-06-18) + + +### Features + +* Add support for specifying control plane subnets separate from those used by node groups (data plane) ([#2113](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2113)) ([ebc91bc](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/ebc91bcd37a919a350d872a5b235ccc2a79955a6)) + +## [18.23.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.22.0...v18.23.0) (2022-06-02) + + +### Features + +* Add `autoscaling_group_tags` variable to self-managed-node-groups ([#2084](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2084)) ([8584dcb](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/8584dcb2e0c9061828505c36a8ed8eb6ced02053)) + +## [18.22.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.21.0...v18.22.0) (2022-06-02) + + +### Features + +* Apply `distinct()` on role arns to ensure no duplicated roles in aws-auth configmap ([#2097](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2097)) ([3feb369](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/3feb36927f92fb72ab0cfc25a3ab67465872f4bf)) + +## [18.21.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.20.5...v18.21.0) (2022-05-12) + + +### Features + +* Add `create_autoscaling_group` option and extra outputs ([#2067](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2067)) ([58420b9](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/58420b92a0838aa2e17b156b174893b349083a2b)) + +### [18.20.5](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.20.4...v18.20.5) (2022-04-21) + + +### Bug Fixes + +* Add conditional variable to allow users to opt out of tagging cluster primary security group ([#2034](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2034)) ([51e4182](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/51e418216f210647b69bbd06e569a061c2f0e3c1)) + +### [18.20.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.20.3...v18.20.4) (2022-04-20) + + +### Bug Fixes + +* Correct DNS suffix for OIDC provider ([#2026](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2026)) ([5da692d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/5da692df67cae313711e94216949d1105da6a87f)) + +### [18.20.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.20.2...v18.20.3) (2022-04-20) + + +### Bug Fixes + +* Add `compact()` to `aws_auth_configmap_yaml` for when node groups are set to `create = false` ([#2029](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2029)) ([c173ba2](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c173ba2d62d228729fe6c68f713af6dbe15e7233)) + +### [18.20.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.20.1...v18.20.2) (2022-04-12) + + +### Bug Fixes + +* Avoid re-naming the primary security group through a `Name` tag and leave to the EKS service to manage ([#2010](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2010)) ([b5ae5da](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/b5ae5daa39f8380dc21c9ef1daff22242930692e)) + +### [18.20.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.20.0...v18.20.1) (2022-04-09) + + +### Bug Fixes + +* iam_role_user_name_prefix type as an bool ([#2000](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2000)) ([c576aad](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c576aadce968d09f3295fc06f0766cc9e2a35e29)) + +## [18.20.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.19.0...v18.20.0) (2022-04-09) + + +### Features + +* Add support for managing `aws-auth` configmap using new `kubernetes_config_map_v1_data` resource ([#1999](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1999)) ([da3d54c](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/da3d54cde70adfd8b5d2770805b17d526923113e)) + +## [18.19.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.18.0...v18.19.0) (2022-04-04) + + +### Features + +* Add `create_before_destroy` lifecycle hook to security groups created ([#1985](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1985)) ([6db89f8](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/6db89f8f20a58ae5cfbab5541ff7e499ddf971b8)) + +## [18.18.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.17.1...v18.18.0) (2022-04-03) + + +### Features + +* Add support for allowing EFA network interfaces ([#1980](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1980)) ([523144e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/523144e1d7d4f64ccf30656078fd10d7cd63a444)) + +### [18.17.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.17.0...v18.17.1) (2022-04-02) + + +### Bug Fixes + +* Correct `capacity_reservation_target` within launch templates of both EKS and self managed node groups ([#1979](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1979)) ([381144e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/381144e3bb604b3086ceea537a6052a6179ce5b3)) + +## [18.17.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.16.0...v18.17.0) (2022-03-30) + + +### Features + +* Add back in CloudWatch log group create deny policy to cluster IAM role ([#1974](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1974)) ([98e137f](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/98e137fad990d51a31d86e908ea593e933fc22a9)) + +## [18.16.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.15.0...v18.16.0) (2022-03-29) + + +### Features + +* Support default_tags in aws_autoscaling_group ([#1973](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1973)) ([7a9458a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/7a9458af52ddf1f6180324e845b1e8a26fd5c1f5)) + +## [18.15.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.14.1...v18.15.0) (2022-03-25) + + +### Features + +* Update TLS provider and remove unnecessary cloud init version requirements ([#1966](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1966)) ([0269d38](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/0269d38fcae2b1ca566427159d33910fe96299a7)) + +### [18.14.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.14.0...v18.14.1) (2022-03-24) + + +### Bug Fixes + +* Default to cluster version for EKS and self managed node groups when a `cluster_version` is not specified ([#1963](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1963)) ([fd3a3e9](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/fd3a3e9a96d9a8fa9b22446e2ac8c36cdf68c5fc)) + +## [18.14.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.13.0...v18.14.0) (2022-03-24) + + +### Features + +* Add tags to EKS created cluster security group to match rest of module tagging scheme ([#1957](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1957)) ([9371a29](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/9371a2943b13cc2d9ceb34aef14ec2ccee1cb721)) + +## [18.13.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.12.0...v18.13.0) (2022-03-23) + + +### Features + +* Allow users to selectively attach the EKS created cluster primary security group to nodes ([#1952](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1952)) ([e21db83](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e21db83d8ff3cd1d3f49acc611931e8917d0b6f8)) + +## [18.12.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.11.0...v18.12.0) (2022-03-22) + + +### Features + +* Add outputs for autoscaling group names created to aid in autoscaling group tagging ([#1953](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1953)) ([8b03b7b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/8b03b7b85ef80db5de766827ef65b700317c68e6)) + +## [18.11.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.10.2...v18.11.0) (2022-03-18) + + +### Features + +* Allow users to specify default launch template name in node groups ([#1946](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1946)) ([a9d2cc8](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/a9d2cc8246128fc7f426f0b4596c6799ecf94d8a)) + +### [18.10.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.10.1...v18.10.2) (2022-03-17) + + +### Bug Fixes + +* Sub-modules output the correct eks worker iam arn when workers utilize custom iam role ([#1912](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1912)) ([06a3469](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/06a3469d203fc4344d5f94564762432b5cfd2043)) + +### [18.10.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.10.0...v18.10.1) (2022-03-15) + + +### Bug Fixes + +* Compact result of cluster security group to avoid disruptive updates when no security groups are supplied ([#1934](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1934)) ([5935670](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/5935670503bba3405b53e49ddd88a6451f534d4a)) + +## [18.10.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.9.0...v18.10.0) (2022-03-12) + + +### Features + +* Made it clear that we stand with Ukraine ([fad350d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/fad350d5bf36a7e39aa3840926b4c9968e9f594c)) + +## [18.9.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.8.1...v18.9.0) (2022-03-09) + + +### Features + +* Add variables to allow users to control attributes on `cluster_encryption` IAM policy ([#1928](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1928)) ([2df1572](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/2df1572b8a031fbd31a845cc5c61f015ec387f56)) + +### [18.8.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.8.0...v18.8.1) (2022-03-02) + + +### Bug Fixes + +* Ensure that cluster encryption policy resources are only relevant when creating the IAM role ([#1917](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1917)) ([0fefca7](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/0fefca76f2258cee565359e36a4851978602f36d)) + +## [18.8.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.7.3...v18.8.0) (2022-03-02) + + +### Features + +* Add additional IAM policy to allow cluster role to use KMS key provided for cluster encryption ([#1915](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1915)) ([7644952](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/7644952131a466ca22ba5b3e62cd988e01eff716)) + +### [18.7.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.7.2...v18.7.3) (2022-03-02) + + +### Bug Fixes + +* Add support for overriding DNS suffix for cluster IAM role service principal endpoint ([#1905](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1905)) ([9af0c24](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/9af0c2495a1fe7a02411ac436f48f6d9ca8b359f)) + +### [18.7.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.7.1...v18.7.2) (2022-02-16) + + +### Bug Fixes + +* Update examples to show integration and usage of new IRSA submodule ([#1882](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1882)) ([8de02b9](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/8de02b9ff4690d1bbefb86d3441662b16abb03dd)) + +### [18.7.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.7.0...v18.7.1) (2022-02-15) + + +### Bug Fixes + +* Add missing quotes to block_duration_minutes ([#1881](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1881)) ([8bc6488](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/8bc6488d559d603b539bc1a9c4eb8c57c529b25e)) + +## [18.7.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.6.1...v18.7.0) (2022-02-15) + + +### Features + +* Add variable to provide additional OIDC thumbprints ([#1865](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1865)) ([3fc9f2d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/3fc9f2d69c32a2536aaee45adbe0c3449d7fc986)) + +### [18.6.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.6.0...v18.6.1) (2022-02-15) + + +### Bug Fixes + +* Update autoscaling group `tags` -> `tag` to support v4 of AWS provider ([#1866](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1866)) ([74ad4b0](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/74ad4b09b7bbee857c833cb92afe07499356831d)) + +## [18.6.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.5.1...v18.6.0) (2022-02-11) + + +### Features + +* Add additional output for OIDC provider (issuer URL without leading `https://`) ([#1870](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1870)) ([d3b6847](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/d3b68479dea49076a36e0c39e8c41407f270dcad)) + +### [18.5.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.5.0...v18.5.1) (2022-02-09) + + +### Bug Fixes + +* Use existing node security group when one is provided ([#1861](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1861)) ([c821ba7](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/c821ba78ca924273d17e9c3b15eae05dd7fb9c94)) + +## [18.5.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.4.1...v18.5.0) (2022-02-08) + + +### Features + +* Allow conditional creation of node groups to be set within node group definitions ([#1848](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1848)) ([665f468](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/665f468c1f4839836b1cb5fa5f18ebba17696288)) + +### [18.4.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.4.0...v18.4.1) (2022-02-07) + + +### Bug Fixes + +* Add node group dependency for EKS addons resource creation ([#1840](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1840)) ([2515e0e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/2515e0e561509d026fd0d4725ab0bd864e7340f9)) + +## [18.4.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.3.1...v18.4.0) (2022-02-06) + + +### Features + +* enable IRSA by default ([#1849](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1849)) ([21c3802](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/21c3802dea52bf51ab99c322fcfdce554086a794)) + +### [18.3.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.3.0...v18.3.1) (2022-02-04) + + +### Bug Fixes + +* The `block_duration_minutes` attribute under launch template `spot_options` is not a required ([#1847](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1847)) ([ccc4747](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/ccc4747122b29ac35975e3c89edaa6ee28a86e4a)) + +## [18.3.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.2.7...v18.3.0) (2022-02-03) + + +### Features + +* Add launch_template_tags variable for additional launch template tags ([#1835](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1835)) ([9186def](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/9186defcf6ef72502131cffb8b781e1591d2139e)) + +### [18.2.7](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.2.6...v18.2.7) (2022-02-02) + + +### Bug Fixes + +* Don't tag self managed node security group with kubernetes.io/cluster tag ([#1774](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1774)) ([a638e4a](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/a638e4a754c15ab230cfb0e91de026e038ca4e26)) + +### [18.2.6](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.2.5...v18.2.6) (2022-02-01) + + +### Bug Fixes + +* Wrong rolearn in aws_auth_configmap_yaml ([#1820](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1820)) ([776009d](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/776009d74b16e97974534668ca01a950d660166a)) + +### [18.2.5](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.2.4...v18.2.5) (2022-02-01) + + +### Bug Fixes + +* Correct issue where custom launch template is not used when EKS managed node group is used externally ([#1824](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1824)) ([e16b3c4](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/e16b3c4cbd5f139d54467965f690e79f8e68b76b)) + +### [18.2.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.2.3...v18.2.4) (2022-01-30) + + +### Bug Fixes + +* add missing `launch_template_use_name_prefix` parameter to the root module ([#1818](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1818)) ([d6888b5](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/d6888b5eb6748a065063b0679f228f9fbbf93284)) + +### [18.2.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.2.2...v18.2.3) (2022-01-24) + + +### Bug Fixes + +* Add missing `mixed_instances_policy` parameter to the root module ([#1808](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1808)) ([4af77f2](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/4af77f244a558ec66db6561488a5d8cd0c0f1aed)) + +### [18.2.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.2.1...v18.2.2) (2022-01-22) + + +### Bug Fixes + +* Attributes in timeouts are erroneously reversed ([#1804](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1804)) ([f8fe584](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f8fe584d5b50cc4009ac6c34e3bbb33a4e282f2e)) + +### [18.2.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.2.0...v18.2.1) (2022-01-18) + + +### Bug Fixes + +* Change `instance_metadata_tags` to default to `null`/`disabled` due to tag key pattern conflict ([#1788](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1788)) ([8e4dfa2](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/8e4dfa2be5c60e98a9b20a8ae716c5c446fe935c)) + +## [18.2.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.1.0...v18.2.0) (2022-01-14) + + +### Features + +* Add `instance_metadata_tags` attribute to launch templates ([#1781](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1781)) ([85bb1a0](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/85bb1a00b6111845141a8c07a9459bbd160d7ed3)) + +## [18.1.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.0.6...v18.1.0) (2022-01-14) + + +### Features + +* Add support for networking `ip_family` which enables support for IPV6 ([#1759](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1759)) ([314192e](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/314192e2ebc5faaf5f027a7d868cd36c4844aee1)) + +### [18.0.6](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.0.5...v18.0.6) (2022-01-11) + + +### Bug Fixes + +* Correct remote access variable for security groups and add example for additional IAM policies ([#1766](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1766)) ([f54bd30](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/f54bd3047ba18179766641e347fe9f4fa60ff11b)) + +### [18.0.5](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.0.4...v18.0.5) (2022-01-08) + + +### Bug Fixes + +* Use the prefix_separator var for node sg prefix ([#1751](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1751)) ([62879dd](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/62879dd81a69ba010f19ba9ece8392e1730b53e0)) + +### [18.0.4](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.0.3...v18.0.4) (2022-01-07) + + +### Bug Fixes + +* Not to iterate over remote_access object in dynamic block ([#1743](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1743)) ([86b3c33](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/86b3c339a772e76239f97a9bb1f710199d1bd04a)) + +### [18.0.3](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.0.2...v18.0.3) (2022-01-06) + + +### Bug Fixes + +* Remove trailing hyphen from cluster security group and iam role name prefix ([#1745](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1745)) ([7089c71](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/7089c71e64dbae281435629e19d647ae6952f9ac)) + +### [18.0.2](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.0.1...v18.0.2) (2022-01-06) + + +### Bug Fixes + +* Change variable "node_security_group_additional_rules" from type map(any) to any ([#1747](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1747)) ([8921827](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/89218279d4439110439ca4cb8ac94575ab92b042)) + +### [18.0.1](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v18.0.0...v18.0.1) (2022-01-06) + + +### Bug Fixes + +* Correct conditional map for cluster security group additional rules ([#1738](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1738)) ([a2c7caa](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/a2c7caac9f01ef167994d8b62afb5f997d0fac66)) + +## [18.0.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.24.0...v18.0.0) (2022-01-05) + + +### ⚠ BREAKING CHANGES + +* Removed support for launch configuration and replace `count` with `for_each` (#1680) + +### Features + +* Removed support for launch configuration and replace `count` with `for_each` ([#1680](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1680)) ([ee9f0c6](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/ee9f0c646a45ca9baa6174a036d1e09bcccb87b1)) + + +### Bug Fixes + +* Update preset rule on semantic-release to use conventional commits ([#1736](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1736)) ([be86c0b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/be86c0b898c34943e898e2ecd4994bb7904663ff)) + +# [17.24.0](https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.23.0...v17.24.0) (2021-11-22) + + +### Bug Fixes + +* Added Deny for CreateLogGroup action in EKS cluster role ([#1594](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1594)) ([6959b9b](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/6959b9bae32309357bc97a85a1f09c7b590c8a6d)) +* update CI/CD process to enable auto-release workflow ([#1698](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1698)) ([b876ff9](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/b876ff95136fbb419cbb33feaa8f354a053047e0)) + + +### Features + +* Add ability to define custom timeout for fargate profiles ([#1614](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1614)) ([b7539dc](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/b7539dc220f6b5fe199d67569b6f3619ec00fdf0)) +* Removed ng_depends_on variable and related hack ([#1672](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1672)) ([56e93d7](https://github.com/terraform-aws-modules/terraform-aws-eks/commit/56e93d77de58f311f1d1d7051f40bf77e7b03524)) + + +## [v17.23.0] - 2021-11-02 +FEATURES: +- Added support for client.authentication.k8s.io/v1beta1 ([#1550](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1550)) +- Improve managed node group bootstrap revisited ([#1577](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1577)) + +BUG FIXES: +- Fixed variable reference for snapshot_id ([#1634](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1634)) + + + +## [v17.22.0] - 2021-10-14 +BUG FIXES: +- MNG cluster datasource errors ([#1639](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1639)) + + + +## [v17.21.0] - 2021-10-12 +FEATURES: +- Fix custom AMI bootstrap ([#1580](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1580)) +- Enable throughput & iops configs for managed node_groups ([#1584](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1584)) +- Allow snapshot_id to be specified for additional_ebs_volumes ([#1431](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1431)) +- Allow interface_type to be specified in worker_groups_launch_template ([#1439](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1439)) + +BUG FIXES: +- Rebuild examples ([#1625](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1625)) +- Bug with data source in managed groups submodule ([#1633](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1633)) +- Fixed launch_templates_with_managed_node_group example ([#1599](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1599)) + +DOCS: +- Update iam-permissions.md ([#1613](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1613)) +- Updated iam-permissions.md ([#1612](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1612)) +- Updated faq about desired count of instances in node and worker groups ([#1604](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1604)) +- Update faq about endpoints ([#1603](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1603)) +- Fix broken URL in README ([#1602](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1602)) +- Remove `asg_recreate_on_change` in faq ([#1596](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1596)) + + + +## [v17.20.0] - 2021-09-17 +FEATURES: +- Ability to specify cluster update timeout ([#1588](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1588)) + + + +## [v17.19.0] - 2021-09-16 +REFACTORS: +- Refactoring to match the rest of terraform-aws-modules ([#1583](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1583)) + + + +## [v17.18.0] - 2021-09-08 +FEATURES: +- Add metadata_options for node_groups ([#1485](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1485)) + + + +## [v17.17.0] - 2021-09-08 +FEATURES: +- Added custom AMI support for managed node groups ([#1473](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1473)) + + + +## [v17.16.0] - 2021-09-08 +BUG FIXES: +- Fixed coalescelist() with subnets in fargate module ([#1576](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1576)) + + + +## [v17.15.0] - 2021-09-06 +FEATURES: +- Added ability to pass different subnets for fargate and the cluster ([#1527](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1527)) + + + +## [v17.14.0] - 2021-09-06 +FEATURES: +- Create SG rule for each new cluster_endpoint_private_access_cidr block ([#1549](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1549)) + + + +## [v17.13.0] - 2021-09-06 +BUG FIXES: +- Worker security group handling when worker_create_security_group=false ([#1461](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1461)) + + + +## [v17.12.0] - 2021-09-06 +FEATURES: +- Add ability to tag network-interface using Launch Template ([#1563](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1563)) + + + +## [v17.11.0] - 2021-09-04 +BUG FIXES: +- Updated required version of AWS provider to 3.56.0 ([#1571](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1571)) + + + +## [v17.10.0] - 2021-09-03 +FEATURES: +- Added support for update_config in EKS managed node groups ([#1560](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1560)) + + + +## [v17.9.0] - 2021-09-03 +FEATURES: +- Allow override of timeouts in node_groups ([#1552](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1552)) +- Ability to tag just EKS cluster ([#1569](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1569)) + + + +## [v17.8.0] - 2021-09-03 +BUG FIXES: +- Put KubeletExtraArgs in double quotes for Windows ([#1082](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1082)) + + + +## [v17.7.0] - 2021-09-02 +FEATURES: +- Added throughput support for root and EBS disks ([#1445](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1445)) + + + +## [v17.6.0] - 2021-08-31 +FEATURES: +- Tags passed into worker_groups_launch_template extend var.tags for the volumes ([#1397](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1397)) + + + +## [v17.5.0] - 2021-08-31 +FEATURES: +- Allow users to add more Audiences to OpenID Connect ([#1451](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1451)) + + + +## [v17.4.0] - 2021-08-27 +BUG FIXES: +- Discourage usage of iam_policy_attachment in example ([#1529](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1529)) +- Allow instance `Name` tag to be overwritten ([#1538](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1538)) + +DOCS: +- Fix cluster-autoscaler tags in irsa example ([#1436](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1436)) +- Add missing comma to docs/iam-permissions.md ([#1437](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1437)) +- Updated autoscaling.md ([#1515](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1515)) + + + +## [v17.3.0] - 2021-08-25 +BUG FIXES: +- Fixed launch template version infinite plan issue and improved rolling updates ([#1447](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1447)) + + + +## [v17.2.0] - 2021-08-25 +FEATURES: +- Support for encrypted root disk in node_groups ([#1428](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1428)) +- Enable ebs_optimized setting for node_groups ([#1459](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1459)) + + + +## [v17.1.0] - 2021-06-09 +FEATURES: +- Add support for Managed Node Groups (`node_groups`) taints ([#1424](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1424)) +- Allow to choose launch template version for Managed Node Groups when `create_launch_template` is set to `true` ([#1419](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1419)) +- Add `capacity_rebalance` support for self-managed worker groups ([#1326](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1326)) +- Add `var.wait_for_cluster_timeout` to allow configuring the wait for cluster timeout ([#1420](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1420)) + + + +## [v17.0.3] - 2021-05-28 +BUG FIXES: +- Fix AMI filtering when the default platform is provided in `var.workers_group_defaults` ([#1413](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1413)) +- Remove duplicated security group rule for EKS private access endpoint ([#1412](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1412)) + +NOTES: +- In this bug fix, we remove a duplicated security rule introduced during a merge conflict resolution in [[#1274](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1274)](https://github.com/terraform-aws-modules/terraform-aws-eks/pull/1274) + + + +## [v17.0.2] - 2021-05-28 +BUG FIXES: +- Don't add tags on network interfaces because it's not supported yet in `terraform-provider-aws` ([#1407](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1407)) + + + +## [v17.0.1] - 2021-05-28 +BUG FIXES: +- Default `root_volume_type` must be `gp2` ([#1404](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1404)) + + + +## [v17.0.0] - 2021-05-28 +FEATURES: +- Add ability to use Security Groups as source for private endpoint access ([#1274](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1274)) +- Define Root device name for Windows self-managed worker groups ([#1401](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1401)) +- Drop random pets from Managed Node Groups ([#1372](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1372)) +- Add multiple selectors on the creation of Fargate profile ([#1378](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1378)) +- Rename `config_output_path` into `kubeconfig_output_path` for naming consistency ([#1399](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1399)) +- Kubeconfig file should not be world or group readable by default ([#1114](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1114)) +- Add tags on network interfaces ([#1362](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1362)) +- Add instance store volume option for instances with local disk ([#1213](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1213)) + +BUG FIXES: +- Add back `depends_on` for `data.wait_for_cluster` ([#1389](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1389)) + +DOCS: +- Clarify about the `cluster_endpoint_private_access_cidrs` usage ([#1400](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1400)) +- Add KMS aliases handling to IAM permissions ([#1288](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1288)) + +BREAKING CHANGES: +- The private endpoint security group rule has been renamed to allow the use of CIDR blocks and Security Groups as source. This will delete the `cluster_private_access` Security Group Rule for existing cluster. Please rename by `aws_security_group_rule.cluster_private_access[0]` into `aws_security_group_rule.cluster_private_access_cidrs_source[0]`. +- We now decided to remove `random_pet` resources in Managed Node Groups (MNG). Those were used to recreate MNG if something change and also simulate the newly added argument `node_group_name_prefix`. But they were causing a lot of troubles. To upgrade the module without recreating your MNG, you will need to explicitly reuse their previous name and set them in your MNG `name` argument. Please see [upgrade docs](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs/upgrades.md#upgrade-module-to-v1700-for-managed-node-groups) for more details. +- To support multiple selectors for Fargate profiles, we introduced the `selectors` argument which is a list of map. This will break previous configuration with a single selector `namespace` and `labels`. You'll need to rewrite your configuration to use the `selectors` argument. See [examples](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/examples/fargate/main.tf) dans [docs](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/modules/fargate/README.md) for details. +- The variable `config_output_path` is renamed into `kubeconfig_output_path` for naming consistency. Please upgrade your configuration accordingly. + +NOTES: +- Since we now search only for Linux or Windows AMI if there is a worker groups for the corresponding platform, we can now define different default root block device name for each platform. Use locals `root_block_device_name` and `root_block_device_name_windows` to define your owns. +- The kubeconfig file permission is not world and group readable anymore. The default permission is now `600`. This value can be changed with the variable `var.kubeconfig_file_permission`. + + + +## [v16.2.0] - 2021-05-24 +FEATURES: +- Add ability to forcefully update nodes in managed node groups ([#1380](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1380)) + +BUG FIXES: +- Bump `terraform-provider-http` required version to 2.4.1 to avoid TLS Cert Pool issue on Windows ([#1387](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1387)) + +DOCS: +- Update license to Apache 2 License ([#1375](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1375)) + + + +## [v16.1.0] - 2021-05-19 +FEATURES: +- Search for Windows or Linux AMIs only if they are needed ([#1371](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1371)) + +BUG FIXES: +- Set an ASG's launch template version to an explicit version to automatically trigger instance refresh ([#1370](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1370)) +- Add description for private API ingress Security Group Rule ([#1299](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1299)) + +DOCS: +- Fix cluster autoscaler tags in IRSA example ([#1204](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1204)) +- Add Bottlerocket example ([#1296](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1296)) + +NOTES: +- Set an ASG's launch template version to an explicit version automatically. This will ensure that an instance refresh will be triggered whenever the launch template changes. The default `launch_template_version` is now used to determine the latest or default version of the created launch template for self-managed worker groups. + + + +## [v16.0.1] - 2021-05-19 +BUG FIXES: +- Bump `terraform-aws-modules/http` provider version to support darwin arm64 release ([#1369](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1369)) + +DOCS: +- Use IRSA for Node Termination Handler IAM policy attachment in Instance Refresh example ([#1373](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1373)) + + + +## [v16.0.0] - 2021-05-17 +FEATURES: +- Add support for Auto Scaling Group Instance Refresh for self-managed worker groups ([#1224](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1224)) +- Drop `asg_recreate_on_change` feature to encourage the usage of Instance Refresh for EC2 Auto Scaling ([#1360](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1360)) +- Add timeout of 5mn when waiting for cluster ([#1359](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1359)) +- Remove dependency on deprecated `hashicorp/template` provider ([#1297](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1297)) +- Replace the local-exec script with a http datasource for waiting cluster ([#1339](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1339)) + +BUG FIXES: +- Remove provider from required providers ([#1357](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1357)) +- Bump AWS provider version to add Warm Pool support ([#1340](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1340)) + +CI: +- Bump terraform-docs to 0.13 ([#1335](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1335)) + +BREAKING CHANGES: +- This module used `random_pet` resources to create a random name for the autoscaling group to force the autoscaling group to be re-created when the launch configuration or launch template was changed (if `recreate_asg_when_lc_changes = true` was set), causing the instances to be removed and re-provisioned each time there was an update. Those random_pet resources has been removed and in its place there is now a set of functionality provided by AWS and the Terraform AWS provider - Instance Refresh. We encourage those users to move on Instance Refresh for EC2 Auto Scaling. +- We remove the dependency on the deprecated `hashicorp/template` provider and use the Terraform built in `templatefile` function. This will broke some workflows due to previously being able to pass in the raw contents of a template file for processing. The `templatefile` function requires a template file that exists before running a plan. + +NOTES: +- Using the [terraform-aws-modules/http](https://registry.terraform.io/providers/terraform-aws-modules/http/latest) provider is a more platform agnostic way to wait for the cluster availability than using a local-exec. With this change we're able to provision EKS clusters and manage the `aws_auth` configmap while still using the `hashicorp/tfc-agent` docker image. + + + +## [v15.2.0] - 2021-05-04 +FEATURES: +- Add tags on additional IAM resources like IAM policies, instance profile, OIDC provider ([#1321](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1321)) +- Allow to override cluster and workers egress CIDRs ([#1237](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1237)) +- Allow to specify the managed cluster IAM role name ([#1199](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1199)) +- Add support for ASG Warm Pools ([#1310](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1310)) +- Add support for specifying elastic inference accelerator ([#1176](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1176)) +- Create launch template for Managed Node Groups ([#1138](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1138)) + +BUG FIXES: +- Replace `list` with `tolist` function for working with terraform v0.15.0 ([#1317](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1317)) +- Limit cluster_name when creating fargate IAM Role ([#1270](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1270)) +- Add mission metadata block for launch configuration ([#1301](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1301)) +- Add missing IAM permission for NLB with EIPs ([#1226](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1226)) +- Change back the default disk type to `gp2` ([#1208](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1208)) + +DOCS: +- Update helm instructions for irsa example ([#1251](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1251)) + + + +## [v15.1.0] - 2021-04-16 +BUG FIXES: +- Fixed list and map usage ([#1307](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1307)) + + + +## [v15.0.0] - 2021-04-16 +BUG FIXES: +- Updated code and version requirements to work with Terraform 0.15 ([#1165](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1165)) + + + +## [v14.0.0] - 2021-01-29 +FEATURES: +- Add nitro enclave support for EKS ([#1185](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1185)) +- Add support for `service_ipv4_cidr` for the EKS cluster ([#1139](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1139)) +- Add the SPOT support for Managed Node Groups ([#1129](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1129)) +- Use `gp3` as default as it saves 20% and is more performant ([#1134](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1134)) +- Allow the overwrite of subnets for Fargate profiles ([#1117](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1117)) +- Add support for throughput parameter for `gp3` volumes ([#1146](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1146)) +- Add customizable Auto Scaling Group health check type ([#1118](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1118)) +- Add permissions boundary to fargate execution IAM role ([#1108](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1108)) + +ENHANCEMENTS: +- Don't set -x in userdata to avoid printing sensitive information in logs ([#1187](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1187)) + +BUG FIXES: +- Merge tags from Fargate profiles with common tags from cluster ([#1159](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1159)) + +DOCS: +- Update changelog generation to use custom sort with git-chglog v0.10.0 ([#1202](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1202)) +- Bump IRSA example dependencies to versions which work with TF 0.14 ([#1184](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1184)) +- Change instance type from `t2` to `t3` in examples ([#1169](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1169)) +- Fix typos in README and CONTRIBUTING ([#1167](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1167)) +- Make it more obvious that `var.cluster_iam_role_name` will allow reusing an existing IAM Role for the cluster. ([#1133](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1133)) +- Fixes typo in variables description ([#1154](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1154)) +- Fix a typo in the `aws-auth` section of the README ([#1099](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1099)) + +BREAKING CHANGES: +- To add add SPOT support for MNG, the `instance_type` is now a list and renamed as `instance_types`. This will probably rebuild existing Managed Node Groups. +- The default root volume type is now `gp3` as it saves 20% and is more performant + +NOTES: +- The EKS cluster can be provisioned with both private and public subnets. But Fargate only accepts private ones. This new variable allows to override the subnets to explicitly pass the private subnets to Fargate and work around that issue. + + + +## [v13.2.1] - 2020-11-12 +ENHANCEMENTS: +- Tags passed into worker groups should also be excluded from Launch Template tag specification ([#1095](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1095)) + +BUG FIXES: +- Don’t add empty Roles ARN in aws-auth configmap, specifically when no Fargate profiles are specified ([#1096](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1096)) + +DOCS: +- Clarify usage of both AWS-Managed Node Groups and Self-Managed Worker Groups ([#1094](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1094)) + + + +## [v13.2.0] - 2020-11-07 +FEATURES: +- Add EKS Fargate support ([#1067](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1067)) +- Tags passed into worker groups override tags from `var.tags` for Autoscaling Groups ([#1092](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1092)) + +BUG FIXES: +- Change the default `launch_template_id` to `null` for Managed Node Groups ([#1088](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1088)) + +DOCS: +- Fix IRSA example when deploying cluster-autoscaler from the latest kubernetes/autoscaler helm repo ([#1090](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1090)) +- Explain node_groups and worker_groups difference in FAQ ([#1081](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1081)) +- Update autoscaler installation in IRSA example ([#1063](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1063)) + +NOTES: +- Tags that are passed into `var.worker_groups_launch_template` or `var.worker_groups` now override tags passed in via `var.tags` for Autoscaling Groups only. This allow ASG Tags to be overwritten, so that `propagate_at_launch` can be tweaked for a particular key. + + + +## [v13.1.0] - 2020-11-02 +FEATURES: +- Add Launch Template support for Managed Node Groups ([#997](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/997)) +- Add `cloudwatch_log_group_arn` to outputs ([#1071](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1071)) +- Add kubernetes standard labels to avoid manual mistakes on the managed `aws-auth` configmap ([#989](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/989)) + +BUG FIXES: +- The type of the output `cloudwatch_log_group_name` should be a string instead of a list of strings ([#1061](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1061)) +- Use splat syntax to avoid errors during destroy with an empty state ([#1041](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1041)) +- Fix cycle error during the destroy phase when we change workers order ([#1043](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1043)) +- Set IAM Path for `cluster_elb_sl_role_creation` IAM policy ([#1045](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1045)) +- Use the amazon `ImageOwnerAlias` for worker ami owner instead of owner id ([#1038](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1038)) + +CI: +- Use ubuntu-latest instead of MacOS for docs checks ([#1074](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1074)) +- Fix GitHub Actions CI macOS build errors ([#1065](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1065)) + +NOTES: +- Managed Node Groups now support Launch Templates. The Launch Template it self is not managed by this module, so you have to create it by your self and pass it's id to this module. See docs and [`examples/launch_templates_with_managed_node_groups/`](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/launch_templates_with_managed_node_group) for more details. +- The output `cloudwatch_log_group_name` was incorrectly returning the log group name as a list of strings. As a workaround, people were using `module.eks_cluster.cloudwatch_log_group_name[0]` but that was totally inconsistent with output name. Those users can now use `module.eks_cluster.cloudwatch_log_group_name` directly. +- Keep in mind that changing the order of workers group is a destructive operation. All workers group are destroyed and recreated. If you want to do this safely, you should move then in state with `terraform state mv` until we manage workers groups as maps. + + + +## [v13.0.0] - 2020-10-06 +FEATURES: +- Add `load_balancers` parameter to associate a CLB (Classic Load Balancer) to worker groups ASG ([#992](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/992)) +- Dynamic Partition for IRSA to support AWS-CN Deployments ([#1028](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1028)) +- Add AmazonEKSVPCResourceController to cluster policy to be able to set AWS Security Groups for pod ([#1011](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1011)) +- Cluster version is now a required variable. ([#972](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/972)) + +ENHANCEMENTS: +- Make the `cpu_credits` optional for workers launch template ([#1030](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1030)) +- Update the `wait_for_cluster_cmd` logic to use `curl` if `wget` doesn't exist ([#1002](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1002)) + +BUG FIXES: +- Use customer managed policy instead of inline policy for `cluster_elb_sl_role_creation` ([#1039](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1039)) +- More compatibility fixes for Terraform v0.13 and aws v3 ([#976](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/976)) +- Create `cluster_private_access` security group rules when it should ([#981](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/981)) +- Random_pet with LT workers under 0.13.0 ([#940](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/940)) + +DOCS: +- Add important notes about the retry logic and the `wget` requirement ([#999](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/999)) +- Update README about `cluster_version` variable requirement ([#988](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/988)) +- Mixed spot + on-demand instance documentation ([#967](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/967)) +- Describe key_name is about AWS EC2 key pairs ([#970](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/970)) +- Better documentation of `cluster_id` output blocking ([#955](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/955)) + +CI: +- Bump terraform pre-commit hook version and re-run terraform-docs with the latest version to fix the CI ([#1033](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/1033)) +- Fix CI lint job ([#973](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/973)) + +BREAKING CHANGES: +- Default for `cluster_endpoint_private_access_cidrs` is now `null` instead of `["0.0.0.0/0"]`. It makes the variable required when `cluster_create_endpoint_private_access_sg_rule` is set to `true`. This will force everyone who want to have a private access to set explicitly their allowed subnets for the sake of the principle of least access by default. +- `cluster_version` variable is now required. + +NOTES: +- `credit_specification` for worker groups launch template can now be set to `null` so that we can use non burstable EC2 families +- Starting in v12.1.0 the `cluster_id` output depends on the +`wait_for_cluster` null resource. This means that initialization of the +kubernetes provider will be blocked until the cluster is really ready, +if the module is set to manage the aws_auth ConfigMap and user followed +the typical Usage Example. kubernetes resources in the same plan do not +need to depend on anything explicitly. + + + +## [v12.2.0] - 2020-07-13 +FEATURES: +- IMDSv2 metadata configuration in Launch Templates ([#938](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/938)) +- Worker launch templates and configurations depend on security group rules and IAM policies ([#933](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/933)) +- Add IAM permissions for ELB svc-linked role creation by EKS cluster ([#902](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/902)) +- Add a homemade `depends_on` for MNG submodule to ensure ordering of resource creation ([#867](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/867)) + +BUG FIXES: +- Strip user Name tag from asg_tags [#946](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/946)) +- Get `on_demand_allocation_strategy` from `local.workers_group_defaults` when deciding to use `mixed_instances_policy` ([#908](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/908)) +- Remove unnecessary conditional in private access security group ([#915](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/915)) + +DOCS: +- Update required IAM permissions list ([#936](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/936)) +- Improve FAQ on how to deploy from Windows ([#927](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/927)) +- Autoscaler X.Y version must match ([#928](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/928)) + +NOTES: +- Addition of the IMDSv2 metadata configuration block to Launch Templates will cause a diff to be generated for existing Launch Templates on first Terraform apply. The defaults match existing behaviour. + + + +## [v12.1.0] - 2020-06-06 +FEATURES: +- Add aws_security_group_rule.cluster_https_worker_ingress to output values ([#901](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/901)) +- Allow communication between pods on workers and pods using the primary cluster security group (optional) ([#892](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/892)) + +BUG FIXES: +- Revert removal of templates provider ([#883](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/883)) +- Ensure kubeconfig ends with \n ([#880](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/880)) +- Work around path bug in aws-iam-authenticator ([#894](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/894)) + +DOCS: +- Update FAQ ([#891](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/891)) + +NOTES: +- New variable `worker_create_cluster_primary_security_group_rules` to allow communication between pods on workers and pods using the primary cluster security group (Managed Node Groups or Fargate). It defaults to `false` to avoid potential conflicts with existing security group rules users may have implemented. + + + +## [v12.0.0] - 2020-05-09 +FEATURES: +- Create kubeconfig with non-executable permissions ([#864](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/864)) +- Change EKS default version to 1.16 ([#857](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/857)) + +ENHANCEMENTS: +- Remove dependency on external template provider ([#854](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/854)) + +BUG FIXES: +- Fix Launch Templates error with aws 2.61.0 ([#875](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/875)) +- Use splat syntax for cluster name to avoid `(known after apply)` in managed node groups ([#868](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/868)) + +DOCS: +- Add notes for Kubernetes 1.16 ([#873](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/873)) +- Remove useless template provider in examples ([#863](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/863)) + +BREAKING CHANGES: +- The default `cluster_version` is now 1.16. Kubernetes 1.16 includes a number of deprecated API removals, and you need to ensure your applications and add ons are updated, or workloads could fail after the upgrade is complete. For more information on the API removals, see the [Kubernetes blog post](https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/). For action you may need to take before upgrading, see the steps in the [EKS documentation](https://docs.aws.amazon.com/eks/latest/userguide/update-cluster.html). Please set explicitly your `cluster_version` to an older EKS version until your workloads are ready for Kubernetes 1.16. + + + +## [v11.1.0] - 2020-04-23 +FEATURES: +- Add support for EC2 principal in assume worker role policy for China ([#827](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/827)) + +BUG FIXES: +- Add `vpc_config.cluster_security_group` output as primary cluster security group id ([#828](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/828)) +- Wrap `local.configmap_roles.groups` with tolist() to avoid panic ([#846](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/846)) +- Prevent `coalescelist` null argument error when destroying worker_group_launch_templates ([#842](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/842)) + + + +## [v11.0.0] - 2020-03-31 +FEATURES: +- Add instance tag specifications to Launch Template ([#822](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/822)) +- Add support for additional volumes in launch templates and launch configurations ([#800](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/800)) +- Add interpreter option to `wait_for_cluster_cmd` ([#795](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/795)) + +ENHANCEMENTS: +- Require kubernetes provider >=1.11.1 ([#784](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/784)) +- Use `aws_partition` to build IAM policy ARNs ([#820](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/820)) +- Generate `aws-auth` configmap's roles from Object. No more string concat. ([#790](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/790)) +- Add timeout to default wait_for_cluster_cmd ([#791](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/791)) +- Automate changelog management ([#786](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/786)) + +BUG FIXES: +- Fix destroy failure when talking to EKS endpoint on private network ([#815](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/815)) +- Add ip address when manage_aws_auth is true and public_access is false ([#745](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/745)) +- Add node_group direct dependency on eks_cluster ([#796](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/796)) +- Do not recreate cluster when no SG given ([#798](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/798)) +- Create `false` and avoid waiting forever for a non-existent cluster to respond ([#789](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/789)) +- Fix git-chglog template to format changelog `Type` nicely ([#803](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/803)) +- Fix git-chglog configuration ([#802](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/802)) + +TESTS: +- Remove unused kitchen test related stuff ([#787](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/787)) + +CI: +- Restrict semantic PR to validate PR title only ([#804](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/804)) + + +[Unreleased]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.23.0...HEAD +[v17.23.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.22.0...v17.23.0 +[v17.22.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.21.0...v17.22.0 +[v17.21.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.20.0...v17.21.0 +[v17.20.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.19.0...v17.20.0 +[v17.19.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.18.0...v17.19.0 +[v17.18.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.17.0...v17.18.0 +[v17.17.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.16.0...v17.17.0 +[v17.16.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.15.0...v17.16.0 +[v17.15.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.14.0...v17.15.0 +[v17.14.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.13.0...v17.14.0 +[v17.13.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.12.0...v17.13.0 +[v17.12.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.11.0...v17.12.0 +[v17.11.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.10.0...v17.11.0 +[v17.10.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.9.0...v17.10.0 +[v17.9.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.8.0...v17.9.0 +[v17.8.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.7.0...v17.8.0 +[v17.7.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.6.0...v17.7.0 +[v17.6.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.5.0...v17.6.0 +[v17.5.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.4.0...v17.5.0 +[v17.4.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.3.0...v17.4.0 +[v17.3.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.2.0...v17.3.0 +[v17.2.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.1.0...v17.2.0 +[v17.1.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.0.3...v17.1.0 +[v17.0.3]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.0.2...v17.0.3 +[v17.0.2]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.0.1...v17.0.2 +[v17.0.1]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v17.0.0...v17.0.1 +[v17.0.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v16.2.0...v17.0.0 +[v16.2.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v16.1.0...v16.2.0 +[v16.1.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v16.0.1...v16.1.0 +[v16.0.1]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v16.0.0...v16.0.1 +[v16.0.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v15.2.0...v16.0.0 +[v15.2.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v15.1.0...v15.2.0 +[v15.1.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v15.0.0...v15.1.0 +[v15.0.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v14.0.0...v15.0.0 +[v14.0.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v13.2.1...v14.0.0 +[v13.2.1]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v13.2.0...v13.2.1 +[v13.2.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v13.1.0...v13.2.0 +[v13.1.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v13.0.0...v13.1.0 +[v13.0.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v12.2.0...v13.0.0 +[v12.2.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v12.1.0...v12.2.0 +[v12.1.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v12.0.0...v12.1.0 +[v12.0.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v11.1.0...v12.0.0 +[v11.1.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v11.0.0...v11.1.0 +[v11.0.0]: https://github.com/terraform-aws-modules/terraform-aws-eks/compare/v10.0.0...v11.0.0 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 7730bca7f3..0000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,46 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -- The use of sexualized language or imagery and unwelcome sexual attention or advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at brandon@atscale.run. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index b81ce2db90..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,13 +0,0 @@ -# Contributing - -When contributing to this repository, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. - -Please note we have a code of conduct, please follow it in all your interactions with the project. - -## Pull Request Process - -1. Ensure any install or build dependencies are removed before the end of the layer when doing a build. -2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. -3. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). -4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you. diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 89be253ef9..0000000000 --- a/Gemfile +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -ruby '2.4.4' - -source 'https://rubygems.org/' do - gem 'awspec', '~> 1.4.2' - gem 'kitchen-terraform', '~> 3.2' - gem 'kitchen-verifier-awspec', '~> 0.1.1' -end diff --git a/LICENSE b/LICENSE index ab6dbd68ad..d9a10c0d8e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,176 @@ -Copyright (c) 2018 Brandon O'Connor - Run at Scale - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md index e882fe9a06..e45cf55a33 100644 --- a/README.md +++ b/README.md @@ -1,182 +1,626 @@ -# terraform-aws-eks +# AWS EKS Terraform module -A terraform module to create a managed Kubernetes cluster on AWS EKS. Available -through the [Terraform registry](https://registry.terraform.io/modules/terraform-aws-modules/eks/aws). -Inspired by and adapted from [this doc](https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html) -and its [source code](https://github.com/terraform-providers/terraform-provider-aws/tree/master/examples/eks-getting-started). -Read the [AWS docs on EKS to get connected to the k8s dashboard](https://docs.aws.amazon.com/eks/latest/userguide/dashboard-tutorial.html). +Terraform module which creates Amazon EKS (Kubernetes) resources -| Branch | Build status | -| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| master | [](https://travis-ci.org/terraform-aws-modules/terraform-aws-eks) | +[](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) -## Assumptions +## [Documentation](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs) -* You want to create an EKS cluster and an autoscaling group of workers for the cluster. -* You want these resources to exist within security groups that allow communication and coordination. These can be user provided or created within the module. -* You've created a Virtual Private Cloud (VPC) and subnets where you intend to put the EKS resources. -* If `manage_aws_auth = true`, it's required that both [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl) (>=1.10) and [`aws-iam-authenticator`](https://github.com/kubernetes-sigs/aws-iam-authenticator#4-set-up-kubectl-to-use-authentication-tokens-provided-by-aws-iam-authenticator-for-kubernetes) are installed and on your shell's PATH. +- [Frequently Asked Questions](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs/faq.md) +- [Compute Resources](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs/compute_resources.md) +- [User Data](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs/user_data.md) +- [Network Connectivity](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs/network_connectivity.md) +- Upgrade Guides + - [Upgrade to v17.x](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs/UPGRADE-17.0.md) + - [Upgrade to v18.x](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs/UPGRADE-18.0.md) + - [Upgrade to v19.x](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs/UPGRADE-19.0.md) + - [Upgrade to v20.x](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs/UPGRADE-20.0.md) + - [Upgrade to v21.x](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/docs/UPGRADE-21.0.md) -## Usage example + +
-A full example leveraging other community modules is contained in the [examples/eks_test_fixture directory](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/eks_test_fixture). Here's the gist of using it via the Terraform registry: +### External Documentation + +Please note that we strive to provide a comprehensive suite of documentation for __*configuring and utilizing the module(s)*__ defined here, and that documentation regarding EKS (including EKS managed node group, self managed node group, and Fargate profile) and/or Kubernetes features, usage, etc. are better left up to their respective sources: + +- [AWS EKS Documentation](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html) +- [Kubernetes Documentation](https://kubernetes.io/docs/home/) + +## Usage + +### EKS Auto Mode + +> [!CAUTION] +> Due to the current EKS Auto Mode API, to disable EKS Auto Mode you will have to explicity set: +> +>```hcl +>compute_config = { +> enabled = false +> } +>``` +> +> If you try to disable by simply removing the `compute_config` block, this will fail to disable EKS Auto Mode. Only after applying with `enabled = false` can you then remove the `compute_config` block from your configurations. ```hcl -module "my-cluster" { - source = "terraform-aws-modules/eks/aws" - cluster_name = "my-cluster" - subnets = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] - vpc_id = "vpc-1234556abcdef" - - worker_groups = [ - { - instance_type = "m4.large" - asg_max_size = 5 - } - ] +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 21.0" + + name = "example" + kubernetes_version = "1.33" + + # Optional + endpoint_public_access = true + + # Optional: Adds the current caller identity as an administrator via cluster access entry + enable_cluster_creator_admin_permissions = true + + compute_config = { + enabled = true + node_pools = ["general-purpose"] + } + + vpc_id = "vpc-1234556abcdef" + subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] tags = { - environment = "test" + Environment = "dev" + Terraform = "true" } } ``` -## Other documentation +### EKS Auto Mode - Custom Node Pools Only + +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 21.0" + + name = "example" + kubernetes_version = "1.33" -- [Autoscaling](docs/autoscaling.md): How to enabled worker node autoscaling. -- [Enable Docker Bridge Network](docs/enable-docker-bridge-network.md): How to enable the docker bridge network when using the EKS-optimized AMI, which disables it by default. + # Optional + endpoint_public_access = true -## Release schedule + # Optional: Adds the current caller identity as an administrator via cluster access entry + enable_cluster_creator_admin_permissions = true -Generally the maintainers will try to release the module once every 2 weeks to -keep up with PR additions. If particularly pressing changes are added or maintainers -come up with the spare time (hah!), release may happen more often on occasion. + # Create just the IAM resources for EKS Auto Mode for use with custom node pools + create_auto_mode_iam_resources = true + compute_config = { + enabled = true + } -## Testing + vpc_id = "vpc-1234556abcdef" + subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] -This module has been packaged with [awspec](https://github.com/k1LoW/awspec) tests through [kitchen](https://kitchen.ci/) and [kitchen-terraform](https://newcontext-oss.github.io/kitchen-terraform/). To run them: + tags = { + Environment = "dev" + Terraform = "true" + } +} +``` -1. Install [rvm](https://rvm.io/rvm/install) and the ruby version specified in the [Gemfile](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/Gemfile). -2. Install bundler and the gems from our Gemfile: +### EKS Provisioned Control Plane - ```bash - gem install bundler && bundle install - ``` +EKS Provisioned Control Plane allows you to provision a control plane with increased capacity for larger workloads. Valid tier values are `standard`, `tier-xl`, `tier-2xl`, and `tier-4xl`. -3. Ensure your AWS environment is configured (i.e. credentials and region) for test. -4. Test using `bundle exec kitchen test` from the root of the repo. +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 21.0" + + name = "my-cluster" + kubernetes_version = "1.33" + + # Optional + endpoint_public_access = true + + # Optional: Adds the current caller identity as an administrator via cluster access entry + enable_cluster_creator_admin_permissions = true + + # EKS Provisioned Control Plane configuration + control_plane_scaling_config = { + tier = "tier-xl" + } -For now, connectivity to the kubernetes cluster is not tested but will be in the -future. Once the test fixture has converged, you can query the test cluster from -that terminal session with -```bash -kubectl get nodes --watch --kubeconfig kubeconfig + vpc_id = "vpc-1234556abcdef" + subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] + + tags = { + Environment = "dev" + Terraform = "true" + } +} ``` -(using default settings `config_output_path = "./"` & `write_kubeconfig = true`) -## Doc generation +### EKS Managed Node Group -Code formatting and documentation for variables and outputs is generated using [pre-commit-terraform hooks](https://github.com/antonbabenko/pre-commit-terraform) which uses [terraform-docs](https://github.com/segmentio/terraform-docs). +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 21.0" -Follow [these instructions](https://github.com/antonbabenko/pre-commit-terraform#how-to-install) to install pre-commit locally. + name = "my-cluster" + kubernetes_version = "1.33" -And install `terraform-docs` with `go get github.com/segmentio/terraform-docs` or `brew install terraform-docs`. + addons = { + coredns = {} + eks-pod-identity-agent = { + before_compute = true + } + kube-proxy = {} + vpc-cni = { + before_compute = true + } + } -## Contributing + # Optional + endpoint_public_access = true -Report issues/questions/feature requests on in the [issues](https://github.com/terraform-aws-modules/terraform-aws-eks/issues/new) section. + # Optional: Adds the current caller identity as an administrator via cluster access entry + enable_cluster_creator_admin_permissions = true -Full contributing [guidelines are covered here](https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/CONTRIBUTING.md). + vpc_id = "vpc-1234556abcdef" + subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] + control_plane_subnet_ids = ["subnet-xyzde987", "subnet-slkjf456", "subnet-qeiru789"] -## IAM Permissions + # EKS Managed Node Group(s) + eks_managed_node_groups = { + example = { + # Starting on 1.30, AL2023 is the default AMI type for EKS managed node groups + ami_type = "AL2023_x86_64_STANDARD" + instance_types = ["m5.xlarge"] -Testing and using this repo requires a minimum set of IAM permissions. Test permissions -are listed in the [eks_test_fixture README](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/eks_test_fixture/README.md). + min_size = 2 + max_size = 10 + desired_size = 2 + } + } -## Change log + tags = { + Environment = "dev" + Terraform = "true" + } +} +``` -The [changelog](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/CHANGELOG.md) captures all important release notes. +### Cluster Access Entry -## Authors +When enabling `authentication_mode = "API_AND_CONFIG_MAP"`, EKS will automatically create an access entry for the IAM role(s) used by managed node group(s) and Fargate profile(s). There are no additional actions required by users. For self-managed node groups and the Karpenter sub-module, this project automatically adds the access entry on behalf of users so there are no additional actions required by users. -Created and maintained by [Brandon O'Connor](https://github.com/brandoconnor) - brandon@atscale.run. -Many thanks to [the contributors listed here](https://github.com/terraform-aws-modules/terraform-aws-eks/graphs/contributors)! +On clusters that were created prior to cluster access management (CAM) support, there will be an existing access entry for the cluster creator. This was previously not visible when using `aws-auth` ConfigMap, but will become visible when access entry is enabled. -## License +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 21.0" + + # Truncated for brevity ... + + access_entries = { + # One access entry with a policy associated + example = { + principal_arn = "arn:aws:iam::123456789012:role/something" + + policy_associations = { + example = { + policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy" + access_scope = { + namespaces = ["default"] + type = "namespace" + } + } + } + } + } +} +``` + +### EKS Hybrid Nodes + +```hcl +locals { + # RFC 1918 IP ranges supported + remote_network_cidr = "172.16.0.0/16" + remote_node_cidr = cidrsubnet(local.remote_network_cidr, 2, 0) + remote_pod_cidr = cidrsubnet(local.remote_network_cidr, 2, 1) +} -MIT Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/LICENSE) for full details. +# SSM and IAM Roles Anywhere supported - SSM is default +module "eks_hybrid_node_role" { + source = "terraform-aws-modules/eks/aws//modules/hybrid-node-role" + version = "~> 21.0" + + tags = { + Environment = "dev" + Terraform = "true" + } +} + +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 21.0" + + name = "example" + kubernetes_version = "1.33" + + addons = { + coredns = {} + eks-pod-identity-agent = {} + kube-proxy = {} + } + + # Optional + endpoint_public_access = true + + # Optional: Adds the current caller identity as an administrator via cluster access entry + enable_cluster_creator_admin_permissions = true + + create_node_security_group = false + security_group_additional_rules = { + hybrid-all = { + cidr_blocks = [local.remote_network_cidr] + description = "Allow all traffic from remote node/pod network" + from_port = 0 + to_port = 0 + protocol = "all" + type = "ingress" + } + } + + # Optional + compute_config = { + enabled = true + node_pools = ["system"] + } + + access_entries = { + hybrid-node-role = { + principal_arn = module.eks_hybrid_node_role.arn + type = "HYBRID_LINUX" + } + } + + vpc_id = "vpc-1234556abcdef" + subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] + + remote_network_config = { + remote_node_networks = { + cidrs = [local.remote_node_cidr] + } + # Required if running webhooks on Hybrid nodes + remote_pod_networks = { + cidrs = [local.remote_pod_cidr] + } + } + + tags = { + Environment = "dev" + Terraform = "true" + } +} +``` + +### Bootstrap Cluster Creator Admin Permissions + +Setting the `bootstrap_cluster_creator_admin_permissions` is a one time operation when the cluster is created; it cannot be modified later through the EKS API. In this project we are hardcoding this to `false`. If users wish to achieve the same functionality, we will do that through an access entry which can be enabled or disabled at any time of their choosing using the variable `enable_cluster_creator_admin_permissions` + +### Enabling EFA Support + +When enabling EFA support via `enable_efa_support = true`, there are two locations this can be specified - one at the cluster level, and one at the node group level. Enabling at the cluster level will add the EFA required ingress/egress rules to the shared security group created for the node group(s). Enabling at the node group level will do the following (per node group where enabled): + +1. All EFA interfaces supported by the instance will be exposed on the launch template used by the node group +2. A placement group with `strategy = "clustered"` per EFA requirements is created and passed to the launch template used by the node group +3. Data sources will reverse lookup the availability zones that support the instance type selected based on the subnets provided, ensuring that only the associated subnets are passed to the launch template and therefore used by the placement group. This avoids the placement group being created in an availability zone that does not support the instance type selected. + +> [!TIP] +> Use the [aws-efa-k8s-device-plugin](https://github.com/aws/eks-charts/tree/master/stable/aws-efa-k8s-device-plugin) Helm chart to expose the EFA interfaces on the nodes as an extended resource, and allow pods to request the interfaces be mounted to their containers. +> +> The EKS AL2 GPU AMI comes with the necessary EFA components pre-installed - you just need to expose the EFA devices on the nodes via their launch templates, ensure the required EFA security group rules are in place, and deploy the `aws-efa-k8s-device-plugin` in order to start utilizing EFA within your cluster. Your application container will need to have the necessary libraries and runtime in order to utilize communication over the EFA interfaces (NCCL, aws-ofi-nccl, hwloc, libfabric, aws-neuornx-collectives, CUDA, etc.). + +If you disable the creation and use of the managed node group custom launch template (`create_launch_template = false` and/or `use_custom_launch_template = false`), this will interfere with the EFA functionality provided. In addition, if you do not supply an `instance_type` for self-managed node group(s), or `instance_types` for the managed node group(s), this will also interfere with the functionality. In order to support the EFA functionality provided by `enable_efa_support = true`, you must utilize the custom launch template created/provided by this module, and supply an `instance_type`/`instance_types` for the respective node group. + +The logic behind supporting EFA uses a data source to lookup the instance type to retrieve the number of interfaces that the instance supports in order to enumerate and expose those interfaces on the launch template created. For managed node groups where a list of instance types are supported, the first instance type in the list is used to calculate the number of EFA interfaces supported. Mixing instance types with varying number of interfaces is not recommended for EFA (or in some cases, mixing instance types is not supported - i.e. - p5.48xlarge and p4d.24xlarge). In addition to exposing the EFA interfaces and updating the security group rules, a placement group is created per the EFA requirements and only the availability zones that support the instance type selected are used in the subnets provided to the node group. + +In order to enable EFA support, you will have to specify `enable_efa_support = true` on both the cluster and each node group that you wish to enable EFA support for: + +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 21.0" + + # Truncated for brevity ... + + # Adds the EFA required security group rules to the shared + # security group created for the node group(s) + enable_efa_support = true + + eks_managed_node_groups = { + example = { + # The EKS AL2023 NVIDIA AMI provides all of the necessary components + # for accelerated workloads w/ EFA + ami_type = "AL2023_x86_64_NVIDIA" + instance_types = ["p5.48xlarge"] + + # Exposes all EFA interfaces on the launch template created by the node group(s) + # This would expose all 32 EFA interfaces for the p5.48xlarge instance type + enable_efa_support = true + + # Mount instance store volumes in RAID-0 for kubelet and containerd + # https://github.com/awslabs/amazon-eks-ami/blob/master/doc/USER_GUIDE.md#raid-0-for-kubelet-and-containerd-raid0 + cloudinit_pre_nodeadm = [ + { + content_type = "application/node.eks.aws" + content = <<-EOT + --- + apiVersion: node.eks.aws/v1alpha1 + kind: NodeConfig + spec: + instance: + localStorage: + strategy: RAID0 + EOT + } + ] + + # EFA should only be enabled when connecting 2 or more nodes + # Do not use EFA on a single node workload + min_size = 2 + max_size = 10 + desired_size = 2 + } + } +} +``` + +## Examples + +- [EKS Auto Mode](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/eks-auto-mode): EKS Cluster with EKS Auto Mode +- [EKS Capabilities](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/eks-capabilities): EKS Cluster with EKS Capabilities +- [EKS Hybrid Nodes](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/eks-hybrid-nodes): EKS Cluster with EKS Hybrid nodes +- [EKS Managed Node Group](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/eks-managed-node-group): EKS Cluster with EKS managed node group(s) +- [Karpenter](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/karpenter): EKS Cluster with [Karpenter](https://karpenter.sh/) provisioned for intelligent data plane management +- [Self Managed Node Group](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/self-managed-node-group): EKS Cluster with self-managed node group(s) + +## Contributing + +We are grateful to the community for contributing bugfixes and improvements! Please see below to learn how you can take part. + +- [Code of Conduct](https://github.com/terraform-aws-modules/.github/blob/master/CODE_OF_CONDUCT.md) +- [Contributing Guide](https://github.com/terraform-aws-modules/.github/blob/master/CONTRIBUTING.md) + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.28 | +| [time](#requirement\_time) | >= 0.9 | +| [tls](#requirement\_tls) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 6.28 | +| [time](#provider\_time) | >= 0.9 | +| [tls](#provider\_tls) | >= 4.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [eks\_managed\_node\_group](#module\_eks\_managed\_node\_group) | ./modules/eks-managed-node-group | n/a | +| [fargate\_profile](#module\_fargate\_profile) | ./modules/fargate-profile | n/a | +| [kms](#module\_kms) | terraform-aws-modules/kms/aws | 4.0.0 | +| [self\_managed\_node\_group](#module\_self\_managed\_node\_group) | ./modules/self-managed-node-group | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_ec2_tag.cluster_primary_security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_tag) | resource | +| [aws_eks_access_entry.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_access_entry) | resource | +| [aws_eks_access_policy_association.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_access_policy_association) | resource | +| [aws_eks_addon.before_compute](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | +| [aws_eks_addon.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | +| [aws_eks_cluster.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster) | resource | +| [aws_eks_identity_provider_config.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_identity_provider_config) | resource | +| [aws_iam_openid_connect_provider.oidc_provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider) | resource | +| [aws_iam_policy.cluster_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.cni_ipv6_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.eks_auto](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.cluster_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.eks_auto](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.eks_auto_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_security_group.cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_security_group.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_security_group_rule.cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [time_sleep.this](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_eks_addon_version.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks_addon_version) | data source | +| [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.cni_ipv6_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.node_assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_session_context.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_session_context) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | +| [tls_certificate.this](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/data-sources/certificate) | data source | - ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| cluster\_create\_security\_group | Whether to create a security group for the cluster or attach the cluster to `cluster_security_group_id`. | string | `"true"` | no | -| cluster\_create\_timeout | Timeout value when creating the EKS cluster. | string | `"15m"` | no | -| cluster\_delete\_timeout | Timeout value when deleting the EKS cluster. | string | `"15m"` | no | -| cluster\_enabled\_log\_types | A list of the desired control plane logging to enable. For more information, see Amazon EKS Control Plane Logging documentation (https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html) | list | `[]` | no | -| cluster\_endpoint\_private\_access | Indicates whether or not the Amazon EKS private API server endpoint is enabled. | string | `"false"` | no | -| cluster\_endpoint\_public\_access | Indicates whether or not the Amazon EKS public API server endpoint is enabled. | string | `"true"` | no | -| cluster\_name | Name of the EKS cluster. Also used as a prefix in names of related resources. | string | n/a | yes | -| cluster\_security\_group\_id | If provided, the EKS cluster will be attached to this security group. If not given, a security group will be created with necessary ingres/egress to work with the workers and provide API access to your current IP/32. | string | `""` | no | -| cluster\_version | Kubernetes version to use for the EKS cluster. | string | `"1.12"` | no | -| config\_output\_path | Where to save the Kubectl config file (if `write_kubeconfig = true`). Should end in a forward slash `/` . | string | `"./"` | no | -| iam\_path | If provided, all IAM roles will be created on this path. | string | `"/"` | no | -| kubeconfig\_aws\_authenticator\_additional\_args | Any additional arguments to pass to the authenticator such as the role to assume. e.g. ["-r", "MyEksRole"]. | list | `[]` | no | -| kubeconfig\_aws\_authenticator\_command | Command to use to fetch AWS EKS credentials. | string | `"aws-iam-authenticator"` | no | -| kubeconfig\_aws\_authenticator\_command\_args | Default arguments passed to the authenticator command. Defaults to [token -i $cluster_name]. | list | `[]` | no | -| kubeconfig\_aws\_authenticator\_env\_variables | Environment variables that should be used when executing the authenticator. e.g. { AWS_PROFILE = "eks"}. | map | `{}` | no | -| kubeconfig\_name | Override the default name used for items kubeconfig. | string | `""` | no | -| local\_exec\_interpreter | Command to run for local-exec resources. Must be a shell-style interpreter. If you are on Windows Git Bash is a good choice. | list | `[ "/bin/sh", "-c" ]` | no | -| manage\_aws\_auth | Whether to apply the aws-auth configmap file. | string | `"true"` | no | -| map\_accounts | Additional AWS account numbers to add to the aws-auth configmap. See examples/eks_test_fixture/variables.tf for example format. | list | `[]` | no | -| map\_accounts\_count | The count of accounts in the map_accounts list. | string | `"0"` | no | -| map\_roles | Additional IAM roles to add to the aws-auth configmap. See examples/eks_test_fixture/variables.tf for example format. | list | `[]` | no | -| map\_roles\_count | The count of roles in the map_roles list. | string | `"0"` | no | -| map\_users | Additional IAM users to add to the aws-auth configmap. See examples/eks_test_fixture/variables.tf for example format. | list | `[]` | no | -| map\_users\_count | The count of roles in the map_users list. | string | `"0"` | no | -| permissions\_boundary | If provided, all IAM roles will be created with this permissions boundary attached. | string | `""` | no | -| subnets | A list of subnets to place the EKS cluster and workers within. | list | n/a | yes | -| tags | A map of tags to add to all resources. | map | `{}` | no | -| vpc\_id | VPC where the cluster and workers will be deployed. | string | n/a | yes | -| worker\_additional\_security\_group\_ids | A list of additional security group ids to attach to worker instances | list | `[]` | no | -| worker\_ami\_name\_filter | Additional name filter for AWS EKS worker AMI. Default behaviour will get latest for the cluster_version but could be set to a release from amazon-eks-ami, e.g. "v20190220" | string | `"v*"` | no | -| worker\_create\_security\_group | Whether to create a security group for the workers or attach the workers to `worker_security_group_id`. | string | `"true"` | no | -| worker\_group\_count | The number of maps contained within the worker_groups list. | string | `"1"` | no | -| worker\_group\_launch\_template\_count | The number of maps contained within the worker_groups_launch_template list. | string | `"0"` | no | -| worker\_group\_launch\_template\_tags | A map defining extra tags to be applied to the worker group template ASG. | map | `{ "default": [] }` | no | -| worker\_group\_tags | A map defining extra tags to be applied to the worker group ASG. | map | `{ "default": [] }` | no | -| worker\_groups | A list of maps defining worker group configurations to be defined using AWS Launch Configurations. See workers_group_defaults for valid keys. | list | `[ { "name": "default" } ]` | no | -| worker\_groups\_launch\_template | A list of maps defining worker group configurations to be defined using AWS Launch Templates. See workers_group_defaults for valid keys. | list | `[ { "name": "default" } ]` | no | -| worker\_security\_group\_id | If provided, all workers will be attached to this security group. If not given, a security group will be created with necessary ingres/egress to work with the EKS cluster. | string | `""` | no | -| worker\_sg\_ingress\_from\_port | Minimum port number from which pods will accept communication. Must be changed to a lower value if some pods in your cluster will expose a port lower than 1025 (e.g. 22, 80, or 443). | string | `"1025"` | no | -| workers\_additional\_policies | Additional policies to be added to workers | list | `[]` | no | -| workers\_additional\_policies\_count | | string | `"0"` | no | -| workers\_group\_defaults | Override default values for target groups. See workers_group_defaults_defaults in local.tf for valid keys. | map | `{}` | no | -| workers\_group\_launch\_template\_defaults | Override default values for target groups. See workers_group_defaults_defaults in local.tf for valid keys. | map | `{}` | no | -| write\_aws\_auth\_config | Whether to write the aws-auth configmap file. | string | `"true"` | no | -| write\_kubeconfig | Whether to write a Kubectl config file containing the cluster configuration. Saved to `config_output_path`. | string | `"true"` | no | +|------|-------------|------|---------|:--------:| +| [access\_entries](#input\_access\_entries) | Map of access entries to add to the cluster |map(object({
# Access entry
kubernetes_groups = optional(list(string))
principal_arn = string
type = optional(string, "STANDARD")
user_name = optional(string)
tags = optional(map(string), {})
# Access policy association
policy_associations = optional(map(object({
policy_arn = string
access_scope = object({
namespaces = optional(list(string))
type = string
})
})), {})
})) | `{}` | no |
+| [additional\_security\_group\_ids](#input\_additional\_security\_group\_ids) | List of additional, externally created security group IDs to attach to the cluster control plane | `list(string)` | `[]` | no |
+| [addons](#input\_addons) | Map of cluster addon configurations to enable for the cluster. Addon name can be the map keys or set with `name` | map(object({
name = optional(string) # will fall back to map key
before_compute = optional(bool, false)
most_recent = optional(bool, true)
addon_version = optional(string)
configuration_values = optional(string)
pod_identity_association = optional(list(object({
role_arn = string
service_account = string
})))
preserve = optional(bool, true)
resolve_conflicts_on_create = optional(string, "NONE")
resolve_conflicts_on_update = optional(string, "OVERWRITE")
service_account_role_arn = optional(string)
timeouts = optional(object({
create = optional(string)
update = optional(string)
delete = optional(string)
}), {})
tags = optional(map(string), {})
})) | `null` | no |
+| [addons\_timeouts](#input\_addons\_timeouts) | Create, update, and delete timeout configurations for the cluster addons | object({
create = optional(string)
update = optional(string)
delete = optional(string)
}) | `{}` | no |
+| [attach\_encryption\_policy](#input\_attach\_encryption\_policy) | Indicates whether or not to attach an additional policy for the cluster IAM role to utilize the encryption key provided | `bool` | `true` | no |
+| [authentication\_mode](#input\_authentication\_mode) | The authentication mode for the cluster. Valid values are `CONFIG_MAP`, `API` or `API_AND_CONFIG_MAP` | `string` | `"API_AND_CONFIG_MAP"` | no |
+| [cloudwatch\_log\_group\_class](#input\_cloudwatch\_log\_group\_class) | Specified the log class of the log group. Possible values are: `STANDARD` or `INFREQUENT_ACCESS` | `string` | `null` | no |
+| [cloudwatch\_log\_group\_kms\_key\_id](#input\_cloudwatch\_log\_group\_kms\_key\_id) | If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html) | `string` | `null` | no |
+| [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Number of days to retain log events. Default retention - 90 days | `number` | `90` | no |
+| [cloudwatch\_log\_group\_tags](#input\_cloudwatch\_log\_group\_tags) | A map of additional tags to add to the cloudwatch log group created | `map(string)` | `{}` | no |
+| [cluster\_tags](#input\_cluster\_tags) | A map of additional tags to add to the cluster | `map(string)` | `{}` | no |
+| [compute\_config](#input\_compute\_config) | Configuration block for the cluster compute configuration | object({
enabled = optional(bool, false)
node_pools = optional(list(string))
node_role_arn = optional(string)
}) | `null` | no |
+| [control\_plane\_scaling\_config](#input\_control\_plane\_scaling\_config) | Configuration block for the EKS Provisioned Control Plane scaling tier. Valid values for tier are `standard`, `tier-xl`, `tier-2xl`, and `tier-4xl` | object({
tier = string
}) | `null` | no |
+| [control\_plane\_subnet\_ids](#input\_control\_plane\_subnet\_ids) | A list of subnet IDs where the EKS cluster control plane (ENIs) will be provisioned. Used for expanding the pool of subnets used by nodes/node groups without replacing the EKS control plane | `list(string)` | `[]` | no |
+| [create](#input\_create) | Controls if resources should be created (affects nearly all resources) | `bool` | `true` | no |
+| [create\_auto\_mode\_iam\_resources](#input\_create\_auto\_mode\_iam\_resources) | Determines whether to create/attach IAM resources for EKS Auto Mode. Useful for when using only custom node pools and not built-in EKS Auto Mode node pools | `bool` | `false` | no |
+| [create\_cloudwatch\_log\_group](#input\_create\_cloudwatch\_log\_group) | Determines whether a log group is created by this module for the cluster logs. If not, AWS will automatically create one if logging is enabled | `bool` | `true` | no |
+| [create\_cni\_ipv6\_iam\_policy](#input\_create\_cni\_ipv6\_iam\_policy) | Determines whether to create an [`AmazonEKS_CNI_IPv6_Policy`](https://docs.aws.amazon.com/eks/latest/userguide/cni-iam-role.html#cni-iam-role-create-ipv6-policy) | `bool` | `false` | no |
+| [create\_iam\_role](#input\_create\_iam\_role) | Determines whether an IAM role is created for the cluster | `bool` | `true` | no |
+| [create\_kms\_key](#input\_create\_kms\_key) | Controls if a KMS key for cluster encryption should be created | `bool` | `true` | no |
+| [create\_node\_iam\_role](#input\_create\_node\_iam\_role) | Determines whether an EKS Auto node IAM role is created | `bool` | `true` | no |
+| [create\_node\_security\_group](#input\_create\_node\_security\_group) | Determines whether to create a security group for the node groups or use the existing `node_security_group_id` | `bool` | `true` | no |
+| [create\_primary\_security\_group\_tags](#input\_create\_primary\_security\_group\_tags) | Indicates whether or not to tag the cluster's primary security group. This security group is created by the EKS service, not the module, and therefore tagging is handled after cluster creation | `bool` | `true` | no |
+| [create\_security\_group](#input\_create\_security\_group) | Determines if a security group is created for the cluster. Note: the EKS service creates a primary security group for the cluster by default | `bool` | `true` | no |
+| [custom\_oidc\_thumbprints](#input\_custom\_oidc\_thumbprints) | Additional list of server certificate thumbprints for the OpenID Connect (OIDC) identity provider's server certificate(s) | `list(string)` | `[]` | no |
+| [dataplane\_wait\_duration](#input\_dataplane\_wait\_duration) | Duration to wait after the EKS cluster has become active before creating the dataplane components (EKS managed node group(s), self-managed node group(s), Fargate profile(s)) | `string` | `"30s"` | no |
+| [deletion\_protection](#input\_deletion\_protection) | Whether to enable deletion protection for the cluster. When enabled, the cluster cannot be deleted unless deletion protection is first disabled | `bool` | `null` | no |
+| [eks\_managed\_node\_groups](#input\_eks\_managed\_node\_groups) | Map of EKS managed node group definitions to create | map(object({
create = optional(bool)
kubernetes_version = optional(string)
# EKS Managed Node Group
name = optional(string) # Will fall back to map key
use_name_prefix = optional(bool)
subnet_ids = optional(list(string))
min_size = optional(number)
max_size = optional(number)
desired_size = optional(number)
ami_id = optional(string)
ami_type = optional(string)
ami_release_version = optional(string)
use_latest_ami_release_version = optional(bool)
capacity_type = optional(string)
disk_size = optional(number)
force_update_version = optional(bool)
instance_types = optional(list(string))
labels = optional(map(string))
node_repair_config = optional(object({
enabled = optional(bool)
max_parallel_nodes_repaired_count = optional(number)
max_parallel_nodes_repaired_percentage = optional(number)
max_unhealthy_node_threshold_count = optional(number)
max_unhealthy_node_threshold_percentage = optional(number)
node_repair_config_overrides = optional(list(object({
min_repair_wait_time_mins = number
node_monitoring_condition = string
node_unhealthy_reason = string
repair_action = string
})))
}))
remote_access = optional(object({
ec2_ssh_key = optional(string)
source_security_group_ids = optional(list(string))
}))
taints = optional(map(object({
key = string
value = optional(string)
effect = string
})))
update_config = optional(object({
max_unavailable = optional(number)
max_unavailable_percentage = optional(number)
update_strategy = optional(string)
}))
timeouts = optional(object({
create = optional(string)
update = optional(string)
delete = optional(string)
}))
# User data
enable_bootstrap_user_data = optional(bool)
pre_bootstrap_user_data = optional(string)
post_bootstrap_user_data = optional(string)
bootstrap_extra_args = optional(string)
user_data_template_path = optional(string)
cloudinit_pre_nodeadm = optional(list(object({
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
})))
cloudinit_post_nodeadm = optional(list(object({
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
})))
# Launch Template
create_launch_template = optional(bool)
use_custom_launch_template = optional(bool)
launch_template_id = optional(string)
launch_template_name = optional(string) # Will fall back to map key
launch_template_use_name_prefix = optional(bool)
launch_template_version = optional(string)
launch_template_default_version = optional(string)
update_launch_template_default_version = optional(bool)
launch_template_description = optional(string)
launch_template_tags = optional(map(string))
tag_specifications = optional(list(string))
ebs_optimized = optional(bool)
key_name = optional(string)
disable_api_termination = optional(bool)
kernel_id = optional(string)
ram_disk_id = optional(string)
block_device_mappings = optional(map(object({
device_name = optional(string)
ebs = optional(object({
delete_on_termination = optional(bool)
encrypted = optional(bool)
iops = optional(number)
kms_key_id = optional(string)
snapshot_id = optional(string)
throughput = optional(number)
volume_initialization_rate = optional(number)
volume_size = optional(number)
volume_type = optional(string)
}))
no_device = optional(string)
virtual_name = optional(string)
})))
capacity_reservation_specification = optional(object({
capacity_reservation_preference = optional(string)
capacity_reservation_target = optional(object({
capacity_reservation_id = optional(string)
capacity_reservation_resource_group_arn = optional(string)
}))
}))
cpu_options = optional(object({
amd_sev_snp = optional(string)
core_count = optional(number)
threads_per_core = optional(number)
}))
credit_specification = optional(object({
cpu_credits = optional(string)
}))
enclave_options = optional(object({
enabled = optional(bool)
}))
instance_market_options = optional(object({
market_type = optional(string)
spot_options = optional(object({
block_duration_minutes = optional(number)
instance_interruption_behavior = optional(string)
max_price = optional(string)
spot_instance_type = optional(string)
valid_until = optional(string)
}))
}))
license_specifications = optional(list(object({
license_configuration_arn = string
})))
metadata_options = optional(object({
http_endpoint = optional(string)
http_protocol_ipv6 = optional(string)
http_put_response_hop_limit = optional(number)
http_tokens = optional(string)
instance_metadata_tags = optional(string)
}))
enable_monitoring = optional(bool)
enable_efa_support = optional(bool)
enable_efa_only = optional(bool)
efa_indices = optional(list(string))
create_placement_group = optional(bool)
placement = optional(object({
affinity = optional(string)
availability_zone = optional(string)
group_name = optional(string)
host_id = optional(string)
host_resource_group_arn = optional(string)
partition_number = optional(number)
spread_domain = optional(string)
tenancy = optional(string)
}))
network_interfaces = optional(list(object({
associate_carrier_ip_address = optional(bool)
associate_public_ip_address = optional(bool)
connection_tracking_specification = optional(object({
tcp_established_timeout = optional(number)
udp_stream_timeout = optional(number)
udp_timeout = optional(number)
}))
delete_on_termination = optional(bool)
description = optional(string)
device_index = optional(number)
ena_srd_specification = optional(object({
ena_srd_enabled = optional(bool)
ena_srd_udp_specification = optional(object({
ena_srd_udp_enabled = optional(bool)
}))
}))
interface_type = optional(string)
ipv4_address_count = optional(number)
ipv4_addresses = optional(list(string))
ipv4_prefix_count = optional(number)
ipv4_prefixes = optional(list(string))
ipv6_address_count = optional(number)
ipv6_addresses = optional(list(string))
ipv6_prefix_count = optional(number)
ipv6_prefixes = optional(list(string))
network_card_index = optional(number)
network_interface_id = optional(string)
primary_ipv6 = optional(bool)
private_ip_address = optional(string)
security_groups = optional(list(string), [])
subnet_id = optional(string)
})))
maintenance_options = optional(object({
auto_recovery = optional(string)
}))
private_dns_name_options = optional(object({
enable_resource_name_dns_aaaa_record = optional(bool)
enable_resource_name_dns_a_record = optional(bool)
hostname_type = optional(string)
}))
# IAM role
create_iam_role = optional(bool)
iam_role_arn = optional(string)
iam_role_name = optional(string)
iam_role_use_name_prefix = optional(bool)
iam_role_path = optional(string)
iam_role_description = optional(string)
iam_role_permissions_boundary = optional(string)
iam_role_tags = optional(map(string))
iam_role_attach_cni_policy = optional(bool)
iam_role_additional_policies = optional(map(string))
create_iam_role_policy = optional(bool)
iam_role_policy_statements = optional(list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})))
# Security group
vpc_security_group_ids = optional(list(string), [])
attach_cluster_primary_security_group = optional(bool, false)
cluster_primary_security_group_id = optional(string)
create_security_group = optional(bool)
security_group_name = optional(string)
security_group_use_name_prefix = optional(bool)
security_group_description = optional(string)
security_group_ingress_rules = optional(map(object({
name = optional(string)
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string)
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool)
tags = optional(map(string))
to_port = optional(string)
})))
security_group_egress_rules = optional(map(object({
name = optional(string)
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string)
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool)
tags = optional(map(string))
to_port = optional(string)
})), {})
security_group_tags = optional(map(string))
tags = optional(map(string))
})) | `null` | no |
+| [enable\_auto\_mode\_custom\_tags](#input\_enable\_auto\_mode\_custom\_tags) | Determines whether to enable permissions for custom tags resources created by EKS Auto Mode | `bool` | `true` | no |
+| [enable\_cluster\_creator\_admin\_permissions](#input\_enable\_cluster\_creator\_admin\_permissions) | Indicates whether or not to add the cluster creator (the identity used by Terraform) as an administrator via access entry | `bool` | `false` | no |
+| [enable\_irsa](#input\_enable\_irsa) | Determines whether to create an OpenID Connect Provider for EKS to enable IRSA | `bool` | `true` | no |
+| [enable\_kms\_key\_rotation](#input\_enable\_kms\_key\_rotation) | Specifies whether key rotation is enabled | `bool` | `true` | no |
+| [enabled\_log\_types](#input\_enabled\_log\_types) | A list of the desired control plane logs to enable. For more information, see Amazon EKS Control Plane Logging documentation (https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html) | `list(string)` | [| no | +| [encryption\_config](#input\_encryption\_config) | Configuration block with encryption configuration for the cluster |
"audit",
"api",
"authenticator"
]
object({
provider_key_arn = optional(string)
resources = optional(list(string), ["secrets"])
}) | `{}` | no |
+| [encryption\_policy\_description](#input\_encryption\_policy\_description) | Description of the cluster encryption policy created | `string` | `"Cluster encryption policy to allow cluster role to utilize CMK provided"` | no |
+| [encryption\_policy\_name](#input\_encryption\_policy\_name) | Name to use on cluster encryption policy created | `string` | `null` | no |
+| [encryption\_policy\_path](#input\_encryption\_policy\_path) | Cluster encryption policy path | `string` | `null` | no |
+| [encryption\_policy\_tags](#input\_encryption\_policy\_tags) | A map of additional tags to add to the cluster encryption policy created | `map(string)` | `{}` | no |
+| [encryption\_policy\_use\_name\_prefix](#input\_encryption\_policy\_use\_name\_prefix) | Determines whether cluster encryption policy name (`cluster_encryption_policy_name`) is used as a prefix | `bool` | `true` | no |
+| [endpoint\_private\_access](#input\_endpoint\_private\_access) | Indicates whether or not the Amazon EKS private API server endpoint is enabled | `bool` | `true` | no |
+| [endpoint\_public\_access](#input\_endpoint\_public\_access) | Indicates whether or not the Amazon EKS public API server endpoint is enabled | `bool` | `false` | no |
+| [endpoint\_public\_access\_cidrs](#input\_endpoint\_public\_access\_cidrs) | List of CIDR blocks which can access the Amazon EKS public API server endpoint | `list(string)` | [| no | +| [fargate\_profiles](#input\_fargate\_profiles) | Map of Fargate Profile definitions to create |
"0.0.0.0/0"
]
map(object({
create = optional(bool)
# Fargate profile
name = optional(string) # Will fall back to map key
subnet_ids = optional(list(string))
selectors = optional(list(object({
labels = optional(map(string))
namespace = string
})))
timeouts = optional(object({
create = optional(string)
delete = optional(string)
}))
# IAM role
create_iam_role = optional(bool)
iam_role_arn = optional(string)
iam_role_name = optional(string)
iam_role_use_name_prefix = optional(bool)
iam_role_path = optional(string)
iam_role_description = optional(string)
iam_role_permissions_boundary = optional(string)
iam_role_tags = optional(map(string))
iam_role_attach_cni_policy = optional(bool)
iam_role_additional_policies = optional(map(string))
create_iam_role_policy = optional(bool)
iam_role_policy_statements = optional(list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})))
tags = optional(map(string))
})) | `null` | no |
+| [force\_update\_version](#input\_force\_update\_version) | Force version update by overriding upgrade-blocking readiness checks when updating a cluster | `bool` | `null` | no |
+| [iam\_role\_additional\_policies](#input\_iam\_role\_additional\_policies) | Additional policies to be added to the IAM role | `map(string)` | `{}` | no |
+| [iam\_role\_arn](#input\_iam\_role\_arn) | Existing IAM role ARN for the cluster. Required if `create_iam_role` is set to `false` | `string` | `null` | no |
+| [iam\_role\_description](#input\_iam\_role\_description) | Description of the role | `string` | `null` | no |
+| [iam\_role\_name](#input\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
+| [iam\_role\_path](#input\_iam\_role\_path) | The IAM role path | `string` | `null` | no |
+| [iam\_role\_permissions\_boundary](#input\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
+| [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
+| [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no |
+| [identity\_providers](#input\_identity\_providers) | Map of cluster identity provider configurations to enable for the cluster. Note - this is different/separate from IRSA | map(object({
client_id = string
groups_claim = optional(string)
groups_prefix = optional(string)
identity_provider_config_name = optional(string) # will fall back to map key
issuer_url = string
required_claims = optional(map(string))
username_claim = optional(string)
username_prefix = optional(string)
tags = optional(map(string), {})
})) | `null` | no |
+| [include\_oidc\_root\_ca\_thumbprint](#input\_include\_oidc\_root\_ca\_thumbprint) | Determines whether to include the root CA thumbprint in the OpenID Connect (OIDC) identity provider's server certificate(s) | `bool` | `true` | no |
+| [ip\_family](#input\_ip\_family) | The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6`. You can only specify an IP family when you create a cluster, changing this value will force a new cluster to be created | `string` | `"ipv4"` | no |
+| [kms\_key\_administrators](#input\_kms\_key\_administrators) | A list of IAM ARNs for [key administrators](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-default-allow-administrators). If no value is provided, the current caller identity is used to ensure at least one key admin is available | `list(string)` | `[]` | no |
+| [kms\_key\_aliases](#input\_kms\_key\_aliases) | A list of aliases to create. Note - due to the use of `toset()`, values must be static strings and not computed values | `list(string)` | `[]` | no |
+| [kms\_key\_deletion\_window\_in\_days](#input\_kms\_key\_deletion\_window\_in\_days) | The waiting period, specified in number of days. After the waiting period ends, AWS KMS deletes the KMS key. If you specify a value, it must be between `7` and `30`, inclusive. If you do not specify a value, it defaults to `30` | `number` | `null` | no |
+| [kms\_key\_description](#input\_kms\_key\_description) | The description of the key as viewed in AWS console | `string` | `null` | no |
+| [kms\_key\_enable\_default\_policy](#input\_kms\_key\_enable\_default\_policy) | Specifies whether to enable the default key policy | `bool` | `true` | no |
+| [kms\_key\_override\_policy\_documents](#input\_kms\_key\_override\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no |
+| [kms\_key\_owners](#input\_kms\_key\_owners) | A list of IAM ARNs for those who will have full key permissions (`kms:*`) | `list(string)` | `[]` | no |
+| [kms\_key\_rotation\_period\_in\_days](#input\_kms\_key\_rotation\_period\_in\_days) | Custom period of time between each key rotation date. If you specify a value, it must be between `90` and `2560`, inclusive. If you do not specify a value, it defaults to `365` | `number` | `null` | no |
+| [kms\_key\_service\_users](#input\_kms\_key\_service\_users) | A list of IAM ARNs for [key service users](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-service-integration) | `list(string)` | `[]` | no |
+| [kms\_key\_source\_policy\_documents](#input\_kms\_key\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no |
+| [kms\_key\_users](#input\_kms\_key\_users) | A list of IAM ARNs for [key users](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-default-allow-users) | `list(string)` | `[]` | no |
+| [kubernetes\_version](#input\_kubernetes\_version) | Kubernetes `map(object({
protocol = optional(string, "tcp")
from_port = number
to_port = number
type = optional(string, "ingress")
description = optional(string)
cidr_blocks = optional(list(string))
ipv6_cidr_blocks = optional(list(string))
prefix_list_ids = optional(list(string))
self = optional(bool)
source_cluster_security_group = optional(bool, false)
source_security_group_id = optional(string)
})) | `{}` | no |
+| [node\_security\_group\_description](#input\_node\_security\_group\_description) | Description of the node security group created | `string` | `"EKS node shared security group"` | no |
+| [node\_security\_group\_enable\_recommended\_rules](#input\_node\_security\_group\_enable\_recommended\_rules) | Determines whether to enable recommended security group rules for the node security group created. This includes node-to-node TCP ingress on ephemeral ports and allows all egress traffic | `bool` | `true` | no |
+| [node\_security\_group\_id](#input\_node\_security\_group\_id) | ID of an existing security group to attach to the node groups created | `string` | `""` | no |
+| [node\_security\_group\_name](#input\_node\_security\_group\_name) | Name to use on node security group created | `string` | `null` | no |
+| [node\_security\_group\_tags](#input\_node\_security\_group\_tags) | A map of additional tags to add to the node security group created | `map(string)` | `{}` | no |
+| [node\_security\_group\_use\_name\_prefix](#input\_node\_security\_group\_use\_name\_prefix) | Determines whether node security group name (`node_security_group_name`) is used as a prefix | `bool` | `true` | no |
+| [openid\_connect\_audiences](#input\_openid\_connect\_audiences) | List of OpenID Connect audience client IDs to add to the IRSA provider | `list(string)` | `[]` | no |
+| [outpost\_config](#input\_outpost\_config) | Configuration for the AWS Outpost to provision the cluster on | object({
control_plane_instance_type = optional(string)
control_plane_placement = optional(object({
group_name = string
}))
outpost_arns = list(string)
}) | `null` | no |
+| [prefix\_separator](#input\_prefix\_separator) | The separator to use between the prefix and the generated timestamp for resource names | `string` | `"-"` | no |
+| [putin\_khuylo](#input\_putin\_khuylo) | Do you agree that Putin doesn't respect Ukrainian sovereignty and territorial integrity? More info: https://en.wikipedia.org/wiki/Putin_khuylo! | `bool` | `true` | no |
+| [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no |
+| [remote\_network\_config](#input\_remote\_network\_config) | Configuration block for the cluster remote network configuration | object({
remote_node_networks = object({
cidrs = optional(list(string))
})
remote_pod_networks = optional(object({
cidrs = optional(list(string))
}))
}) | `null` | no |
+| [security\_group\_additional\_rules](#input\_security\_group\_additional\_rules) | List of additional security group rules to add to the cluster security group created. Set `source_node_security_group = true` inside rules to set the `node_security_group` as source | map(object({
protocol = optional(string, "tcp")
from_port = number
to_port = number
type = optional(string, "ingress")
description = optional(string)
cidr_blocks = optional(list(string))
ipv6_cidr_blocks = optional(list(string))
prefix_list_ids = optional(list(string))
self = optional(bool)
source_node_security_group = optional(bool, false)
source_security_group_id = optional(string)
})) | `{}` | no |
+| [security\_group\_description](#input\_security\_group\_description) | Description of the cluster security group created | `string` | `"EKS cluster security group"` | no |
+| [security\_group\_id](#input\_security\_group\_id) | Existing security group ID to be attached to the cluster | `string` | `""` | no |
+| [security\_group\_name](#input\_security\_group\_name) | Name to use on cluster security group created | `string` | `null` | no |
+| [security\_group\_tags](#input\_security\_group\_tags) | A map of additional tags to add to the cluster security group created | `map(string)` | `{}` | no |
+| [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Determines whether cluster security group name (`cluster_security_group_name`) is used as a prefix | `bool` | `true` | no |
+| [self\_managed\_node\_groups](#input\_self\_managed\_node\_groups) | Map of self-managed node group definitions to create | map(object({
create = optional(bool)
kubernetes_version = optional(string)
# Autoscaling Group
create_autoscaling_group = optional(bool)
name = optional(string) # Will fall back to map key
use_name_prefix = optional(bool)
availability_zones = optional(list(string))
subnet_ids = optional(list(string))
min_size = optional(number)
max_size = optional(number)
desired_size = optional(number)
desired_size_type = optional(string)
capacity_rebalance = optional(bool)
default_instance_warmup = optional(number)
protect_from_scale_in = optional(bool)
context = optional(string)
create_placement_group = optional(bool)
placement_group = optional(string)
health_check_type = optional(string)
health_check_grace_period = optional(number)
ignore_failed_scaling_activities = optional(bool)
force_delete = optional(bool)
termination_policies = optional(list(string))
suspended_processes = optional(list(string))
max_instance_lifetime = optional(number)
enabled_metrics = optional(list(string))
metrics_granularity = optional(string)
initial_lifecycle_hooks = optional(list(object({
default_result = optional(string)
heartbeat_timeout = optional(number)
lifecycle_transition = string
name = string
notification_metadata = optional(string)
notification_target_arn = optional(string)
role_arn = optional(string)
})))
instance_maintenance_policy = optional(object({
max_healthy_percentage = number
min_healthy_percentage = number
}))
instance_refresh = optional(object({
preferences = optional(object({
alarm_specification = optional(object({
alarms = optional(list(string))
}))
auto_rollback = optional(bool)
checkpoint_delay = optional(number)
checkpoint_percentages = optional(list(number))
instance_warmup = optional(number)
max_healthy_percentage = optional(number)
min_healthy_percentage = optional(number)
scale_in_protected_instances = optional(string)
skip_matching = optional(bool)
standby_instances = optional(string)
}))
strategy = optional(string)
triggers = optional(list(string))
})
)
use_mixed_instances_policy = optional(bool)
mixed_instances_policy = optional(object({
instances_distribution = optional(object({
on_demand_allocation_strategy = optional(string)
on_demand_base_capacity = optional(number)
on_demand_percentage_above_base_capacity = optional(number)
spot_allocation_strategy = optional(string)
spot_instance_pools = optional(number)
spot_max_price = optional(string)
}))
launch_template = object({
override = optional(list(object({
instance_requirements = optional(object({
accelerator_count = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_manufacturers = optional(list(string))
accelerator_names = optional(list(string))
accelerator_total_memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_types = optional(list(string))
allowed_instance_types = optional(list(string))
bare_metal = optional(string)
baseline_ebs_bandwidth_mbps = optional(object({
max = optional(number)
min = optional(number)
}))
burstable_performance = optional(string)
cpu_manufacturers = optional(list(string))
excluded_instance_types = optional(list(string))
instance_generations = optional(list(string))
local_storage = optional(string)
local_storage_types = optional(list(string))
max_spot_price_as_percentage_of_optimal_on_demand_price = optional(number)
memory_gib_per_vcpu = optional(object({
max = optional(number)
min = optional(number)
}))
memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
network_bandwidth_gbps = optional(object({
max = optional(number)
min = optional(number)
}))
network_interface_count = optional(object({
max = optional(number)
min = optional(number)
}))
on_demand_max_price_percentage_over_lowest_price = optional(number)
require_hibernate_support = optional(bool)
spot_max_price_percentage_over_lowest_price = optional(number)
total_local_storage_gb = optional(object({
max = optional(number)
min = optional(number)
}))
vcpu_count = optional(object({
max = optional(number)
min = optional(number)
}))
}))
instance_type = optional(string)
launch_template_specification = optional(object({
launch_template_id = optional(string)
launch_template_name = optional(string)
version = optional(string)
}))
weighted_capacity = optional(string)
})))
})
}))
timeouts = optional(object({
delete = optional(string)
}))
autoscaling_group_tags = optional(map(string))
# User data
ami_type = optional(string)
additional_cluster_dns_ips = optional(list(string))
pre_bootstrap_user_data = optional(string)
post_bootstrap_user_data = optional(string)
bootstrap_extra_args = optional(string)
user_data_template_path = optional(string)
cloudinit_pre_nodeadm = optional(list(object({
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
})))
cloudinit_post_nodeadm = optional(list(object({
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
})))
# Launch Template
create_launch_template = optional(bool)
use_custom_launch_template = optional(bool)
launch_template_id = optional(string)
launch_template_name = optional(string) # Will fall back to map key
launch_template_use_name_prefix = optional(bool)
launch_template_version = optional(string)
launch_template_default_version = optional(string)
update_launch_template_default_version = optional(bool)
launch_template_description = optional(string)
launch_template_tags = optional(map(string))
tag_specifications = optional(list(string))
ebs_optimized = optional(bool)
ami_id = optional(string)
instance_type = optional(string)
key_name = optional(string)
disable_api_termination = optional(bool)
instance_initiated_shutdown_behavior = optional(string)
kernel_id = optional(string)
ram_disk_id = optional(string)
block_device_mappings = optional(map(object({
device_name = optional(string)
ebs = optional(object({
delete_on_termination = optional(bool)
encrypted = optional(bool)
iops = optional(number)
kms_key_id = optional(string)
snapshot_id = optional(string)
throughput = optional(number)
volume_initialization_rate = optional(number)
volume_size = optional(number)
volume_type = optional(string)
}))
no_device = optional(string)
virtual_name = optional(string)
})))
capacity_reservation_specification = optional(object({
capacity_reservation_preference = optional(string)
capacity_reservation_target = optional(object({
capacity_reservation_id = optional(string)
capacity_reservation_resource_group_arn = optional(string)
}))
}))
cpu_options = optional(object({
amd_sev_snp = optional(string)
core_count = optional(number)
threads_per_core = optional(number)
}))
credit_specification = optional(object({
cpu_credits = optional(string)
}))
enclave_options = optional(object({
enabled = optional(bool)
}))
instance_requirements = optional(object({
accelerator_count = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_manufacturers = optional(list(string))
accelerator_names = optional(list(string))
accelerator_total_memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_types = optional(list(string))
allowed_instance_types = optional(list(string))
bare_metal = optional(string)
baseline_ebs_bandwidth_mbps = optional(object({
max = optional(number)
min = optional(number)
}))
burstable_performance = optional(string)
cpu_manufacturers = optional(list(string))
excluded_instance_types = optional(list(string))
instance_generations = optional(list(string))
local_storage = optional(string)
local_storage_types = optional(list(string))
max_spot_price_as_percentage_of_optimal_on_demand_price = optional(number)
memory_gib_per_vcpu = optional(object({
max = optional(number)
min = optional(number)
}))
memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
network_bandwidth_gbps = optional(object({
max = optional(number)
min = optional(number)
}))
network_interface_count = optional(object({
max = optional(number)
min = optional(number)
}))
on_demand_max_price_percentage_over_lowest_price = optional(number)
require_hibernate_support = optional(bool)
spot_max_price_percentage_over_lowest_price = optional(number)
total_local_storage_gb = optional(object({
max = optional(number)
min = optional(number)
}))
vcpu_count = optional(object({
max = optional(number)
min = string
}))
}))
instance_market_options = optional(object({
market_type = optional(string)
spot_options = optional(object({
block_duration_minutes = optional(number)
instance_interruption_behavior = optional(string)
max_price = optional(string)
spot_instance_type = optional(string)
valid_until = optional(string)
}))
}))
license_specifications = optional(list(object({
license_configuration_arn = string
})))
metadata_options = optional(object({
http_endpoint = optional(string)
http_protocol_ipv6 = optional(string)
http_put_response_hop_limit = optional(number)
http_tokens = optional(string)
instance_metadata_tags = optional(string)
}))
enable_monitoring = optional(bool)
enable_efa_support = optional(bool)
enable_efa_only = optional(bool)
efa_indices = optional(list(string))
network_interfaces = optional(list(object({
associate_carrier_ip_address = optional(bool)
associate_public_ip_address = optional(bool)
connection_tracking_specification = optional(object({
tcp_established_timeout = optional(number)
udp_stream_timeout = optional(number)
udp_timeout = optional(number)
}))
delete_on_termination = optional(bool)
description = optional(string)
device_index = optional(number)
ena_srd_specification = optional(object({
ena_srd_enabled = optional(bool)
ena_srd_udp_specification = optional(object({
ena_srd_udp_enabled = optional(bool)
}))
}))
interface_type = optional(string)
ipv4_address_count = optional(number)
ipv4_addresses = optional(list(string))
ipv4_prefix_count = optional(number)
ipv4_prefixes = optional(list(string))
ipv6_address_count = optional(number)
ipv6_addresses = optional(list(string))
ipv6_prefix_count = optional(number)
ipv6_prefixes = optional(list(string))
network_card_index = optional(number)
network_interface_id = optional(string)
primary_ipv6 = optional(bool)
private_ip_address = optional(string)
security_groups = optional(list(string))
subnet_id = optional(string)
})))
placement = optional(object({
affinity = optional(string)
availability_zone = optional(string)
group_name = optional(string)
host_id = optional(string)
host_resource_group_arn = optional(string)
partition_number = optional(number)
spread_domain = optional(string)
tenancy = optional(string)
}))
maintenance_options = optional(object({
auto_recovery = optional(string)
}))
private_dns_name_options = optional(object({
enable_resource_name_dns_aaaa_record = optional(bool)
enable_resource_name_dns_a_record = optional(bool)
hostname_type = optional(string)
}))
# IAM role
create_iam_instance_profile = optional(bool)
iam_instance_profile_arn = optional(string)
iam_role_name = optional(string)
iam_role_use_name_prefix = optional(bool)
iam_role_path = optional(string)
iam_role_description = optional(string)
iam_role_permissions_boundary = optional(string)
iam_role_tags = optional(map(string))
iam_role_attach_cni_policy = optional(bool)
iam_role_additional_policies = optional(map(string))
create_iam_role_policy = optional(bool)
iam_role_policy_statements = optional(list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})))
# Access entry
create_access_entry = optional(bool)
iam_role_arn = optional(string)
# Security group
vpc_security_group_ids = optional(list(string), [])
attach_cluster_primary_security_group = optional(bool, false)
create_security_group = optional(bool)
security_group_name = optional(string)
security_group_use_name_prefix = optional(bool)
security_group_description = optional(string)
security_group_ingress_rules = optional(map(object({
name = optional(string)
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string)
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool)
tags = optional(map(string))
to_port = optional(string)
})))
security_group_egress_rules = optional(map(object({
name = optional(string)
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string)
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool)
tags = optional(map(string))
to_port = optional(string)
})))
security_group_tags = optional(map(string))
tags = optional(map(string))
})) | `null` | no |
+| [service\_ipv4\_cidr](#input\_service\_ipv4\_cidr) | The CIDR block to assign Kubernetes service IP addresses from. If you don't specify a block, Kubernetes assigns addresses from either the 10.100.0.0/16 or 172.20.0.0/16 CIDR blocks | `string` | `null` | no |
+| [service\_ipv6\_cidr](#input\_service\_ipv6\_cidr) | The CIDR block to assign Kubernetes pod and service IP addresses from if `ipv6` was specified when the cluster was created. Kubernetes assigns service addresses from the unique local address range (fc00::/7) because you can't specify a custom IPv6 CIDR block when you create the cluster | `string` | `null` | no |
+| [subnet\_ids](#input\_subnet\_ids) | A list of subnet IDs where the nodes/node groups will be provisioned. If `control_plane_subnet_ids` is not provided, the EKS cluster control plane (ENIs) will be provisioned in these subnets | `list(string)` | `[]` | no |
+| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
+| [timeouts](#input\_timeouts) | Create, update, and delete timeout configurations for the cluster | object({
create = optional(string)
update = optional(string)
delete = optional(string)
}) | `null` | no |
+| [upgrade\_policy](#input\_upgrade\_policy) | Configuration block for the cluster upgrade policy | object({
support_type = optional(string)
}) | `null` | no |
+| [vpc\_id](#input\_vpc\_id) | ID of the VPC where the cluster security group will be provisioned | `string` | `null` | no |
+| [zonal\_shift\_config](#input\_zonal\_shift\_config) | Configuration block for the cluster zonal shift | object({
enabled = optional(bool)
}) | `null` | no |
## Outputs
| Name | Description |
|------|-------------|
-| cluster\_certificate\_authority\_data | Nested attribute containing certificate-authority-data for your cluster. This is the base64 encoded certificate data required to communicate with your cluster. |
-| cluster\_endpoint | The endpoint for your EKS Kubernetes API. |
-| cluster\_iam\_role\_arn | IAM role ARN of the EKS cluster. |
-| cluster\_iam\_role\_name | IAM role name of the EKS cluster. |
-| cluster\_id | The name/id of the EKS cluster. |
-| cluster\_security\_group\_id | Security group ID attached to the EKS cluster. |
-| cluster\_version | The Kubernetes server version for the EKS cluster. |
-| config\_map\_aws\_auth | A kubernetes configuration to authenticate to this EKS cluster. |
-| kubeconfig | kubectl config file contents for this EKS cluster. |
-| kubeconfig\_filename | The filename of the generated kubectl config. |
-| worker\_iam\_instance\_profile\_arns | default IAM instance profile ARNs for EKS worker group |
-| worker\_iam\_instance\_profile\_names | default IAM instance profile names for EKS worker group |
-| worker\_iam\_role\_arn | default IAM role ARN for EKS worker groups |
-| worker\_iam\_role\_name | default IAM role name for EKS worker groups |
-| worker\_security\_group\_id | Security group ID attached to the EKS workers. |
-| workers\_asg\_arns | IDs of the autoscaling groups containing workers. |
-| workers\_asg\_names | Names of the autoscaling groups containing workers. |
-
-
+| [access\_entries](#output\_access\_entries) | Map of access entries created and their attributes |
+| [access\_policy\_associations](#output\_access\_policy\_associations) | Map of eks cluster access policy associations created and their attributes |
+| [cloudwatch\_log\_group\_arn](#output\_cloudwatch\_log\_group\_arn) | Arn of cloudwatch log group created |
+| [cloudwatch\_log\_group\_name](#output\_cloudwatch\_log\_group\_name) | Name of cloudwatch log group created |
+| [cluster\_addons](#output\_cluster\_addons) | Map of attribute maps for all EKS cluster addons enabled |
+| [cluster\_arn](#output\_cluster\_arn) | The Amazon Resource Name (ARN) of the cluster |
+| [cluster\_certificate\_authority\_data](#output\_cluster\_certificate\_authority\_data) | Base64 encoded certificate data required to communicate with the cluster |
+| [cluster\_control\_plane\_scaling\_tier](#output\_cluster\_control\_plane\_scaling\_tier) | The EKS Provisioned Control Plane scaling tier for the cluster |
+| [cluster\_dualstack\_oidc\_issuer\_url](#output\_cluster\_dualstack\_oidc\_issuer\_url) | Dual-stack compatible URL on the EKS cluster for the OpenID Connect identity provider |
+| [cluster\_endpoint](#output\_cluster\_endpoint) | Endpoint for your Kubernetes API server |
+| [cluster\_iam\_role\_arn](#output\_cluster\_iam\_role\_arn) | Cluster IAM role ARN |
+| [cluster\_iam\_role\_name](#output\_cluster\_iam\_role\_name) | Cluster IAM role name |
+| [cluster\_iam\_role\_unique\_id](#output\_cluster\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
+| [cluster\_id](#output\_cluster\_id) | The ID of the EKS cluster. Note: currently a value is returned only for local EKS clusters created on Outposts |
+| [cluster\_identity\_providers](#output\_cluster\_identity\_providers) | Map of attribute maps for all EKS identity providers enabled |
+| [cluster\_ip\_family](#output\_cluster\_ip\_family) | The IP family used by the cluster (e.g. `ipv4` or `ipv6`) |
+| [cluster\_name](#output\_cluster\_name) | The name of the EKS cluster |
+| [cluster\_oidc\_issuer\_url](#output\_cluster\_oidc\_issuer\_url) | The URL on the EKS cluster for the OpenID Connect identity provider |
+| [cluster\_platform\_version](#output\_cluster\_platform\_version) | Platform version for the cluster |
+| [cluster\_primary\_security\_group\_id](#output\_cluster\_primary\_security\_group\_id) | Cluster security group that was created by Amazon EKS for the cluster. Managed node groups use this security group for control-plane-to-data-plane communication. Referred to as 'Cluster security group' in the EKS console |
+| [cluster\_security\_group\_arn](#output\_cluster\_security\_group\_arn) | Amazon Resource Name (ARN) of the cluster security group |
+| [cluster\_security\_group\_id](#output\_cluster\_security\_group\_id) | ID of the cluster security group |
+| [cluster\_service\_cidr](#output\_cluster\_service\_cidr) | The CIDR block where Kubernetes pod and service IP addresses are assigned from |
+| [cluster\_status](#output\_cluster\_status) | Status of the EKS cluster. One of `CREATING`, `ACTIVE`, `DELETING`, `FAILED` |
+| [cluster\_tls\_certificate\_sha1\_fingerprint](#output\_cluster\_tls\_certificate\_sha1\_fingerprint) | The SHA1 fingerprint of the public key of the cluster's certificate |
+| [cluster\_version](#output\_cluster\_version) | The Kubernetes version for the cluster |
+| [eks\_managed\_node\_groups](#output\_eks\_managed\_node\_groups) | Map of attribute maps for all EKS managed node groups created |
+| [eks\_managed\_node\_groups\_autoscaling\_group\_names](#output\_eks\_managed\_node\_groups\_autoscaling\_group\_names) | List of the autoscaling group names created by EKS managed node groups |
+| [fargate\_profiles](#output\_fargate\_profiles) | Map of attribute maps for all EKS Fargate Profiles created |
+| [kms\_key\_arn](#output\_kms\_key\_arn) | The Amazon Resource Name (ARN) of the key |
+| [kms\_key\_id](#output\_kms\_key\_id) | The globally unique identifier for the key |
+| [kms\_key\_policy](#output\_kms\_key\_policy) | The IAM resource policy set on the key |
+| [node\_iam\_role\_arn](#output\_node\_iam\_role\_arn) | EKS Auto node IAM role ARN |
+| [node\_iam\_role\_name](#output\_node\_iam\_role\_name) | EKS Auto node IAM role name |
+| [node\_iam\_role\_unique\_id](#output\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
+| [node\_security\_group\_arn](#output\_node\_security\_group\_arn) | Amazon Resource Name (ARN) of the node shared security group |
+| [node\_security\_group\_id](#output\_node\_security\_group\_id) | ID of the node shared security group |
+| [oidc\_provider](#output\_oidc\_provider) | The OpenID Connect identity provider (issuer URL without leading `https://`) |
+| [oidc\_provider\_arn](#output\_oidc\_provider\_arn) | The ARN of the OIDC Provider if `enable_irsa = true` |
+| [self\_managed\_node\_groups](#output\_self\_managed\_node\_groups) | Map of attribute maps for all self managed node groups created |
+| [self\_managed\_node\_groups\_autoscaling\_group\_names](#output\_self\_managed\_node\_groups\_autoscaling\_group\_names) | List of the autoscaling group names created by self-managed node groups |
+
+
+## License
+
+Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/LICENSE) for full details.
+
+## Additional information for users from Russia and Belarus
+
+* Russia has [illegally annexed Crimea in 2014](https://en.wikipedia.org/wiki/Annexation_of_Crimea_by_the_Russian_Federation) and [brought the war in Donbas](https://en.wikipedia.org/wiki/War_in_Donbas) followed by [full-scale invasion of Ukraine in 2022](https://en.wikipedia.org/wiki/2022_Russian_invasion_of_Ukraine).
+* Russia has brought sorrow and devastations to millions of Ukrainians, killed hundreds of innocent people, damaged thousands of buildings, and forced several million people to flee.
+* [Putin khuylo!](https://en.wikipedia.org/wiki/Putin_khuylo!)
diff --git a/aws_auth.tf b/aws_auth.tf
deleted file mode 100644
index abe8d0766f..0000000000
--- a/aws_auth.tf
+++ /dev/null
@@ -1,95 +0,0 @@
-resource "local_file" "config_map_aws_auth" {
- content = "${data.template_file.config_map_aws_auth.rendered}"
- filename = "${var.config_output_path}config-map-aws-auth_${var.cluster_name}.yaml"
- count = "${var.write_aws_auth_config ? 1 : 0}"
-}
-
-resource "null_resource" "update_config_map_aws_auth" {
- depends_on = ["aws_eks_cluster.this"]
-
- provisioner "local-exec" {
- working_dir = "${path.module}"
-
- command = <
+
+
+
+
list(object({
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
})) | `[]` | no |
+| [cloudinit\_pre\_nodeadm](#input\_cloudinit\_pre\_nodeadm) | Array of cloud-init document parts that are created before the nodeadm document part | list(object({
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
})) | `[]` | no |
+| [cluster\_auth\_base64](#input\_cluster\_auth\_base64) | Base64 encoded CA of associated EKS cluster | `string` | `""` | no |
+| [cluster\_endpoint](#input\_cluster\_endpoint) | Endpoint of associated EKS cluster | `string` | `""` | no |
+| [cluster\_ip\_family](#input\_cluster\_ip\_family) | The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6` | `string` | `"ipv4"` | no |
+| [cluster\_name](#input\_cluster\_name) | Name of the EKS cluster | `string` | `""` | no |
+| [cluster\_service\_cidr](#input\_cluster\_service\_cidr) | The CIDR block (IPv4 or IPv6) used by the cluster to assign Kubernetes service IP addresses. This is derived from the cluster itself | `string` | `""` | no |
+| [create](#input\_create) | Determines whether to create user-data or not | `bool` | `true` | no |
+| [enable\_bootstrap\_user\_data](#input\_enable\_bootstrap\_user\_data) | Determines whether the bootstrap configurations are populated within the user data template | `bool` | `false` | no |
+| [is\_eks\_managed\_node\_group](#input\_is\_eks\_managed\_node\_group) | Determines whether the user data is used on nodes in an EKS managed node group. Used to determine if user data will be appended or not | `bool` | `true` | no |
+| [post\_bootstrap\_user\_data](#input\_post\_bootstrap\_user\_data) | User data that is appended to the user data script after of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*` | `string` | `""` | no |
+| [pre\_bootstrap\_user\_data](#input\_pre\_bootstrap\_user\_data) | User data that is injected into the user data script ahead of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*` | `string` | `""` | no |
+| [user\_data\_template\_path](#input\_user\_data\_template\_path) | Path to a local, custom user data template file to use when rendering user data | `string` | `""` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [user\_data](#output\_user\_data) | Base64 encoded user data rendered for the provided inputs |
+
diff --git a/modules/_user_data/main.tf b/modules/_user_data/main.tf
new file mode 100644
index 0000000000..c394421c4c
--- /dev/null
+++ b/modules/_user_data/main.tf
@@ -0,0 +1,136 @@
+# The `cluster_service_cidr` is required when `create == true`
+# This is a hacky way to make that logic work, otherwise Terraform always wants a value
+# and supplying any old value like `""` or `null` is not valid and will silently
+# fail to join nodes to the cluster
+resource "null_resource" "validate_cluster_service_cidr" {
+ lifecycle {
+ precondition {
+ # The length 6 is currently arbitrary, but it's a safe bet that the CIDR will be longer than that
+ # The main point is that a value needs to be provided when `create = true`
+ condition = var.create ? length(var.cluster_service_cidr) > 6 : true
+ error_message = "`cluster_service_cidr` is required when `create = true`."
+ }
+ }
+}
+
+locals {
+ is_al2 = startswith(var.ami_type, "AL2_")
+ is_al2023 = startswith(var.ami_type, "AL2023_")
+
+ # Converts AMI type into user data template path
+ ami_type_to_user_data_path = {
+ AL2_ARM_64 = "${path.module}/../../templates/al2_user_data.tpl"
+ AL2_x86_64 = "${path.module}/../../templates/al2_user_data.tpl"
+ AL2_x86_64_GPU = "${path.module}/../../templates/al2_user_data.tpl"
+
+ AL2023_x86_64_STANDARD = "${path.module}/../../templates/al2023_user_data.tpl"
+ AL2023_ARM_64_STANDARD = "${path.module}/../../templates/al2023_user_data.tpl"
+ AL2023_x86_64_NEURON = "${path.module}/../../templates/al2023_user_data.tpl"
+ AL2023_x86_64_NVIDIA = "${path.module}/../../templates/al2023_user_data.tpl"
+ AL2023_ARM_64_NVIDIA = "${path.module}/../../templates/al2023_user_data.tpl"
+
+ BOTTLEROCKET_ARM_64 = "${path.module}/../../templates/bottlerocket_user_data.tpl"
+ BOTTLEROCKET_x86_64 = "${path.module}/../../templates/bottlerocket_user_data.tpl"
+ BOTTLEROCKET_ARM_64_FIPS = "${path.module}/../../templates/bottlerocket_user_data.tpl"
+ BOTTLEROCKET_x86_64_FIPS = "${path.module}/../../templates/bottlerocket_user_data.tpl"
+ BOTTLEROCKET_ARM_64_NVIDIA = "${path.module}/../../templates/bottlerocket_user_data.tpl"
+ BOTTLEROCKET_x86_64_NVIDIA = "${path.module}/../../templates/bottlerocket_user_data.tpl"
+
+ WINDOWS_CORE_2019_x86_64 = "${path.module}/../../templates/windows_user_data.tpl"
+ WINDOWS_FULL_2019_x86_64 = "${path.module}/../../templates/windows_user_data.tpl"
+ WINDOWS_CORE_2022_x86_64 = "${path.module}/../../templates/windows_user_data.tpl"
+ WINDOWS_FULL_2022_x86_64 = "${path.module}/../../templates/windows_user_data.tpl"
+
+ CUSTOM = var.user_data_template_path
+ }
+ user_data_path = coalesce(var.user_data_template_path, local.ami_type_to_user_data_path[var.ami_type])
+
+ cluster_dns_ips = flatten(concat([try(cidrhost(var.cluster_service_cidr, 10), "")], var.additional_cluster_dns_ips))
+
+ user_data = var.create ? base64encode(templatefile(local.user_data_path,
+ {
+ # https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html#launch-template-custom-ami
+ enable_bootstrap_user_data = var.enable_bootstrap_user_data
+
+ # Required to bootstrap node
+ cluster_name = var.cluster_name
+ cluster_endpoint = var.cluster_endpoint
+ cluster_auth_base64 = var.cluster_auth_base64
+
+ cluster_service_cidr = var.cluster_service_cidr
+ cluster_ip_family = var.cluster_ip_family
+
+ # Bottlerocket
+ cluster_dns_ips = "[${join(", ", formatlist("\"%s\"", local.cluster_dns_ips))}]"
+
+ # Optional
+ bootstrap_extra_args = var.bootstrap_extra_args
+ pre_bootstrap_user_data = var.pre_bootstrap_user_data
+ post_bootstrap_user_data = var.post_bootstrap_user_data
+ }
+ )) : ""
+
+ user_data_type_to_rendered = try(coalesce(
+ local.is_al2 ? try(data.cloudinit_config.al2_eks_managed_node_group[0].rendered, local.user_data) : null,
+ local.is_al2023 ? try(data.cloudinit_config.al2023_eks_managed_node_group[0].rendered, local.user_data) : null,
+ local.user_data,
+ ), "")
+}
+
+# https://github.com/aws/containers-roadmap/issues/596#issuecomment-675097667
+# Managed node group data must in MIME multi-part archive format,
+# as by default, EKS will merge the bootstrapping command required for nodes to join the
+# cluster with your user data. If you use a custom AMI in your launch template,
+# this merging will NOT happen and you are responsible for nodes joining the cluster.
+# See docs for more details -> https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html#launch-template-user-data
+
+data "cloudinit_config" "al2_eks_managed_node_group" {
+ count = var.create && local.is_al2 && var.is_eks_managed_node_group && !var.enable_bootstrap_user_data && var.pre_bootstrap_user_data != "" && var.user_data_template_path == "" ? 1 : 0
+
+ base64_encode = true
+ gzip = false
+ boundary = "//"
+
+ # Prepend to existing user data supplied by AWS EKS
+ part {
+ content = var.pre_bootstrap_user_data
+ content_type = "text/x-shellscript"
+ }
+}
+
+# Scenarios:
+#
+# 1. Do nothing - provide nothing
+# 2. Prepend stuff on EKS MNG (before EKS MNG adds its bit at the end)
+# 3. Own all of the stuff on self-MNG or EKS MNG w/ custom AMI
+
+locals {
+ nodeadm_cloudinit = var.enable_bootstrap_user_data ? concat(
+ var.cloudinit_pre_nodeadm,
+ [{
+ content_type = "application/node.eks.aws"
+ content = base64decode(local.user_data)
+ }],
+ var.cloudinit_post_nodeadm
+ ) : var.cloudinit_pre_nodeadm
+}
+
+data "cloudinit_config" "al2023_eks_managed_node_group" {
+ count = var.create && local.is_al2023 && length(local.nodeadm_cloudinit) > 0 ? 1 : 0
+
+ base64_encode = true
+ gzip = false
+ boundary = "MIMEBOUNDARY"
+
+ dynamic "part" {
+ # Using the index is fine in this context since any change in user data will be a replacement
+ for_each = { for i, v in local.nodeadm_cloudinit : i => v }
+
+ content {
+ content = part.value.content
+ content_type = try(part.value.content_type, null)
+ filename = try(part.value.filename, null)
+ merge_type = try(part.value.merge_type, null)
+ }
+ }
+}
diff --git a/modules/_user_data/outputs.tf b/modules/_user_data/outputs.tf
new file mode 100644
index 0000000000..dda4b5195d
--- /dev/null
+++ b/modules/_user_data/outputs.tf
@@ -0,0 +1,4 @@
+output "user_data" {
+ description = "Base64 encoded user data rendered for the provided inputs"
+ value = local.user_data_type_to_rendered
+}
diff --git a/modules/_user_data/variables.tf b/modules/_user_data/variables.tf
new file mode 100644
index 0000000000..bfc32ab688
--- /dev/null
+++ b/modules/_user_data/variables.tf
@@ -0,0 +1,121 @@
+variable "create" {
+ description = "Determines whether to create user-data or not"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "ami_type" {
+ description = "Type of Amazon Machine Image (AMI) associated with the EKS Node Group. See the [AWS documentation](https://docs.aws.amazon.com/eks/latest/APIReference/API_Nodegroup.html#AmazonEKS-Type-Nodegroup-amiType) for valid values"
+ type = string
+ default = "AL2023_x86_64_STANDARD"
+ nullable = false
+}
+
+variable "enable_bootstrap_user_data" {
+ description = "Determines whether the bootstrap configurations are populated within the user data template"
+ type = bool
+ default = false
+ nullable = false
+}
+
+variable "is_eks_managed_node_group" {
+ description = "Determines whether the user data is used on nodes in an EKS managed node group. Used to determine if user data will be appended or not"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "cluster_name" {
+ description = "Name of the EKS cluster"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "cluster_endpoint" {
+ description = "Endpoint of associated EKS cluster"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "cluster_auth_base64" {
+ description = "Base64 encoded CA of associated EKS cluster"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "cluster_service_cidr" {
+ description = "The CIDR block (IPv4 or IPv6) used by the cluster to assign Kubernetes service IP addresses. This is derived from the cluster itself"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "cluster_ip_family" {
+ description = "The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6`"
+ type = string
+ default = "ipv4"
+ nullable = false
+}
+
+variable "additional_cluster_dns_ips" {
+ description = "Additional DNS IP addresses to use for the cluster. Only used when `ami_type` = `BOTTLEROCKET_*`"
+ type = list(string)
+ default = []
+ nullable = false
+}
+
+variable "pre_bootstrap_user_data" {
+ description = "User data that is injected into the user data script ahead of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*`"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "post_bootstrap_user_data" {
+ description = "User data that is appended to the user data script after of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*`"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "bootstrap_extra_args" {
+ description = "Additional arguments passed to the bootstrap script. When `ami_type` = `BOTTLEROCKET_*`; these are additional [settings](https://github.com/bottlerocket-os/bottlerocket#settings) that are provided to the Bottlerocket user data"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "user_data_template_path" {
+ description = "Path to a local, custom user data template file to use when rendering user data"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "cloudinit_pre_nodeadm" {
+ description = "Array of cloud-init document parts that are created before the nodeadm document part"
+ type = list(object({
+ content = string
+ content_type = optional(string)
+ filename = optional(string)
+ merge_type = optional(string)
+ }))
+ default = []
+ nullable = false
+}
+
+variable "cloudinit_post_nodeadm" {
+ description = "Array of cloud-init document parts that are created after the nodeadm document part"
+ type = list(object({
+ content = string
+ content_type = optional(string)
+ filename = optional(string)
+ merge_type = optional(string)
+ }))
+ default = []
+ nullable = false
+}
diff --git a/modules/_user_data/versions.tf b/modules/_user_data/versions.tf
new file mode 100644
index 0000000000..a9802b0dea
--- /dev/null
+++ b/modules/_user_data/versions.tf
@@ -0,0 +1,14 @@
+terraform {
+ required_version = ">= 1.5.7"
+
+ required_providers {
+ cloudinit = {
+ source = "hashicorp/cloudinit"
+ version = ">= 2.0"
+ }
+ null = {
+ source = "hashicorp/null"
+ version = ">= 3.0"
+ }
+ }
+}
diff --git a/modules/capability/README.md b/modules/capability/README.md
new file mode 100644
index 0000000000..42232ebae5
--- /dev/null
+++ b/modules/capability/README.md
@@ -0,0 +1,172 @@
+# EKS Capability Module
+
+Configuration in this directory creates the AWS resources required by EKS capabilities
+
+## Usage
+
+### ACK
+
+```hcl
+module "ack_eks_capability" {
+ source = "terraform-aws-modules/eks/aws//modules/capability"
+
+ name = "example-ack"
+ cluster_name = "example"
+ type = "ACK"
+
+ # IAM Role/Policy
+ iam_role_policies = {
+ AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess"
+ }
+
+ tags = {
+ Environment = "dev"
+ Terraform = "true"
+ }
+}
+```
+
+### ArgoCD
+
+```hcl
+module "argocd_eks_capability" {
+ source = "terraform-aws-modules/eks/aws//modules/capability"
+
+ name = "example-argocd"
+ cluster_name = "example"
+ type = "ARGOCD"
+
+ configuration = {
+ argo_cd = {
+ aws_idc = {
+ idc_instance_arn = "arn:aws:sso:::instance/ssoins-1234567890abcdef0"
+ }
+ namespace = "argocd"
+ rbac_role_mapping = [{
+ role = "ADMIN"
+ identity = [{
+ id = "686103e0-f051-7068-b225-e6392b959d9e"
+ type = "SSO_GROUP"
+ }]
+ }]
+ }
+ }
+
+ # IAM Role/Policy
+ iam_policy_statements = {
+ ECRRead = {
+ actions = [
+ "ecr:GetAuthorizationToken",
+ "ecr:BatchCheckLayerAvailability",
+ "ecr:GetDownloadUrlForLayer",
+ "ecr:BatchGetImage",
+ ]
+ resources = ["*"]
+ }
+ }
+
+ tags = {
+ Environment = "dev"
+ Terraform = "true"
+ }
+}
+```
+
+### KRO
+
+```hcl
+module "kro_eks_capability" {
+ source = "terraform-aws-modules/eks/aws//modules/capability"
+
+ name = "example-kro"
+ cluster_name = "example"
+ type = "KRO"
+
+ tags = {
+ Environment = "dev"
+ Terraform = "true"
+ }
+}
+```
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.5.7 |
+| [aws](#requirement\_aws) | >= 6.28 |
+| [time](#requirement\_time) | >= 0.9 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 6.28 |
+| [time](#provider\_time) | >= 0.9 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_eks_capability.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_capability) | resource |
+| [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
+| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [time_sleep.this](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource |
+| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_service_principal.capabilities_eks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/service_principal) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [cluster\_name](#input\_cluster\_name) | The name of the EKS cluster | `string` | `""` | no |
+| [configuration](#input\_configuration) | Configuration for the capability | object({
argo_cd = optional(object({
aws_idc = object({
idc_instance_arn = string
idc_region = optional(string)
})
namespace = optional(string)
network_access = optional(object({
vpce_ids = optional(list(string))
}))
rbac_role_mapping = optional(list(object({
identity = list(object({
id = string
type = string
}))
role = string
})))
}))
}) | `null` | no |
+| [create](#input\_create) | Controls if resources should be created (affects nearly all resources) | `bool` | `true` | no |
+| [create\_iam\_role](#input\_create\_iam\_role) | Determines whether an IAM role is created | `bool` | `true` | no |
+| [delete\_propagation\_policy](#input\_delete\_propagation\_policy) | The propagation policy to use when deleting the capability. Valid values: `RETAIN` | `string` | `"RETAIN"` | no |
+| [iam\_policy\_description](#input\_iam\_policy\_description) | IAM policy description | `string` | `null` | no |
+| [iam\_policy\_name](#input\_iam\_policy\_name) | Name of the IAM policy | `string` | `null` | no |
+| [iam\_policy\_path](#input\_iam\_policy\_path) | Path of the IAM policy | `string` | `null` | no |
+| [iam\_policy\_statements](#input\_iam\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed | map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})) | `null` | no |
+| [iam\_policy\_use\_name\_prefix](#input\_iam\_policy\_use\_name\_prefix) | Determines whether the name of the IAM policy (`iam_policy_name`) is used as a prefix | `bool` | `true` | no |
+| [iam\_role\_arn](#input\_iam\_role\_arn) | The ARN of the IAM role that provides permissions for the capability | `string` | `null` | no |
+| [iam\_role\_description](#input\_iam\_role\_description) | IAM role description | `string` | `null` | no |
+| [iam\_role\_max\_session\_duration](#input\_iam\_role\_max\_session\_duration) | Maximum API session duration in seconds between 3600 and 43200 | `number` | `null` | no |
+| [iam\_role\_name](#input\_iam\_role\_name) | Name of the IAM role | `string` | `null` | no |
+| [iam\_role\_override\_assume\_policy\_documents](#input\_iam\_role\_override\_assume\_policy\_documents) | A list of IAM policy documents to override the default assume role policy document for the Karpenter controller IAM role | `list(string)` | `[]` | no |
+| [iam\_role\_path](#input\_iam\_role\_path) | Path of the IAM role | `string` | `null` | no |
+| [iam\_role\_permissions\_boundary\_arn](#input\_iam\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for the IAM role | `string` | `null` | no |
+| [iam\_role\_policies](#input\_iam\_role\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no |
+| [iam\_role\_source\_assume\_policy\_documents](#input\_iam\_role\_source\_assume\_policy\_documents) | A list of IAM policy documents to use as a source for the assume role policy document for the Karpenter controller IAM role | `list(string)` | `[]` | no |
+| [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add the the IAM role | `map(string)` | `{}` | no |
+| [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the name of the IAM role (`iam_role_name`) is used as a prefix | `bool` | `true` | no |
+| [name](#input\_name) | The name of the capability to add to the cluster | `string` | `""` | no |
+| [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no |
+| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
+| [timeouts](#input\_timeouts) | Create, update, and delete timeout configurations for the capability | object({
create = optional(string)
update = optional(string)
delete = optional(string)
}) | `null` | no |
+| [type](#input\_type) | Type of the capability. Valid values: `ACK`, `KRO`, `ARGOCD` | `string` | `""` | no |
+| [wait\_duration](#input\_wait\_duration) | Duration to wait between creating the IAM role/policy and creating the capability | `string` | `"20s"` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [argocd\_server\_url](#output\_argocd\_server\_url) | URL of the Argo CD server |
+| [arn](#output\_arn) | The ARN of the EKS Capability |
+| [iam\_role\_arn](#output\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [iam\_role\_name](#output\_iam\_role\_name) | The name of the IAM role |
+| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
+| [version](#output\_version) | The version of the EKS Capability |
+
+
+## License
+
+Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/LICENSE) for full details.
diff --git a/modules/capability/main.tf b/modules/capability/main.tf
new file mode 100644
index 0000000000..51c7bd6bb4
--- /dev/null
+++ b/modules/capability/main.tf
@@ -0,0 +1,222 @@
+data "aws_service_principal" "capabilities_eks" {
+ count = var.create ? 1 : 0
+
+ service_name = "capabilities.eks"
+}
+
+# It appears that the EKS capability API checks for the IAM role trust policy *VERY* early in the process
+# Our standard approach to ordering IAM role/permission dependencies does not work here, so we add an explicit wait
+resource "time_sleep" "this" {
+ count = var.create ? 1 : 0
+
+ create_duration = var.wait_duration
+
+ triggers = {
+ iam_role_arn = local.create_iam_role ? aws_iam_role.this[0].arn : var.iam_role_arn
+ }
+}
+
+################################################################################
+# Capability
+################################################################################
+
+resource "aws_eks_capability" "this" {
+ count = var.create ? 1 : 0
+
+ region = var.region
+
+ capability_name = try(coalesce(var.name, lower(var.type)))
+ cluster_name = var.cluster_name
+
+ dynamic "configuration" {
+ for_each = var.configuration != null ? [var.configuration] : []
+
+ content {
+ dynamic "argo_cd" {
+ for_each = configuration.value.argo_cd != null ? [configuration.value.argo_cd] : []
+
+ content {
+ dynamic "aws_idc" {
+ for_each = [argo_cd.value.aws_idc]
+
+ content {
+ idc_instance_arn = aws_idc.value.idc_instance_arn
+ idc_region = aws_idc.value.idc_region
+ }
+ }
+
+ namespace = argo_cd.value.namespace
+
+ dynamic "network_access" {
+ for_each = argo_cd.value.network_access != null ? [argo_cd.value.network_access] : []
+
+ content {
+ vpce_ids = network_access.value.vpce_ids
+ }
+ }
+
+ dynamic "rbac_role_mapping" {
+ for_each = argo_cd.value.rbac_role_mapping != null ? argo_cd.value.rbac_role_mapping : []
+
+ content {
+ dynamic "identity" {
+ for_each = rbac_role_mapping.value.identity
+
+ content {
+ id = identity.value.id
+ type = identity.value.type
+ }
+ }
+
+ role = rbac_role_mapping.value.role
+ }
+ }
+ }
+ }
+ }
+ }
+
+ delete_propagation_policy = var.delete_propagation_policy
+ role_arn = time_sleep.this[0].triggers["iam_role_arn"]
+ type = var.type
+
+ dynamic "timeouts" {
+ for_each = var.timeouts != null ? [var.timeouts] : []
+
+ content {
+ create = timeouts.value.create
+ delete = timeouts.value.delete
+ update = timeouts.value.update
+ }
+ }
+
+ tags = var.tags
+
+ depends_on = [
+ aws_iam_role_policy_attachment.this,
+ aws_iam_role_policy_attachment.additional,
+ ]
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+locals {
+ create_iam_role = var.create && var.create_iam_role
+ iam_role_name = try(coalesce(var.iam_role_name, var.name), "")
+}
+
+data "aws_iam_policy_document" "assume_role" {
+ count = local.create_iam_role ? 1 : 0
+
+ override_policy_documents = var.iam_role_override_assume_policy_documents
+ source_policy_documents = var.iam_role_source_assume_policy_documents
+
+ statement {
+ sid = "EKSCapabilitiesAssumeRole"
+ actions = [
+ "sts:AssumeRole",
+ "sts:TagSession",
+ ]
+
+ principals {
+ type = "Service"
+ identifiers = [data.aws_service_principal.capabilities_eks[0].name]
+ }
+ }
+}
+
+resource "aws_iam_role" "this" {
+ count = local.create_iam_role ? 1 : 0
+
+ name = var.iam_role_use_name_prefix ? null : local.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
+ path = var.iam_role_path
+ description = coalesce(var.iam_role_description, "EKS Capability IAM role for ${var.type} capability in cluster ${var.cluster_name}")
+
+ assume_role_policy = data.aws_iam_policy_document.assume_role[0].json
+ max_session_duration = var.iam_role_max_session_duration
+ permissions_boundary = var.iam_role_permissions_boundary_arn
+ force_detach_policies = true
+
+ tags = merge(var.tags, var.iam_role_tags)
+}
+
+################################################################################
+# IAM Role Policy
+################################################################################
+
+locals {
+ create_iam_role_policy = local.create_iam_role && var.iam_policy_statements != null
+ iam_policy_name = try(coalesce(var.iam_policy_name, local.iam_role_name), "")
+}
+
+data "aws_iam_policy_document" "this" {
+ count = local.create_iam_role_policy ? 1 : 0
+
+ dynamic "statement" {
+ for_each = var.iam_policy_statements != null ? var.iam_policy_statements : {}
+
+ content {
+ sid = try(coalesce(statement.value.sid, statement.key))
+ actions = statement.value.actions
+ not_actions = statement.value.not_actions
+ effect = statement.value.effect
+ resources = statement.value.resources
+ not_resources = statement.value.not_resources
+
+ dynamic "principals" {
+ for_each = statement.value.principals != null ? statement.value.principals : []
+
+ content {
+ type = principals.value.type
+ identifiers = principals.value.identifiers
+ }
+ }
+
+ dynamic "not_principals" {
+ for_each = statement.value.not_principals != null ? statement.value.not_principals : []
+
+ content {
+ type = not_principals.value.type
+ identifiers = not_principals.value.identifiers
+ }
+ }
+
+ dynamic "condition" {
+ for_each = statement.value.condition != null ? statement.value.condition : []
+
+ content {
+ test = condition.value.test
+ values = condition.value.values
+ variable = condition.value.variable
+ }
+ }
+ }
+ }
+}
+
+resource "aws_iam_policy" "this" {
+ count = local.create_iam_role_policy ? 1 : 0
+
+ name = var.iam_policy_use_name_prefix ? null : local.iam_policy_name
+ name_prefix = var.iam_policy_use_name_prefix ? "${local.iam_policy_name}-" : null
+ path = var.iam_policy_path
+ description = coalesce(var.iam_policy_description, "IAM policy for EKS Capability ${var.type} capability in cluster ${var.cluster_name}")
+ policy = data.aws_iam_policy_document.this[0].json
+}
+
+resource "aws_iam_role_policy_attachment" "this" {
+ count = local.create_iam_role_policy ? 1 : 0
+
+ role = aws_iam_role.this[0].name
+ policy_arn = aws_iam_policy.this[0].arn
+}
+
+resource "aws_iam_role_policy_attachment" "additional" {
+ for_each = { for k, v in var.iam_role_policies : k => v if local.create_iam_role }
+
+ role = aws_iam_role.this[0].name
+ policy_arn = each.value
+}
diff --git a/modules/capability/outputs.tf b/modules/capability/outputs.tf
new file mode 100644
index 0000000000..b812a91c40
--- /dev/null
+++ b/modules/capability/outputs.tf
@@ -0,0 +1,37 @@
+################################################################################
+# Capability
+################################################################################
+
+output "arn" {
+ description = "The ARN of the EKS Capability"
+ value = try(aws_eks_capability.this[0].arn, null)
+}
+
+output "version" {
+ description = "The version of the EKS Capability"
+ value = try(aws_eks_capability.this[0].version, null)
+}
+
+output "argocd_server_url" {
+ description = "URL of the Argo CD server"
+ value = try(aws_eks_capability.this[0].configuration[0].argo_cd[0].server_url, null)
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+output "iam_role_name" {
+ description = "The name of the IAM role"
+ value = try(aws_iam_role.this[0].name, null)
+}
+
+output "iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
+ value = try(aws_iam_role.this[0].arn, null)
+}
+
+output "iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = try(aws_iam_role.this[0].unique_id, null)
+}
diff --git a/modules/capability/variables.tf b/modules/capability/variables.tf
new file mode 100644
index 0000000000..4458453bfb
--- /dev/null
+++ b/modules/capability/variables.tf
@@ -0,0 +1,215 @@
+variable "create" {
+ description = "Controls if resources should be created (affects nearly all resources)"
+ type = bool
+ default = true
+}
+
+variable "tags" {
+ description = "A map of tags to add to all resources"
+ type = map(string)
+ default = {}
+}
+
+variable "region" {
+ description = "Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration"
+ type = string
+ default = null
+}
+
+################################################################################
+# Capabilities
+################################################################################
+
+variable "name" {
+ description = "The name of the capability to add to the cluster"
+ type = string
+ default = ""
+}
+
+variable "cluster_name" {
+ description = "The name of the EKS cluster"
+ type = string
+ default = ""
+}
+
+variable "configuration" {
+ description = "Configuration for the capability"
+ type = object({
+ argo_cd = optional(object({
+ aws_idc = object({
+ idc_instance_arn = string
+ idc_region = optional(string)
+ })
+ namespace = optional(string)
+ network_access = optional(object({
+ vpce_ids = optional(list(string))
+ }))
+ rbac_role_mapping = optional(list(object({
+ identity = list(object({
+ id = string
+ type = string
+ }))
+ role = string
+ })))
+ }))
+ })
+ default = null
+}
+
+variable "delete_propagation_policy" {
+ description = "The propagation policy to use when deleting the capability. Valid values: `RETAIN`"
+ type = string
+ default = "RETAIN"
+}
+
+variable "type" {
+ description = "Type of the capability. Valid values: `ACK`, `KRO`, `ARGOCD`"
+ type = string
+ default = ""
+}
+
+variable "timeouts" {
+ description = "Create, update, and delete timeout configurations for the capability"
+ type = object({
+ create = optional(string)
+ update = optional(string)
+ delete = optional(string)
+ })
+ default = null
+}
+
+variable "wait_duration" {
+ description = "Duration to wait between creating the IAM role/policy and creating the capability"
+ type = string
+ default = "20s"
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+variable "create_iam_role" {
+ description = "Determines whether an IAM role is created"
+ type = bool
+ default = true
+}
+
+variable "iam_role_arn" {
+ description = "The ARN of the IAM role that provides permissions for the capability"
+ type = string
+ default = null
+}
+
+variable "iam_role_name" {
+ description = "Name of the IAM role"
+ type = string
+ default = null
+}
+
+variable "iam_role_use_name_prefix" {
+ description = "Determines whether the name of the IAM role (`iam_role_name`) is used as a prefix"
+ type = bool
+ default = true
+}
+
+variable "iam_role_path" {
+ description = "Path of the IAM role"
+ type = string
+ default = null
+}
+
+variable "iam_role_description" {
+ description = "IAM role description"
+ type = string
+ default = null
+}
+
+variable "iam_role_max_session_duration" {
+ description = "Maximum API session duration in seconds between 3600 and 43200"
+ type = number
+ default = null
+}
+
+variable "iam_role_permissions_boundary_arn" {
+ description = "Permissions boundary ARN to use for the IAM role"
+ type = string
+ default = null
+}
+
+variable "iam_role_tags" {
+ description = "A map of additional tags to add the the IAM role"
+ type = map(string)
+ default = {}
+}
+
+################################################################################
+# IAM Role Policy
+################################################################################
+
+variable "iam_policy_name" {
+ description = "Name of the IAM policy"
+ type = string
+ default = null
+}
+
+variable "iam_policy_use_name_prefix" {
+ description = "Determines whether the name of the IAM policy (`iam_policy_name`) is used as a prefix"
+ type = bool
+ default = true
+}
+
+variable "iam_policy_path" {
+ description = "Path of the IAM policy"
+ type = string
+ default = null
+}
+
+variable "iam_policy_description" {
+ description = "IAM policy description"
+ type = string
+ default = null
+}
+
+variable "iam_role_override_assume_policy_documents" {
+ description = "A list of IAM policy documents to override the default assume role policy document for the Karpenter controller IAM role"
+ type = list(string)
+ default = []
+}
+
+variable "iam_role_source_assume_policy_documents" {
+ description = "A list of IAM policy documents to use as a source for the assume role policy document for the Karpenter controller IAM role"
+ type = list(string)
+ default = []
+}
+
+variable "iam_policy_statements" {
+ description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed"
+ type = map(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string)
+ resources = optional(list(string))
+ not_resources = optional(list(string))
+ principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ not_principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ condition = optional(list(object({
+ test = string
+ values = list(string)
+ variable = string
+ })))
+ }))
+ default = null
+}
+
+variable "iam_role_policies" {
+ description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format"
+ type = map(string)
+ default = {}
+}
diff --git a/modules/capability/versions.tf b/modules/capability/versions.tf
new file mode 100644
index 0000000000..25ff202c0f
--- /dev/null
+++ b/modules/capability/versions.tf
@@ -0,0 +1,20 @@
+terraform {
+ required_version = ">= 1.5.7"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 6.28"
+ }
+ time = {
+ source = "hashicorp/time"
+ version = ">= 0.9"
+ }
+ }
+
+ provider_meta "aws" {
+ user_agent = [
+ "github.com/terraform-aws-modules/terraform-aws-eks"
+ ]
+ }
+}
diff --git a/modules/eks-managed-node-group/README.md b/modules/eks-managed-node-group/README.md
new file mode 100644
index 0000000000..453410cb40
--- /dev/null
+++ b/modules/eks-managed-node-group/README.md
@@ -0,0 +1,225 @@
+# EKS Managed Node Group Module
+
+Configuration in this directory creates an EKS Managed Node Group along with an IAM role, security group, and launch template
+
+## Usage
+
+```hcl
+module "eks_managed_node_group" {
+ source = "terraform-aws-modules/eks/aws//modules/eks-managed-node-group"
+
+ name = "separate-eks-mng"
+ cluster_name = "my-cluster"
+ cluster_version = "1.31"
+
+ subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"]
+
+ // The following variables are necessary if you decide to use the module outside of the parent EKS module context.
+ // Without it, the security groups of the nodes are empty and thus won't join the cluster.
+ cluster_primary_security_group_id = module.eks.cluster_primary_security_group_id
+ vpc_security_group_ids = [module.eks.node_security_group_id]
+
+ // Note: `disk_size`, and `remote_access` can only be set when using the EKS managed node group default launch template
+ // This module defaults to providing a custom launch template to allow for custom security groups, tag propagation, etc.
+ // use_custom_launch_template = false
+ // disk_size = 50
+ //
+ // # Remote access cannot be specified with a launch template
+ // remote_access = {
+ // ec2_ssh_key = module.key_pair.key_pair_name
+ // source_security_group_ids = [aws_security_group.remote_access.id]
+ // }
+
+ min_size = 1
+ max_size = 10
+ desired_size = 1
+
+ instance_types = ["t3.large"]
+ capacity_type = "SPOT"
+
+ labels = {
+ Environment = "test"
+ GithubRepo = "terraform-aws-eks"
+ GithubOrg = "terraform-aws-modules"
+ }
+
+ taints = {
+ dedicated = {
+ key = "dedicated"
+ value = "gpuGroup"
+ effect = "NO_SCHEDULE"
+ }
+ }
+
+ tags = {
+ Environment = "dev"
+ Terraform = "true"
+ }
+}
+```
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.5.7 |
+| [aws](#requirement\_aws) | >= 6.28 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 6.28 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [user\_data](#module\_user\_data) | ../_user_data | n/a |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_eks_node_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_node_group) | resource |
+| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
+| [aws_iam_role_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_launch_template.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template) | resource |
+| [aws_placement_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/placement_group) | resource |
+| [aws_security_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
+| [aws_vpc_security_group_egress_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource |
+| [aws_vpc_security_group_ingress_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |
+| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
+| [aws_ec2_instance_type.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_instance_type) | data source |
+| [aws_eks_cluster_versions.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks_cluster_versions) | data source |
+| [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
+| [aws_ssm_parameter.ami](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
+| [aws_subnet.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [account\_id](#input\_account\_id) | The AWS account ID - pass through value to reduce number of GET requests from data sources | `string` | `""` | no |
+| [ami\_id](#input\_ami\_id) | The AMI from which to launch the instance. If not supplied, EKS will use its own default image | `string` | `""` | no |
+| [ami\_release\_version](#input\_ami\_release\_version) | The AMI version. Defaults to latest AMI release version for the given Kubernetes version and AMI type | `string` | `null` | no |
+| [ami\_type](#input\_ami\_type) | Type of Amazon Machine Image (AMI) associated with the EKS Node Group. See the [AWS documentation](https://docs.aws.amazon.com/eks/latest/APIReference/API_Nodegroup.html#AmazonEKS-Type-Nodegroup-amiType) for valid values | `string` | `"AL2023_x86_64_STANDARD"` | no |
+| [block\_device\_mappings](#input\_block\_device\_mappings) | Specify volumes to attach to the instance besides the volumes specified by the AMI | map(object({
device_name = optional(string)
ebs = optional(object({
delete_on_termination = optional(bool)
encrypted = optional(bool)
iops = optional(number)
kms_key_id = optional(string)
snapshot_id = optional(string)
throughput = optional(number)
volume_initialization_rate = optional(number)
volume_size = optional(number)
volume_type = optional(string)
}))
no_device = optional(string)
virtual_name = optional(string)
})) | `null` | no |
+| [bootstrap\_extra\_args](#input\_bootstrap\_extra\_args) | Additional arguments passed to the bootstrap script. When `ami_type` = `BOTTLEROCKET_*`; these are additional [settings](https://github.com/bottlerocket-os/bottlerocket#settings) that are provided to the Bottlerocket user data | `string` | `null` | no |
+| [capacity\_reservation\_specification](#input\_capacity\_reservation\_specification) | Targeting for EC2 capacity reservations | object({
capacity_reservation_preference = optional(string)
capacity_reservation_target = optional(object({
capacity_reservation_id = optional(string)
capacity_reservation_resource_group_arn = optional(string)
}))
}) | `null` | no |
+| [capacity\_type](#input\_capacity\_type) | Type of capacity associated with the EKS Node Group. Valid values: `ON_DEMAND`, `SPOT` | `string` | `"ON_DEMAND"` | no |
+| [cloudinit\_post\_nodeadm](#input\_cloudinit\_post\_nodeadm) | Array of cloud-init document parts that are created after the nodeadm document part | list(object({
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
})) | `null` | no |
+| [cloudinit\_pre\_nodeadm](#input\_cloudinit\_pre\_nodeadm) | Array of cloud-init document parts that are created before the nodeadm document part | list(object({
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
})) | `null` | no |
+| [cluster\_auth\_base64](#input\_cluster\_auth\_base64) | Base64 encoded CA of associated EKS cluster | `string` | `null` | no |
+| [cluster\_endpoint](#input\_cluster\_endpoint) | Endpoint of associated EKS cluster | `string` | `null` | no |
+| [cluster\_ip\_family](#input\_cluster\_ip\_family) | The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6` | `string` | `"ipv4"` | no |
+| [cluster\_name](#input\_cluster\_name) | Name of associated EKS cluster | `string` | `""` | no |
+| [cluster\_primary\_security\_group\_id](#input\_cluster\_primary\_security\_group\_id) | The ID of the EKS cluster primary security group to associate with the instance(s). This is the security group that is automatically created by the EKS service | `string` | `null` | no |
+| [cluster\_service\_cidr](#input\_cluster\_service\_cidr) | The CIDR block (IPv4 or IPv6) used by the cluster to assign Kubernetes service IP addresses. This is derived from the cluster itself | `string` | `null` | no |
+| [cpu\_options](#input\_cpu\_options) | The CPU options for the instance | object({
amd_sev_snp = optional(string)
core_count = optional(number)
threads_per_core = optional(number)
}) | `null` | no |
+| [create](#input\_create) | Determines whether to create EKS managed node group or not | `bool` | `true` | no |
+| [create\_iam\_role](#input\_create\_iam\_role) | Determines whether an IAM role is created or to use an existing IAM role | `bool` | `true` | no |
+| [create\_iam\_role\_policy](#input\_create\_iam\_role\_policy) | Determines whether an IAM role policy is created or not | `bool` | `true` | no |
+| [create\_launch\_template](#input\_create\_launch\_template) | Determines whether to create a launch template or not. If set to `false`, EKS will use its own default launch template | `bool` | `true` | no |
+| [create\_placement\_group](#input\_create\_placement\_group) | Determines whether a placement group is created & used by the node group | `bool` | `false` | no |
+| [create\_security\_group](#input\_create\_security\_group) | Determines if a security group is created | `bool` | `true` | no |
+| [credit\_specification](#input\_credit\_specification) | Customize the credit specification of the instance | object({
cpu_credits = optional(string)
}) | `null` | no |
+| [desired\_size](#input\_desired\_size) | Desired number of instances/nodes | `number` | `1` | no |
+| [disable\_api\_termination](#input\_disable\_api\_termination) | If true, enables EC2 instance termination protection | `bool` | `null` | no |
+| [disk\_size](#input\_disk\_size) | Disk size in GiB for nodes. Defaults to `20`. Only valid when `use_custom_launch_template` = `false` | `number` | `null` | no |
+| [ebs\_optimized](#input\_ebs\_optimized) | If true, the launched EC2 instance(s) will be EBS-optimized | `bool` | `null` | no |
+| [efa\_indices](#input\_efa\_indices) | The indices of the network interfaces that should be EFA-enabled. Only valid when `enable_efa_support` = `true` | `list(number)` | [| no | +| [enable\_bootstrap\_user\_data](#input\_enable\_bootstrap\_user\_data) | Determines whether the bootstrap configurations are populated within the user data template. Only valid when using a custom AMI via `ami_id` | `bool` | `false` | no | +| [enable\_efa\_only](#input\_enable\_efa\_only) | Determines whether to enable EFA (`false`, default) or EFA and EFA-only (`true`) network interfaces. Note: requires vpc-cni version `v1.18.4` or later | `bool` | `true` | no | +| [enable\_efa\_support](#input\_enable\_efa\_support) | Determines whether to enable Elastic Fabric Adapter (EFA) support | `bool` | `false` | no | +| [enable\_monitoring](#input\_enable\_monitoring) | Enables/disables detailed monitoring | `bool` | `false` | no | +| [enclave\_options](#input\_enclave\_options) | Enable Nitro Enclaves on launched instances |
0
]
object({
enabled = optional(bool)
}) | `null` | no |
+| [force\_update\_version](#input\_force\_update\_version) | Force version update if existing pods are unable to be drained due to a pod disruption budget issue | `bool` | `null` | no |
+| [iam\_role\_additional\_policies](#input\_iam\_role\_additional\_policies) | Additional policies to be added to the IAM role | `map(string)` | `{}` | no |
+| [iam\_role\_arn](#input\_iam\_role\_arn) | Existing IAM role ARN for the node group. Required if `create_iam_role` is set to `false` | `string` | `null` | no |
+| [iam\_role\_attach\_cni\_policy](#input\_iam\_role\_attach\_cni\_policy) | Whether to attach the `AmazonEKS_CNI_Policy`/`AmazonEKS_CNI_IPv6_Policy` IAM policy to the IAM IAM role. WARNING: If set `false` the permissions must be assigned to the `aws-node` DaemonSet pods via another method or nodes will not be able to join the cluster | `bool` | `true` | no |
+| [iam\_role\_description](#input\_iam\_role\_description) | Description of the role | `string` | `"EKS managed node group IAM role"` | no |
+| [iam\_role\_name](#input\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
+| [iam\_role\_path](#input\_iam\_role\_path) | IAM role path | `string` | `null` | no |
+| [iam\_role\_permissions\_boundary](#input\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
+| [iam\_role\_policy\_statements](#input\_iam\_role\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed | list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})) | `null` | no |
+| [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
+| [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no |
+| [instance\_market\_options](#input\_instance\_market\_options) | The market (purchasing) option for the instance | object({
market_type = optional(string)
spot_options = optional(object({
block_duration_minutes = optional(number)
instance_interruption_behavior = optional(string)
max_price = optional(string)
spot_instance_type = optional(string)
valid_until = optional(string)
}))
}) | `null` | no |
+| [instance\_types](#input\_instance\_types) | Set of instance types associated with the EKS Node Group. Defaults to `["t3.medium"]` | `list(string)` | `null` | no |
+| [kernel\_id](#input\_kernel\_id) | The kernel ID | `string` | `null` | no |
+| [key\_name](#input\_key\_name) | The key name that should be used for the instance(s) | `string` | `null` | no |
+| [kubernetes\_version](#input\_kubernetes\_version) | Kubernetes version. Defaults to EKS Cluster Kubernetes version | `string` | `null` | no |
+| [labels](#input\_labels) | Key-value map of Kubernetes labels. Only labels that are applied with the EKS API are managed by this argument. Other Kubernetes labels applied to the EKS Node Group will not be managed | `map(string)` | `null` | no |
+| [launch\_template\_default\_version](#input\_launch\_template\_default\_version) | Default version of the launch template | `string` | `null` | no |
+| [launch\_template\_description](#input\_launch\_template\_description) | Description of the launch template | `string` | `null` | no |
+| [launch\_template\_id](#input\_launch\_template\_id) | The ID of an existing launch template to use. Required when `create_launch_template` = `false` and `use_custom_launch_template` = `true` | `string` | `""` | no |
+| [launch\_template\_name](#input\_launch\_template\_name) | Name of launch template to be created | `string` | `null` | no |
+| [launch\_template\_tags](#input\_launch\_template\_tags) | A map of additional tags to add to the tag\_specifications of launch template created | `map(string)` | `{}` | no |
+| [launch\_template\_use\_name\_prefix](#input\_launch\_template\_use\_name\_prefix) | Determines whether to use `launch_template_name` as is or create a unique name beginning with the `launch_template_name` as the prefix | `bool` | `true` | no |
+| [launch\_template\_version](#input\_launch\_template\_version) | Launch template version number. The default is `$Default` | `string` | `null` | no |
+| [license\_specifications](#input\_license\_specifications) | A list of license specifications to associate with | list(object({
license_configuration_arn = string
})) | `null` | no |
+| [maintenance\_options](#input\_maintenance\_options) | The maintenance options for the instance | object({
auto_recovery = optional(string)
}) | `null` | no |
+| [max\_size](#input\_max\_size) | Maximum number of instances/nodes | `number` | `3` | no |
+| [metadata\_options](#input\_metadata\_options) | Customize the metadata options for the instance | object({
http_endpoint = optional(string, "enabled")
http_protocol_ipv6 = optional(string)
http_put_response_hop_limit = optional(number, 1)
http_tokens = optional(string, "required")
instance_metadata_tags = optional(string)
}) | {
"http_endpoint": "enabled",
"http_put_response_hop_limit": 1,
"http_tokens": "required"
} | no |
+| [min\_size](#input\_min\_size) | Minimum number of instances/nodes | `number` | `1` | no |
+| [name](#input\_name) | Name of the EKS managed node group | `string` | `""` | no |
+| [network\_interfaces](#input\_network\_interfaces) | Customize network interfaces to be attached at instance boot time | list(object({
associate_carrier_ip_address = optional(bool)
associate_public_ip_address = optional(bool)
connection_tracking_specification = optional(object({
tcp_established_timeout = optional(number)
udp_stream_timeout = optional(number)
udp_timeout = optional(number)
}))
delete_on_termination = optional(bool)
description = optional(string)
device_index = optional(number)
ena_srd_specification = optional(object({
ena_srd_enabled = optional(bool)
ena_srd_udp_specification = optional(object({
ena_srd_udp_enabled = optional(bool)
}))
}))
interface_type = optional(string)
ipv4_address_count = optional(number)
ipv4_addresses = optional(list(string))
ipv4_prefix_count = optional(number)
ipv4_prefixes = optional(list(string))
ipv6_address_count = optional(number)
ipv6_addresses = optional(list(string))
ipv6_prefix_count = optional(number)
ipv6_prefixes = optional(list(string))
network_card_index = optional(number)
network_interface_id = optional(string)
primary_ipv6 = optional(bool)
private_ip_address = optional(string)
security_groups = optional(list(string), [])
})) | `[]` | no |
+| [node\_repair\_config](#input\_node\_repair\_config) | The node auto repair configuration for the node group | object({
enabled = optional(bool, true)
max_parallel_nodes_repaired_count = optional(number)
max_parallel_nodes_repaired_percentage = optional(number)
max_unhealthy_node_threshold_count = optional(number)
max_unhealthy_node_threshold_percentage = optional(number)
node_repair_config_overrides = optional(list(object({
min_repair_wait_time_mins = number
node_monitoring_condition = string
node_unhealthy_reason = string
repair_action = string
})))
}) | `null` | no |
+| [partition](#input\_partition) | The AWS partition - pass through value to reduce number of GET requests from data sources | `string` | `""` | no |
+| [placement](#input\_placement) | The placement of the instance | object({
affinity = optional(string)
availability_zone = optional(string)
group_name = optional(string)
host_id = optional(string)
host_resource_group_arn = optional(string)
partition_number = optional(number)
spread_domain = optional(string)
tenancy = optional(string)
}) | `null` | no |
+| [post\_bootstrap\_user\_data](#input\_post\_bootstrap\_user\_data) | User data that is appended to the user data script after of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*` | `string` | `null` | no |
+| [pre\_bootstrap\_user\_data](#input\_pre\_bootstrap\_user\_data) | User data that is injected into the user data script ahead of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*` | `string` | `null` | no |
+| [private\_dns\_name\_options](#input\_private\_dns\_name\_options) | The options for the instance hostname. The default values are inherited from the subnet | object({
enable_resource_name_dns_aaaa_record = optional(bool)
enable_resource_name_dns_a_record = optional(bool)
hostname_type = optional(string)
}) | `null` | no |
+| [ram\_disk\_id](#input\_ram\_disk\_id) | The ID of the ram disk | `string` | `null` | no |
+| [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no |
+| [remote\_access](#input\_remote\_access) | Configuration block with remote access settings. Only valid when `use_custom_launch_template` = `false` | object({
ec2_ssh_key = optional(string)
source_security_group_ids = optional(list(string))
}) | `null` | no |
+| [security\_group\_description](#input\_security\_group\_description) | Description of the security group created | `string` | `null` | no |
+| [security\_group\_egress\_rules](#input\_security\_group\_egress\_rules) | Security group egress rules to add to the security group created | map(object({
name = optional(string)
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool, false)
tags = optional(map(string), {})
to_port = optional(string)
})) | `{}` | no |
+| [security\_group\_ingress\_rules](#input\_security\_group\_ingress\_rules) | Security group ingress rules to add to the security group created | map(object({
name = optional(string)
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool, false)
tags = optional(map(string), {})
to_port = optional(string)
})) | `{}` | no |
+| [security\_group\_name](#input\_security\_group\_name) | Name to use on security group created | `string` | `null` | no |
+| [security\_group\_tags](#input\_security\_group\_tags) | A map of additional tags to add to the security group created | `map(string)` | `{}` | no |
+| [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Determines whether the security group name (`security_group_name`) is used as a prefix | `bool` | `true` | no |
+| [subnet\_ids](#input\_subnet\_ids) | Identifiers of EC2 Subnets to associate with the EKS Node Group. These subnets must have the following resource tag: `kubernetes.io/cluster/CLUSTER_NAME` | `list(string)` | `null` | no |
+| [tag\_specifications](#input\_tag\_specifications) | The tags to apply to the resources during launch | `list(string)` | [| no | +| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | +| [taints](#input\_taints) | The Kubernetes taints to be applied to the nodes in the node group. Maximum of 50 taints per node group |
"instance",
"volume",
"network-interface"
]
map(object({
key = string
value = optional(string)
effect = string
})) | `null` | no |
+| [timeouts](#input\_timeouts) | Create, update, and delete timeout configurations for the node group | object({
create = optional(string)
update = optional(string)
delete = optional(string)
}) | `null` | no |
+| [update\_config](#input\_update\_config) | Configuration block of settings for max unavailable resources during node group updates | object({
max_unavailable = optional(number)
max_unavailable_percentage = optional(number)
update_strategy = optional(string)
}) | {
"max_unavailable_percentage": 33
} | no |
+| [update\_launch\_template\_default\_version](#input\_update\_launch\_template\_default\_version) | Whether to update the launch templates default version on each update. Conflicts with `launch_template_default_version` | `bool` | `true` | no |
+| [use\_custom\_launch\_template](#input\_use\_custom\_launch\_template) | Determines whether to use a custom launch template or not. If set to `false`, EKS will use its own default launch template | `bool` | `true` | no |
+| [use\_latest\_ami\_release\_version](#input\_use\_latest\_ami\_release\_version) | Determines whether to use the latest AMI release version for the given `ami_type` (except for `CUSTOM`). Note: `ami_type` and `kubernetes_version` must be supplied in order to enable this feature | `bool` | `true` | no |
+| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether to use `name` as is or create a unique name beginning with the `name` as the prefix | `bool` | `true` | no |
+| [user\_data\_template\_path](#input\_user\_data\_template\_path) | Path to a local, custom user data template file to use when rendering user data | `string` | `null` | no |
+| [vpc\_security\_group\_ids](#input\_vpc\_security\_group\_ids) | A list of security group IDs to associate | `list(string)` | `[]` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [iam\_role\_arn](#output\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [iam\_role\_name](#output\_iam\_role\_name) | The name of the IAM role |
+| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
+| [launch\_template\_arn](#output\_launch\_template\_arn) | The ARN of the launch template |
+| [launch\_template\_id](#output\_launch\_template\_id) | The ID of the launch template |
+| [launch\_template\_latest\_version](#output\_launch\_template\_latest\_version) | The latest version of the launch template |
+| [launch\_template\_name](#output\_launch\_template\_name) | The name of the launch template |
+| [node\_group\_arn](#output\_node\_group\_arn) | Amazon Resource Name (ARN) of the EKS Node Group |
+| [node\_group\_autoscaling\_group\_names](#output\_node\_group\_autoscaling\_group\_names) | List of the autoscaling group names |
+| [node\_group\_id](#output\_node\_group\_id) | EKS Cluster name and EKS Node Group name separated by a colon (`:`) |
+| [node\_group\_labels](#output\_node\_group\_labels) | Map of labels applied to the node group |
+| [node\_group\_resources](#output\_node\_group\_resources) | List of objects containing information about underlying resources |
+| [node\_group\_status](#output\_node\_group\_status) | Status of the EKS Node Group |
+| [node\_group\_taints](#output\_node\_group\_taints) | List of objects containing information about taints applied to the node group |
+| [security\_group\_arn](#output\_security\_group\_arn) | Amazon Resource Name (ARN) of the security group |
+| [security\_group\_id](#output\_security\_group\_id) | ID of the security group |
+
+
+## License
+
+Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/LICENSE) for full details.
diff --git a/modules/eks-managed-node-group/main.tf b/modules/eks-managed-node-group/main.tf
new file mode 100644
index 0000000000..e153147953
--- /dev/null
+++ b/modules/eks-managed-node-group/main.tf
@@ -0,0 +1,832 @@
+data "aws_partition" "current" {
+ count = var.create && var.partition == "" ? 1 : 0
+}
+data "aws_caller_identity" "current" {
+ count = var.create && var.account_id == "" ? 1 : 0
+}
+
+locals {
+ partition = try(data.aws_partition.current[0].partition, var.partition)
+ account_id = try(data.aws_caller_identity.current[0].account_id, var.account_id)
+}
+
+################################################################################
+# User Data
+################################################################################
+
+module "user_data" {
+ source = "../_user_data"
+
+ create = var.create
+ ami_type = var.ami_type
+
+ cluster_name = var.cluster_name
+ cluster_endpoint = var.cluster_endpoint
+ cluster_auth_base64 = var.cluster_auth_base64
+ cluster_ip_family = var.cluster_ip_family
+ cluster_service_cidr = var.cluster_service_cidr
+
+ enable_bootstrap_user_data = var.enable_bootstrap_user_data
+ pre_bootstrap_user_data = var.pre_bootstrap_user_data
+ post_bootstrap_user_data = var.post_bootstrap_user_data
+ bootstrap_extra_args = var.bootstrap_extra_args
+ user_data_template_path = var.user_data_template_path
+
+ cloudinit_pre_nodeadm = var.cloudinit_pre_nodeadm
+ cloudinit_post_nodeadm = var.cloudinit_post_nodeadm
+}
+
+################################################################################
+# EFA Support
+################################################################################
+
+data "aws_ec2_instance_type" "this" {
+ count = local.enable_efa_support ? 1 : 0
+
+ region = var.region
+
+ instance_type = local.efa_instance_type
+}
+
+locals {
+ enable_efa_support = var.create && var.enable_efa_support
+
+ efa_instance_type = try(element(var.instance_types, 0), "")
+ num_network_cards = try(data.aws_ec2_instance_type.this[0].maximum_network_cards, 0)
+
+ # Primary network interface must be EFA, remaining can be EFA or EFA-only
+ efa_network_interfaces = [
+ for i in range(local.num_network_cards) : {
+ associate_public_ip_address = false
+ delete_on_termination = true
+ device_index = i == 0 ? 0 : 1
+ network_card_index = i
+ interface_type = var.enable_efa_only ? contains(concat([0], var.efa_indices), i) ? "efa" : "efa-only" : "efa"
+
+ # Null out due to error: The true and false result expressions must have consistent types. The 'true' value is tuple, but the 'false' value is list of objects.
+ associate_carrier_ip_address = null
+ connection_tracking_specification = null
+ description = "EFA${var.enable_efa_only ? "-only" : ""} Network Interface ${i}"
+ ena_srd_specification = null
+ ipv4_address_count = null
+ ipv4_addresses = null
+ ipv4_prefix_count = null
+ ipv4_prefixes = null
+ ipv6_address_count = null
+ ipv6_addresses = null
+ ipv6_prefix_count = null
+ ipv6_prefixes = null
+ network_interface_id = null
+ primary_ipv6 = null
+ private_ip_address = null
+ security_groups = []
+ }
+ ]
+
+ network_interfaces = local.enable_efa_support ? local.efa_network_interfaces : var.network_interfaces
+}
+
+################################################################################
+# Launch template
+################################################################################
+
+locals {
+ launch_template_name = coalesce(var.launch_template_name, "${var.name}-eks-node-group")
+ security_group_ids = compact(concat([var.cluster_primary_security_group_id], var.vpc_security_group_ids, aws_security_group.this[*].id))
+}
+
+resource "aws_launch_template" "this" {
+ count = var.create && var.create_launch_template && var.use_custom_launch_template ? 1 : 0
+
+ region = var.region
+
+ dynamic "block_device_mappings" {
+ for_each = var.block_device_mappings != null ? var.block_device_mappings : {}
+
+ content {
+ device_name = block_device_mappings.value.device_name
+
+ dynamic "ebs" {
+ for_each = block_device_mappings.value.ebs != null ? [block_device_mappings.value.ebs] : []
+
+ content {
+ delete_on_termination = ebs.value.delete_on_termination
+ encrypted = ebs.value.encrypted
+ iops = ebs.value.iops
+ kms_key_id = ebs.value.kms_key_id
+ snapshot_id = ebs.value.snapshot_id
+ throughput = ebs.value.throughput
+ volume_initialization_rate = ebs.value.volume_initialization_rate
+ volume_size = ebs.value.volume_size
+ volume_type = ebs.value.volume_type
+ }
+ }
+
+ no_device = block_device_mappings.value.no_device
+ virtual_name = block_device_mappings.value.virtual_name
+ }
+ }
+
+ dynamic "capacity_reservation_specification" {
+ for_each = var.capacity_reservation_specification != null ? [var.capacity_reservation_specification] : []
+
+ content {
+ capacity_reservation_preference = capacity_reservation_specification.value.capacity_reservation_preference
+
+ dynamic "capacity_reservation_target" {
+ for_each = capacity_reservation_specification.value.capacity_reservation_target != null ? [capacity_reservation_specification.value.capacity_reservation_target] : []
+ content {
+ capacity_reservation_id = capacity_reservation_target.value.capacity_reservation_id
+ capacity_reservation_resource_group_arn = capacity_reservation_target.value.capacity_reservation_resource_group_arn
+ }
+ }
+ }
+ }
+
+ dynamic "cpu_options" {
+ for_each = var.cpu_options != null ? [var.cpu_options] : []
+
+ content {
+ amd_sev_snp = cpu_options.value.amd_sev_snp
+ core_count = cpu_options.value.core_count
+ threads_per_core = cpu_options.value.threads_per_core
+ }
+ }
+
+ dynamic "credit_specification" {
+ for_each = var.credit_specification != null ? [var.credit_specification] : []
+
+ content {
+ cpu_credits = credit_specification.value.cpu_credits
+ }
+ }
+
+ default_version = var.launch_template_default_version
+ description = var.launch_template_description
+ disable_api_termination = var.disable_api_termination
+ ebs_optimized = var.ebs_optimized
+
+ dynamic "enclave_options" {
+ for_each = var.enclave_options != null ? [var.enclave_options] : []
+
+ content {
+ enabled = enclave_options.value.enabled
+ }
+ }
+
+ # Set on EKS managed node group, will fail if set here
+ # https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html#launch-template-basics
+ # dynamic "hibernation_options" {
+ # for_each = length(var.hibernation_options) > 0 ? [var.hibernation_options] : []
+
+ # content {
+ # configured = hibernation_options.value.configured
+ # }
+ # }
+
+ # Set on EKS managed node group, will fail if set here
+ # https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html#launch-template-basics
+ # dynamic "iam_instance_profile" {
+ # for_each = [var.iam_instance_profile]
+ # content {
+ # name = lookup(var.iam_instance_profile, "name", null)
+ # arn = lookup(var.iam_instance_profile, "arn", null)
+ # }
+ # }
+
+ image_id = var.ami_id
+ # Set on EKS managed node group, will fail if set here
+ # https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html#launch-template-basics
+ # instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior
+
+ dynamic "instance_market_options" {
+ for_each = var.instance_market_options != null ? [var.instance_market_options] : []
+
+ content {
+ market_type = instance_market_options.value.market_type
+
+ dynamic "spot_options" {
+ for_each = instance_market_options.value.spot_options != null ? [instance_market_options.value.spot_options] : []
+
+ content {
+ block_duration_minutes = spot_options.value.block_duration_minutes
+ instance_interruption_behavior = spot_options.value.instance_interruption_behavior
+ max_price = spot_options.value.max_price
+ spot_instance_type = spot_options.value.spot_instance_type
+ valid_until = spot_options.value.valid_until
+ }
+ }
+ }
+ }
+
+ # Instance type(s) are generally set on the node group,
+ # except when a ML capacity block reseravtion is used
+ instance_type = var.capacity_type == "CAPACITY_BLOCK" ? element(var.instance_types, 0) : null
+ kernel_id = var.kernel_id
+ key_name = var.key_name
+
+ dynamic "license_specification" {
+ for_each = var.license_specifications != null ? var.license_specifications : []
+
+ content {
+ license_configuration_arn = license_specification.value.license_configuration_arn
+ }
+ }
+
+ dynamic "maintenance_options" {
+ for_each = var.maintenance_options != null ? [var.maintenance_options] : []
+
+ content {
+ auto_recovery = maintenance_options.value.auto_recovery
+ }
+ }
+
+ dynamic "metadata_options" {
+ for_each = [var.metadata_options]
+
+ content {
+ http_endpoint = metadata_options.value.http_endpoint
+ http_protocol_ipv6 = metadata_options.value.http_protocol_ipv6
+ http_put_response_hop_limit = metadata_options.value.http_put_response_hop_limit
+ http_tokens = metadata_options.value.http_tokens
+ instance_metadata_tags = metadata_options.value.instance_metadata_tags
+ }
+ }
+
+ dynamic "monitoring" {
+ for_each = var.enable_monitoring ? [1] : []
+
+ content {
+ enabled = var.enable_monitoring
+ }
+ }
+
+ name = var.launch_template_use_name_prefix ? null : local.launch_template_name
+ name_prefix = var.launch_template_use_name_prefix ? "${local.launch_template_name}-" : null
+
+ dynamic "network_interfaces" {
+ for_each = length(local.network_interfaces) > 0 ? local.network_interfaces : []
+
+ content {
+ associate_carrier_ip_address = network_interfaces.value.associate_carrier_ip_address
+ associate_public_ip_address = network_interfaces.value.associate_public_ip_address
+
+ dynamic "connection_tracking_specification" {
+ for_each = network_interfaces.value.connection_tracking_specification != null ? [network_interfaces.value.connection_tracking_specification] : []
+
+ content {
+ tcp_established_timeout = connection_tracking_specification.value.tcp_established_timeout
+ udp_stream_timeout = connection_tracking_specification.value.udp_stream_timeout
+ udp_timeout = connection_tracking_specification.value.udp_timeout
+ }
+ }
+
+ delete_on_termination = network_interfaces.value.delete_on_termination
+ description = network_interfaces.value.description
+ device_index = network_interfaces.value.device_index
+
+ dynamic "ena_srd_specification" {
+ for_each = network_interfaces.value.ena_srd_specification != null ? [network_interfaces.value.ena_srd_specification] : []
+
+ content {
+ ena_srd_enabled = ena_srd_specification.value.ena_srd_enabled
+
+ dynamic "ena_srd_udp_specification" {
+ for_each = ena_srd_specification.value.ena_srd_udp_specification != null ? [ena_srd_specification.value.ena_srd_udp_specification] : []
+
+ content {
+ ena_srd_udp_enabled = ena_srd_udp_specification.value.ena_srd_udp_enabled
+ }
+ }
+ }
+ }
+
+ interface_type = network_interfaces.value.interface_type
+ ipv4_address_count = network_interfaces.value.ipv4_address_count
+ ipv4_addresses = network_interfaces.value.ipv4_addresses
+ ipv4_prefix_count = network_interfaces.value.ipv4_prefix_count
+ ipv4_prefixes = network_interfaces.value.ipv4_prefixes
+ ipv6_address_count = network_interfaces.value.ipv6_address_count
+ ipv6_addresses = network_interfaces.value.ipv6_addresses
+ ipv6_prefix_count = network_interfaces.value.ipv6_prefix_count
+ ipv6_prefixes = network_interfaces.value.ipv6_prefixes
+ network_card_index = network_interfaces.value.network_card_index
+ network_interface_id = network_interfaces.value.network_interface_id
+ primary_ipv6 = network_interfaces.value.primary_ipv6
+ private_ip_address = network_interfaces.value.private_ip_address
+ # Ref: https://github.com/hashicorp/terraform-provider-aws/issues/4570
+ security_groups = compact(concat(network_interfaces.value.security_groups, local.security_group_ids))
+ # Set on EKS managed node group, will fail if set here
+ # https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html#launch-template-basics
+ # subnet_id = try(network_interfaces.value.subnet_id, null)
+ }
+ }
+
+ dynamic "placement" {
+ for_each = var.placement != null || local.create_placement_group ? [var.placement] : []
+
+ content {
+ affinity = try(placement.value.affinity, null)
+ availability_zone = try(placement.value.availability_zone, null)
+ group_name = try(aws_placement_group.this[0].name, placement.value.group_name)
+ host_id = try(placement.value.host_id, null)
+ host_resource_group_arn = try(placement.value.host_resource_group_arn, null)
+ partition_number = try(placement.value.partition_number, null)
+ spread_domain = try(placement.value.spread_domain, null)
+ tenancy = try(placement.value.tenancy, null)
+ }
+ }
+
+ dynamic "private_dns_name_options" {
+ for_each = var.private_dns_name_options != null ? [var.private_dns_name_options] : []
+
+ content {
+ enable_resource_name_dns_aaaa_record = private_dns_name_options.value.enable_resource_name_dns_aaaa_record
+ enable_resource_name_dns_a_record = private_dns_name_options.value.enable_resource_name_dns_a_record
+ hostname_type = private_dns_name_options.value.hostname_type
+ }
+ }
+
+ ram_disk_id = var.ram_disk_id
+
+ dynamic "tag_specifications" {
+ for_each = toset(var.tag_specifications)
+
+ content {
+ resource_type = tag_specifications.key
+ tags = merge(var.tags, { Name = var.name }, var.launch_template_tags)
+ }
+ }
+
+ update_default_version = var.update_launch_template_default_version
+ user_data = module.user_data.user_data
+ vpc_security_group_ids = length(local.network_interfaces) > 0 ? [] : local.security_group_ids
+
+ tags = merge(
+ var.tags,
+ var.launch_template_tags,
+ )
+
+ # Prevent premature access of policies by pods that
+ # require permissions on create/destroy that depend on nodes
+ depends_on = [
+ aws_iam_role_policy_attachment.this,
+ aws_iam_role_policy_attachment.additional,
+ ]
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+################################################################################
+# AMI SSM Parameter
+################################################################################
+
+data "aws_eks_cluster_versions" "this" {
+ count = var.create && var.kubernetes_version == null ? 1 : 0
+
+ region = var.region
+
+ cluster_type = "eks"
+ version_status = "STANDARD_SUPPORT"
+}
+
+locals {
+ # Just to ensure templating doesn't fail when values are not provided
+ ssm_kubernetes_version = var.kubernetes_version != null ? var.kubernetes_version : try(data.aws_eks_cluster_versions.this[0].cluster_versions[0].cluster_version, "UNSPECIFIED")
+ ssm_ami_type = var.ami_type != null ? var.ami_type : ""
+
+ # Map the AMI type to the respective SSM param path
+ ssm_ami_type_to_ssm_param = {
+ AL2_x86_64 = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2/recommended/release_version"
+ AL2_x86_64_GPU = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2-gpu/recommended/release_version"
+ AL2_ARM_64 = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2-arm64/recommended/release_version"
+ CUSTOM = "NONE"
+ BOTTLEROCKET_ARM_64 = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}/arm64/latest/image_version"
+ BOTTLEROCKET_x86_64 = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}/x86_64/latest/image_version"
+ BOTTLEROCKET_ARM_64_FIPS = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}-fips/arm64/latest/image_version"
+ BOTTLEROCKET_x86_64_FIPS = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}-fips/x86_64/latest/image_version"
+ BOTTLEROCKET_ARM_64_NVIDIA = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}-nvidia/arm64/latest/image_version"
+ BOTTLEROCKET_x86_64_NVIDIA = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}-nvidia/x86_64/latest/image_version"
+ WINDOWS_CORE_2019_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-EKS_Optimized-${local.ssm_kubernetes_version}"
+ WINDOWS_FULL_2019_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2019-English-Core-EKS_Optimized-${local.ssm_kubernetes_version}"
+ WINDOWS_CORE_2022_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2022-English-Full-EKS_Optimized-${local.ssm_kubernetes_version}"
+ WINDOWS_FULL_2022_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2022-English-Core-EKS_Optimized-${local.ssm_kubernetes_version}"
+ AL2023_x86_64_STANDARD = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2023/x86_64/standard/recommended/release_version"
+ AL2023_ARM_64_STANDARD = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2023/arm64/standard/recommended/release_version"
+ AL2023_x86_64_NEURON = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2023/x86_64/neuron/recommended/release_version"
+ AL2023_x86_64_NVIDIA = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2023/x86_64/nvidia/recommended/release_version"
+ AL2023_ARM_64_NVIDIA = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2023/arm64/nvidia/recommended/release_version"
+ }
+
+ # The Windows SSM params currently do not have a release version, so we have to get the full output JSON blob and parse out the release version
+ windows_latest_ami_release_version = var.create && var.use_latest_ami_release_version && startswith(local.ssm_ami_type, "WINDOWS") ? nonsensitive(jsondecode(data.aws_ssm_parameter.ami[0].value)["release_version"]) : null
+ # Based on the steps above, try to get an AMI release version - if not, `null` is returned
+ latest_ami_release_version = startswith(local.ssm_ami_type, "WINDOWS") ? local.windows_latest_ami_release_version : try(nonsensitive(data.aws_ssm_parameter.ami[0].value), null)
+}
+
+data "aws_ssm_parameter" "ami" {
+ count = var.create && var.use_latest_ami_release_version ? 1 : 0
+
+ region = var.region
+
+ name = local.ssm_ami_type_to_ssm_param[var.ami_type]
+}
+
+################################################################################
+# Node Group
+################################################################################
+
+locals {
+ launch_template_id = var.create && var.create_launch_template ? try(aws_launch_template.this[0].id, null) : var.launch_template_id
+ # Change order to allow users to set version priority before using defaults
+ launch_template_version = coalesce(var.launch_template_version, try(aws_launch_template.this[0].default_version, "$Default"))
+}
+
+resource "aws_eks_node_group" "this" {
+ count = var.create ? 1 : 0
+
+ region = var.region
+
+ # Required
+ cluster_name = var.cluster_name
+ node_role_arn = var.create_iam_role ? aws_iam_role.this[0].arn : var.iam_role_arn
+ subnet_ids = var.subnet_ids
+
+ scaling_config {
+ min_size = var.min_size
+ max_size = var.max_size
+ desired_size = var.desired_size
+ }
+
+ # Optional
+ node_group_name = var.use_name_prefix ? null : var.name
+ node_group_name_prefix = var.use_name_prefix ? "${var.name}-" : null
+
+ # https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html#launch-template-custom-ami
+ ami_type = var.ami_id != "" ? null : var.ami_type
+ release_version = var.ami_id != "" ? null : var.use_latest_ami_release_version ? local.latest_ami_release_version : var.ami_release_version
+ version = var.ami_id != "" ? null : var.kubernetes_version
+
+ capacity_type = var.capacity_type
+ disk_size = var.use_custom_launch_template ? null : var.disk_size # if using a custom LT, set disk size on custom LT or else it will error here
+ force_update_version = var.force_update_version
+ # ML capacity block reservation requires instance type to be set on the launch template
+ instance_types = var.capacity_type == "CAPACITY_BLOCK" ? null : var.instance_types
+ labels = var.labels
+
+ dynamic "launch_template" {
+ for_each = var.use_custom_launch_template ? [1] : []
+
+ content {
+ id = local.launch_template_id
+ version = local.launch_template_version
+ }
+ }
+
+ dynamic "remote_access" {
+ for_each = var.remote_access != null ? [var.remote_access] : []
+
+ content {
+ ec2_ssh_key = remote_access.value.ec2_ssh_key
+ source_security_group_ids = remote_access.value.source_security_group_ids
+ }
+ }
+
+ dynamic "taint" {
+ for_each = var.taints != null ? var.taints : {}
+
+ content {
+ key = taint.value.key
+ value = taint.value.value
+ effect = taint.value.effect
+ }
+ }
+
+ dynamic "update_config" {
+ for_each = var.update_config != null ? [var.update_config] : []
+
+ content {
+ max_unavailable_percentage = update_config.value.max_unavailable_percentage
+ max_unavailable = update_config.value.max_unavailable
+ update_strategy = update_config.value.update_strategy
+ }
+ }
+
+ dynamic "node_repair_config" {
+ for_each = var.node_repair_config != null ? [var.node_repair_config] : []
+
+ content {
+ enabled = node_repair_config.value.enabled
+ max_parallel_nodes_repaired_count = node_repair_config.value.max_parallel_nodes_repaired_count
+ max_parallel_nodes_repaired_percentage = node_repair_config.value.max_parallel_nodes_repaired_percentage
+ max_unhealthy_node_threshold_count = node_repair_config.value.max_unhealthy_node_threshold_count
+ max_unhealthy_node_threshold_percentage = node_repair_config.value.max_unhealthy_node_threshold_percentage
+
+ dynamic "node_repair_config_overrides" {
+ for_each = node_repair_config.value.node_repair_config_overrides != null ? node_repair_config.value.node_repair_config_overrides : []
+
+ content {
+ min_repair_wait_time_mins = node_repair_config_overrides.value.min_repair_wait_time_mins
+ node_monitoring_condition = node_repair_config_overrides.value.node_monitoring_condition
+ node_unhealthy_reason = node_repair_config_overrides.value.node_unhealthy_reason
+ repair_action = node_repair_config_overrides.value.repair_action
+ }
+ }
+ }
+ }
+
+ dynamic "timeouts" {
+ for_each = var.timeouts != null ? [var.timeouts] : []
+
+ content {
+ create = var.timeouts.create
+ update = var.timeouts.update
+ delete = var.timeouts.delete
+ }
+ }
+
+ lifecycle {
+ create_before_destroy = true
+ ignore_changes = [
+ scaling_config[0].desired_size,
+ ]
+ }
+
+ tags = merge(
+ var.tags,
+ { Name = var.name }
+ )
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+locals {
+ create_iam_role = var.create && var.create_iam_role
+
+ iam_role_name = coalesce(var.iam_role_name, "${var.name}-eks-node-group")
+ iam_role_policy_prefix = "arn:${local.partition}:iam::aws:policy"
+
+ ipv4_cni_policy = { for k, v in {
+ AmazonEKS_CNI_Policy = "${local.iam_role_policy_prefix}/AmazonEKS_CNI_Policy"
+ } : k => v if var.iam_role_attach_cni_policy && var.cluster_ip_family == "ipv4" }
+ ipv6_cni_policy = { for k, v in {
+ AmazonEKS_CNI_IPv6_Policy = "arn:${local.partition}:iam::${local.account_id}:policy/AmazonEKS_CNI_IPv6_Policy"
+ } : k => v if var.iam_role_attach_cni_policy && var.cluster_ip_family == "ipv6" }
+}
+
+data "aws_iam_policy_document" "assume_role_policy" {
+ count = local.create_iam_role ? 1 : 0
+
+ statement {
+ sid = "EKSNodeAssumeRole"
+ actions = ["sts:AssumeRole"]
+
+ principals {
+ type = "Service"
+ identifiers = ["ec2.amazonaws.com"]
+ }
+ }
+}
+
+resource "aws_iam_role" "this" {
+ count = local.create_iam_role ? 1 : 0
+
+ name = var.iam_role_use_name_prefix ? null : local.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
+ path = var.iam_role_path
+ description = var.iam_role_description
+
+ assume_role_policy = data.aws_iam_policy_document.assume_role_policy[0].json
+ permissions_boundary = var.iam_role_permissions_boundary
+ force_detach_policies = true
+
+ tags = merge(var.tags, var.iam_role_tags)
+}
+
+# Policies attached ref https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_node_group
+resource "aws_iam_role_policy_attachment" "this" {
+ for_each = { for k, v in merge(
+ {
+ AmazonEKSWorkerNodePolicy = "${local.iam_role_policy_prefix}/AmazonEKSWorkerNodePolicy"
+ AmazonEC2ContainerRegistryReadOnly = "${local.iam_role_policy_prefix}/AmazonEC2ContainerRegistryReadOnly"
+ },
+ local.ipv4_cni_policy,
+ local.ipv6_cni_policy
+ ) : k => v if local.create_iam_role }
+
+ policy_arn = each.value
+ role = aws_iam_role.this[0].name
+}
+
+resource "aws_iam_role_policy_attachment" "additional" {
+ for_each = { for k, v in var.iam_role_additional_policies : k => v if local.create_iam_role }
+
+ policy_arn = each.value
+ role = aws_iam_role.this[0].name
+}
+
+################################################################################
+# IAM Role Policy
+################################################################################
+
+locals {
+ create_iam_role_policy = local.create_iam_role && var.create_iam_role_policy && var.iam_role_policy_statements != null
+}
+
+data "aws_iam_policy_document" "role" {
+ count = local.create_iam_role_policy ? 1 : 0
+
+ dynamic "statement" {
+ for_each = var.iam_role_policy_statements != null ? var.iam_role_policy_statements : []
+
+ content {
+ sid = statement.value.sid
+ actions = statement.value.actions
+ not_actions = statement.value.not_actions
+ effect = statement.value.effect
+ resources = statement.value.resources
+ not_resources = statement.value.not_resources
+
+ dynamic "principals" {
+ for_each = statement.value.principals != null ? statement.value.principals : []
+
+ content {
+ type = principals.value.type
+ identifiers = principals.value.identifiers
+ }
+ }
+
+ dynamic "not_principals" {
+ for_each = statement.value.not_principals != null ? statement.value.not_principals : []
+
+ content {
+ type = not_principals.value.type
+ identifiers = not_principals.value.identifiers
+ }
+ }
+
+ dynamic "condition" {
+ for_each = statement.value.condition != null ? statement.value.condition : []
+
+ content {
+ test = condition.value.test
+ values = condition.value.values
+ variable = condition.value.variable
+ }
+ }
+ }
+ }
+}
+
+resource "aws_iam_role_policy" "this" {
+ count = local.create_iam_role_policy ? 1 : 0
+
+ name = var.iam_role_use_name_prefix ? null : local.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
+ policy = data.aws_iam_policy_document.role[0].json
+ role = aws_iam_role.this[0].id
+}
+
+################################################################################
+# Placement Group
+################################################################################
+
+locals {
+ create_placement_group = var.create && (local.enable_efa_support || var.create_placement_group)
+}
+
+resource "aws_placement_group" "this" {
+ count = local.create_placement_group ? 1 : 0
+
+ region = var.region
+
+ name = "${var.cluster_name}-${var.name}"
+ strategy = "cluster"
+
+ tags = var.tags
+}
+
+################################################################################
+# Security Group
+################################################################################
+
+locals {
+ create_security_group = var.create && var.create_security_group && length(merge(local.security_group_ingress_rules, local.security_group_egress_rules)) > 0
+ security_group_name = coalesce(var.security_group_name, "${var.cluster_name}-${var.name}")
+
+ security_group_ingress_rules = merge({ for k, v in
+ {
+ all_self_efa = {
+ description = "Node to node EFA"
+ ip_protocol = "-1"
+ self = true
+
+ # Null out due to variable type and not using `try()` in resource
+ cidr_ipv4 = null
+ cidr_ipv6 = null
+ from_port = null
+ name = null
+ prefix_list_id = null
+ tags = {}
+ }
+ } : k => v if var.enable_efa_support
+ },
+ var.security_group_ingress_rules
+ )
+ security_group_egress_rules = merge({ for k, v in
+ {
+ all_self_efa = {
+ description = "Node to node EFA"
+ ip_protocol = "-1"
+ self = true
+
+ # Null out due to variable type and not using `try()` in resource
+ cidr_ipv4 = null
+ cidr_ipv6 = null
+ to_port = null
+ name = null
+ prefix_list_id = null
+ tags = {}
+ }
+ } : k => v if var.enable_efa_support
+ },
+ var.security_group_egress_rules
+ )
+}
+
+data "aws_subnet" "this" {
+ count = local.create_security_group ? 1 : 0
+
+ region = var.region
+
+ id = element(var.subnet_ids, 0)
+}
+
+resource "aws_security_group" "this" {
+ count = local.create_security_group ? 1 : 0
+
+ region = var.region
+
+ name = var.security_group_use_name_prefix ? null : local.security_group_name
+ name_prefix = var.security_group_use_name_prefix ? "${local.security_group_name}-" : null
+ description = var.security_group_description
+ vpc_id = data.aws_subnet.this[0].vpc_id
+
+ tags = merge(
+ var.tags,
+ { "Name" = local.security_group_name },
+ var.security_group_tags
+ )
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+resource "aws_vpc_security_group_ingress_rule" "this" {
+ for_each = { for k, v in local.security_group_ingress_rules : k => v if length(local.security_group_ingress_rules) > 0 && local.create_security_group }
+
+ region = var.region
+
+ cidr_ipv4 = each.value.cidr_ipv4
+ cidr_ipv6 = each.value.cidr_ipv6
+ description = each.value.description
+ from_port = each.value.from_port
+ ip_protocol = each.value.ip_protocol
+ prefix_list_id = each.value.prefix_list_id
+ referenced_security_group_id = each.value.self ? aws_security_group.this[0].id : each.value.referenced_security_group_id
+ security_group_id = aws_security_group.this[0].id
+ tags = merge(
+ var.tags,
+ var.security_group_tags,
+ { "Name" = coalesce(each.value.name, "${local.security_group_name}-${each.key}") },
+ each.value.tags
+ )
+ to_port = try(coalesce(each.value.to_port, each.value.from_port), null)
+}
+
+resource "aws_vpc_security_group_egress_rule" "this" {
+ for_each = { for k, v in local.security_group_egress_rules : k => v if length(local.security_group_egress_rules) > 0 && local.create_security_group }
+
+ region = var.region
+
+ cidr_ipv4 = each.value.cidr_ipv4
+ cidr_ipv6 = each.value.cidr_ipv6
+ description = each.value.description
+ from_port = try(coalesce(each.value.from_port, each.value.to_port), null)
+ ip_protocol = each.value.ip_protocol
+ prefix_list_id = each.value.prefix_list_id
+ referenced_security_group_id = each.value.self ? aws_security_group.this[0].id : each.value.referenced_security_group_id
+ security_group_id = aws_security_group.this[0].id
+ tags = merge(
+ var.tags,
+ var.security_group_tags,
+ { "Name" = coalesce(each.value.name, "${local.security_group_name}-${each.key}") },
+ each.value.tags
+ )
+ to_port = each.value.to_port
+}
diff --git a/modules/eks-managed-node-group/migrations.tf b/modules/eks-managed-node-group/migrations.tf
new file mode 100644
index 0000000000..5d51a7208a
--- /dev/null
+++ b/modules/eks-managed-node-group/migrations.tf
@@ -0,0 +1,20 @@
+################################################################################
+# Migrations: v20.7 -> v20.8
+################################################################################
+
+# Node IAM role policy attachment
+# Commercial partition only - `moved` does now allow multiple moves to same target
+moved {
+ from = aws_iam_role_policy_attachment.this["arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"]
+ to = aws_iam_role_policy_attachment.this["AmazonEKSWorkerNodePolicy"]
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.this["arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"]
+ to = aws_iam_role_policy_attachment.this["AmazonEC2ContainerRegistryReadOnly"]
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.this["arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"]
+ to = aws_iam_role_policy_attachment.this["AmazonEKS_CNI_Policy"]
+}
diff --git a/modules/eks-managed-node-group/outputs.tf b/modules/eks-managed-node-group/outputs.tf
new file mode 100644
index 0000000000..a7d6fcf62b
--- /dev/null
+++ b/modules/eks-managed-node-group/outputs.tf
@@ -0,0 +1,95 @@
+################################################################################
+# Launch template
+################################################################################
+
+output "launch_template_id" {
+ description = "The ID of the launch template"
+ value = try(aws_launch_template.this[0].id, null)
+}
+
+output "launch_template_arn" {
+ description = "The ARN of the launch template"
+ value = try(aws_launch_template.this[0].arn, null)
+}
+
+output "launch_template_latest_version" {
+ description = "The latest version of the launch template"
+ value = try(aws_launch_template.this[0].latest_version, null)
+}
+
+output "launch_template_name" {
+ description = "The name of the launch template"
+ value = try(aws_launch_template.this[0].name, null)
+}
+
+################################################################################
+# Node Group
+################################################################################
+
+output "node_group_arn" {
+ description = "Amazon Resource Name (ARN) of the EKS Node Group"
+ value = try(aws_eks_node_group.this[0].arn, null)
+}
+
+output "node_group_id" {
+ description = "EKS Cluster name and EKS Node Group name separated by a colon (`:`)"
+ value = try(aws_eks_node_group.this[0].id, null)
+}
+
+output "node_group_resources" {
+ description = "List of objects containing information about underlying resources"
+ value = try(aws_eks_node_group.this[0].resources, null)
+}
+
+output "node_group_autoscaling_group_names" {
+ description = "List of the autoscaling group names"
+ value = try(flatten(aws_eks_node_group.this[0].resources[*].autoscaling_groups[*].name), [])
+}
+
+output "node_group_status" {
+ description = "Status of the EKS Node Group"
+ value = try(aws_eks_node_group.this[0].status, null)
+}
+
+output "node_group_labels" {
+ description = "Map of labels applied to the node group"
+ value = try(aws_eks_node_group.this[0].labels, {})
+}
+
+output "node_group_taints" {
+ description = "List of objects containing information about taints applied to the node group"
+ value = try(aws_eks_node_group.this[0].taint, [])
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+output "iam_role_name" {
+ description = "The name of the IAM role"
+ value = try(aws_iam_role.this[0].name, null)
+}
+
+output "iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
+ value = try(aws_iam_role.this[0].arn, var.iam_role_arn)
+}
+
+output "iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = try(aws_iam_role.this[0].unique_id, null)
+}
+
+################################################################################
+# Security Group
+################################################################################
+
+output "security_group_arn" {
+ description = "Amazon Resource Name (ARN) of the security group"
+ value = try(aws_security_group.this[0].arn, null)
+}
+
+output "security_group_id" {
+ description = "ID of the security group"
+ value = try(aws_security_group.this[0].id, null)
+}
diff --git a/modules/eks-managed-node-group/variables.tf b/modules/eks-managed-node-group/variables.tf
new file mode 100644
index 0000000000..430d1460a7
--- /dev/null
+++ b/modules/eks-managed-node-group/variables.tf
@@ -0,0 +1,790 @@
+variable "create" {
+ description = "Determines whether to create EKS managed node group or not"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "tags" {
+ description = "A map of tags to add to all resources"
+ type = map(string)
+ default = {}
+}
+
+variable "region" {
+ description = "Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration"
+ type = string
+ default = null
+}
+
+variable "partition" {
+ description = "The AWS partition - pass through value to reduce number of GET requests from data sources"
+ type = string
+ default = ""
+}
+
+variable "account_id" {
+ description = "The AWS account ID - pass through value to reduce number of GET requests from data sources"
+ type = string
+ default = ""
+}
+
+################################################################################
+# User Data
+################################################################################
+
+variable "enable_bootstrap_user_data" {
+ description = "Determines whether the bootstrap configurations are populated within the user data template. Only valid when using a custom AMI via `ami_id`"
+ type = bool
+ default = false
+ nullable = false
+}
+
+variable "cluster_name" {
+ description = "Name of associated EKS cluster"
+ type = string
+ default = ""
+}
+
+variable "cluster_endpoint" {
+ description = "Endpoint of associated EKS cluster"
+ type = string
+ default = null
+}
+
+variable "cluster_auth_base64" {
+ description = "Base64 encoded CA of associated EKS cluster"
+ type = string
+ default = null
+}
+
+variable "cluster_service_cidr" {
+ description = "The CIDR block (IPv4 or IPv6) used by the cluster to assign Kubernetes service IP addresses. This is derived from the cluster itself"
+ type = string
+ default = null
+}
+
+variable "pre_bootstrap_user_data" {
+ description = "User data that is injected into the user data script ahead of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*`"
+ type = string
+ default = null
+}
+
+variable "post_bootstrap_user_data" {
+ description = "User data that is appended to the user data script after of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*`"
+ type = string
+ default = null
+}
+
+variable "bootstrap_extra_args" {
+ description = "Additional arguments passed to the bootstrap script. When `ami_type` = `BOTTLEROCKET_*`; these are additional [settings](https://github.com/bottlerocket-os/bottlerocket#settings) that are provided to the Bottlerocket user data"
+ type = string
+ default = null
+}
+
+variable "user_data_template_path" {
+ description = "Path to a local, custom user data template file to use when rendering user data"
+ type = string
+ default = null
+}
+
+variable "cloudinit_pre_nodeadm" {
+ description = "Array of cloud-init document parts that are created before the nodeadm document part"
+ type = list(object({
+ content = string
+ content_type = optional(string)
+ filename = optional(string)
+ merge_type = optional(string)
+ }))
+ default = null
+}
+
+variable "cloudinit_post_nodeadm" {
+ description = "Array of cloud-init document parts that are created after the nodeadm document part"
+ type = list(object({
+ content = string
+ content_type = optional(string)
+ filename = optional(string)
+ merge_type = optional(string)
+ }))
+ default = null
+}
+
+################################################################################
+# Launch template
+################################################################################
+
+variable "create_launch_template" {
+ description = "Determines whether to create a launch template or not. If set to `false`, EKS will use its own default launch template"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "use_custom_launch_template" {
+ description = "Determines whether to use a custom launch template or not. If set to `false`, EKS will use its own default launch template"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "launch_template_id" {
+ description = "The ID of an existing launch template to use. Required when `create_launch_template` = `false` and `use_custom_launch_template` = `true`"
+ type = string
+ default = ""
+}
+
+variable "launch_template_name" {
+ description = "Name of launch template to be created"
+ type = string
+ default = null
+}
+
+variable "launch_template_use_name_prefix" {
+ description = "Determines whether to use `launch_template_name` as is or create a unique name beginning with the `launch_template_name` as the prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "launch_template_description" {
+ description = "Description of the launch template"
+ type = string
+ default = null
+}
+
+variable "ebs_optimized" {
+ description = "If true, the launched EC2 instance(s) will be EBS-optimized"
+ type = bool
+ default = null
+}
+
+variable "ami_id" {
+ description = "The AMI from which to launch the instance. If not supplied, EKS will use its own default image"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "key_name" {
+ description = "The key name that should be used for the instance(s)"
+ type = string
+ default = null
+}
+
+variable "vpc_security_group_ids" {
+ description = "A list of security group IDs to associate"
+ type = list(string)
+ default = []
+ nullable = false
+}
+
+variable "cluster_primary_security_group_id" {
+ description = "The ID of the EKS cluster primary security group to associate with the instance(s). This is the security group that is automatically created by the EKS service"
+ type = string
+ default = null
+}
+
+variable "launch_template_default_version" {
+ description = "Default version of the launch template"
+ type = string
+ default = null
+}
+
+variable "update_launch_template_default_version" {
+ description = "Whether to update the launch templates default version on each update. Conflicts with `launch_template_default_version`"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "disable_api_termination" {
+ description = "If true, enables EC2 instance termination protection"
+ type = bool
+ default = null
+}
+
+variable "kernel_id" {
+ description = "The kernel ID"
+ type = string
+ default = null
+}
+
+variable "ram_disk_id" {
+ description = "The ID of the ram disk"
+ type = string
+ default = null
+}
+
+variable "block_device_mappings" {
+ description = "Specify volumes to attach to the instance besides the volumes specified by the AMI"
+ type = map(object({
+ device_name = optional(string)
+ ebs = optional(object({
+ delete_on_termination = optional(bool)
+ encrypted = optional(bool)
+ iops = optional(number)
+ kms_key_id = optional(string)
+ snapshot_id = optional(string)
+ throughput = optional(number)
+ volume_initialization_rate = optional(number)
+ volume_size = optional(number)
+ volume_type = optional(string)
+ }))
+ no_device = optional(string)
+ virtual_name = optional(string)
+ }))
+ default = null
+}
+
+variable "capacity_reservation_specification" {
+ description = "Targeting for EC2 capacity reservations"
+ type = object({
+ capacity_reservation_preference = optional(string)
+ capacity_reservation_target = optional(object({
+ capacity_reservation_id = optional(string)
+ capacity_reservation_resource_group_arn = optional(string)
+ }))
+ })
+ default = null
+}
+
+variable "cpu_options" {
+ description = "The CPU options for the instance"
+ type = object({
+ amd_sev_snp = optional(string)
+ core_count = optional(number)
+ threads_per_core = optional(number)
+ })
+ default = null
+}
+
+variable "credit_specification" {
+ description = "Customize the credit specification of the instance"
+ type = object({
+ cpu_credits = optional(string)
+ })
+ default = null
+}
+
+variable "enclave_options" {
+ description = "Enable Nitro Enclaves on launched instances"
+ type = object({
+ enabled = optional(bool)
+ })
+ default = null
+}
+
+variable "instance_market_options" {
+ description = "The market (purchasing) option for the instance"
+ type = object({
+ market_type = optional(string)
+ spot_options = optional(object({
+ block_duration_minutes = optional(number)
+ instance_interruption_behavior = optional(string)
+ max_price = optional(string)
+ spot_instance_type = optional(string)
+ valid_until = optional(string)
+ }))
+ })
+ default = null
+}
+
+variable "maintenance_options" {
+ description = "The maintenance options for the instance"
+ type = object({
+ auto_recovery = optional(string)
+ })
+ default = null
+}
+
+variable "license_specifications" {
+ description = "A list of license specifications to associate with"
+ type = list(object({
+ license_configuration_arn = string
+ }))
+ default = null
+}
+
+variable "metadata_options" {
+ description = "Customize the metadata options for the instance"
+ type = object({
+ http_endpoint = optional(string, "enabled")
+ http_protocol_ipv6 = optional(string)
+ http_put_response_hop_limit = optional(number, 1)
+ http_tokens = optional(string, "required")
+ instance_metadata_tags = optional(string)
+ })
+ default = {
+ http_endpoint = "enabled"
+ http_put_response_hop_limit = 1
+ http_tokens = "required"
+ }
+ nullable = false
+}
+
+variable "enable_monitoring" {
+ description = "Enables/disables detailed monitoring"
+ type = bool
+ default = false
+ nullable = false
+}
+
+variable "enable_efa_support" {
+ description = "Determines whether to enable Elastic Fabric Adapter (EFA) support"
+ type = bool
+ default = false
+ nullable = false
+}
+
+variable "enable_efa_only" {
+ description = "Determines whether to enable EFA (`false`, default) or EFA and EFA-only (`true`) network interfaces. Note: requires vpc-cni version `v1.18.4` or later"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "efa_indices" {
+ description = "The indices of the network interfaces that should be EFA-enabled. Only valid when `enable_efa_support` = `true`"
+ type = list(number)
+ default = [0]
+ nullable = false
+}
+
+variable "network_interfaces" {
+ description = "Customize network interfaces to be attached at instance boot time"
+ type = list(object({
+ associate_carrier_ip_address = optional(bool)
+ associate_public_ip_address = optional(bool)
+ connection_tracking_specification = optional(object({
+ tcp_established_timeout = optional(number)
+ udp_stream_timeout = optional(number)
+ udp_timeout = optional(number)
+ }))
+ delete_on_termination = optional(bool)
+ description = optional(string)
+ device_index = optional(number)
+ ena_srd_specification = optional(object({
+ ena_srd_enabled = optional(bool)
+ ena_srd_udp_specification = optional(object({
+ ena_srd_udp_enabled = optional(bool)
+ }))
+ }))
+ interface_type = optional(string)
+ ipv4_address_count = optional(number)
+ ipv4_addresses = optional(list(string))
+ ipv4_prefix_count = optional(number)
+ ipv4_prefixes = optional(list(string))
+ ipv6_address_count = optional(number)
+ ipv6_addresses = optional(list(string))
+ ipv6_prefix_count = optional(number)
+ ipv6_prefixes = optional(list(string))
+ network_card_index = optional(number)
+ network_interface_id = optional(string)
+ primary_ipv6 = optional(bool)
+ private_ip_address = optional(string)
+ security_groups = optional(list(string), [])
+ }))
+ default = []
+ nullable = false
+}
+
+variable "placement" {
+ description = "The placement of the instance"
+ type = object({
+ affinity = optional(string)
+ availability_zone = optional(string)
+ group_name = optional(string)
+ host_id = optional(string)
+ host_resource_group_arn = optional(string)
+ partition_number = optional(number)
+ spread_domain = optional(string)
+ tenancy = optional(string)
+ })
+ default = null
+}
+
+variable "create_placement_group" {
+ description = "Determines whether a placement group is created & used by the node group"
+ type = bool
+ default = false
+ nullable = false
+}
+
+variable "private_dns_name_options" {
+ description = "The options for the instance hostname. The default values are inherited from the subnet"
+ type = object({
+ enable_resource_name_dns_aaaa_record = optional(bool)
+ enable_resource_name_dns_a_record = optional(bool)
+ hostname_type = optional(string)
+ })
+ default = null
+}
+
+variable "launch_template_tags" {
+ description = "A map of additional tags to add to the tag_specifications of launch template created"
+ type = map(string)
+ default = {}
+}
+
+variable "tag_specifications" {
+ description = "The tags to apply to the resources during launch"
+ type = list(string)
+ default = ["instance", "volume", "network-interface"]
+ nullable = false
+}
+
+################################################################################
+# EKS Managed Node Group
+################################################################################
+
+variable "subnet_ids" {
+ description = "Identifiers of EC2 Subnets to associate with the EKS Node Group. These subnets must have the following resource tag: `kubernetes.io/cluster/CLUSTER_NAME`"
+ type = list(string)
+ default = null
+}
+
+variable "min_size" {
+ description = "Minimum number of instances/nodes"
+ type = number
+ default = 1
+ nullable = false
+}
+
+variable "max_size" {
+ description = "Maximum number of instances/nodes"
+ type = number
+ default = 3
+ nullable = false
+}
+
+variable "desired_size" {
+ description = "Desired number of instances/nodes"
+ type = number
+ default = 1
+ nullable = false
+}
+
+variable "name" {
+ description = "Name of the EKS managed node group"
+ type = string
+ default = ""
+}
+
+variable "use_name_prefix" {
+ description = "Determines whether to use `name` as is or create a unique name beginning with the `name` as the prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "ami_type" {
+ description = "Type of Amazon Machine Image (AMI) associated with the EKS Node Group. See the [AWS documentation](https://docs.aws.amazon.com/eks/latest/APIReference/API_Nodegroup.html#AmazonEKS-Type-Nodegroup-amiType) for valid values"
+ type = string
+ default = "AL2023_x86_64_STANDARD"
+ nullable = false
+}
+
+variable "ami_release_version" {
+ description = "The AMI version. Defaults to latest AMI release version for the given Kubernetes version and AMI type"
+ type = string
+ default = null
+}
+
+variable "use_latest_ami_release_version" {
+ description = "Determines whether to use the latest AMI release version for the given `ami_type` (except for `CUSTOM`). Note: `ami_type` and `kubernetes_version` must be supplied in order to enable this feature"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "capacity_type" {
+ description = "Type of capacity associated with the EKS Node Group. Valid values: `ON_DEMAND`, `SPOT`"
+ type = string
+ default = "ON_DEMAND"
+ nullable = false
+}
+
+variable "disk_size" {
+ description = "Disk size in GiB for nodes. Defaults to `20`. Only valid when `use_custom_launch_template` = `false`"
+ type = number
+ default = null
+}
+
+variable "force_update_version" {
+ description = "Force version update if existing pods are unable to be drained due to a pod disruption budget issue"
+ type = bool
+ default = null
+}
+
+variable "instance_types" {
+ description = "Set of instance types associated with the EKS Node Group. Defaults to `[\"t3.medium\"]`"
+ type = list(string)
+ default = null
+}
+
+variable "labels" {
+ description = "Key-value map of Kubernetes labels. Only labels that are applied with the EKS API are managed by this argument. Other Kubernetes labels applied to the EKS Node Group will not be managed"
+ type = map(string)
+ default = null
+}
+
+variable "kubernetes_version" {
+ description = "Kubernetes version. Defaults to EKS Cluster Kubernetes version"
+ type = string
+ default = null
+}
+
+variable "launch_template_version" {
+ description = "Launch template version number. The default is `$Default`"
+ type = string
+ default = null
+}
+
+variable "remote_access" {
+ description = "Configuration block with remote access settings. Only valid when `use_custom_launch_template` = `false`"
+ type = object({
+ ec2_ssh_key = optional(string)
+ source_security_group_ids = optional(list(string))
+ })
+ default = null
+}
+
+variable "taints" {
+ description = "The Kubernetes taints to be applied to the nodes in the node group. Maximum of 50 taints per node group"
+ type = map(object({
+ key = string
+ value = optional(string)
+ effect = string
+ }))
+ default = null
+}
+
+variable "update_config" {
+ description = "Configuration block of settings for max unavailable resources during node group updates"
+ type = object({
+ max_unavailable = optional(number)
+ max_unavailable_percentage = optional(number)
+ update_strategy = optional(string)
+ })
+ default = {
+ max_unavailable_percentage = 33
+ }
+ nullable = false
+}
+
+variable "node_repair_config" {
+ description = "The node auto repair configuration for the node group"
+ type = object({
+ enabled = optional(bool, true)
+ max_parallel_nodes_repaired_count = optional(number)
+ max_parallel_nodes_repaired_percentage = optional(number)
+ max_unhealthy_node_threshold_count = optional(number)
+ max_unhealthy_node_threshold_percentage = optional(number)
+ node_repair_config_overrides = optional(list(object({
+ min_repair_wait_time_mins = number
+ node_monitoring_condition = string
+ node_unhealthy_reason = string
+ repair_action = string
+ })))
+ })
+ default = null
+}
+
+variable "timeouts" {
+ description = "Create, update, and delete timeout configurations for the node group"
+ type = object({
+ create = optional(string)
+ update = optional(string)
+ delete = optional(string)
+ })
+ default = null
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+variable "create_iam_role" {
+ description = "Determines whether an IAM role is created or to use an existing IAM role"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "cluster_ip_family" {
+ description = "The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6`"
+ type = string
+ default = "ipv4"
+ nullable = false
+}
+
+variable "iam_role_arn" {
+ description = "Existing IAM role ARN for the node group. Required if `create_iam_role` is set to `false`"
+ type = string
+ default = null
+}
+
+variable "iam_role_name" {
+ description = "Name to use on IAM role created"
+ type = string
+ default = null
+}
+
+variable "iam_role_use_name_prefix" {
+ description = "Determines whether the IAM role name (`iam_role_name`) is used as a prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_role_path" {
+ description = "IAM role path"
+ type = string
+ default = null
+}
+
+variable "iam_role_description" {
+ description = "Description of the role"
+ type = string
+ default = "EKS managed node group IAM role"
+ nullable = false
+}
+
+variable "iam_role_permissions_boundary" {
+ description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
+ type = string
+ default = null
+}
+
+variable "iam_role_attach_cni_policy" {
+ description = "Whether to attach the `AmazonEKS_CNI_Policy`/`AmazonEKS_CNI_IPv6_Policy` IAM policy to the IAM IAM role. WARNING: If set `false` the permissions must be assigned to the `aws-node` DaemonSet pods via another method or nodes will not be able to join the cluster"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_role_additional_policies" {
+ description = "Additional policies to be added to the IAM role"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+variable "iam_role_tags" {
+ description = "A map of additional tags to add to the IAM role created"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+################################################################################
+# IAM Role Policy
+################################################################################
+
+variable "create_iam_role_policy" {
+ description = "Determines whether an IAM role policy is created or not"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_role_policy_statements" {
+ description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed"
+ type = list(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string)
+ resources = optional(list(string))
+ not_resources = optional(list(string))
+ principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ not_principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ condition = optional(list(object({
+ test = string
+ values = list(string)
+ variable = string
+ })))
+ }))
+ default = null
+}
+
+################################################################################
+# Security Group
+################################################################################
+
+variable "create_security_group" {
+ description = "Determines if a security group is created"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "security_group_name" {
+ description = "Name to use on security group created"
+ type = string
+ default = null
+}
+
+variable "security_group_use_name_prefix" {
+ description = "Determines whether the security group name (`security_group_name`) is used as a prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "security_group_description" {
+ description = "Description of the security group created"
+ type = string
+ default = null
+}
+
+variable "security_group_ingress_rules" {
+ description = "Security group ingress rules to add to the security group created"
+ type = map(object({
+ name = optional(string)
+
+ cidr_ipv4 = optional(string)
+ cidr_ipv6 = optional(string)
+ description = optional(string)
+ from_port = optional(string)
+ ip_protocol = optional(string, "tcp")
+ prefix_list_id = optional(string)
+ referenced_security_group_id = optional(string)
+ self = optional(bool, false)
+ tags = optional(map(string), {})
+ to_port = optional(string)
+ }))
+ default = {}
+}
+
+variable "security_group_egress_rules" {
+ description = "Security group egress rules to add to the security group created"
+ type = map(object({
+ name = optional(string)
+
+ cidr_ipv4 = optional(string)
+ cidr_ipv6 = optional(string)
+ description = optional(string)
+ from_port = optional(string)
+ ip_protocol = optional(string, "tcp")
+ prefix_list_id = optional(string)
+ referenced_security_group_id = optional(string)
+ self = optional(bool, false)
+ tags = optional(map(string), {})
+ to_port = optional(string)
+ }))
+ default = {}
+}
+
+variable "security_group_tags" {
+ description = "A map of additional tags to add to the security group created"
+ type = map(string)
+ default = {}
+}
diff --git a/modules/eks-managed-node-group/versions.tf b/modules/eks-managed-node-group/versions.tf
new file mode 100644
index 0000000000..af99edcc27
--- /dev/null
+++ b/modules/eks-managed-node-group/versions.tf
@@ -0,0 +1,16 @@
+terraform {
+ required_version = ">= 1.5.7"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 6.28"
+ }
+ }
+
+ provider_meta "aws" {
+ user_agent = [
+ "github.com/terraform-aws-modules/terraform-aws-eks"
+ ]
+ }
+}
diff --git a/modules/fargate-profile/README.md b/modules/fargate-profile/README.md
new file mode 100644
index 0000000000..4af5fd327e
--- /dev/null
+++ b/modules/fargate-profile/README.md
@@ -0,0 +1,102 @@
+# EKS Fargate Profile Module
+
+Configuration in this directory creates a Fargate EKS Profile
+
+## Usage
+
+```hcl
+module "fargate_profile" {
+ source = "terraform-aws-modules/eks/aws//modules/fargate-profile"
+
+ name = "separate-fargate-profile"
+ cluster_name = "my-cluster"
+
+ subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"]
+ selectors = [{
+ namespace = "kube-system"
+ }]
+
+ tags = {
+ Environment = "dev"
+ Terraform = "true"
+ }
+}
+```
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.5.7 |
+| [aws](#requirement\_aws) | >= 6.28 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 6.28 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_eks_fargate_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_fargate_profile) | resource |
+| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
+| [aws_iam_role_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
+| [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
+| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [account\_id](#input\_account\_id) | The AWS account ID - pass through value to reduce number of GET requests from data sources | `string` | `""` | no |
+| [cluster\_ip\_family](#input\_cluster\_ip\_family) | The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6` | `string` | `"ipv4"` | no |
+| [cluster\_name](#input\_cluster\_name) | Name of the EKS cluster | `string` | `""` | no |
+| [create](#input\_create) | Determines whether to create Fargate profile or not | `bool` | `true` | no |
+| [create\_iam\_role](#input\_create\_iam\_role) | Determines whether an IAM role is created or to use an existing IAM role | `bool` | `true` | no |
+| [create\_iam\_role\_policy](#input\_create\_iam\_role\_policy) | Determines whether an IAM role policy is created or not | `bool` | `true` | no |
+| [iam\_role\_additional\_policies](#input\_iam\_role\_additional\_policies) | Additional policies to be added to the IAM role | `map(string)` | `{}` | no |
+| [iam\_role\_arn](#input\_iam\_role\_arn) | Existing IAM role ARN for the Fargate profile. Required if `create_iam_role` is set to `false` | `string` | `null` | no |
+| [iam\_role\_attach\_cni\_policy](#input\_iam\_role\_attach\_cni\_policy) | Whether to attach the `AmazonEKS_CNI_Policy`/`AmazonEKS_CNI_IPv6_Policy` IAM policy to the IAM IAM role. WARNING: If set `false` the permissions must be assigned to the `aws-node` DaemonSet pods via another method or nodes will not be able to join the cluster | `bool` | `true` | no |
+| [iam\_role\_description](#input\_iam\_role\_description) | Description of the role | `string` | `"Fargate profile IAM role"` | no |
+| [iam\_role\_name](#input\_iam\_role\_name) | Name to use on IAM role created | `string` | `""` | no |
+| [iam\_role\_path](#input\_iam\_role\_path) | IAM role path | `string` | `null` | no |
+| [iam\_role\_permissions\_boundary](#input\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
+| [iam\_role\_policy\_statements](#input\_iam\_role\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed | list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})) | `null` | no |
+| [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
+| [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no |
+| [name](#input\_name) | Name of the EKS Fargate Profile | `string` | `""` | no |
+| [partition](#input\_partition) | The AWS partition - pass through value to reduce number of GET requests from data sources | `string` | `""` | no |
+| [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no |
+| [selectors](#input\_selectors) | Configuration block(s) for selecting Kubernetes Pods to execute with this Fargate Profile | list(object({
labels = optional(map(string))
namespace = string
})) | `null` | no |
+| [subnet\_ids](#input\_subnet\_ids) | A list of subnet IDs for the EKS Fargate Profile | `list(string)` | `[]` | no |
+| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
+| [timeouts](#input\_timeouts) | Create and delete timeout configurations for the Fargate Profile | object({
create = optional(string)
delete = optional(string)
}) | `null` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [fargate\_profile\_arn](#output\_fargate\_profile\_arn) | Amazon Resource Name (ARN) of the EKS Fargate Profile |
+| [fargate\_profile\_id](#output\_fargate\_profile\_id) | EKS Cluster name and EKS Fargate Profile name separated by a colon (`:`) |
+| [fargate\_profile\_pod\_execution\_role\_arn](#output\_fargate\_profile\_pod\_execution\_role\_arn) | Amazon Resource Name (ARN) of the EKS Fargate Profile Pod execution role ARN |
+| [fargate\_profile\_status](#output\_fargate\_profile\_status) | Status of the EKS Fargate Profile |
+| [iam\_role\_arn](#output\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [iam\_role\_name](#output\_iam\_role\_name) | The name of the IAM role |
+| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
+
+
+## License
+
+Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/LICENSE) for full details.
diff --git a/modules/fargate-profile/main.tf b/modules/fargate-profile/main.tf
new file mode 100644
index 0000000000..78c94357f4
--- /dev/null
+++ b/modules/fargate-profile/main.tf
@@ -0,0 +1,190 @@
+data "aws_region" "current" {
+ count = var.create ? 1 : 0
+
+ region = var.region
+}
+data "aws_partition" "current" {
+ count = var.create && var.partition == "" ? 1 : 0
+}
+data "aws_caller_identity" "current" {
+ count = var.create && var.account_id == "" ? 1 : 0
+}
+
+locals {
+ account_id = try(data.aws_caller_identity.current[0].account_id, var.account_id)
+ partition = try(data.aws_partition.current[0].partition, var.partition)
+ region = try(data.aws_region.current[0].region, "")
+}
+
+locals {
+ create_iam_role = var.create && var.create_iam_role
+
+ iam_role_name = coalesce(var.iam_role_name, var.name, "fargate-profile")
+ iam_role_policy_prefix = "arn:${local.partition}:iam::aws:policy"
+
+ ipv4_cni_policy = { for k, v in {
+ AmazonEKS_CNI_Policy = "${local.iam_role_policy_prefix}/AmazonEKS_CNI_Policy"
+ } : k => v if var.iam_role_attach_cni_policy && var.cluster_ip_family == "ipv4" }
+ ipv6_cni_policy = { for k, v in {
+ AmazonEKS_CNI_IPv6_Policy = "arn:${local.partition}:iam::${local.account_id}:policy/AmazonEKS_CNI_IPv6_Policy"
+ } : k => v if var.iam_role_attach_cni_policy && var.cluster_ip_family == "ipv6" }
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+data "aws_iam_policy_document" "assume_role_policy" {
+ count = local.create_iam_role ? 1 : 0
+
+ statement {
+ effect = "Allow"
+ actions = ["sts:AssumeRole"]
+
+ principals {
+ type = "Service"
+ identifiers = ["eks-fargate-pods.amazonaws.com"]
+ }
+
+ condition {
+ test = "ArnLike"
+ variable = "aws:SourceArn"
+
+ values = [
+ "arn:${local.partition}:eks:${local.region}:${local.account_id}:fargateprofile/${var.cluster_name}/*",
+ ]
+ }
+ }
+}
+
+resource "aws_iam_role" "this" {
+ count = local.create_iam_role ? 1 : 0
+
+ name = var.iam_role_use_name_prefix ? null : local.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
+ path = var.iam_role_path
+ description = var.iam_role_description
+
+ assume_role_policy = data.aws_iam_policy_document.assume_role_policy[0].json
+ permissions_boundary = var.iam_role_permissions_boundary
+ force_detach_policies = true
+
+ tags = merge(var.tags, var.iam_role_tags)
+}
+
+resource "aws_iam_role_policy_attachment" "this" {
+ for_each = { for k, v in merge(
+ {
+ AmazonEKSFargatePodExecutionRolePolicy = "${local.iam_role_policy_prefix}/AmazonEKSFargatePodExecutionRolePolicy"
+ },
+ local.ipv4_cni_policy,
+ local.ipv6_cni_policy
+ ) : k => v if local.create_iam_role }
+
+ policy_arn = each.value
+ role = aws_iam_role.this[0].name
+}
+
+resource "aws_iam_role_policy_attachment" "additional" {
+ for_each = { for k, v in var.iam_role_additional_policies : k => v if local.create_iam_role }
+
+ policy_arn = each.value
+ role = aws_iam_role.this[0].name
+}
+
+################################################################################
+# IAM Role Policy
+################################################################################
+
+locals {
+ create_iam_role_policy = local.create_iam_role && var.create_iam_role_policy && var.iam_role_policy_statements != null
+}
+
+data "aws_iam_policy_document" "role" {
+ count = local.create_iam_role_policy ? 1 : 0
+
+ dynamic "statement" {
+ for_each = var.iam_role_policy_statements != null ? var.iam_role_policy_statements : []
+
+ content {
+ sid = statement.value.sid
+ actions = statement.value.actions
+ not_actions = statement.value.not_actions
+ effect = statement.value.effect
+ resources = statement.value.resources
+ not_resources = statement.value.not_resources
+
+ dynamic "principals" {
+ for_each = statement.value.principals != null ? statement.value.principals : []
+
+ content {
+ type = principals.value.type
+ identifiers = principals.value.identifiers
+ }
+ }
+
+ dynamic "not_principals" {
+ for_each = statement.value.not_principals != null ? statement.value.not_principals : []
+
+ content {
+ type = not_principals.value.type
+ identifiers = not_principals.value.identifiers
+ }
+ }
+
+ dynamic "condition" {
+ for_each = statement.value.condition != null ? statement.value.condition : []
+
+ content {
+ test = condition.value.test
+ values = condition.value.values
+ variable = condition.value.variable
+ }
+ }
+ }
+ }
+}
+
+resource "aws_iam_role_policy" "this" {
+ count = local.create_iam_role_policy ? 1 : 0
+
+ name = var.iam_role_use_name_prefix ? null : local.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
+ policy = data.aws_iam_policy_document.role[0].json
+ role = aws_iam_role.this[0].id
+}
+
+################################################################################
+# Fargate Profile
+################################################################################
+
+resource "aws_eks_fargate_profile" "this" {
+ count = var.create ? 1 : 0
+
+ region = var.region
+
+ cluster_name = var.cluster_name
+ fargate_profile_name = var.name
+ pod_execution_role_arn = var.create_iam_role ? aws_iam_role.this[0].arn : var.iam_role_arn
+ subnet_ids = var.subnet_ids
+
+ dynamic "selector" {
+ for_each = var.selectors != null ? var.selectors : []
+
+ content {
+ namespace = selector.value.namespace
+ labels = selector.value.labels
+ }
+ }
+
+ dynamic "timeouts" {
+ for_each = var.timeouts != null ? [var.timeouts] : []
+
+ content {
+ create = var.timeouts.create
+ delete = var.timeouts.delete
+ }
+ }
+
+ tags = var.tags
+}
diff --git a/modules/fargate-profile/migrations.tf b/modules/fargate-profile/migrations.tf
new file mode 100644
index 0000000000..02494f6893
--- /dev/null
+++ b/modules/fargate-profile/migrations.tf
@@ -0,0 +1,15 @@
+################################################################################
+# Migrations: v20.8 -> v20.9
+################################################################################
+
+# Node IAM role policy attachment
+# Commercial partition only - `moved` does now allow multiple moves to same target
+moved {
+ from = aws_iam_role_policy_attachment.this["arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy"]
+ to = aws_iam_role_policy_attachment.this["AmazonEKSFargatePodExecutionRolePolicy"]
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.this["arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"]
+ to = aws_iam_role_policy_attachment.this["AmazonEKS_CNI_Policy"]
+}
diff --git a/modules/fargate-profile/outputs.tf b/modules/fargate-profile/outputs.tf
new file mode 100644
index 0000000000..96763bfb1f
--- /dev/null
+++ b/modules/fargate-profile/outputs.tf
@@ -0,0 +1,42 @@
+################################################################################
+# IAM Role
+################################################################################
+
+output "iam_role_name" {
+ description = "The name of the IAM role"
+ value = try(aws_iam_role.this[0].name, null)
+}
+
+output "iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
+ value = try(aws_iam_role.this[0].arn, var.iam_role_arn)
+}
+
+output "iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = try(aws_iam_role.this[0].unique_id, null)
+}
+
+################################################################################
+# Fargate Profile
+################################################################################
+
+output "fargate_profile_arn" {
+ description = "Amazon Resource Name (ARN) of the EKS Fargate Profile"
+ value = try(aws_eks_fargate_profile.this[0].arn, null)
+}
+
+output "fargate_profile_id" {
+ description = "EKS Cluster name and EKS Fargate Profile name separated by a colon (`:`)"
+ value = try(aws_eks_fargate_profile.this[0].id, null)
+}
+
+output "fargate_profile_status" {
+ description = "Status of the EKS Fargate Profile"
+ value = try(aws_eks_fargate_profile.this[0].status, null)
+}
+
+output "fargate_profile_pod_execution_role_arn" {
+ description = "Amazon Resource Name (ARN) of the EKS Fargate Profile Pod execution role ARN"
+ value = try(aws_eks_fargate_profile.this[0].pod_execution_role_arn, null)
+}
diff --git a/modules/fargate-profile/variables.tf b/modules/fargate-profile/variables.tf
new file mode 100644
index 0000000000..5d87e56447
--- /dev/null
+++ b/modules/fargate-profile/variables.tf
@@ -0,0 +1,186 @@
+variable "create" {
+ description = "Determines whether to create Fargate profile or not"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "tags" {
+ description = "A map of tags to add to all resources"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+variable "region" {
+ description = "Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration"
+ type = string
+ default = null
+}
+
+variable "partition" {
+ description = "The AWS partition - pass through value to reduce number of GET requests from data sources"
+ type = string
+ default = ""
+}
+
+variable "account_id" {
+ description = "The AWS account ID - pass through value to reduce number of GET requests from data sources"
+ type = string
+ default = ""
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+variable "create_iam_role" {
+ description = "Determines whether an IAM role is created or to use an existing IAM role"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "cluster_ip_family" {
+ description = "The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6`"
+ type = string
+ default = "ipv4"
+}
+
+variable "iam_role_arn" {
+ description = "Existing IAM role ARN for the Fargate profile. Required if `create_iam_role` is set to `false`"
+ type = string
+ default = null
+}
+
+variable "iam_role_name" {
+ description = "Name to use on IAM role created"
+ type = string
+ default = ""
+}
+
+variable "iam_role_use_name_prefix" {
+ description = "Determines whether the IAM role name (`iam_role_name`) is used as a prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_role_path" {
+ description = "IAM role path"
+ type = string
+ default = null
+}
+
+variable "iam_role_description" {
+ description = "Description of the role"
+ type = string
+ default = "Fargate profile IAM role"
+ nullable = false
+}
+
+variable "iam_role_permissions_boundary" {
+ description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
+ type = string
+ default = null
+}
+
+variable "iam_role_attach_cni_policy" {
+ description = "Whether to attach the `AmazonEKS_CNI_Policy`/`AmazonEKS_CNI_IPv6_Policy` IAM policy to the IAM IAM role. WARNING: If set `false` the permissions must be assigned to the `aws-node` DaemonSet pods via another method or nodes will not be able to join the cluster"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_role_additional_policies" {
+ description = "Additional policies to be added to the IAM role"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+variable "iam_role_tags" {
+ description = "A map of additional tags to add to the IAM role created"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+################################################################################
+# IAM Role Policy
+################################################################################
+
+variable "create_iam_role_policy" {
+ description = "Determines whether an IAM role policy is created or not"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_role_policy_statements" {
+ description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed"
+ type = list(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string)
+ resources = optional(list(string))
+ not_resources = optional(list(string))
+ principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ not_principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ condition = optional(list(object({
+ test = string
+ values = list(string)
+ variable = string
+ })))
+ }))
+ default = null
+}
+
+################################################################################
+# Fargate Profile
+################################################################################
+
+variable "cluster_name" {
+ description = "Name of the EKS cluster"
+ type = string
+ default = ""
+}
+
+variable "name" {
+ description = "Name of the EKS Fargate Profile"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "subnet_ids" {
+ description = "A list of subnet IDs for the EKS Fargate Profile"
+ type = list(string)
+ default = []
+ nullable = false
+}
+
+variable "selectors" {
+ description = "Configuration block(s) for selecting Kubernetes Pods to execute with this Fargate Profile"
+ type = list(object({
+ labels = optional(map(string))
+ namespace = string
+ }))
+ default = null
+}
+
+variable "timeouts" {
+ description = "Create and delete timeout configurations for the Fargate Profile"
+ type = object({
+ create = optional(string)
+ delete = optional(string)
+ })
+ default = null
+}
diff --git a/modules/fargate-profile/versions.tf b/modules/fargate-profile/versions.tf
new file mode 100644
index 0000000000..af99edcc27
--- /dev/null
+++ b/modules/fargate-profile/versions.tf
@@ -0,0 +1,16 @@
+terraform {
+ required_version = ">= 1.5.7"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 6.28"
+ }
+ }
+
+ provider_meta "aws" {
+ user_agent = [
+ "github.com/terraform-aws-modules/terraform-aws-eks"
+ ]
+ }
+}
diff --git a/modules/hybrid-node-role/README.md b/modules/hybrid-node-role/README.md
new file mode 100644
index 0000000000..874021b26f
--- /dev/null
+++ b/modules/hybrid-node-role/README.md
@@ -0,0 +1,163 @@
+# EKS Hybrid Node Role Module
+
+Terraform module which creates IAM role and policy resources for Amazon EKS Hybrid Node(s).
+
+## Usage
+
+EKS Hybrid nodes use the AWS IAM Authenticator and temporary IAM credentials provisioned by AWS SSM or AWS IAM Roles Anywhere to authenticate with the EKS cluster. This module supports both SSM and IAM Roles Anywhere based IAM permissions.
+
+### SSM
+
+```hcl
+module "eks" {
+ source = "terraform-aws-modules/eks/aws"
+
+ ...
+ access_entries = {
+ hybrid-node-role = {
+ principal_arn = module.eks_hybrid_node_role.arn
+ type = "HYBRID_LINUX"
+ }
+ }
+}
+
+module "eks_hybrid_node_role" {
+ source = "terraform-aws-modules/eks/aws//modules/hybrid-node-role"
+
+ name = "hybrid"
+
+ tags = {
+ Environment = "dev"
+ Terraform = "true"
+ }
+}
+```
+
+### IAM Roles Anywhere
+
+```hcl
+module "eks" {
+ source = "terraform-aws-modules/eks/aws"
+
+ ...
+ access_entries = {
+ hybrid-node-role = {
+ principal_arn = module.eks_hybrid_node_role.arn
+ type = "HYBRID_LINUX"
+ }
+ }
+}
+
+module "eks_hybrid_node_role" {
+ source = "terraform-aws-modules/eks/aws//modules/hybrid-node-role"
+
+ name = "hybrid-ira"
+
+ enable_ira = true
+
+ ira_trust_anchor_source_type = "CERTIFICATE_BUNDLE"
+ ira_trust_anchor_x509_certificate_data = <<-EOT
+ MIIFMzCCAxugAwIBAgIRAMnVXU7ncv/+Cl16eJbZ9hswDQYJKoZIhvcNAQELBQAw
+ ...
+ MGx/BMRkrNUVcg3xA0lhECo/olodCkmZo5/mjybbjFQwJzDSKFoW
+ EOT
+
+ tags = {
+ Environment = "dev"
+ Terraform = "true"
+ }
+}
+```
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.5.7 |
+| [aws](#requirement\_aws) | >= 6.28 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 6.28 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_iam_policy.intermediate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
+| [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
+| [aws_iam_role.intermediate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy_attachment.intermediate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_rolesanywhere_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rolesanywhere_profile) | resource |
+| [aws_rolesanywhere_trust_anchor.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rolesanywhere_trust_anchor) | resource |
+| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.intermediate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.intermediate_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [cluster\_arns](#input\_cluster\_arns) | List of EKS cluster ARNs to allow the node to describe | `list(string)` | [| no | +| [create](#input\_create) | Controls if resources should be created (affects nearly all resources) | `bool` | `true` | no | +| [description](#input\_description) | IAM role description | `string` | `"EKS Hybrid Node IAM role"` | no | +| [enable\_ira](#input\_enable\_ira) | Enables IAM Roles Anywhere based IAM permissions on the node | `bool` | `false` | no | +| [enable\_pod\_identity](#input\_enable\_pod\_identity) | Enables EKS Pod Identity based IAM permissions on the node | `bool` | `true` | no | +| [intermediate\_policy\_name](#input\_intermediate\_policy\_name) | Name of the IAM policy | `string` | `null` | no | +| [intermediate\_policy\_statements](#input\_intermediate\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed |
"*"
]
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})) | `null` | no |
+| [intermediate\_policy\_use\_name\_prefix](#input\_intermediate\_policy\_use\_name\_prefix) | Determines whether the name of the IAM policy (`intermediate_policy_name`) is used as a prefix | `bool` | `true` | no |
+| [intermediate\_role\_description](#input\_intermediate\_role\_description) | IAM role description | `string` | `"EKS Hybrid Node IAM Roles Anywhere intermediate IAM role"` | no |
+| [intermediate\_role\_name](#input\_intermediate\_role\_name) | Name of the IAM role | `string` | `null` | no |
+| [intermediate\_role\_path](#input\_intermediate\_role\_path) | Path of the IAM role | `string` | `"/"` | no |
+| [intermediate\_role\_policies](#input\_intermediate\_role\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no |
+| [intermediate\_role\_use\_name\_prefix](#input\_intermediate\_role\_use\_name\_prefix) | Determines whether the name of the IAM role (`intermediate_role_name`) is used as a prefix | `bool` | `true` | no |
+| [ira\_profile\_duration\_seconds](#input\_ira\_profile\_duration\_seconds) | The number of seconds the vended session credentials are valid for. Defaults to `3600` | `number` | `null` | no |
+| [ira\_profile\_managed\_policy\_arns](#input\_ira\_profile\_managed\_policy\_arns) | A list of managed policy ARNs that apply to the vended session credentials | `list(string)` | `[]` | no |
+| [ira\_profile\_name](#input\_ira\_profile\_name) | Name of the Roles Anywhere profile | `string` | `null` | no |
+| [ira\_profile\_require\_instance\_properties](#input\_ira\_profile\_require\_instance\_properties) | Specifies whether instance properties are required in [CreateSession](https://docs.aws.amazon.com/rolesanywhere/latest/APIReference/API_CreateSession.html) requests with this profile | `bool` | `null` | no |
+| [ira\_profile\_session\_policy](#input\_ira\_profile\_session\_policy) | A session policy that applies to the trust boundary of the vended session credentials | `string` | `null` | no |
+| [ira\_trust\_anchor\_acm\_pca\_arn](#input\_ira\_trust\_anchor\_acm\_pca\_arn) | The ARN of the ACM PCA that issued the trust anchor certificate | `string` | `null` | no |
+| [ira\_trust\_anchor\_name](#input\_ira\_trust\_anchor\_name) | Name of the Roles Anywhere trust anchor | `string` | `null` | no |
+| [ira\_trust\_anchor\_notification\_settings](#input\_ira\_trust\_anchor\_notification\_settings) | Notification settings for the trust anchor | list(object({
channel = optional(string)
enabled = optional(bool)
event = optional(string)
threshold = optional(number)
})) | `null` | no |
+| [ira\_trust\_anchor\_source\_type](#input\_ira\_trust\_anchor\_source\_type) | The source type of the trust anchor | `string` | `null` | no |
+| [ira\_trust\_anchor\_x509\_certificate\_data](#input\_ira\_trust\_anchor\_x509\_certificate\_data) | The X.509 certificate data of the trust anchor | `string` | `null` | no |
+| [max\_session\_duration](#input\_max\_session\_duration) | Maximum API session duration in seconds between 3600 and 43200 | `number` | `null` | no |
+| [name](#input\_name) | Name of the IAM role | `string` | `"EKSHybridNode"` | no |
+| [path](#input\_path) | Path of the IAM role | `string` | `"/"` | no |
+| [permissions\_boundary\_arn](#input\_permissions\_boundary\_arn) | Permissions boundary ARN to use for the IAM role | `string` | `null` | no |
+| [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no |
+| [policy\_description](#input\_policy\_description) | IAM policy description | `string` | `"EKS Hybrid Node IAM role policy"` | no |
+| [policy\_name](#input\_policy\_name) | Name of the IAM policy | `string` | `"EKSHybridNode"` | no |
+| [policy\_path](#input\_policy\_path) | Path of the IAM policy | `string` | `"/"` | no |
+| [policy\_statements](#input\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed | list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})) | `null` | no |
+| [policy\_use\_name\_prefix](#input\_policy\_use\_name\_prefix) | Determines whether the name of the IAM policy (`policy_name`) is used as a prefix | `bool` | `true` | no |
+| [tags](#input\_tags) | A map of additional tags to add the the IAM role | `map(string)` | `{}` | no |
+| [trust\_anchor\_arns](#input\_trust\_anchor\_arns) | List of IAM Roles Anywhere trust anchor ARNs. Required if `enable_ira` is set to `true` | `list(string)` | `[]` | no |
+| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the name of the IAM role (`name`) is used as a prefix | `bool` | `true` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [arn](#output\_arn) | The Amazon Resource Name (ARN) specifying the node IAM role |
+| [intermediate\_role\_arn](#output\_intermediate\_role\_arn) | The Amazon Resource Name (ARN) specifying the node IAM role |
+| [intermediate\_role\_name](#output\_intermediate\_role\_name) | The name of the node IAM role |
+| [intermediate\_role\_unique\_id](#output\_intermediate\_role\_unique\_id) | Stable and unique string identifying the node IAM role |
+| [name](#output\_name) | The name of the node IAM role |
+| [unique\_id](#output\_unique\_id) | Stable and unique string identifying the node IAM role |
+
+
+## License
+
+Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/LICENSE) for full details.
diff --git a/modules/hybrid-node-role/main.tf b/modules/hybrid-node-role/main.tf
new file mode 100644
index 0000000000..71e89aa58d
--- /dev/null
+++ b/modules/hybrid-node-role/main.tf
@@ -0,0 +1,396 @@
+data "aws_partition" "current" {
+ count = var.create ? 1 : 0
+}
+
+locals {
+ partition = try(data.aws_partition.current[0].partition, "")
+}
+
+################################################################################
+# Node IAM Role
+################################################################################
+
+data "aws_iam_policy_document" "assume_role" {
+ count = var.create ? 1 : 0
+
+ # SSM
+ dynamic "statement" {
+ for_each = var.enable_ira ? [] : [1]
+
+ content {
+ actions = [
+ "sts:AssumeRole",
+ "sts:TagSession",
+ ]
+
+ principals {
+ type = "Service"
+ identifiers = ["ssm.amazonaws.com"]
+ }
+ }
+ }
+
+ # IAM Roles Anywhere
+ dynamic "statement" {
+ for_each = var.enable_ira ? [1] : []
+
+ content {
+ actions = [
+ "sts:TagSession",
+ "sts:SetSourceIdentity",
+ ]
+
+ principals {
+ type = "AWS"
+ identifiers = [aws_iam_role.intermediate[0].arn]
+ }
+ }
+ }
+
+ dynamic "statement" {
+ for_each = var.enable_ira ? [1] : []
+
+ content {
+ actions = [
+ "sts:AssumeRole",
+ "sts:TagSession",
+ ]
+
+ principals {
+ type = "AWS"
+ identifiers = [aws_iam_role.intermediate[0].arn]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "sts:RoleSessionName"
+ values = ["$${aws:PrincipalTag/x509Subject/CN}"]
+ }
+ }
+ }
+}
+
+resource "aws_iam_role" "this" {
+ count = var.create ? 1 : 0
+
+ name = var.use_name_prefix ? null : var.name
+ name_prefix = var.use_name_prefix ? "${var.name}-" : null
+ path = var.path
+ description = var.description
+
+ assume_role_policy = data.aws_iam_policy_document.assume_role[0].json
+ max_session_duration = var.max_session_duration
+ permissions_boundary = var.permissions_boundary_arn
+ force_detach_policies = true
+
+ tags = var.tags
+}
+
+################################################################################
+# Node IAM Role Policy
+################################################################################
+
+data "aws_iam_policy_document" "this" {
+ count = var.create ? 1 : 0
+
+ dynamic "statement" {
+ for_each = var.enable_ira ? [] : [1]
+
+ content {
+ sid = "AllowDeregisterOwnInstance"
+ actions = ["ssm:DeregisterManagedInstance"]
+ resources = ["arn:${local.partition}:ssm:*:*:managed-instance/*"]
+
+ condition {
+ test = "ArnLike"
+ variable = "ssm:SourceInstanceARN"
+ values = ["arn:${local.partition}:ssm:*:*:managed-instance/*"]
+ }
+ }
+ }
+
+ dynamic "statement" {
+ for_each = var.enable_ira ? [] : [1]
+
+ content {
+ sid = "AllowDescribeInstances"
+ actions = ["ssm:DescribeInstanceInformation"]
+ resources = ["*"]
+
+ condition {
+ test = "ArnLike"
+ variable = "ssm:SourceInstanceARN"
+ values = ["arn:${local.partition}:ssm:*:*:managed-instance/*"]
+ }
+ }
+ }
+
+ statement {
+ sid = "DescribeEKSCluster"
+ actions = [
+ "eks:DescribeCluster",
+ "eks:ListAccessEntries",
+ ]
+ resources = var.cluster_arns
+ }
+
+ dynamic "statement" {
+ for_each = var.enable_pod_identity ? [1] : []
+
+ content {
+ actions = ["eks-auth:AssumeRoleForPodIdentity"]
+ resources = ["*"]
+ }
+ }
+
+ dynamic "statement" {
+ for_each = var.policy_statements != null ? var.policy_statements : []
+
+ content {
+ sid = statement.value.sid
+ actions = statement.value.actions
+ not_actions = statement.value.not_actions
+ effect = statement.value.effect
+ resources = statement.value.resources
+ not_resources = statement.value.not_resources
+
+ dynamic "principals" {
+ for_each = statement.value.principals != null ? statement.value.principals : []
+
+ content {
+ type = principals.value.type
+ identifiers = principals.value.identifiers
+ }
+ }
+
+ dynamic "not_principals" {
+ for_each = statement.value.not_principals != null ? statement.value.not_principals : []
+
+ content {
+ type = not_principals.value.type
+ identifiers = not_principals.value.identifiers
+ }
+ }
+
+ dynamic "condition" {
+ for_each = statement.value.condition != null ? statement.value.condition : []
+
+ content {
+ test = condition.value.test
+ values = condition.value.values
+ variable = condition.value.variable
+ }
+ }
+ }
+ }
+}
+
+resource "aws_iam_policy" "this" {
+ count = var.create ? 1 : 0
+
+ name = var.policy_use_name_prefix ? null : var.policy_name
+ name_prefix = var.policy_use_name_prefix ? "${var.policy_name}-" : null
+ path = var.policy_path
+ description = var.policy_description
+ policy = data.aws_iam_policy_document.this[0].json
+
+ tags = var.tags
+}
+
+resource "aws_iam_role_policy_attachment" "this" {
+ for_each = { for k, v in merge(
+ {
+ node = try(aws_iam_policy.this[0].arn, null)
+ AmazonSSMManagedInstanceCore = "arn:${local.partition}:iam::aws:policy/AmazonSSMManagedInstanceCore"
+ AmazonEC2ContainerRegistryPullOnly = "arn:${local.partition}:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly"
+ },
+ var.policies
+ ) : k => v if var.create }
+
+ policy_arn = each.value
+ role = aws_iam_role.this[0].name
+}
+
+################################################################################
+# Roles Anywhere Profile
+################################################################################
+
+locals {
+ enable_ira = var.create && var.enable_ira
+}
+
+resource "aws_rolesanywhere_profile" "this" {
+ count = local.enable_ira ? 1 : 0
+
+ duration_seconds = var.ira_profile_duration_seconds
+ managed_policy_arns = var.ira_profile_managed_policy_arns
+ name = try(coalesce(var.ira_profile_name, var.name), null)
+ require_instance_properties = var.ira_profile_require_instance_properties
+ role_arns = [aws_iam_role.intermediate[0].arn]
+ session_policy = var.ira_profile_session_policy
+
+ tags = var.tags
+}
+
+################################################################################
+# Roles Anywhere Trust Anchor
+################################################################################
+
+resource "aws_rolesanywhere_trust_anchor" "this" {
+ count = local.enable_ira ? 1 : 0
+
+ name = try(coalesce(var.ira_trust_anchor_name, var.name), null)
+
+ dynamic "notification_settings" {
+ for_each = var.ira_trust_anchor_notification_settings != null ? var.ira_trust_anchor_notification_settings : []
+
+ content {
+ channel = try(notification_settings.value.channel, null)
+ enabled = try(notification_settings.value.enabled, null)
+ event = try(notification_settings.value.event, null)
+ threshold = try(notification_settings.value.threshold, null)
+ }
+ }
+
+ source {
+ source_data {
+ acm_pca_arn = var.ira_trust_anchor_acm_pca_arn
+ x509_certificate_data = var.ira_trust_anchor_x509_certificate_data
+ }
+ source_type = var.ira_trust_anchor_source_type
+ }
+
+ tags = var.tags
+}
+
+################################################################################
+# Intermediate IAM Role
+################################################################################
+
+data "aws_iam_policy_document" "intermediate_assume_role" {
+ count = local.enable_ira ? 1 : 0
+
+ statement {
+ actions = [
+ "sts:AssumeRole",
+ "sts:TagSession",
+ "sts:SetSourceIdentity",
+ ]
+
+ principals {
+ type = "Service"
+ identifiers = ["rolesanywhere.amazonaws.com"]
+ }
+
+ condition {
+ test = "ArnEquals"
+ variable = "aws:SourceArn"
+ values = concat(var.trust_anchor_arns, aws_rolesanywhere_trust_anchor.this[*].arn)
+ }
+ }
+}
+
+locals {
+ intermediate_role_use_name_prefix = coalesce(var.intermediate_role_use_name_prefix, var.use_name_prefix)
+ intermediate_role_name = coalesce(var.intermediate_role_name, "${var.name}-inter")
+}
+
+resource "aws_iam_role" "intermediate" {
+ count = local.enable_ira ? 1 : 0
+
+ name = local.intermediate_role_use_name_prefix ? null : local.intermediate_role_name
+ name_prefix = local.intermediate_role_use_name_prefix ? "${local.intermediate_role_name}-" : null
+ path = coalesce(var.intermediate_role_path, var.path)
+ description = var.intermediate_role_description
+
+ assume_role_policy = data.aws_iam_policy_document.intermediate_assume_role[0].json
+ max_session_duration = var.max_session_duration
+ permissions_boundary = var.permissions_boundary_arn
+ force_detach_policies = true
+
+ tags = var.tags
+}
+
+################################################################################
+# Intermediate IAM Role Policy
+################################################################################
+
+data "aws_iam_policy_document" "intermediate" {
+ count = local.enable_ira ? 1 : 0
+
+ statement {
+ actions = ["eks:DescribeCluster"]
+ resources = var.cluster_arns
+ }
+
+ dynamic "statement" {
+ for_each = var.intermediate_policy_statements != null ? var.intermediate_policy_statements : []
+
+ content {
+ sid = statement.value.sid
+ actions = statement.value.actions
+ not_actions = statement.value.not_actions
+ effect = statement.value.effect
+ resources = statement.value.resources
+ not_resources = statement.value.not_resources
+
+ dynamic "principals" {
+ for_each = statement.value.principals != null ? statement.value.principals : []
+
+ content {
+ type = principals.value.type
+ identifiers = principals.value.identifiers
+ }
+ }
+
+ dynamic "not_principals" {
+ for_each = statement.value.not_principals != null ? statement.value.not_principals : []
+
+ content {
+ type = not_principals.value.type
+ identifiers = not_principals.value.identifiers
+ }
+ }
+
+ dynamic "condition" {
+ for_each = statement.value.condition != null ? statement.value.condition : []
+
+ content {
+ test = condition.value.test
+ values = condition.value.values
+ variable = condition.value.variable
+ }
+ }
+ }
+ }
+}
+
+locals {
+ intermediate_policy_use_name_prefix = coalesce(var.intermediate_policy_use_name_prefix, var.policy_use_name_prefix)
+ intermediate_policy_name = coalesce(var.intermediate_policy_name, var.policy_name)
+}
+
+resource "aws_iam_policy" "intermediate" {
+ count = local.enable_ira ? 1 : 0
+
+ name = local.intermediate_policy_use_name_prefix ? null : local.intermediate_policy_name
+ name_prefix = local.intermediate_policy_use_name_prefix ? "${local.intermediate_policy_name}-" : null
+ path = var.policy_path
+ description = var.policy_description
+ policy = data.aws_iam_policy_document.intermediate[0].json
+
+ tags = var.tags
+}
+
+resource "aws_iam_role_policy_attachment" "intermediate" {
+ for_each = { for k, v in merge(
+ {
+ intermediate = try(aws_iam_policy.intermediate[0].arn, null)
+ AmazonEC2ContainerRegistryPullOnly = "arn:${local.partition}:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly"
+ },
+ var.intermediate_role_policies
+ ) : k => v if local.enable_ira }
+
+ policy_arn = each.value
+ role = aws_iam_role.this[0].name
+}
diff --git a/modules/hybrid-node-role/outputs.tf b/modules/hybrid-node-role/outputs.tf
new file mode 100644
index 0000000000..dc4e26e519
--- /dev/null
+++ b/modules/hybrid-node-role/outputs.tf
@@ -0,0 +1,37 @@
+################################################################################
+# Node IAM Role
+################################################################################
+
+output "name" {
+ description = "The name of the node IAM role"
+ value = try(aws_iam_role.this[0].name, null)
+}
+
+output "arn" {
+ description = "The Amazon Resource Name (ARN) specifying the node IAM role"
+ value = try(aws_iam_role.this[0].arn, null)
+}
+
+output "unique_id" {
+ description = "Stable and unique string identifying the node IAM role"
+ value = try(aws_iam_role.this[0].unique_id, null)
+}
+
+################################################################################
+# Intermedaite IAM Role
+################################################################################
+
+output "intermediate_role_name" {
+ description = "The name of the node IAM role"
+ value = try(aws_iam_role.intermediate[0].name, null)
+}
+
+output "intermediate_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the node IAM role"
+ value = try(aws_iam_role.intermediate[0].arn, null)
+}
+
+output "intermediate_role_unique_id" {
+ description = "Stable and unique string identifying the node IAM role"
+ value = try(aws_iam_role.intermediate[0].unique_id, null)
+}
diff --git a/modules/hybrid-node-role/variables.tf b/modules/hybrid-node-role/variables.tf
new file mode 100644
index 0000000000..34e1247667
--- /dev/null
+++ b/modules/hybrid-node-role/variables.tf
@@ -0,0 +1,284 @@
+variable "create" {
+ description = "Controls if resources should be created (affects nearly all resources)"
+ type = bool
+ default = true
+}
+
+################################################################################
+# Node IAM Role
+################################################################################
+
+variable "name" {
+ description = "Name of the IAM role"
+ type = string
+ default = "EKSHybridNode"
+}
+
+variable "use_name_prefix" {
+ description = "Determines whether the name of the IAM role (`name`) is used as a prefix"
+ type = bool
+ default = true
+}
+
+variable "path" {
+ description = "Path of the IAM role"
+ type = string
+ default = "/"
+}
+
+variable "description" {
+ description = "IAM role description"
+ type = string
+ default = "EKS Hybrid Node IAM role"
+}
+
+variable "max_session_duration" {
+ description = "Maximum API session duration in seconds between 3600 and 43200"
+ type = number
+ default = null
+}
+
+variable "permissions_boundary_arn" {
+ description = "Permissions boundary ARN to use for the IAM role"
+ type = string
+ default = null
+}
+
+variable "tags" {
+ description = "A map of additional tags to add the the IAM role"
+ type = map(string)
+ default = {}
+}
+
+variable "enable_ira" {
+ description = "Enables IAM Roles Anywhere based IAM permissions on the node"
+ type = bool
+ default = false
+}
+
+variable "trust_anchor_arns" {
+ description = "List of IAM Roles Anywhere trust anchor ARNs. Required if `enable_ira` is set to `true`"
+ type = list(string)
+ default = []
+}
+
+################################################################################
+# Node IAM Role Policy
+################################################################################
+
+variable "policy_name" {
+ description = "Name of the IAM policy"
+ type = string
+ default = "EKSHybridNode"
+}
+
+variable "policy_use_name_prefix" {
+ description = "Determines whether the name of the IAM policy (`policy_name`) is used as a prefix"
+ type = bool
+ default = true
+}
+
+variable "policy_path" {
+ description = "Path of the IAM policy"
+ type = string
+ default = "/"
+}
+
+variable "policy_description" {
+ description = "IAM policy description"
+ type = string
+ default = "EKS Hybrid Node IAM role policy"
+}
+
+variable "policy_statements" {
+ description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed"
+ type = list(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string)
+ resources = optional(list(string))
+ not_resources = optional(list(string))
+ principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ not_principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ condition = optional(list(object({
+ test = string
+ values = list(string)
+ variable = string
+ })))
+ }))
+ default = null
+}
+
+variable "policies" {
+ description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format"
+ type = map(string)
+ default = {}
+}
+
+variable "cluster_arns" {
+ description = "List of EKS cluster ARNs to allow the node to describe"
+ type = list(string)
+ default = ["*"]
+}
+
+variable "enable_pod_identity" {
+ description = "Enables EKS Pod Identity based IAM permissions on the node"
+ type = bool
+ default = true
+}
+
+################################################################################
+# IAM Roles Anywhere Profile
+################################################################################
+
+variable "ira_profile_name" {
+ description = "Name of the Roles Anywhere profile"
+ type = string
+ default = null
+}
+
+variable "ira_profile_duration_seconds" {
+ description = "The number of seconds the vended session credentials are valid for. Defaults to `3600`"
+ type = number
+ default = null
+}
+
+variable "ira_profile_managed_policy_arns" {
+ description = "A list of managed policy ARNs that apply to the vended session credentials"
+ type = list(string)
+ default = []
+}
+
+variable "ira_profile_require_instance_properties" {
+ description = "Specifies whether instance properties are required in [CreateSession](https://docs.aws.amazon.com/rolesanywhere/latest/APIReference/API_CreateSession.html) requests with this profile"
+ type = bool
+ default = null
+}
+
+variable "ira_profile_session_policy" {
+ description = "A session policy that applies to the trust boundary of the vended session credentials"
+ type = string
+ default = null
+}
+
+################################################################################
+# Roles Anywhere Trust Anchor
+################################################################################
+
+variable "ira_trust_anchor_name" {
+ description = "Name of the Roles Anywhere trust anchor"
+ type = string
+ default = null
+}
+
+variable "ira_trust_anchor_notification_settings" {
+ description = "Notification settings for the trust anchor"
+ type = list(object({
+ channel = optional(string)
+ enabled = optional(bool)
+ event = optional(string)
+ threshold = optional(number)
+ }))
+ default = null
+}
+
+variable "ira_trust_anchor_acm_pca_arn" {
+ description = "The ARN of the ACM PCA that issued the trust anchor certificate"
+ type = string
+ default = null
+}
+
+variable "ira_trust_anchor_x509_certificate_data" {
+ description = "The X.509 certificate data of the trust anchor"
+ type = string
+ default = null
+}
+
+variable "ira_trust_anchor_source_type" {
+ description = "The source type of the trust anchor"
+ type = string
+ default = null
+}
+
+################################################################################
+# Intermediate IAM Role
+################################################################################
+
+variable "intermediate_role_name" {
+ description = "Name of the IAM role"
+ type = string
+ default = null
+}
+
+variable "intermediate_role_use_name_prefix" {
+ description = "Determines whether the name of the IAM role (`intermediate_role_name`) is used as a prefix"
+ type = bool
+ default = true
+}
+
+variable "intermediate_role_path" {
+ description = "Path of the IAM role"
+ type = string
+ default = "/"
+}
+
+variable "intermediate_role_description" {
+ description = "IAM role description"
+ type = string
+ default = "EKS Hybrid Node IAM Roles Anywhere intermediate IAM role"
+}
+
+################################################################################
+# Intermediate IAM Role Policy
+################################################################################
+
+variable "intermediate_policy_name" {
+ description = "Name of the IAM policy"
+ type = string
+ default = null
+}
+
+variable "intermediate_policy_use_name_prefix" {
+ description = "Determines whether the name of the IAM policy (`intermediate_policy_name`) is used as a prefix"
+ type = bool
+ default = true
+}
+
+variable "intermediate_policy_statements" {
+ description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed"
+ type = list(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string)
+ resources = optional(list(string))
+ not_resources = optional(list(string))
+ principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ not_principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ condition = optional(list(object({
+ test = string
+ values = list(string)
+ variable = string
+ })))
+ }))
+ default = null
+}
+
+variable "intermediate_role_policies" {
+ description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format"
+ type = map(string)
+ default = {}
+}
diff --git a/modules/hybrid-node-role/versions.tf b/modules/hybrid-node-role/versions.tf
new file mode 100644
index 0000000000..af99edcc27
--- /dev/null
+++ b/modules/hybrid-node-role/versions.tf
@@ -0,0 +1,16 @@
+terraform {
+ required_version = ">= 1.5.7"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 6.28"
+ }
+ }
+
+ provider_meta "aws" {
+ user_agent = [
+ "github.com/terraform-aws-modules/terraform-aws-eks"
+ ]
+ }
+}
diff --git a/modules/karpenter/README.md b/modules/karpenter/README.md
new file mode 100644
index 0000000000..69c03af907
--- /dev/null
+++ b/modules/karpenter/README.md
@@ -0,0 +1,208 @@
+# Karpenter Module
+
+Configuration in this directory creates the AWS resources required by Karpenter
+
+## Usage
+
+### All Resources (Default)
+
+In the following example, the Karpenter module will create:
+
+- An IAM role for use with Pod Identity and a scoped IAM policy for the Karpenter controller
+- A Pod Identity association to grant Karpenter controller access provided by the IAM Role
+- A Node IAM role that Karpenter will use to create an Instance Profile for the nodes to receive IAM permissions
+- An access entry for the Node IAM role to allow nodes to join the cluster
+- SQS queue and EventBridge event rules for Karpenter to utilize for spot termination handling, capacity re-balancing, etc.
+
+```hcl
+module "eks" {
+ source = "terraform-aws-modules/eks/aws"
+
+ ...
+}
+
+module "karpenter" {
+ source = "terraform-aws-modules/eks/aws//modules/karpenter"
+
+ cluster_name = module.eks.cluster_name
+
+ # Attach additional IAM policies to the Karpenter node IAM role
+ node_iam_role_additional_policies = {
+ AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+ }
+
+ tags = {
+ Environment = "dev"
+ Terraform = "true"
+ }
+}
+```
+
+### Re-Use Existing Node IAM Role
+
+In the following example, the Karpenter module will create:
+
+- An IAM role for use with Pod Identity and a scoped IAM policy for the Karpenter controller
+- SQS queue and EventBridge event rules for Karpenter to utilize for spot termination handling, capacity re-balancing, etc.
+
+In this scenario, Karpenter will re-use an existing Node IAM role from the EKS managed node group which already has the necessary access entry permissions:
+
+```hcl
+module "eks" {
+ source = "terraform-aws-modules/eks"
+
+ # Shown just for connection between cluster and Karpenter sub-module below
+ eks_managed_node_groups = {
+ initial = {
+ instance_types = ["t3.medium"]
+
+ min_size = 1
+ max_size = 3
+ desired_size = 1
+ }
+ }
+ ...
+}
+
+module "karpenter" {
+ source = "terraform-aws-modules/eks/aws//modules/karpenter"
+
+ cluster_name = module.eks.cluster_name
+
+ create_node_iam_role = false
+ node_iam_role_arn = module.eks.eks_managed_node_groups["initial"].iam_role_arn
+
+ # Since the node group role will already have an access entry
+ create_access_entry = false
+
+ tags = {
+ Environment = "dev"
+ Terraform = "true"
+ }
+}
+```
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.5.7 |
+| [aws](#requirement\_aws) | >= 6.28 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 6.28 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_cloudwatch_event_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |
+| [aws_cloudwatch_event_target.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |
+| [aws_eks_access_entry.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_access_entry) | resource |
+| [aws_eks_pod_identity_association.karpenter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_pod_identity_association) | resource |
+| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource |
+| [aws_iam_policy.controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
+| [aws_iam_role.controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy.controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
+| [aws_iam_role_policy_attachment.controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.controller_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.node_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_sqs_queue.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue) | resource |
+| [aws_sqs_queue_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy) | resource |
+| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
+| [aws_iam_policy_document.controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.controller_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.node_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.queue](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
+| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
+| [aws_service_principal.ec2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/service_principal) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [access\_entry\_type](#input\_access\_entry\_type) | Type of the access entry. `EC2_LINUX`, `FARGATE_LINUX`, or `EC2_WINDOWS`; defaults to `EC2_LINUX` | `string` | `"EC2_LINUX"` | no |
+| [ami\_id\_ssm\_parameter\_arns](#input\_ami\_id\_ssm\_parameter\_arns) | List of SSM Parameter ARNs that Karpenter controller is allowed read access (for retrieving AMI IDs) | `list(string)` | `[]` | no |
+| [cluster\_ip\_family](#input\_cluster\_ip\_family) | The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6`. Note: If `ipv6` is specified, the `AmazonEKS_CNI_IPv6_Policy` must exist in the account. This policy is created by the EKS module with `create_cni_ipv6_iam_policy = true` | `string` | `"ipv4"` | no |
+| [cluster\_name](#input\_cluster\_name) | The name of the EKS cluster | `string` | `""` | no |
+| [create](#input\_create) | Controls if resources should be created (affects nearly all resources) | `bool` | `true` | no |
+| [create\_access\_entry](#input\_create\_access\_entry) | Determines whether an access entry is created for the IAM role used by the node IAM role | `bool` | `true` | no |
+| [create\_iam\_role](#input\_create\_iam\_role) | Determines whether an IAM role is created | `bool` | `true` | no |
+| [create\_instance\_profile](#input\_create\_instance\_profile) | Whether to create an IAM instance profile | `bool` | `false` | no |
+| [create\_node\_iam\_role](#input\_create\_node\_iam\_role) | Determines whether an IAM role is created or to use an existing IAM role | `bool` | `true` | no |
+| [create\_pod\_identity\_association](#input\_create\_pod\_identity\_association) | Determines whether to create pod identity association | `bool` | `true` | no |
+| [enable\_inline\_policy](#input\_enable\_inline\_policy) | Determines whether the controller policy is created as a standard IAM policy or inline IAM policy. This can be enabled when the error `LimitExceeded: Cannot exceed quota for PolicySize: 6144` is received since standard IAM policies have a limit of 6,144 characters versus an inline role policy's limit of 10,240 ([Reference](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html)) | `bool` | `false` | no |
+| [enable\_spot\_termination](#input\_enable\_spot\_termination) | Determines whether to enable native spot termination handling | `bool` | `true` | no |
+| [iam\_policy\_description](#input\_iam\_policy\_description) | IAM policy description | `string` | `"Karpenter controller IAM policy"` | no |
+| [iam\_policy\_name](#input\_iam\_policy\_name) | Name of the IAM policy | `string` | `"KarpenterController"` | no |
+| [iam\_policy\_path](#input\_iam\_policy\_path) | Path of the IAM policy | `string` | `"/"` | no |
+| [iam\_policy\_statements](#input\_iam\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed | list(object({ # TODO - change to `map(object({...}))` in next major version
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})) | `null` | no |
+| [iam\_policy\_use\_name\_prefix](#input\_iam\_policy\_use\_name\_prefix) | Determines whether the name of the IAM policy (`iam_policy_name`) is used as a prefix | `bool` | `true` | no |
+| [iam\_role\_description](#input\_iam\_role\_description) | IAM role description | `string` | `"Karpenter controller IAM role"` | no |
+| [iam\_role\_max\_session\_duration](#input\_iam\_role\_max\_session\_duration) | Maximum API session duration in seconds between 3600 and 43200 | `number` | `null` | no |
+| [iam\_role\_name](#input\_iam\_role\_name) | Name of the IAM role | `string` | `"KarpenterController"` | no |
+| [iam\_role\_override\_assume\_policy\_documents](#input\_iam\_role\_override\_assume\_policy\_documents) | A list of IAM policy documents to override the default assume role policy document for the Karpenter controller IAM role | `list(string)` | `[]` | no |
+| [iam\_role\_path](#input\_iam\_role\_path) | Path of the IAM role | `string` | `"/"` | no |
+| [iam\_role\_permissions\_boundary\_arn](#input\_iam\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for the IAM role | `string` | `null` | no |
+| [iam\_role\_policies](#input\_iam\_role\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no |
+| [iam\_role\_source\_assume\_policy\_documents](#input\_iam\_role\_source\_assume\_policy\_documents) | A list of IAM policy documents to use as a source for the assume role policy document for the Karpenter controller IAM role | `list(string)` | `[]` | no |
+| [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add the the IAM role | `map(string)` | `{}` | no |
+| [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the name of the IAM role (`iam_role_name`) is used as a prefix | `bool` | `true` | no |
+| [namespace](#input\_namespace) | Namespace to associate with the Karpenter Pod Identity | `string` | `"kube-system"` | no |
+| [node\_iam\_role\_additional\_policies](#input\_node\_iam\_role\_additional\_policies) | Additional policies to be added to the IAM role | `map(string)` | `{}` | no |
+| [node\_iam\_role\_arn](#input\_node\_iam\_role\_arn) | Existing IAM role ARN for the IAM instance profile. Required if `create_node_iam_role` is set to `false` | `string` | `null` | no |
+| [node\_iam\_role\_attach\_cni\_policy](#input\_node\_iam\_role\_attach\_cni\_policy) | Whether to attach the `AmazonEKS_CNI_Policy`/`AmazonEKS_CNI_IPv6_Policy` IAM policy to the IAM IAM role. WARNING: If set `false` the permissions must be assigned to the `aws-node` DaemonSet pods via another method or nodes will not be able to join the cluster | `bool` | `true` | no |
+| [node\_iam\_role\_description](#input\_node\_iam\_role\_description) | Description of the role | `string` | `null` | no |
+| [node\_iam\_role\_max\_session\_duration](#input\_node\_iam\_role\_max\_session\_duration) | Maximum API session duration in seconds between 3600 and 43200 | `number` | `null` | no |
+| [node\_iam\_role\_name](#input\_node\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
+| [node\_iam\_role\_path](#input\_node\_iam\_role\_path) | IAM role path | `string` | `"/"` | no |
+| [node\_iam\_role\_permissions\_boundary](#input\_node\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
+| [node\_iam\_role\_tags](#input\_node\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
+| [node\_iam\_role\_use\_name\_prefix](#input\_node\_iam\_role\_use\_name\_prefix) | Determines whether the Node IAM role name (`node_iam_role_name`) is used as a prefix | `bool` | `true` | no |
+| [queue\_kms\_data\_key\_reuse\_period\_seconds](#input\_queue\_kms\_data\_key\_reuse\_period\_seconds) | The length of time, in seconds, for which Amazon SQS can reuse a data key to encrypt or decrypt messages before calling AWS KMS again | `number` | `null` | no |
+| [queue\_kms\_master\_key\_id](#input\_queue\_kms\_master\_key\_id) | The ID of an AWS-managed customer master key (CMK) for Amazon SQS or a custom CMK | `string` | `null` | no |
+| [queue\_managed\_sse\_enabled](#input\_queue\_managed\_sse\_enabled) | Boolean to enable server-side encryption (SSE) of message content with SQS-owned encryption keys | `bool` | `true` | no |
+| [queue\_name](#input\_queue\_name) | Name of the SQS queue | `string` | `null` | no |
+| [queue\_policy\_statements](#input\_queue\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific SQS queue policy permissions as needed | map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})) | `null` | no |
+| [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no |
+| [rule\_name\_prefix](#input\_rule\_name\_prefix) | Prefix used for all event bridge rules | `string` | `"Karpenter"` | no |
+| [service\_account](#input\_service\_account) | Service account to associate with the Karpenter Pod Identity | `string` | `"karpenter"` | no |
+| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [event\_rules](#output\_event\_rules) | Map of the event rules created and their attributes |
+| [iam\_role\_arn](#output\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the controller IAM role |
+| [iam\_role\_name](#output\_iam\_role\_name) | The name of the controller IAM role |
+| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the controller IAM role |
+| [instance\_profile\_arn](#output\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile |
+| [instance\_profile\_id](#output\_instance\_profile\_id) | Instance profile's ID |
+| [instance\_profile\_name](#output\_instance\_profile\_name) | Name of the instance profile |
+| [instance\_profile\_unique](#output\_instance\_profile\_unique) | Stable and unique string identifying the IAM instance profile |
+| [namespace](#output\_namespace) | Namespace associated with the Karpenter Pod Identity |
+| [node\_access\_entry\_arn](#output\_node\_access\_entry\_arn) | Amazon Resource Name (ARN) of the node Access Entry |
+| [node\_iam\_role\_arn](#output\_node\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the node IAM role |
+| [node\_iam\_role\_name](#output\_node\_iam\_role\_name) | The name of the node IAM role |
+| [node\_iam\_role\_unique\_id](#output\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the node IAM role |
+| [queue\_arn](#output\_queue\_arn) | The ARN of the SQS queue |
+| [queue\_name](#output\_queue\_name) | The name of the created Amazon SQS queue |
+| [queue\_url](#output\_queue\_url) | The URL for the created Amazon SQS queue |
+| [service\_account](#output\_service\_account) | Service Account associated with the Karpenter Pod Identity |
+
+
+## License
+
+Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/LICENSE) for full details.
diff --git a/modules/karpenter/main.tf b/modules/karpenter/main.tf
new file mode 100644
index 0000000000..af0e947e76
--- /dev/null
+++ b/modules/karpenter/main.tf
@@ -0,0 +1,417 @@
+data "aws_region" "current" {
+ count = var.create ? 1 : 0
+
+ region = var.region
+}
+
+data "aws_partition" "current" {
+ count = var.create ? 1 : 0
+}
+
+data "aws_caller_identity" "current" {
+ count = var.create ? 1 : 0
+}
+
+data "aws_service_principal" "ec2" {
+ count = var.create ? 1 : 0
+
+ service_name = "ec2"
+}
+
+locals {
+ account_id = try(data.aws_caller_identity.current[0].account_id, "")
+ ec2_sp_name = try(data.aws_service_principal.ec2[0].name, "")
+ partition = try(data.aws_partition.current[0].partition, "")
+ region = try(data.aws_region.current[0].region, "")
+}
+
+################################################################################
+# Karpenter controller IAM Role
+################################################################################
+
+locals {
+ create_iam_role = var.create && var.create_iam_role
+}
+
+data "aws_iam_policy_document" "controller_assume_role" {
+ count = local.create_iam_role ? 1 : 0
+
+ override_policy_documents = var.iam_role_override_assume_policy_documents
+ source_policy_documents = var.iam_role_source_assume_policy_documents
+
+ # Pod Identity
+ statement {
+ sid = "PodIdentity"
+ actions = [
+ "sts:AssumeRole",
+ "sts:TagSession",
+ ]
+
+ principals {
+ type = "Service"
+ identifiers = ["pods.eks.amazonaws.com"]
+ }
+ }
+}
+
+resource "aws_iam_role" "controller" {
+ count = local.create_iam_role ? 1 : 0
+
+ name = var.iam_role_use_name_prefix ? null : var.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${var.iam_role_name}-" : null
+ path = var.iam_role_path
+ description = var.iam_role_description
+
+ assume_role_policy = data.aws_iam_policy_document.controller_assume_role[0].json
+ max_session_duration = var.iam_role_max_session_duration
+ permissions_boundary = var.iam_role_permissions_boundary_arn
+ force_detach_policies = true
+
+ tags = merge(var.tags, var.iam_role_tags)
+}
+
+resource "aws_iam_role_policy" "controller" {
+ count = local.create_iam_role && var.enable_inline_policy ? 1 : 0
+
+ name = var.iam_policy_use_name_prefix ? null : var.iam_policy_name
+ name_prefix = var.iam_policy_use_name_prefix ? "${var.iam_policy_name}-" : null
+ role = aws_iam_role.controller[0].name
+ policy = data.aws_iam_policy_document.controller[0].json
+}
+
+resource "aws_iam_policy" "controller" {
+ count = local.create_iam_role && !var.enable_inline_policy ? 1 : 0
+
+ name = var.iam_policy_use_name_prefix ? null : var.iam_policy_name
+ name_prefix = var.iam_policy_use_name_prefix ? "${var.iam_policy_name}-" : null
+ path = var.iam_policy_path
+ description = var.iam_policy_description
+ policy = data.aws_iam_policy_document.controller[0].json
+
+ tags = var.tags
+}
+
+resource "aws_iam_role_policy_attachment" "controller" {
+ count = local.create_iam_role && !var.enable_inline_policy ? 1 : 0
+
+ role = aws_iam_role.controller[0].name
+ policy_arn = aws_iam_policy.controller[0].arn
+}
+
+resource "aws_iam_role_policy_attachment" "controller_additional" {
+ for_each = { for k, v in var.iam_role_policies : k => v if local.create_iam_role }
+
+ role = aws_iam_role.controller[0].name
+ policy_arn = each.value
+}
+
+################################################################################
+# Pod Identity Association
+################################################################################
+
+resource "aws_eks_pod_identity_association" "karpenter" {
+ count = local.create_iam_role && var.create_pod_identity_association ? 1 : 0
+
+ region = var.region
+
+ cluster_name = var.cluster_name
+ namespace = var.namespace
+ service_account = var.service_account
+ role_arn = aws_iam_role.controller[0].arn
+
+ tags = var.tags
+}
+
+################################################################################
+# Node Termination Queue
+################################################################################
+
+locals {
+ enable_spot_termination = var.create && var.enable_spot_termination
+
+ queue_name = coalesce(var.queue_name, "Karpenter-${var.cluster_name}")
+}
+
+resource "aws_sqs_queue" "this" {
+ count = local.enable_spot_termination ? 1 : 0
+
+ region = var.region
+
+ name = local.queue_name
+ message_retention_seconds = 300
+ sqs_managed_sse_enabled = var.queue_managed_sse_enabled ? var.queue_managed_sse_enabled : null
+ kms_master_key_id = var.queue_kms_master_key_id
+ kms_data_key_reuse_period_seconds = var.queue_kms_data_key_reuse_period_seconds
+
+ tags = var.tags
+}
+
+data "aws_iam_policy_document" "queue" {
+ count = local.enable_spot_termination ? 1 : 0
+
+ statement {
+ sid = "SqsWrite"
+ actions = ["sqs:SendMessage"]
+ resources = [aws_sqs_queue.this[0].arn]
+
+ principals {
+ type = "Service"
+ identifiers = [
+ "events.amazonaws.com",
+ "sqs.amazonaws.com",
+ ]
+ }
+ }
+
+ statement {
+ sid = "DenyHTTP"
+ effect = "Deny"
+ actions = [
+ "sqs:*"
+ ]
+ resources = [aws_sqs_queue.this[0].arn]
+ condition {
+ test = "Bool"
+ variable = "aws:SecureTransport"
+ values = [
+ "false"
+ ]
+ }
+ principals {
+ type = "*"
+ identifiers = [
+ "*"
+ ]
+ }
+ }
+
+ dynamic "statement" {
+ for_each = var.queue_policy_statements != null ? var.queue_policy_statements : {}
+
+ content {
+ sid = try(coalesce(statement.value.sid, statement.key))
+ actions = statement.value.actions
+ not_actions = statement.value.not_actions
+ effect = statement.value.effect
+ resources = statement.value.resources
+ not_resources = statement.value.not_resources
+
+ dynamic "principals" {
+ for_each = statement.value.principals != null ? statement.value.principals : []
+
+ content {
+ type = principals.value.type
+ identifiers = principals.value.identifiers
+ }
+ }
+
+ dynamic "not_principals" {
+ for_each = statement.value.not_principals != null ? statement.value.not_principals : []
+
+ content {
+ type = not_principals.value.type
+ identifiers = not_principals.value.identifiers
+ }
+ }
+
+ dynamic "condition" {
+ for_each = statement.value.condition != null ? statement.value.condition : []
+
+ content {
+ test = condition.value.test
+ values = condition.value.values
+ variable = condition.value.variable
+ }
+ }
+ }
+ }
+}
+
+resource "aws_sqs_queue_policy" "this" {
+ count = local.enable_spot_termination ? 1 : 0
+
+ region = var.region
+
+ queue_url = aws_sqs_queue.this[0].url
+ policy = data.aws_iam_policy_document.queue[0].json
+}
+
+################################################################################
+# Node Termination Event Rules
+################################################################################
+
+locals {
+ events = {
+ health_event = {
+ name = "HealthEvent"
+ description = "Karpenter interrupt - AWS health event"
+ event_pattern = {
+ source = ["aws.health"]
+ detail-type = ["AWS Health Event"]
+ }
+ }
+ spot_interrupt = {
+ name = "SpotInterrupt"
+ description = "Karpenter interrupt - EC2 spot instance interruption warning"
+ event_pattern = {
+ source = ["aws.ec2"]
+ detail-type = ["EC2 Spot Instance Interruption Warning"]
+ }
+ }
+ instance_rebalance = {
+ name = "InstanceRebalance"
+ description = "Karpenter interrupt - EC2 instance rebalance recommendation"
+ event_pattern = {
+ source = ["aws.ec2"]
+ detail-type = ["EC2 Instance Rebalance Recommendation"]
+ }
+ }
+ instance_state_change = {
+ name = "InstanceStateChange"
+ description = "Karpenter interrupt - EC2 instance state-change notification"
+ event_pattern = {
+ source = ["aws.ec2"]
+ detail-type = ["EC2 Instance State-change Notification"]
+ }
+ }
+ }
+}
+
+resource "aws_cloudwatch_event_rule" "this" {
+ for_each = { for k, v in local.events : k => v if local.enable_spot_termination }
+
+ region = var.region
+
+ name_prefix = "${var.rule_name_prefix}${each.value.name}-"
+ description = each.value.description
+ event_pattern = jsonencode(each.value.event_pattern)
+
+ tags = merge(
+ { "ClusterName" : var.cluster_name },
+ var.tags,
+ )
+}
+
+resource "aws_cloudwatch_event_target" "this" {
+ for_each = { for k, v in local.events : k => v if local.enable_spot_termination }
+
+ region = var.region
+
+ rule = aws_cloudwatch_event_rule.this[each.key].name
+ target_id = "KarpenterInterruptionQueueTarget"
+ arn = aws_sqs_queue.this[0].arn
+}
+
+################################################################################
+# Node IAM Role
+# This is used by the nodes launched by Karpenter
+################################################################################
+
+locals {
+ create_node_iam_role = var.create && var.create_node_iam_role
+
+ node_iam_role_name = coalesce(var.node_iam_role_name, "Karpenter-${var.cluster_name}")
+ node_iam_role_policy_prefix = "arn:${local.partition}:iam::aws:policy"
+
+ ipv4_cni_policy = { for k, v in {
+ AmazonEKS_CNI_Policy = "${local.node_iam_role_policy_prefix}/AmazonEKS_CNI_Policy"
+ } : k => v if var.node_iam_role_attach_cni_policy && var.cluster_ip_family == "ipv4" }
+ ipv6_cni_policy = { for k, v in {
+ AmazonEKS_CNI_IPv6_Policy = "arn:${local.partition}:iam::${local.account_id}:policy/AmazonEKS_CNI_IPv6_Policy"
+ } : k => v if var.node_iam_role_attach_cni_policy && var.cluster_ip_family == "ipv6" }
+}
+
+data "aws_iam_policy_document" "node_assume_role" {
+ count = local.create_node_iam_role ? 1 : 0
+
+ statement {
+ sid = "EKSNodeAssumeRole"
+ actions = ["sts:AssumeRole"]
+
+ principals {
+ type = "Service"
+ identifiers = [local.ec2_sp_name]
+ }
+ }
+}
+
+resource "aws_iam_role" "node" {
+ count = local.create_node_iam_role ? 1 : 0
+
+ name = var.node_iam_role_use_name_prefix ? null : local.node_iam_role_name
+ name_prefix = var.node_iam_role_use_name_prefix ? "${local.node_iam_role_name}-" : null
+ path = var.node_iam_role_path
+ description = var.node_iam_role_description
+
+ assume_role_policy = data.aws_iam_policy_document.node_assume_role[0].json
+ max_session_duration = var.node_iam_role_max_session_duration
+ permissions_boundary = var.node_iam_role_permissions_boundary
+ force_detach_policies = true
+
+ tags = merge(var.tags, var.node_iam_role_tags)
+}
+
+# Policies attached ref https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_node_group
+resource "aws_iam_role_policy_attachment" "node" {
+ for_each = { for k, v in merge(
+ {
+ AmazonEKSWorkerNodePolicy = "${local.node_iam_role_policy_prefix}/AmazonEKSWorkerNodePolicy"
+ AmazonEC2ContainerRegistryPullOnly = "${local.node_iam_role_policy_prefix}/AmazonEC2ContainerRegistryPullOnly"
+ },
+ local.ipv4_cni_policy,
+ local.ipv6_cni_policy
+ ) : k => v if local.create_node_iam_role }
+
+ policy_arn = each.value
+ role = aws_iam_role.node[0].name
+}
+
+resource "aws_iam_role_policy_attachment" "node_additional" {
+ for_each = { for k, v in var.node_iam_role_additional_policies : k => v if local.create_node_iam_role }
+
+ policy_arn = each.value
+ role = aws_iam_role.node[0].name
+}
+
+################################################################################
+# Access Entry
+################################################################################
+
+resource "aws_eks_access_entry" "node" {
+ count = var.create && var.create_access_entry ? 1 : 0
+
+ region = var.region
+
+ cluster_name = var.cluster_name
+ principal_arn = var.create_node_iam_role ? aws_iam_role.node[0].arn : var.node_iam_role_arn
+ type = var.access_entry_type
+
+ tags = var.tags
+
+ depends_on = [
+ # If we try to add this too quickly, it fails. So .... we wait
+ aws_sqs_queue_policy.this,
+ ]
+}
+
+################################################################################
+# Node IAM Instance Profile
+# This is used by the nodes launched by Karpenter
+# Starting with Karpenter 0.32 this is no longer required as Karpenter will
+# create the Instance Profile
+################################################################################
+
+locals {
+ external_role_name = try(replace(var.node_iam_role_arn, "/^(.*role/)/", ""), null)
+}
+
+resource "aws_iam_instance_profile" "this" {
+ count = var.create && var.create_instance_profile ? 1 : 0
+
+ name = var.node_iam_role_use_name_prefix ? null : local.node_iam_role_name
+ name_prefix = var.node_iam_role_use_name_prefix ? "${local.node_iam_role_name}-" : null
+ path = var.node_iam_role_path
+ role = var.create_node_iam_role ? aws_iam_role.node[0].name : local.external_role_name
+
+ tags = merge(var.tags, var.node_iam_role_tags)
+}
diff --git a/modules/karpenter/migrations.tf b/modules/karpenter/migrations.tf
new file mode 100644
index 0000000000..b40040f330
--- /dev/null
+++ b/modules/karpenter/migrations.tf
@@ -0,0 +1,77 @@
+################################################################################
+# Migrations: v19.21 -> v20.0
+################################################################################
+
+# Node IAM role
+moved {
+ from = aws_iam_role.this
+ to = aws_iam_role.node
+}
+
+moved {
+ from = aws_iam_policy.this
+ to = aws_iam_policy.node
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.this
+ to = aws_iam_role_policy_attachment.node
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.additional
+ to = aws_iam_role_policy_attachment.node_additional
+}
+
+# Controller IAM role
+moved {
+ from = aws_iam_role.irsa
+ to = aws_iam_role.controller
+}
+
+moved {
+ from = aws_iam_policy.irsa
+ to = aws_iam_policy.controller
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.irsa
+ to = aws_iam_role_policy_attachment.controller
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.irsa_additional
+ to = aws_iam_role_policy_attachment.controller_additional
+}
+
+# Spelling correction
+moved {
+ from = aws_cloudwatch_event_target.this["spot_interupt"]
+ to = aws_cloudwatch_event_target.this["spot_interrupt"]
+}
+
+moved {
+ from = aws_cloudwatch_event_rule.this["spot_interupt"]
+ to = aws_cloudwatch_event_rule.this["spot_interrupt"]
+}
+
+################################################################################
+# Migrations: v20.7 -> v20.8
+################################################################################
+
+# Node IAM role policy attachment
+# Commercial partition only - `moved` does now allow multiple moves to same target
+moved {
+ from = aws_iam_role_policy_attachment.node["arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"]
+ to = aws_iam_role_policy_attachment.node["AmazonEKSWorkerNodePolicy"]
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.node["arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"]
+ to = aws_iam_role_policy_attachment.node["AmazonEC2ContainerRegistryReadOnly"]
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.node["arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"]
+ to = aws_iam_role_policy_attachment.node["AmazonEKS_CNI_Policy"]
+}
diff --git a/modules/karpenter/outputs.tf b/modules/karpenter/outputs.tf
new file mode 100644
index 0000000000..a71d47242d
--- /dev/null
+++ b/modules/karpenter/outputs.tf
@@ -0,0 +1,112 @@
+################################################################################
+# Karpenter controller IAM Role
+################################################################################
+
+output "iam_role_name" {
+ description = "The name of the controller IAM role"
+ value = try(aws_iam_role.controller[0].name, null)
+}
+
+output "iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the controller IAM role"
+ value = try(aws_iam_role.controller[0].arn, null)
+}
+
+output "iam_role_unique_id" {
+ description = "Stable and unique string identifying the controller IAM role"
+ value = try(aws_iam_role.controller[0].unique_id, null)
+}
+
+################################################################################
+# Node Termination Queue
+################################################################################
+
+output "queue_arn" {
+ description = "The ARN of the SQS queue"
+ value = try(aws_sqs_queue.this[0].arn, null)
+}
+
+output "queue_name" {
+ description = "The name of the created Amazon SQS queue"
+ value = try(aws_sqs_queue.this[0].name, null)
+}
+
+output "queue_url" {
+ description = "The URL for the created Amazon SQS queue"
+ value = try(aws_sqs_queue.this[0].url, null)
+}
+
+################################################################################
+# Node Termination Event Rules
+################################################################################
+
+output "event_rules" {
+ description = "Map of the event rules created and their attributes"
+ value = aws_cloudwatch_event_rule.this
+}
+
+################################################################################
+# Node IAM Role
+################################################################################
+
+output "node_iam_role_name" {
+ description = "The name of the node IAM role"
+ value = try(aws_iam_role.node[0].name, null)
+}
+
+output "node_iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the node IAM role"
+ value = try(aws_iam_role.node[0].arn, var.node_iam_role_arn)
+}
+
+output "node_iam_role_unique_id" {
+ description = "Stable and unique string identifying the node IAM role"
+ value = try(aws_iam_role.node[0].unique_id, null)
+}
+
+################################################################################
+# Access Entry
+################################################################################
+
+output "node_access_entry_arn" {
+ description = "Amazon Resource Name (ARN) of the node Access Entry"
+ value = try(aws_eks_access_entry.node[0].access_entry_arn, null)
+}
+
+################################################################################
+# Node IAM Instance Profile
+################################################################################
+
+output "instance_profile_arn" {
+ description = "ARN assigned by AWS to the instance profile"
+ value = try(aws_iam_instance_profile.this[0].arn, null)
+}
+
+output "instance_profile_id" {
+ description = "Instance profile's ID"
+ value = try(aws_iam_instance_profile.this[0].id, null)
+}
+
+output "instance_profile_name" {
+ description = "Name of the instance profile"
+ value = try(aws_iam_instance_profile.this[0].name, null)
+}
+
+output "instance_profile_unique" {
+ description = "Stable and unique string identifying the IAM instance profile"
+ value = try(aws_iam_instance_profile.this[0].unique_id, null)
+}
+
+################################################################################
+# Pod Identity
+################################################################################
+
+output "namespace" {
+ description = "Namespace associated with the Karpenter Pod Identity"
+ value = var.namespace
+}
+
+output "service_account" {
+ description = "Service Account associated with the Karpenter Pod Identity"
+ value = var.service_account
+}
diff --git a/modules/karpenter/policy.tf b/modules/karpenter/policy.tf
new file mode 100644
index 0000000000..34937f36eb
--- /dev/null
+++ b/modules/karpenter/policy.tf
@@ -0,0 +1,405 @@
+data "aws_iam_policy_document" "controller" {
+ count = local.create_iam_role ? 1 : 0
+
+ statement {
+ sid = "AllowScopedEC2InstanceAccessActions"
+ resources = [
+ "arn:${local.partition}:ec2:${local.region}::image/*",
+ "arn:${local.partition}:ec2:${local.region}::snapshot/*",
+ "arn:${local.partition}:ec2:${local.region}:*:security-group/*",
+ "arn:${local.partition}:ec2:${local.region}:*:subnet/*",
+ "arn:${local.partition}:ec2:${local.region}:*:capacity-reservation/*",
+ ]
+
+ actions = [
+ "ec2:RunInstances",
+ "ec2:CreateFleet"
+ ]
+ }
+
+ statement {
+ sid = "AllowScopedEC2LaunchTemplateAccessActions"
+ resources = [
+ "arn:${local.partition}:ec2:${local.region}:*:launch-template/*"
+ ]
+
+ actions = [
+ "ec2:RunInstances",
+ "ec2:CreateFleet"
+ ]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}"
+ values = ["owned"]
+ }
+
+ condition {
+ test = "StringLike"
+ variable = "aws:ResourceTag/karpenter.sh/nodepool"
+ values = ["*"]
+ }
+ }
+
+ statement {
+ sid = "AllowScopedEC2InstanceActionsWithTags"
+ resources = [
+ "arn:${local.partition}:ec2:${local.region}:*:fleet/*",
+ "arn:${local.partition}:ec2:${local.region}:*:instance/*",
+ "arn:${local.partition}:ec2:${local.region}:*:volume/*",
+ "arn:${local.partition}:ec2:${local.region}:*:network-interface/*",
+ "arn:${local.partition}:ec2:${local.region}:*:launch-template/*",
+ "arn:${local.partition}:ec2:${local.region}:*:spot-instances-request/*",
+ "arn:${local.partition}:ec2:${local.region}:*:capacity-reservation/*"
+ ]
+ actions = [
+ "ec2:RunInstances",
+ "ec2:CreateFleet",
+ "ec2:CreateLaunchTemplate"
+ ]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}"
+ values = ["owned"]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/eks:eks-cluster-name"
+ values = [var.cluster_name]
+ }
+
+ condition {
+ test = "StringLike"
+ variable = "aws:RequestTag/karpenter.sh/nodepool"
+ values = ["*"]
+ }
+ }
+
+ statement {
+ sid = "AllowScopedResourceCreationTagging"
+ resources = [
+ "arn:${local.partition}:ec2:${local.region}:*:fleet/*",
+ "arn:${local.partition}:ec2:${local.region}:*:instance/*",
+ "arn:${local.partition}:ec2:${local.region}:*:volume/*",
+ "arn:${local.partition}:ec2:${local.region}:*:network-interface/*",
+ "arn:${local.partition}:ec2:${local.region}:*:launch-template/*",
+ "arn:${local.partition}:ec2:${local.region}:*:spot-instances-request/*",
+ ]
+ actions = ["ec2:CreateTags"]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}"
+ values = ["owned"]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/eks:eks-cluster-name"
+ values = [var.cluster_name]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "ec2:CreateAction"
+ values = [
+ "RunInstances",
+ "CreateFleet",
+ "CreateLaunchTemplate",
+ ]
+ }
+
+ condition {
+ test = "StringLike"
+ variable = "aws:RequestTag/karpenter.sh/nodepool"
+ values = ["*"]
+ }
+ }
+
+ statement {
+ sid = "AllowScopedResourceTagging"
+ resources = ["arn:${local.partition}:ec2:${local.region}:*:instance/*"]
+ actions = ["ec2:CreateTags"]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}"
+ values = ["owned"]
+ }
+
+ condition {
+ test = "StringLike"
+ variable = "aws:ResourceTag/karpenter.sh/nodepool"
+ values = ["*"]
+ }
+
+ condition {
+ test = "StringEqualsIfExists"
+ variable = "aws:RequestTag/eks:eks-cluster-name"
+ values = [var.cluster_name]
+ }
+
+ condition {
+ test = "ForAllValues:StringEquals"
+ variable = "aws:TagKeys"
+ values = [
+ "eks:eks-cluster-name",
+ "karpenter.sh/nodeclaim",
+ "Name",
+ ]
+ }
+ }
+
+ statement {
+ sid = "AllowScopedDeletion"
+ resources = [
+ "arn:${local.partition}:ec2:${local.region}:*:instance/*",
+ "arn:${local.partition}:ec2:${local.region}:*:launch-template/*"
+ ]
+
+ actions = [
+ "ec2:TerminateInstances",
+ "ec2:DeleteLaunchTemplate"
+ ]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}"
+ values = ["owned"]
+ }
+
+ condition {
+ test = "StringLike"
+ variable = "aws:ResourceTag/karpenter.sh/nodepool"
+ values = ["*"]
+ }
+ }
+
+ statement {
+ sid = "AllowRegionalReadActions"
+ resources = ["*"]
+ actions = [
+ "ec2:DescribeCapacityReservations",
+ "ec2:DescribeAvailabilityZones",
+ "ec2:DescribeImages",
+ "ec2:DescribeInstances",
+ "ec2:DescribeInstanceTypeOfferings",
+ "ec2:DescribeInstanceTypes",
+ "ec2:DescribeLaunchTemplates",
+ "ec2:DescribeSecurityGroups",
+ "ec2:DescribeSpotPriceHistory",
+ "ec2:DescribeSubnets"
+ ]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestedRegion"
+ values = [local.region]
+ }
+ }
+
+ statement {
+ sid = "AllowSSMReadActions"
+ resources = coalescelist(var.ami_id_ssm_parameter_arns, ["arn:${local.partition}:ssm:${local.region}::parameter/aws/service/*"])
+ actions = ["ssm:GetParameter"]
+ }
+
+ statement {
+ sid = "AllowPricingReadActions"
+ resources = ["*"]
+ actions = ["pricing:GetProducts"]
+ }
+
+ dynamic "statement" {
+ for_each = local.enable_spot_termination ? [1] : []
+
+ content {
+ sid = "AllowInterruptionQueueActions"
+ resources = [try(aws_sqs_queue.this[0].arn, null)]
+ actions = [
+ "sqs:DeleteMessage",
+ "sqs:GetQueueUrl",
+ "sqs:ReceiveMessage"
+ ]
+ }
+ }
+
+ statement {
+ sid = "AllowPassingInstanceRole"
+ resources = var.create_node_iam_role ? [aws_iam_role.node[0].arn] : [var.node_iam_role_arn]
+ actions = ["iam:PassRole"]
+
+ condition {
+ test = "StringEquals"
+ variable = "iam:PassedToService"
+ values = distinct([local.ec2_sp_name, "ec2.amazonaws.com"])
+ }
+ }
+
+ statement {
+ sid = "AllowScopedInstanceProfileCreationActions"
+ resources = ["arn:${local.partition}:iam::${local.account_id}:instance-profile/*"]
+ actions = ["iam:CreateInstanceProfile"]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}"
+ values = ["owned"]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/eks:eks-cluster-name"
+ values = [var.cluster_name]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/topology.kubernetes.io/region"
+ values = [local.region]
+ }
+
+ condition {
+ test = "StringLike"
+ variable = "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass"
+ values = ["*"]
+ }
+ }
+
+ statement {
+ sid = "AllowScopedInstanceProfileTagActions"
+ resources = ["arn:${local.partition}:iam::${local.account_id}:instance-profile/*"]
+ actions = ["iam:TagInstanceProfile"]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}"
+ values = ["owned"]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:ResourceTag/topology.kubernetes.io/region"
+ values = [local.region]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}"
+ values = ["owned"]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/eks:eks-cluster-name"
+ values = [var.cluster_name]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/topology.kubernetes.io/region"
+ values = [local.region]
+ }
+
+ condition {
+ test = "StringLike"
+ variable = "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass"
+ values = ["*"]
+ }
+
+ condition {
+ test = "StringLike"
+ variable = "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass"
+ values = ["*"]
+ }
+ }
+
+ statement {
+ sid = "AllowScopedInstanceProfileActions"
+ resources = ["arn:${local.partition}:iam::${local.account_id}:instance-profile/*"]
+ actions = [
+ "iam:AddRoleToInstanceProfile",
+ "iam:RemoveRoleFromInstanceProfile",
+ "iam:DeleteInstanceProfile"
+ ]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}"
+ values = ["owned"]
+ }
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:ResourceTag/topology.kubernetes.io/region"
+ values = [local.region]
+ }
+
+ condition {
+ test = "StringLike"
+ variable = "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass"
+ values = ["*"]
+ }
+ }
+
+ statement {
+ sid = "AllowInstanceProfileReadActions"
+ resources = ["arn:${local.partition}:iam::${local.account_id}:instance-profile/*"]
+ actions = ["iam:GetInstanceProfile"]
+ }
+
+ statement {
+ sid = "AllowUnscopedInstanceProfileListAction"
+ resources = ["*"]
+ actions = ["iam:ListInstanceProfiles"]
+ }
+
+ statement {
+ sid = "AllowAPIServerEndpointDiscovery"
+ resources = ["arn:${local.partition}:eks:${local.region}:${local.account_id}:cluster/${var.cluster_name}"]
+ actions = ["eks:DescribeCluster"]
+ }
+
+ dynamic "statement" {
+ for_each = var.iam_policy_statements != null ? var.iam_policy_statements : []
+
+ content {
+ sid = statement.value.sid
+ actions = statement.value.actions
+ not_actions = statement.value.not_actions
+ effect = statement.value.effect
+ resources = statement.value.resources
+ not_resources = statement.value.not_resources
+
+ dynamic "principals" {
+ for_each = statement.value.principals != null ? statement.value.principals : []
+
+ content {
+ type = principals.value.type
+ identifiers = principals.value.identifiers
+ }
+ }
+
+ dynamic "not_principals" {
+ for_each = statement.value.not_principals != null ? statement.value.not_principals : []
+
+ content {
+ type = not_principals.value.type
+ identifiers = not_principals.value.identifiers
+ }
+ }
+
+ dynamic "condition" {
+ for_each = statement.value.condition != null ? statement.value.condition : []
+
+ content {
+ test = condition.value.test
+ values = condition.value.values
+ variable = condition.value.variable
+ }
+ }
+ }
+ }
+}
diff --git a/modules/karpenter/variables.tf b/modules/karpenter/variables.tf
new file mode 100644
index 0000000000..7e73a3883f
--- /dev/null
+++ b/modules/karpenter/variables.tf
@@ -0,0 +1,349 @@
+variable "create" {
+ description = "Controls if resources should be created (affects nearly all resources)"
+ type = bool
+ default = true
+}
+
+variable "tags" {
+ description = "A map of tags to add to all resources"
+ type = map(string)
+ default = {}
+}
+
+variable "cluster_name" {
+ description = "The name of the EKS cluster"
+ type = string
+ default = ""
+}
+
+variable "region" {
+ description = "Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration"
+ type = string
+ default = null
+}
+
+################################################################################
+# Karpenter controller IAM Role
+################################################################################
+
+variable "create_iam_role" {
+ description = "Determines whether an IAM role is created"
+ type = bool
+ default = true
+}
+
+variable "enable_inline_policy" {
+ description = "Determines whether the controller policy is created as a standard IAM policy or inline IAM policy. This can be enabled when the error `LimitExceeded: Cannot exceed quota for PolicySize: 6144` is received since standard IAM policies have a limit of 6,144 characters versus an inline role policy's limit of 10,240 ([Reference](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html))"
+ type = bool
+ default = false
+}
+
+variable "iam_role_name" {
+ description = "Name of the IAM role"
+ type = string
+ default = "KarpenterController"
+}
+
+variable "iam_role_use_name_prefix" {
+ description = "Determines whether the name of the IAM role (`iam_role_name`) is used as a prefix"
+ type = bool
+ default = true
+}
+
+variable "iam_role_path" {
+ description = "Path of the IAM role"
+ type = string
+ default = "/"
+}
+
+variable "iam_role_description" {
+ description = "IAM role description"
+ type = string
+ default = "Karpenter controller IAM role"
+}
+
+variable "iam_role_max_session_duration" {
+ description = "Maximum API session duration in seconds between 3600 and 43200"
+ type = number
+ default = null
+}
+
+variable "iam_role_permissions_boundary_arn" {
+ description = "Permissions boundary ARN to use for the IAM role"
+ type = string
+ default = null
+}
+
+variable "iam_role_tags" {
+ description = "A map of additional tags to add the the IAM role"
+ type = map(string)
+ default = {}
+}
+
+variable "iam_policy_name" {
+ description = "Name of the IAM policy"
+ type = string
+ default = "KarpenterController"
+}
+
+variable "iam_policy_use_name_prefix" {
+ description = "Determines whether the name of the IAM policy (`iam_policy_name`) is used as a prefix"
+ type = bool
+ default = true
+}
+
+variable "iam_policy_path" {
+ description = "Path of the IAM policy"
+ type = string
+ default = "/"
+}
+
+variable "iam_policy_description" {
+ description = "IAM policy description"
+ type = string
+ default = "Karpenter controller IAM policy"
+}
+
+variable "iam_role_override_assume_policy_documents" {
+ description = "A list of IAM policy documents to override the default assume role policy document for the Karpenter controller IAM role"
+ type = list(string)
+ default = []
+}
+
+variable "iam_role_source_assume_policy_documents" {
+ description = "A list of IAM policy documents to use as a source for the assume role policy document for the Karpenter controller IAM role"
+ type = list(string)
+ default = []
+}
+
+variable "iam_policy_statements" {
+ description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed"
+ type = list(object({ # TODO - change to `map(object({...}))` in next major version
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string)
+ resources = optional(list(string))
+ not_resources = optional(list(string))
+ principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ not_principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ condition = optional(list(object({
+ test = string
+ values = list(string)
+ variable = string
+ })))
+ }))
+ default = null
+}
+
+variable "iam_role_policies" {
+ description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format"
+ type = map(string)
+ default = {}
+}
+
+variable "ami_id_ssm_parameter_arns" {
+ description = "List of SSM Parameter ARNs that Karpenter controller is allowed read access (for retrieving AMI IDs)"
+ type = list(string)
+ default = []
+}
+
+################################################################################
+# Pod Identity Association
+################################################################################
+
+variable "create_pod_identity_association" {
+ description = "Determines whether to create pod identity association"
+ type = bool
+ default = true
+}
+
+variable "namespace" {
+ description = "Namespace to associate with the Karpenter Pod Identity"
+ type = string
+ default = "kube-system"
+}
+
+variable "service_account" {
+ description = "Service account to associate with the Karpenter Pod Identity"
+ type = string
+ default = "karpenter"
+}
+
+################################################################################
+# Node Termination Queue
+################################################################################
+
+variable "enable_spot_termination" {
+ description = "Determines whether to enable native spot termination handling"
+ type = bool
+ default = true
+}
+
+variable "queue_name" {
+ description = "Name of the SQS queue"
+ type = string
+ default = null
+}
+
+variable "queue_managed_sse_enabled" {
+ description = "Boolean to enable server-side encryption (SSE) of message content with SQS-owned encryption keys"
+ type = bool
+ default = true
+}
+
+variable "queue_kms_master_key_id" {
+ description = "The ID of an AWS-managed customer master key (CMK) for Amazon SQS or a custom CMK"
+ type = string
+ default = null
+}
+
+variable "queue_kms_data_key_reuse_period_seconds" {
+ description = "The length of time, in seconds, for which Amazon SQS can reuse a data key to encrypt or decrypt messages before calling AWS KMS again"
+ type = number
+ default = null
+}
+
+variable "queue_policy_statements" {
+ description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific SQS queue policy permissions as needed"
+ type = map(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string)
+ resources = optional(list(string))
+ not_resources = optional(list(string))
+ principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ not_principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ condition = optional(list(object({
+ test = string
+ values = list(string)
+ variable = string
+ })))
+ }))
+ default = null
+}
+
+################################################################################
+# Node IAM Role
+################################################################################
+
+variable "create_node_iam_role" {
+ description = "Determines whether an IAM role is created or to use an existing IAM role"
+ type = bool
+ default = true
+}
+
+variable "cluster_ip_family" {
+ description = "The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6`. Note: If `ipv6` is specified, the `AmazonEKS_CNI_IPv6_Policy` must exist in the account. This policy is created by the EKS module with `create_cni_ipv6_iam_policy = true`"
+ type = string
+ default = "ipv4"
+}
+
+variable "node_iam_role_arn" {
+ description = "Existing IAM role ARN for the IAM instance profile. Required if `create_node_iam_role` is set to `false`"
+ type = string
+ default = null
+}
+
+variable "node_iam_role_name" {
+ description = "Name to use on IAM role created"
+ type = string
+ default = null
+}
+
+variable "node_iam_role_use_name_prefix" {
+ description = "Determines whether the Node IAM role name (`node_iam_role_name`) is used as a prefix"
+ type = bool
+ default = true
+}
+
+variable "node_iam_role_path" {
+ description = "IAM role path"
+ type = string
+ default = "/"
+}
+
+variable "node_iam_role_description" {
+ description = "Description of the role"
+ type = string
+ default = null
+}
+
+variable "node_iam_role_max_session_duration" {
+ description = "Maximum API session duration in seconds between 3600 and 43200"
+ type = number
+ default = null
+}
+
+variable "node_iam_role_permissions_boundary" {
+ description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
+ type = string
+ default = null
+}
+
+variable "node_iam_role_attach_cni_policy" {
+ description = "Whether to attach the `AmazonEKS_CNI_Policy`/`AmazonEKS_CNI_IPv6_Policy` IAM policy to the IAM IAM role. WARNING: If set `false` the permissions must be assigned to the `aws-node` DaemonSet pods via another method or nodes will not be able to join the cluster"
+ type = bool
+ default = true
+}
+
+variable "node_iam_role_additional_policies" {
+ description = "Additional policies to be added to the IAM role"
+ type = map(string)
+ default = {}
+}
+
+variable "node_iam_role_tags" {
+ description = "A map of additional tags to add to the IAM role created"
+ type = map(string)
+ default = {}
+}
+
+################################################################################
+# Access Entry
+################################################################################
+
+variable "create_access_entry" {
+ description = "Determines whether an access entry is created for the IAM role used by the node IAM role"
+ type = bool
+ default = true
+}
+
+variable "access_entry_type" {
+ description = "Type of the access entry. `EC2_LINUX`, `FARGATE_LINUX`, or `EC2_WINDOWS`; defaults to `EC2_LINUX`"
+ type = string
+ default = "EC2_LINUX"
+}
+
+################################################################################
+# Node IAM Instance Profile
+################################################################################
+
+variable "create_instance_profile" {
+ description = "Whether to create an IAM instance profile"
+ type = bool
+ default = false
+}
+
+################################################################################
+# Event Bridge Rules
+################################################################################
+
+variable "rule_name_prefix" {
+ description = "Prefix used for all event bridge rules"
+ type = string
+ default = "Karpenter"
+}
diff --git a/modules/karpenter/versions.tf b/modules/karpenter/versions.tf
new file mode 100644
index 0000000000..af99edcc27
--- /dev/null
+++ b/modules/karpenter/versions.tf
@@ -0,0 +1,16 @@
+terraform {
+ required_version = ">= 1.5.7"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 6.28"
+ }
+ }
+
+ provider_meta "aws" {
+ user_agent = [
+ "github.com/terraform-aws-modules/terraform-aws-eks"
+ ]
+ }
+}
diff --git a/modules/self-managed-node-group/README.md b/modules/self-managed-node-group/README.md
new file mode 100644
index 0000000000..fb267b246a
--- /dev/null
+++ b/modules/self-managed-node-group/README.md
@@ -0,0 +1,231 @@
+# Self Managed Node Group Module
+
+Configuration in this directory creates a Self Managed Node Group (AutoScaling Group) along with an IAM role, security group, and launch template
+
+## Usage
+
+```hcl
+module "self_managed_node_group" {
+ source = "terraform-aws-modules/eks/aws//modules/self-managed-node-group"
+
+ name = "separate-self-mng"
+ cluster_name = "my-cluster"
+ kubernetes_version = "1.31"
+ cluster_endpoint = "https://012345678903AB2BAE5D1E0BFE0E2B50.gr7.us-east-1.eks.amazonaws.com"
+ cluster_auth_base64 = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKbXFqQ1VqNGdGR2w3ZW5PeWthWnZ2RjROOTVOUEZCM2o0cGhVZUsrWGFtN2ZSQnZya0d6OGxKZmZEZWF2b2plTwpQK2xOZFlqdHZncmxCUEpYdHZIZmFzTzYxVzdIZmdWQ2EvamdRM2w3RmkvL1dpQmxFOG9oWUZkdWpjc0s1SXM2CnNkbk5KTTNYUWN2TysrSitkV09NT2ZlNzlsSWdncmdQLzgvRU9CYkw3eUY1aU1hS3lsb1RHL1V3TlhPUWt3ZUcKblBNcjdiUmdkQ1NCZTlXYXowOGdGRmlxV2FOditsTDhsODBTdFZLcWVNVlUxbjQyejVwOVpQRTd4T2l6L0xTNQpYV2lXWkVkT3pMN0xBWGVCS2gzdkhnczFxMkI2d1BKZnZnS1NzWllQRGFpZTloT1NNOUJkNFNPY3JrZTRYSVBOCkVvcXVhMlYrUDRlTWJEQzhMUkVWRDdCdVZDdWdMTldWOTBoL3VJUy9WU2VOcEdUOGVScE5DakszSjc2aFlsWm8KWjNGRG5QWUY0MWpWTHhiOXF0U1ROdEp6amYwWXBEYnFWci9xZzNmQWlxbVorMzd3YWM1eHlqMDZ4cmlaRUgzZgpUM002d2lCUEVHYVlGeWN5TmNYTk5aYW9DWDJVL0N1d2JsUHAKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ=="
+
+ subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"]
+
+ // The following variables are necessary if you decide to use the module outside of the parent EKS module context.
+ // Without it, the security groups of the nodes are empty and thus won't join the cluster.
+ vpc_security_group_ids = [
+ module.eks.cluster_primary_security_group_id,
+ module.eks.cluster_security_group_id,
+ ]
+
+ min_size = 1
+ max_size = 10
+ desired_size = 1
+
+ launch_template_name = "separate-self-mng"
+ instance_type = "m5.large"
+
+ tags = {
+ Environment = "dev"
+ Terraform = "true"
+ }
+}
+```
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.5.7 |
+| [aws](#requirement\_aws) | >= 6.28 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 6.28 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [user\_data](#module\_user\_data) | ../_user_data | n/a |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_autoscaling_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group) | resource |
+| [aws_eks_access_entry.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_access_entry) | resource |
+| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource |
+| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
+| [aws_iam_role_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_launch_template.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template) | resource |
+| [aws_placement_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/placement_group) | resource |
+| [aws_security_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
+| [aws_vpc_security_group_egress_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource |
+| [aws_vpc_security_group_ingress_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |
+| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
+| [aws_ec2_instance_type.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_instance_type) | data source |
+| [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
+| [aws_ssm_parameter.ami](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
+| [aws_subnet.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [account\_id](#input\_account\_id) | The AWS account ID - pass through value to reduce number of GET requests from data sources | `string` | `""` | no |
+| [additional\_cluster\_dns\_ips](#input\_additional\_cluster\_dns\_ips) | Additional DNS IP addresses to use for the cluster. Only used when `ami_type` = `BOTTLEROCKET_*` | `list(string)` | `null` | no |
+| [ami\_id](#input\_ami\_id) | The AMI from which to launch the instance | `string` | `""` | no |
+| [ami\_type](#input\_ami\_type) | Type of Amazon Machine Image (AMI) associated with the node group. See the [AWS documentation](https://docs.aws.amazon.com/eks/latest/APIReference/API_Nodegroup.html#AmazonEKS-Type-Nodegroup-amiType) for valid values | `string` | `"AL2023_x86_64_STANDARD"` | no |
+| [autoscaling\_group\_tags](#input\_autoscaling\_group\_tags) | A map of additional tags to add to the autoscaling group created. Tags are applied to the autoscaling group only and are NOT propagated to instances | `map(string)` | `{}` | no |
+| [availability\_zones](#input\_availability\_zones) | A list of one or more availability zones for the group. Used for EC2-Classic and default subnets when not specified with `subnet_ids` argument. Conflicts with `subnet_ids` | `list(string)` | `null` | no |
+| [block\_device\_mappings](#input\_block\_device\_mappings) | Specify volumes to attach to the instance besides the volumes specified by the AMI | map(object({
device_name = optional(string)
ebs = optional(object({
delete_on_termination = optional(bool)
encrypted = optional(bool)
iops = optional(number)
kms_key_id = optional(string)
snapshot_id = optional(string)
throughput = optional(number)
volume_initialization_rate = optional(number)
volume_size = optional(number)
volume_type = optional(string)
}))
no_device = optional(string)
virtual_name = optional(string)
})) | `null` | no |
+| [bootstrap\_extra\_args](#input\_bootstrap\_extra\_args) | Additional arguments passed to the bootstrap script. When `ami_type` = `BOTTLEROCKET_*`; these are additional [settings](https://github.com/bottlerocket-os/bottlerocket#settings) that are provided to the Bottlerocket user data | `string` | `null` | no |
+| [capacity\_rebalance](#input\_capacity\_rebalance) | Indicates whether capacity rebalance is enabled | `bool` | `null` | no |
+| [capacity\_reservation\_specification](#input\_capacity\_reservation\_specification) | Targeting for EC2 capacity reservations | object({
capacity_reservation_preference = optional(string)
capacity_reservation_target = optional(object({
capacity_reservation_id = optional(string)
capacity_reservation_resource_group_arn = optional(string)
}))
}) | `null` | no |
+| [cloudinit\_post\_nodeadm](#input\_cloudinit\_post\_nodeadm) | Array of cloud-init document parts that are created after the nodeadm document part | list(object({
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
})) | `null` | no |
+| [cloudinit\_pre\_nodeadm](#input\_cloudinit\_pre\_nodeadm) | Array of cloud-init document parts that are created before the nodeadm document part | list(object({
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
})) | `null` | no |
+| [cluster\_auth\_base64](#input\_cluster\_auth\_base64) | Base64 encoded CA of associated EKS cluster | `string` | `null` | no |
+| [cluster\_endpoint](#input\_cluster\_endpoint) | Endpoint of associated EKS cluster | `string` | `null` | no |
+| [cluster\_ip\_family](#input\_cluster\_ip\_family) | The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6` | `string` | `null` | no |
+| [cluster\_name](#input\_cluster\_name) | Name of associated EKS cluster | `string` | `""` | no |
+| [cluster\_primary\_security\_group\_id](#input\_cluster\_primary\_security\_group\_id) | The ID of the EKS cluster primary security group to associate with the instance(s). This is the security group that is automatically created by the EKS service | `string` | `null` | no |
+| [cluster\_service\_cidr](#input\_cluster\_service\_cidr) | The CIDR block (IPv4 or IPv6) used by the cluster to assign Kubernetes service IP addresses. This is derived from the cluster itself | `string` | `null` | no |
+| [context](#input\_context) | Reserved | `string` | `null` | no |
+| [cpu\_options](#input\_cpu\_options) | The CPU options for the instance | object({
amd_sev_snp = optional(string)
core_count = optional(number)
threads_per_core = optional(number)
}) | `null` | no |
+| [create](#input\_create) | Determines whether to create self managed node group or not | `bool` | `true` | no |
+| [create\_access\_entry](#input\_create\_access\_entry) | Determines whether an access entry is created for the IAM role used by the node group | `bool` | `true` | no |
+| [create\_autoscaling\_group](#input\_create\_autoscaling\_group) | Determines whether to create autoscaling group or not | `bool` | `true` | no |
+| [create\_iam\_instance\_profile](#input\_create\_iam\_instance\_profile) | Determines whether an IAM instance profile is created or to use an existing IAM instance profile | `bool` | `true` | no |
+| [create\_iam\_role\_policy](#input\_create\_iam\_role\_policy) | Determines whether an IAM role policy is created or not | `bool` | `true` | no |
+| [create\_launch\_template](#input\_create\_launch\_template) | Determines whether to create launch template or not | `bool` | `true` | no |
+| [create\_placement\_group](#input\_create\_placement\_group) | Determines whether a placement group is created & used by the node group | `bool` | `false` | no |
+| [create\_security\_group](#input\_create\_security\_group) | Determines if a security group is created | `bool` | `true` | no |
+| [credit\_specification](#input\_credit\_specification) | Customize the credit specification of the instance | object({
cpu_credits = optional(string)
}) | `null` | no |
+| [default\_instance\_warmup](#input\_default\_instance\_warmup) | Amount of time, in seconds, until a newly launched instance can contribute to the Amazon CloudWatch metrics. This delay lets an instance finish initializing before Amazon EC2 Auto Scaling aggregates instance metrics, resulting in more reliable usage data | `number` | `null` | no |
+| [desired\_size](#input\_desired\_size) | The number of Amazon EC2 instances that should be running in the autoscaling group | `number` | `1` | no |
+| [desired\_size\_type](#input\_desired\_size\_type) | The unit of measurement for the value specified for `desired_size`. Supported for attribute-based instance type selection only. Valid values: `units`, `vcpu`, `memory-mib` | `string` | `null` | no |
+| [disable\_api\_termination](#input\_disable\_api\_termination) | If true, enables EC2 instance termination protection | `bool` | `null` | no |
+| [ebs\_optimized](#input\_ebs\_optimized) | If true, the launched EC2 instance will be EBS-optimized | `bool` | `null` | no |
+| [efa\_indices](#input\_efa\_indices) | The indices of the network interfaces that should be EFA-enabled. Only valid when `enable_efa_support` = `true` | `list(number)` | [| no | +| [enable\_efa\_only](#input\_enable\_efa\_only) | Determines whether to enable EFA (`false`, default) or EFA and EFA-only (`true`) network interfaces. Note: requires vpc-cni version `v1.18.4` or later | `bool` | `true` | no | +| [enable\_efa\_support](#input\_enable\_efa\_support) | Determines whether to enable Elastic Fabric Adapter (EFA) support | `bool` | `false` | no | +| [enable\_monitoring](#input\_enable\_monitoring) | Enables/disables detailed monitoring | `bool` | `false` | no | +| [enabled\_metrics](#input\_enabled\_metrics) | A list of metrics to collect. The allowed values are `GroupDesiredCapacity`, `GroupInServiceCapacity`, `GroupPendingCapacity`, `GroupMinSize`, `GroupMaxSize`, `GroupInServiceInstances`, `GroupPendingInstances`, `GroupStandbyInstances`, `GroupStandbyCapacity`, `GroupTerminatingCapacity`, `GroupTerminatingInstances`, `GroupTotalCapacity`, `GroupTotalInstances` | `list(string)` | `[]` | no | +| [enclave\_options](#input\_enclave\_options) | Enable Nitro Enclaves on launched instances |
0
]
object({
enabled = optional(bool)
}) | `null` | no |
+| [force\_delete](#input\_force\_delete) | Allows deleting the Auto Scaling Group without waiting for all instances in the pool to terminate. You can force an Auto Scaling Group to delete even if it's in the process of scaling a resource. Normally, Terraform drains all the instances before deleting the group. This bypasses that behavior and potentially leaves resources dangling | `bool` | `null` | no |
+| [health\_check\_grace\_period](#input\_health\_check\_grace\_period) | Time (in seconds) after instance comes into service before checking health | `number` | `null` | no |
+| [health\_check\_type](#input\_health\_check\_type) | `EC2` or `ELB`. Controls how health checking is done | `string` | `null` | no |
+| [iam\_instance\_profile\_arn](#input\_iam\_instance\_profile\_arn) | Amazon Resource Name (ARN) of an existing IAM instance profile that provides permissions for the node group. Required if `create_iam_instance_profile` = `false` | `string` | `null` | no |
+| [iam\_role\_additional\_policies](#input\_iam\_role\_additional\_policies) | Additional policies to be added to the IAM role | `map(string)` | `{}` | no |
+| [iam\_role\_arn](#input\_iam\_role\_arn) | ARN of the IAM role used by the instance profile. Required when `create_access_entry = true` and `create_iam_instance_profile = false` | `string` | `null` | no |
+| [iam\_role\_attach\_cni\_policy](#input\_iam\_role\_attach\_cni\_policy) | Whether to attach the `AmazonEKS_CNI_Policy`/`AmazonEKS_CNI_IPv6_Policy` IAM policy to the IAM IAM role. WARNING: If set `false` the permissions must be assigned to the `aws-node` DaemonSet pods via another method or nodes will not be able to join the cluster | `bool` | `true` | no |
+| [iam\_role\_description](#input\_iam\_role\_description) | Description of the role | `string` | `"Self managed node group IAM role"` | no |
+| [iam\_role\_name](#input\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
+| [iam\_role\_path](#input\_iam\_role\_path) | IAM role path | `string` | `null` | no |
+| [iam\_role\_permissions\_boundary](#input\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
+| [iam\_role\_policy\_statements](#input\_iam\_role\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed | list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})) | `null` | no |
+| [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
+| [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether cluster IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no |
+| [ignore\_failed\_scaling\_activities](#input\_ignore\_failed\_scaling\_activities) | Whether to ignore failed Auto Scaling scaling activities while waiting for capacity | `bool` | `null` | no |
+| [initial\_lifecycle\_hooks](#input\_initial\_lifecycle\_hooks) | One or more Lifecycle Hooks to attach to the Auto Scaling Group before instances are launched. The syntax is exactly the same as the separate `aws_autoscaling_lifecycle_hook` resource, without the `autoscaling_group_name` attribute. Please note that this will only work when creating a new Auto Scaling Group. For all other use-cases, please use `aws_autoscaling_lifecycle_hook` resource | list(object({
default_result = optional(string)
heartbeat_timeout = optional(number)
lifecycle_transition = string
name = string
notification_metadata = optional(string)
notification_target_arn = optional(string)
role_arn = optional(string)
})) | `null` | no |
+| [instance\_initiated\_shutdown\_behavior](#input\_instance\_initiated\_shutdown\_behavior) | Shutdown behavior for the instance. Can be `stop` or `terminate`. (Default: `stop`) | `string` | `null` | no |
+| [instance\_maintenance\_policy](#input\_instance\_maintenance\_policy) | If this block is configured, add a instance maintenance policy to the specified Auto Scaling group | object({
max_healthy_percentage = number
min_healthy_percentage = number
}) | `null` | no |
+| [instance\_market\_options](#input\_instance\_market\_options) | The market (purchasing) option for the instance | object({
market_type = optional(string)
spot_options = optional(object({
block_duration_minutes = optional(number)
instance_interruption_behavior = optional(string)
max_price = optional(string)
spot_instance_type = optional(string)
valid_until = optional(string)
}))
}) | `null` | no |
+| [instance\_refresh](#input\_instance\_refresh) | If this block is configured, start an Instance Refresh when this Auto Scaling Group is updated | object({
preferences = optional(object({
alarm_specification = optional(object({
alarms = optional(list(string))
}))
auto_rollback = optional(bool)
checkpoint_delay = optional(number)
checkpoint_percentages = optional(list(number))
instance_warmup = optional(number)
max_healthy_percentage = optional(number)
min_healthy_percentage = optional(number)
scale_in_protected_instances = optional(string)
skip_matching = optional(bool)
standby_instances = optional(string)
}))
strategy = optional(string)
triggers = optional(list(string))
}) | {
"preferences": {
"min_healthy_percentage": 66
},
"strategy": "Rolling"
} | no |
+| [instance\_requirements](#input\_instance\_requirements) | The attribute requirements for the type of instance. If present then `instance_type` cannot be present | object({
accelerator_count = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_manufacturers = optional(list(string))
accelerator_names = optional(list(string))
accelerator_total_memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_types = optional(list(string))
allowed_instance_types = optional(list(string))
bare_metal = optional(string)
baseline_ebs_bandwidth_mbps = optional(object({
max = optional(number)
min = optional(number)
}))
burstable_performance = optional(string)
cpu_manufacturers = optional(list(string))
excluded_instance_types = optional(list(string))
instance_generations = optional(list(string))
local_storage = optional(string)
local_storage_types = optional(list(string))
max_spot_price_as_percentage_of_optimal_on_demand_price = optional(number)
memory_gib_per_vcpu = optional(object({
max = optional(number)
min = optional(number)
}))
memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
network_bandwidth_gbps = optional(object({
max = optional(number)
min = optional(number)
}))
network_interface_count = optional(object({
max = optional(number)
min = optional(number)
}))
on_demand_max_price_percentage_over_lowest_price = optional(number)
require_hibernate_support = optional(bool)
spot_max_price_percentage_over_lowest_price = optional(number)
total_local_storage_gb = optional(object({
max = optional(number)
min = optional(number)
}))
vcpu_count = optional(object({
max = optional(number)
min = string
}))
}) | `null` | no |
+| [instance\_type](#input\_instance\_type) | The type of the instance to launch | `string` | `"m6i.large"` | no |
+| [kernel\_id](#input\_kernel\_id) | The kernel ID | `string` | `null` | no |
+| [key\_name](#input\_key\_name) | The key name that should be used for the instance | `string` | `null` | no |
+| [kubernetes\_version](#input\_kubernetes\_version) | Kubernetes cluster version - used to lookup default AMI ID if one is not provided | `string` | `null` | no |
+| [launch\_template\_default\_version](#input\_launch\_template\_default\_version) | Default Version of the launch template | `string` | `null` | no |
+| [launch\_template\_description](#input\_launch\_template\_description) | Description of the launch template | `string` | `null` | no |
+| [launch\_template\_id](#input\_launch\_template\_id) | The ID of an existing launch template to use. Required when `create_launch_template` = `false` | `string` | `""` | no |
+| [launch\_template\_name](#input\_launch\_template\_name) | Name of launch template to be created | `string` | `null` | no |
+| [launch\_template\_tags](#input\_launch\_template\_tags) | A map of additional tags to add to the tag\_specifications of launch template created | `map(string)` | `{}` | no |
+| [launch\_template\_use\_name\_prefix](#input\_launch\_template\_use\_name\_prefix) | Determines whether to use `launch_template_name` as is or create a unique name beginning with the `launch_template_name` as the prefix | `bool` | `true` | no |
+| [launch\_template\_version](#input\_launch\_template\_version) | Launch template version. Can be version number, `$Latest`, or `$Default` | `string` | `null` | no |
+| [license\_specifications](#input\_license\_specifications) | A list of license specifications to associate with | list(object({
license_configuration_arn = string
})) | `null` | no |
+| [maintenance\_options](#input\_maintenance\_options) | The maintenance options for the instance | object({
auto_recovery = optional(string)
}) | `null` | no |
+| [max\_instance\_lifetime](#input\_max\_instance\_lifetime) | The maximum amount of time, in seconds, that an instance can be in service, values must be either equal to 0 or between 604800 and 31536000 seconds | `number` | `null` | no |
+| [max\_size](#input\_max\_size) | The maximum size of the autoscaling group | `number` | `3` | no |
+| [metadata\_options](#input\_metadata\_options) | Customize the metadata options for the instance | object({
http_endpoint = optional(string, "enabled")
http_protocol_ipv6 = optional(string)
http_put_response_hop_limit = optional(number, 1)
http_tokens = optional(string, "required")
instance_metadata_tags = optional(string)
}) | {
"http_endpoint": "enabled",
"http_put_response_hop_limit": 1,
"http_tokens": "required"
} | no |
+| [metrics\_granularity](#input\_metrics\_granularity) | The granularity to associate with the metrics to collect. The only valid value is `1Minute` | `string` | `null` | no |
+| [min\_size](#input\_min\_size) | The minimum size of the autoscaling group | `number` | `1` | no |
+| [mixed\_instances\_policy](#input\_mixed\_instances\_policy) | Configuration block containing settings to define launch targets for Auto Scaling groups | object({
instances_distribution = optional(object({
on_demand_allocation_strategy = optional(string)
on_demand_base_capacity = optional(number)
on_demand_percentage_above_base_capacity = optional(number)
spot_allocation_strategy = optional(string)
spot_instance_pools = optional(number)
spot_max_price = optional(string)
}))
launch_template = object({
override = optional(list(object({
instance_requirements = optional(object({
accelerator_count = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_manufacturers = optional(list(string))
accelerator_names = optional(list(string))
accelerator_total_memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_types = optional(list(string))
allowed_instance_types = optional(list(string))
bare_metal = optional(string)
baseline_ebs_bandwidth_mbps = optional(object({
max = optional(number)
min = optional(number)
}))
burstable_performance = optional(string)
cpu_manufacturers = optional(list(string))
excluded_instance_types = optional(list(string))
instance_generations = optional(list(string))
local_storage = optional(string)
local_storage_types = optional(list(string))
max_spot_price_as_percentage_of_optimal_on_demand_price = optional(number)
memory_gib_per_vcpu = optional(object({
max = optional(number)
min = optional(number)
}))
memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
network_bandwidth_gbps = optional(object({
max = optional(number)
min = optional(number)
}))
network_interface_count = optional(object({
max = optional(number)
min = optional(number)
}))
on_demand_max_price_percentage_over_lowest_price = optional(number)
require_hibernate_support = optional(bool)
spot_max_price_percentage_over_lowest_price = optional(number)
total_local_storage_gb = optional(object({
max = optional(number)
min = optional(number)
}))
vcpu_count = optional(object({
max = optional(number)
min = optional(number)
}))
}))
instance_type = optional(string)
launch_template_specification = optional(object({
launch_template_id = optional(string)
launch_template_name = optional(string)
version = optional(string)
}))
weighted_capacity = optional(string)
})))
})
}) | `null` | no |
+| [name](#input\_name) | Name of the Self managed Node Group | `string` | `""` | no |
+| [network\_interfaces](#input\_network\_interfaces) | Customize network interfaces to be attached at instance boot time | list(object({
associate_carrier_ip_address = optional(bool)
associate_public_ip_address = optional(bool)
connection_tracking_specification = optional(object({
tcp_established_timeout = optional(number)
udp_stream_timeout = optional(number)
udp_timeout = optional(number)
}))
delete_on_termination = optional(bool)
description = optional(string)
device_index = optional(number)
ena_srd_specification = optional(object({
ena_srd_enabled = optional(bool)
ena_srd_udp_specification = optional(object({
ena_srd_udp_enabled = optional(bool)
}))
}))
interface_type = optional(string)
ipv4_address_count = optional(number)
ipv4_addresses = optional(list(string))
ipv4_prefix_count = optional(number)
ipv4_prefixes = optional(list(string))
ipv6_address_count = optional(number)
ipv6_addresses = optional(list(string))
ipv6_prefix_count = optional(number)
ipv6_prefixes = optional(list(string))
network_card_index = optional(number)
network_interface_id = optional(string)
primary_ipv6 = optional(bool)
private_ip_address = optional(string)
security_groups = optional(list(string), [])
})) | `[]` | no |
+| [partition](#input\_partition) | The AWS partition - pass through value to reduce number of GET requests from data sources | `string` | `""` | no |
+| [placement](#input\_placement) | The placement of the instance | object({
affinity = optional(string)
availability_zone = optional(string)
group_name = optional(string)
host_id = optional(string)
host_resource_group_arn = optional(string)
partition_number = optional(number)
spread_domain = optional(string)
tenancy = optional(string)
}) | `null` | no |
+| [placement\_group](#input\_placement\_group) | The name of the placement group into which you'll launch your instances | `string` | `null` | no |
+| [post\_bootstrap\_user\_data](#input\_post\_bootstrap\_user\_data) | User data that is appended to the user data script after of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*` | `string` | `null` | no |
+| [pre\_bootstrap\_user\_data](#input\_pre\_bootstrap\_user\_data) | User data that is injected into the user data script ahead of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*` | `string` | `null` | no |
+| [private\_dns\_name\_options](#input\_private\_dns\_name\_options) | The options for the instance hostname. The default values are inherited from the subnet | object({
enable_resource_name_dns_aaaa_record = optional(bool)
enable_resource_name_dns_a_record = optional(bool)
hostname_type = optional(string)
}) | `null` | no |
+| [protect\_from\_scale\_in](#input\_protect\_from\_scale\_in) | Allows setting instance protection. The autoscaling group will not select instances with this setting for termination during scale in events | `bool` | `false` | no |
+| [ram\_disk\_id](#input\_ram\_disk\_id) | The ID of the ram disk | `string` | `null` | no |
+| [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no |
+| [security\_group\_description](#input\_security\_group\_description) | Description of the security group created | `string` | `null` | no |
+| [security\_group\_egress\_rules](#input\_security\_group\_egress\_rules) | Security group egress rules to add to the security group created | map(object({
name = optional(string)
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool, false)
tags = optional(map(string), {})
to_port = optional(string)
})) | `{}` | no |
+| [security\_group\_ingress\_rules](#input\_security\_group\_ingress\_rules) | Security group ingress rules to add to the security group created | map(object({
name = optional(string)
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool, false)
tags = optional(map(string), {})
to_port = optional(string)
})) | `{}` | no |
+| [security\_group\_name](#input\_security\_group\_name) | Name to use on security group created | `string` | `null` | no |
+| [security\_group\_tags](#input\_security\_group\_tags) | A map of additional tags to add to the security group created | `map(string)` | `{}` | no |
+| [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Determines whether the security group name (`security_group_name`) is used as a prefix | `bool` | `true` | no |
+| [subnet\_ids](#input\_subnet\_ids) | A list of subnet IDs to launch resources in. Subnets automatically determine which availability zones the group will reside. Conflicts with `availability_zones` | `list(string)` | `null` | no |
+| [suspended\_processes](#input\_suspended\_processes) | A list of processes to suspend for the Auto Scaling Group. The allowed values are `Launch`, `Terminate`, `HealthCheck`, `ReplaceUnhealthy`, `AZRebalance`, `AlarmNotification`, `ScheduledActions`, `AddToLoadBalancer`. Note that if you suspend either the `Launch` or `Terminate` process types, it can prevent your Auto Scaling Group from functioning properly | `list(string)` | `[]` | no |
+| [tag\_specifications](#input\_tag\_specifications) | The tags to apply to the resources during launch | `list(string)` | [| no | +| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | +| [termination\_policies](#input\_termination\_policies) | A list of policies to decide how the instances in the Auto Scaling Group should be terminated. The allowed values are `OldestInstance`, `NewestInstance`, `OldestLaunchConfiguration`, `ClosestToNextInstanceHour`, `OldestLaunchTemplate`, `AllocationStrategy`, `Default` | `list(string)` | `[]` | no | +| [timeouts](#input\_timeouts) | Timeout configurations for the autoscaling group |
"instance",
"volume",
"network-interface"
]
object({
delete = optional(string)
}) | `null` | no |
+| [update\_launch\_template\_default\_version](#input\_update\_launch\_template\_default\_version) | Whether to update Default Version each update. Conflicts with `launch_template_default_version` | `bool` | `true` | no |
+| [use\_mixed\_instances\_policy](#input\_use\_mixed\_instances\_policy) | Determines whether to use a mixed instances policy in the autoscaling group or not | `bool` | `false` | no |
+| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether to use `name` as is or create a unique name beginning with the `name` as the prefix | `bool` | `true` | no |
+| [user\_data\_template\_path](#input\_user\_data\_template\_path) | Path to a local, custom user data template file to use when rendering user data | `string` | `null` | no |
+| [vpc\_security\_group\_ids](#input\_vpc\_security\_group\_ids) | A list of security group IDs to associate | `list(string)` | `[]` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [access\_entry\_arn](#output\_access\_entry\_arn) | Amazon Resource Name (ARN) of the Access Entry |
+| [autoscaling\_group\_arn](#output\_autoscaling\_group\_arn) | The ARN for this autoscaling group |
+| [autoscaling\_group\_availability\_zones](#output\_autoscaling\_group\_availability\_zones) | The availability zones of the autoscaling group |
+| [autoscaling\_group\_default\_cooldown](#output\_autoscaling\_group\_default\_cooldown) | Time between a scaling activity and the succeeding scaling activity |
+| [autoscaling\_group\_desired\_capacity](#output\_autoscaling\_group\_desired\_capacity) | The number of Amazon EC2 instances that should be running in the group |
+| [autoscaling\_group\_health\_check\_grace\_period](#output\_autoscaling\_group\_health\_check\_grace\_period) | Time after instance comes into service before checking health |
+| [autoscaling\_group\_health\_check\_type](#output\_autoscaling\_group\_health\_check\_type) | EC2 or ELB. Controls how health checking is done |
+| [autoscaling\_group\_id](#output\_autoscaling\_group\_id) | The autoscaling group id |
+| [autoscaling\_group\_max\_size](#output\_autoscaling\_group\_max\_size) | The maximum size of the autoscaling group |
+| [autoscaling\_group\_min\_size](#output\_autoscaling\_group\_min\_size) | The minimum size of the autoscaling group |
+| [autoscaling\_group\_name](#output\_autoscaling\_group\_name) | The autoscaling group name |
+| [autoscaling\_group\_vpc\_zone\_identifier](#output\_autoscaling\_group\_vpc\_zone\_identifier) | The VPC zone identifier |
+| [iam\_instance\_profile\_arn](#output\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile |
+| [iam\_instance\_profile\_id](#output\_iam\_instance\_profile\_id) | Instance profile's ID |
+| [iam\_instance\_profile\_unique](#output\_iam\_instance\_profile\_unique) | Stable and unique string identifying the IAM instance profile |
+| [iam\_role\_arn](#output\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [iam\_role\_name](#output\_iam\_role\_name) | The name of the IAM role |
+| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
+| [image\_id](#output\_image\_id) | ID of the image |
+| [launch\_template\_arn](#output\_launch\_template\_arn) | The ARN of the launch template |
+| [launch\_template\_id](#output\_launch\_template\_id) | The ID of the launch template |
+| [launch\_template\_latest\_version](#output\_launch\_template\_latest\_version) | The latest version of the launch template |
+| [launch\_template\_name](#output\_launch\_template\_name) | The name of the launch template |
+| [security\_group\_arn](#output\_security\_group\_arn) | Amazon Resource Name (ARN) of the security group |
+| [security\_group\_id](#output\_security\_group\_id) | ID of the security group |
+| [user\_data](#output\_user\_data) | Base64 encoded user data |
+
+
+## License
+
+Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/LICENSE) for full details.
diff --git a/modules/self-managed-node-group/main.tf b/modules/self-managed-node-group/main.tf
new file mode 100644
index 0000000000..8a65dc6a66
--- /dev/null
+++ b/modules/self-managed-node-group/main.tf
@@ -0,0 +1,1104 @@
+data "aws_partition" "current" {
+ count = var.create && var.partition == "" ? 1 : 0
+}
+data "aws_caller_identity" "current" {
+ count = var.create && var.account_id == "" ? 1 : 0
+}
+
+locals {
+ partition = try(data.aws_partition.current[0].partition, var.partition)
+ account_id = try(data.aws_caller_identity.current[0].account_id, var.account_id)
+}
+
+################################################################################
+# AMI SSM Parameter
+################################################################################
+
+locals {
+ # Just to ensure templating doesn't fail when values are not provided
+ ssm_kubernetes_version = var.kubernetes_version != null ? var.kubernetes_version : ""
+
+ # Map the AMI type to the respective SSM param path
+ ami_type_to_ssm_param = {
+ AL2_x86_64 = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2/recommended/image_id"
+ AL2_x86_64_GPU = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2-gpu/recommended/image_id"
+ AL2_ARM_64 = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2-arm64/recommended/image_id"
+ BOTTLEROCKET_ARM_64 = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}/arm64/latest/image_id"
+ BOTTLEROCKET_x86_64 = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}/x86_64/latest/image_id"
+ BOTTLEROCKET_ARM_64_FIPS = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}-fips/arm64/latest/image_id"
+ BOTTLEROCKET_x86_64_FIPS = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}-fips/x86_64/latest/image_id"
+ BOTTLEROCKET_ARM_64_NVIDIA = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}-nvidia/arm64/latest/image_id"
+ BOTTLEROCKET_x86_64_NVIDIA = "/aws/service/bottlerocket/aws-k8s-${local.ssm_kubernetes_version}-nvidia/x86_64/latest/image_id"
+ WINDOWS_CORE_2019_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-EKS_Optimized-${local.ssm_kubernetes_version}/image_id"
+ WINDOWS_FULL_2019_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2019-English-Core-EKS_Optimized-${local.ssm_kubernetes_version}/image_id"
+ WINDOWS_CORE_2022_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2022-English-Full-EKS_Optimized-${local.ssm_kubernetes_version}/image_id"
+ WINDOWS_FULL_2022_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2022-English-Core-EKS_Optimized-${local.ssm_kubernetes_version}/image_id"
+ AL2023_x86_64_STANDARD = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2023/x86_64/standard/recommended/image_id"
+ AL2023_ARM_64_STANDARD = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2023/arm64/standard/recommended/image_id"
+ AL2023_x86_64_NEURON = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2023/x86_64/neuron/recommended/image_id"
+ AL2023_x86_64_NVIDIA = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2023/x86_64/nvidia/recommended/image_id"
+ AL2023_ARM_64_NVIDIA = "/aws/service/eks/optimized-ami/${local.ssm_kubernetes_version}/amazon-linux-2023/arm64/nvidia/recommended/image_id"
+ }
+}
+
+data "aws_ssm_parameter" "ami" {
+ count = var.create ? 1 : 0
+
+ region = var.region
+
+ name = local.ami_type_to_ssm_param[var.ami_type]
+}
+
+################################################################################
+# User Data
+################################################################################
+
+module "user_data" {
+ source = "../_user_data"
+
+ create = var.create
+ ami_type = var.ami_type
+ is_eks_managed_node_group = false
+
+ cluster_name = var.cluster_name
+ cluster_endpoint = var.cluster_endpoint
+ cluster_auth_base64 = var.cluster_auth_base64
+ cluster_ip_family = var.cluster_ip_family
+ cluster_service_cidr = var.cluster_service_cidr
+ additional_cluster_dns_ips = var.additional_cluster_dns_ips
+
+ enable_bootstrap_user_data = true
+ pre_bootstrap_user_data = var.pre_bootstrap_user_data
+ post_bootstrap_user_data = var.post_bootstrap_user_data
+ bootstrap_extra_args = var.bootstrap_extra_args
+ user_data_template_path = var.user_data_template_path
+
+ cloudinit_pre_nodeadm = var.cloudinit_pre_nodeadm
+ cloudinit_post_nodeadm = var.cloudinit_post_nodeadm
+}
+
+################################################################################
+# EFA Support
+################################################################################
+
+data "aws_ec2_instance_type" "this" {
+ count = local.enable_efa_support ? 1 : 0
+
+ region = var.region
+
+ instance_type = var.instance_type
+}
+
+locals {
+ enable_efa_support = var.create && var.enable_efa_support && local.instance_type_provided
+
+ instance_type_provided = var.instance_type != ""
+ num_network_cards = try(data.aws_ec2_instance_type.this[0].maximum_network_cards, 0)
+
+ # Primary network interface must be EFA, remaining can be EFA or EFA-only
+ efa_network_interfaces = [
+ for i in range(local.num_network_cards) : {
+ associate_public_ip_address = false
+ delete_on_termination = true
+ device_index = i == 0 ? 0 : 1
+ network_card_index = i
+ interface_type = var.enable_efa_only ? contains(concat([0], var.efa_indices), i) ? "efa" : "efa-only" : "efa"
+
+ # Null out due to error: The true and false result expressions must have consistent types. The 'true' value is tuple, but the 'false' value is list of objects.
+ associate_carrier_ip_address = null
+ connection_tracking_specification = null
+ description = "EFA${var.enable_efa_only ? "-only" : ""} Network Interface ${i}"
+ ena_srd_specification = null
+ ipv4_address_count = null
+ ipv4_addresses = null
+ ipv4_prefix_count = null
+ ipv4_prefixes = null
+ ipv6_address_count = null
+ ipv6_addresses = null
+ ipv6_prefix_count = null
+ ipv6_prefixes = null
+ network_interface_id = null
+ primary_ipv6 = null
+ private_ip_address = null
+ security_groups = []
+ }
+ ]
+
+ network_interfaces = local.enable_efa_support ? local.efa_network_interfaces : var.network_interfaces
+}
+
+################################################################################
+# Launch template
+################################################################################
+
+locals {
+ launch_template_name = coalesce(var.launch_template_name, "${var.name}-node-group")
+ security_group_ids = compact(concat([var.cluster_primary_security_group_id], var.vpc_security_group_ids))
+}
+
+resource "aws_launch_template" "this" {
+ count = var.create && var.create_launch_template ? 1 : 0
+
+ region = var.region
+
+ dynamic "block_device_mappings" {
+ for_each = var.block_device_mappings != null ? var.block_device_mappings : {}
+
+ content {
+ device_name = block_device_mappings.value.device_name
+
+ dynamic "ebs" {
+ for_each = block_device_mappings.value.ebs != null ? [block_device_mappings.value.ebs] : []
+
+ content {
+ delete_on_termination = ebs.value.delete_on_termination
+ encrypted = ebs.value.encrypted
+ iops = ebs.value.iops
+ kms_key_id = ebs.value.kms_key_id
+ snapshot_id = ebs.value.snapshot_id
+ throughput = ebs.value.throughput
+ volume_initialization_rate = ebs.value.volume_initialization_rate
+ volume_size = ebs.value.volume_size
+ volume_type = ebs.value.volume_type
+ }
+ }
+
+ no_device = block_device_mappings.value.no_device
+ virtual_name = block_device_mappings.value.virtual_name
+ }
+ }
+
+ dynamic "capacity_reservation_specification" {
+ for_each = var.capacity_reservation_specification != null ? [var.capacity_reservation_specification] : []
+
+ content {
+ capacity_reservation_preference = capacity_reservation_specification.value.capacity_reservation_preference
+
+ dynamic "capacity_reservation_target" {
+ for_each = capacity_reservation_specification.value.capacity_reservation_target != null ? [capacity_reservation_specification.value.capacity_reservation_target] : []
+ content {
+ capacity_reservation_id = capacity_reservation_target.value.capacity_reservation_id
+ capacity_reservation_resource_group_arn = capacity_reservation_target.value.capacity_reservation_resource_group_arn
+ }
+ }
+ }
+ }
+
+ dynamic "cpu_options" {
+ for_each = var.cpu_options != null ? [var.cpu_options] : []
+
+ content {
+ amd_sev_snp = cpu_options.value.amd_sev_snp
+ core_count = cpu_options.value.core_count
+ threads_per_core = cpu_options.value.threads_per_core
+ }
+ }
+
+ dynamic "credit_specification" {
+ for_each = var.credit_specification != null ? [var.credit_specification] : []
+
+ content {
+ cpu_credits = credit_specification.value.cpu_credits
+ }
+ }
+
+ default_version = var.launch_template_default_version
+ description = var.launch_template_description
+ disable_api_termination = var.disable_api_termination
+ ebs_optimized = var.ebs_optimized
+
+ dynamic "enclave_options" {
+ for_each = var.enclave_options != null ? [var.enclave_options] : []
+
+ content {
+ enabled = enclave_options.value.enabled
+ }
+ }
+
+ iam_instance_profile {
+ arn = var.create_iam_instance_profile ? aws_iam_instance_profile.this[0].arn : var.iam_instance_profile_arn
+ }
+
+ image_id = coalesce(var.ami_id, nonsensitive(data.aws_ssm_parameter.ami[0].value))
+ instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior
+
+ dynamic "instance_market_options" {
+ for_each = var.instance_market_options != null ? [var.instance_market_options] : []
+
+ content {
+ market_type = instance_market_options.value.market_type
+
+ dynamic "spot_options" {
+ for_each = instance_market_options.value.spot_options != null ? [instance_market_options.value.spot_options] : []
+
+ content {
+ block_duration_minutes = spot_options.value.block_duration_minutes
+ instance_interruption_behavior = spot_options.value.instance_interruption_behavior
+ max_price = spot_options.value.max_price
+ spot_instance_type = spot_options.value.spot_instance_type
+ valid_until = spot_options.value.valid_until
+ }
+ }
+ }
+ }
+
+ dynamic "instance_requirements" {
+ for_each = var.instance_requirements != null ? [var.instance_requirements] : []
+
+ content {
+ dynamic "accelerator_count" {
+ for_each = instance_requirements.value.accelerator_count != null ? [instance_requirements.value.accelerator_count] : []
+
+ content {
+ max = accelerator_count.value.max
+ min = accelerator_count.value.min
+ }
+ }
+
+ accelerator_manufacturers = instance_requirements.value.accelerator_manufacturers
+ accelerator_names = instance_requirements.value.accelerator_names
+
+ dynamic "accelerator_total_memory_mib" {
+ for_each = instance_requirements.value.accelerator_total_memory_mib != null ? [instance_requirements.value.accelerator_total_memory_mib] : []
+
+ content {
+ max = accelerator_total_memory_mib.value.max
+ min = accelerator_total_memory_mib.value.min
+ }
+ }
+
+ accelerator_types = instance_requirements.value.accelerator_types
+ allowed_instance_types = instance_requirements.value.allowed_instance_types
+ bare_metal = instance_requirements.value.bare_metal
+
+ dynamic "baseline_ebs_bandwidth_mbps" {
+ for_each = instance_requirements.value.baseline_ebs_bandwidth_mbps != null ? [instance_requirements.value.baseline_ebs_bandwidth_mbps] : []
+
+ content {
+ max = baseline_ebs_bandwidth_mbps.value.max
+ min = baseline_ebs_bandwidth_mbps.value.min
+ }
+ }
+
+ burstable_performance = instance_requirements.value.burstable_performance
+ cpu_manufacturers = instance_requirements.value.cpu_manufacturers
+ excluded_instance_types = instance_requirements.value.excluded_instance_types
+ instance_generations = instance_requirements.value.instance_generations
+ local_storage = instance_requirements.value.local_storage
+ local_storage_types = instance_requirements.value.local_storage_types
+ max_spot_price_as_percentage_of_optimal_on_demand_price = instance_requirements.value.max_spot_price_as_percentage_of_optimal_on_demand_price
+
+ dynamic "memory_gib_per_vcpu" {
+ for_each = instance_requirements.value.memory_gib_per_vcpu != null ? [instance_requirements.value.memory_gib_per_vcpu] : []
+
+ content {
+ max = memory_gib_per_vcpu.value.max
+ min = memory_gib_per_vcpu.value.min
+ }
+ }
+
+ dynamic "memory_mib" {
+ for_each = instance_requirements.value.memory_mib != null ? [instance_requirements.value.memory_mib] : []
+
+ content {
+ max = memory_mib.value.max
+ min = memory_mib.value.min
+ }
+ }
+
+ dynamic "network_interface_count" {
+ for_each = instance_requirements.value.network_interface_count != null ? [instance_requirements.value.network_interface_count] : []
+
+ content {
+ max = network_interface_count.value.max
+ min = network_interface_count.value.min
+ }
+ }
+
+ on_demand_max_price_percentage_over_lowest_price = instance_requirements.value.on_demand_max_price_percentage_over_lowest_price
+ require_hibernate_support = instance_requirements.value.require_hibernate_support
+ spot_max_price_percentage_over_lowest_price = instance_requirements.value.spot_max_price_percentage_over_lowest_price
+
+ dynamic "total_local_storage_gb" {
+ for_each = instance_requirements.value.total_local_storage_gb != null ? [instance_requirements.value.total_local_storage_gb] : []
+
+ content {
+ max = total_local_storage_gb.value.max
+ min = total_local_storage_gb.value.min
+ }
+ }
+
+ dynamic "vcpu_count" {
+ for_each = instance_requirements.value.vcpu_count != null ? [instance_requirements.value.vcpu_count] : []
+
+ content {
+ max = vcpu_count.value.max
+ min = vcpu_count.value.min
+ }
+ }
+ }
+ }
+
+ instance_type = var.instance_requirements != null ? null : var.instance_type
+ kernel_id = var.kernel_id
+ key_name = var.key_name
+
+ dynamic "license_specification" {
+ for_each = var.license_specifications != null ? var.license_specifications : []
+
+ content {
+ license_configuration_arn = license_specification.value.license_configuration_arn
+ }
+ }
+
+ dynamic "maintenance_options" {
+ for_each = var.maintenance_options != null ? [var.maintenance_options] : []
+
+ content {
+ auto_recovery = maintenance_options.value.auto_recovery
+ }
+ }
+
+ dynamic "metadata_options" {
+ for_each = [var.metadata_options]
+
+ content {
+ http_endpoint = metadata_options.value.http_endpoint
+ http_protocol_ipv6 = metadata_options.value.http_protocol_ipv6
+ http_put_response_hop_limit = metadata_options.value.http_put_response_hop_limit
+ http_tokens = metadata_options.value.http_tokens
+ instance_metadata_tags = metadata_options.value.instance_metadata_tags
+ }
+ }
+
+ dynamic "monitoring" {
+ for_each = var.enable_monitoring ? [1] : []
+
+ content {
+ enabled = var.enable_monitoring
+ }
+ }
+
+ name = var.launch_template_use_name_prefix ? null : local.launch_template_name
+ name_prefix = var.launch_template_use_name_prefix ? "${local.launch_template_name}-" : null
+
+ dynamic "network_interfaces" {
+ for_each = length(local.network_interfaces) > 0 ? local.network_interfaces : []
+
+ content {
+ associate_carrier_ip_address = network_interfaces.value.associate_carrier_ip_address
+ associate_public_ip_address = network_interfaces.value.associate_public_ip_address
+
+ dynamic "connection_tracking_specification" {
+ for_each = network_interfaces.value.connection_tracking_specification != null ? [network_interfaces.value.connection_tracking_specification] : []
+
+ content {
+ tcp_established_timeout = connection_tracking_specification.value.tcp_established_timeout
+ udp_stream_timeout = connection_tracking_specification.value.udp_stream_timeout
+ udp_timeout = connection_tracking_specification.value.udp_timeout
+ }
+ }
+
+ delete_on_termination = network_interfaces.value.delete_on_termination
+ description = network_interfaces.value.description
+ device_index = network_interfaces.value.device_index
+
+ dynamic "ena_srd_specification" {
+ for_each = network_interfaces.value.ena_srd_specification != null ? [network_interfaces.value.ena_srd_specification] : []
+
+ content {
+ ena_srd_enabled = ena_srd_specification.value.ena_srd_enabled
+
+ dynamic "ena_srd_udp_specification" {
+ for_each = ena_srd_specification.value.ena_srd_udp_specification != null ? [ena_srd_specification.value.ena_srd_udp_specification] : []
+
+ content {
+ ena_srd_udp_enabled = ena_srd_udp_specification.value.ena_srd_udp_enabled
+ }
+ }
+ }
+ }
+
+ interface_type = network_interfaces.value.interface_type
+ ipv4_address_count = network_interfaces.value.ipv4_address_count
+ ipv4_addresses = network_interfaces.value.ipv4_addresses
+ ipv4_prefix_count = network_interfaces.value.ipv4_prefix_count
+ ipv4_prefixes = network_interfaces.value.ipv4_prefixes
+ ipv6_address_count = network_interfaces.value.ipv6_address_count
+ ipv6_addresses = network_interfaces.value.ipv6_addresses
+ ipv6_prefix_count = network_interfaces.value.ipv6_prefix_count
+ ipv6_prefixes = network_interfaces.value.ipv6_prefixes
+ network_card_index = network_interfaces.value.network_card_index
+ network_interface_id = network_interfaces.value.network_interface_id
+ primary_ipv6 = network_interfaces.value.primary_ipv6
+ private_ip_address = network_interfaces.value.private_ip_address
+ # Ref: https://github.com/hashicorp/terraform-provider-aws/issues/4570
+ security_groups = compact(concat(network_interfaces.value.security_groups, local.security_group_ids))
+ # Set on EKS managed node group, will fail if set here
+ # https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html#launch-template-basics
+ # subnet_id = try(network_interfaces.value.subnet_id, null)
+ }
+ }
+
+ dynamic "placement" {
+ for_each = var.placement != null || local.create_placement_group ? [var.placement] : []
+
+ content {
+ affinity = try(placement.value.affinity, null)
+ availability_zone = try(placement.value.availability_zone, null)
+ group_name = try(aws_placement_group.this[0].name, placement.value.group_name)
+ host_id = try(placement.value.host_id, null)
+ host_resource_group_arn = try(placement.value.host_resource_group_arn, null)
+ partition_number = try(placement.value.partition_number, null)
+ spread_domain = try(placement.value.spread_domain, null)
+ tenancy = try(placement.value.tenancy, null)
+ }
+ }
+
+ dynamic "private_dns_name_options" {
+ for_each = var.private_dns_name_options != null ? [var.private_dns_name_options] : []
+
+ content {
+ enable_resource_name_dns_aaaa_record = private_dns_name_options.value.enable_resource_name_dns_aaaa_record
+ enable_resource_name_dns_a_record = private_dns_name_options.value.enable_resource_name_dns_a_record
+ hostname_type = private_dns_name_options.value.hostname_type
+ }
+ }
+
+ ram_disk_id = var.ram_disk_id
+
+ dynamic "tag_specifications" {
+ for_each = toset(var.tag_specifications)
+
+ content {
+ resource_type = tag_specifications.key
+ tags = merge(var.tags, { Name = var.name }, var.launch_template_tags)
+ }
+ }
+
+ update_default_version = var.update_launch_template_default_version
+ user_data = module.user_data.user_data
+ vpc_security_group_ids = length(local.network_interfaces) > 0 ? [] : local.security_group_ids
+
+ tags = var.tags
+
+ # Prevent premature access of policies by pods that
+ # require permissions on create/destroy that depend on nodes
+ depends_on = [
+ aws_iam_role_policy_attachment.this,
+ aws_iam_role_policy_attachment.additional,
+ ]
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+################################################################################
+# Node Group
+################################################################################
+
+locals {
+ launch_template_id = var.create && var.create_launch_template ? aws_launch_template.this[0].id : var.launch_template_id
+ # Change order to allow users to set version priority before using defaults
+ launch_template_version = coalesce(var.launch_template_version, try(aws_launch_template.this[0].default_version, "$Default"))
+}
+
+resource "aws_autoscaling_group" "this" {
+ count = var.create && var.create_autoscaling_group ? 1 : 0
+
+ region = var.region
+
+ availability_zones = var.availability_zones
+ capacity_rebalance = var.capacity_rebalance
+ context = var.context
+ default_instance_warmup = var.default_instance_warmup
+ desired_capacity = var.desired_size
+ desired_capacity_type = var.desired_size_type
+ enabled_metrics = var.enabled_metrics
+ force_delete = var.force_delete
+ health_check_grace_period = var.health_check_grace_period
+ health_check_type = var.health_check_type
+
+ dynamic "initial_lifecycle_hook" {
+ for_each = var.initial_lifecycle_hooks != null ? var.initial_lifecycle_hooks : []
+
+ content {
+ default_result = initial_lifecycle_hook.value.default_result
+ heartbeat_timeout = initial_lifecycle_hook.value.heartbeat_timeout
+ lifecycle_transition = initial_lifecycle_hook.value.lifecycle_transition
+ name = initial_lifecycle_hook.value.name
+ notification_metadata = initial_lifecycle_hook.value.notification_metadata
+ notification_target_arn = initial_lifecycle_hook.value.notification_target_arn
+ role_arn = initial_lifecycle_hook.value.role_arn
+ }
+ }
+
+ dynamic "instance_maintenance_policy" {
+ for_each = var.instance_maintenance_policy != null ? [var.instance_maintenance_policy] : []
+
+ content {
+ min_healthy_percentage = instance_maintenance_policy.value.min_healthy_percentage
+ max_healthy_percentage = instance_maintenance_policy.value.max_healthy_percentage
+ }
+ }
+
+ dynamic "instance_refresh" {
+ for_each = length({ for k, v in var.instance_refresh : k => v if v != null }) > 0 ? [var.instance_refresh] : []
+
+ content {
+ dynamic "preferences" {
+ for_each = instance_refresh.value.preferences != null ? [instance_refresh.value.preferences] : []
+
+ content {
+ dynamic "alarm_specification" {
+ for_each = preferences.value.alarm_specification != null ? [preferences.value.alarm_specification] : []
+
+ content {
+ alarms = alarm_specification.value.alarms
+ }
+ }
+
+ auto_rollback = preferences.value.auto_rollback
+ checkpoint_delay = preferences.value.checkpoint_delay
+ checkpoint_percentages = preferences.value.checkpoint_percentages
+ instance_warmup = preferences.value.instance_warmup
+ max_healthy_percentage = preferences.value.max_healthy_percentage
+ min_healthy_percentage = preferences.value.min_healthy_percentage
+ scale_in_protected_instances = preferences.value.scale_in_protected_instances
+ skip_matching = preferences.value.skip_matching
+ standby_instances = preferences.value.standby_instances
+ }
+ }
+
+ strategy = instance_refresh.value.strategy
+ triggers = instance_refresh.value.triggers
+ }
+ }
+
+ dynamic "launch_template" {
+ for_each = var.use_mixed_instances_policy ? [] : [1]
+
+ content {
+ id = local.launch_template_id
+ version = local.launch_template_version
+ }
+ }
+
+ max_instance_lifetime = var.max_instance_lifetime
+ max_size = var.max_size
+ metrics_granularity = var.metrics_granularity
+ min_size = var.min_size
+
+ ignore_failed_scaling_activities = var.ignore_failed_scaling_activities
+
+ dynamic "mixed_instances_policy" {
+ for_each = var.use_mixed_instances_policy ? [var.mixed_instances_policy] : []
+
+ content {
+ dynamic "instances_distribution" {
+ for_each = mixed_instances_policy.value.instances_distribution != null ? [mixed_instances_policy.value.instances_distribution] : []
+
+ content {
+ on_demand_allocation_strategy = instances_distribution.value.on_demand_allocation_strategy
+ on_demand_base_capacity = instances_distribution.value.on_demand_base_capacity
+ on_demand_percentage_above_base_capacity = instances_distribution.value.on_demand_percentage_above_base_capacity
+ spot_allocation_strategy = instances_distribution.value.spot_allocation_strategy
+ spot_instance_pools = instances_distribution.value.spot_instance_pools
+ spot_max_price = instances_distribution.value.spot_max_price
+ }
+ }
+
+ dynamic "launch_template" {
+ for_each = [mixed_instances_policy.value.launch_template]
+
+ content {
+ launch_template_specification {
+ launch_template_id = local.launch_template_id
+ version = local.launch_template_version
+ }
+
+ dynamic "override" {
+ for_each = launch_template.value.override != null ? launch_template.value.override : []
+
+ content {
+ dynamic "instance_requirements" {
+ for_each = override.value.instance_requirements != null ? [override.value.instance_requirements] : []
+
+ content {
+ dynamic "accelerator_count" {
+ for_each = instance_requirements.value.accelerator_count != null ? [instance_requirements.value.accelerator_count] : []
+
+ content {
+ max = accelerator_count.value.max
+ min = accelerator_count.value.min
+ }
+ }
+
+ accelerator_manufacturers = instance_requirements.value.accelerator_manufacturers
+ accelerator_names = instance_requirements.value.accelerator_names
+
+ dynamic "accelerator_total_memory_mib" {
+ for_each = instance_requirements.value.accelerator_total_memory_mib != null ? [instance_requirements.value.accelerator_total_memory_mib] : []
+
+ content {
+ max = accelerator_total_memory_mib.value.max
+ min = accelerator_total_memory_mib.value.min
+ }
+ }
+
+ accelerator_types = instance_requirements.value.accelerator_types
+ allowed_instance_types = instance_requirements.value.allowed_instance_types
+ bare_metal = instance_requirements.value.bare_metal
+
+ dynamic "baseline_ebs_bandwidth_mbps" {
+ for_each = instance_requirements.value.baseline_ebs_bandwidth_mbps != null ? [instance_requirements.value.baseline_ebs_bandwidth_mbps] : []
+
+ content {
+ max = baseline_ebs_bandwidth_mbps.value.max
+ min = baseline_ebs_bandwidth_mbps.value.min
+ }
+ }
+
+ burstable_performance = instance_requirements.value.burstable_performance
+ cpu_manufacturers = instance_requirements.value.cpu_manufacturers
+ excluded_instance_types = instance_requirements.value.excluded_instance_types
+ instance_generations = instance_requirements.value.instance_generations
+ local_storage = instance_requirements.value.local_storage
+ local_storage_types = instance_requirements.value.local_storage_types
+ max_spot_price_as_percentage_of_optimal_on_demand_price = instance_requirements.value.max_spot_price_as_percentage_of_optimal_on_demand_price
+
+ dynamic "memory_gib_per_vcpu" {
+ for_each = instance_requirements.value.memory_gib_per_vcpu != null ? [instance_requirements.value.memory_gib_per_vcpu] : []
+
+ content {
+ max = memory_gib_per_vcpu.value.max
+ min = memory_gib_per_vcpu.value.min
+ }
+ }
+
+ dynamic "memory_mib" {
+ for_each = instance_requirements.value.memory_mib != null ? [instance_requirements.value.memory_mib] : []
+
+ content {
+ max = memory_mib.value.max
+ min = memory_mib.value.min
+ }
+ }
+
+ dynamic "network_bandwidth_gbps" {
+ for_each = instance_requirements.value.network_bandwidth_gbps != null ? [instance_requirements.value.network_bandwidth_gbps] : []
+
+ content {
+ max = network_bandwidth_gbps.value.max
+ min = network_bandwidth_gbps.value.min
+ }
+ }
+
+ dynamic "network_interface_count" {
+ for_each = instance_requirements.value.network_interface_count != null ? [instance_requirements.value.network_interface_count] : []
+
+ content {
+ max = network_interface_count.value.max
+ min = network_interface_count.value.min
+ }
+ }
+
+ on_demand_max_price_percentage_over_lowest_price = instance_requirements.value.on_demand_max_price_percentage_over_lowest_price
+ require_hibernate_support = instance_requirements.value.require_hibernate_support
+ spot_max_price_percentage_over_lowest_price = instance_requirements.value.spot_max_price_percentage_over_lowest_price
+
+ dynamic "total_local_storage_gb" {
+ for_each = instance_requirements.value.total_local_storage_gb != null ? [instance_requirements.value.total_local_storage_gb] : []
+
+ content {
+ max = total_local_storage_gb.value.max
+ min = total_local_storage_gb.value.min
+ }
+ }
+
+ dynamic "vcpu_count" {
+ for_each = instance_requirements.value.vcpu_count != null ? [instance_requirements.value.vcpu_count] : []
+
+ content {
+ max = vcpu_count.value.max
+ min = vcpu_count.value.min
+ }
+ }
+ }
+ }
+
+ instance_type = override.value.instance_type
+
+ dynamic "launch_template_specification" {
+ for_each = override.value.launch_template_specification != null ? [override.value.launch_template_specification] : []
+
+ content {
+ launch_template_id = launch_template_specification.value.launch_template_id
+ launch_template_name = launch_template_specification.value.launch_template_name
+ version = launch_template_specification.value.version
+ }
+ }
+
+ weighted_capacity = override.value.weighted_capacity
+ }
+ }
+ }
+ }
+ }
+ }
+
+ name = var.use_name_prefix ? null : var.name
+ name_prefix = var.use_name_prefix ? "${var.name}-" : null
+ placement_group = var.placement_group
+ protect_from_scale_in = var.protect_from_scale_in
+ suspended_processes = var.suspended_processes
+
+ dynamic "tag" {
+ for_each = merge(
+ {
+ "Name" = var.name
+ "kubernetes.io/cluster/${var.cluster_name}" = "owned"
+ "k8s.io/cluster/${var.cluster_name}" = "owned"
+ },
+ var.tags
+ )
+
+ content {
+ key = tag.key
+ value = tag.value
+ propagate_at_launch = true
+ }
+ }
+
+ dynamic "tag" {
+ for_each = var.autoscaling_group_tags
+
+ content {
+ key = tag.key
+ value = tag.value
+ propagate_at_launch = false
+ }
+ }
+
+ termination_policies = var.termination_policies
+ vpc_zone_identifier = var.subnet_ids
+
+ dynamic "timeouts" {
+ for_each = var.timeouts != null ? [var.timeouts] : []
+
+ content {
+ delete = var.timeouts.delete
+ }
+ }
+
+ lifecycle {
+ create_before_destroy = true
+ ignore_changes = [
+ desired_capacity
+ ]
+ }
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+locals {
+ create_iam_instance_profile = var.create && var.create_iam_instance_profile
+
+ iam_role_name = coalesce(var.iam_role_name, "${var.name}-node-group")
+ iam_role_policy_prefix = "arn:${local.partition}:iam::aws:policy"
+
+ ipv4_cni_policy = { for k, v in {
+ AmazonEKS_CNI_Policy = "${local.iam_role_policy_prefix}/AmazonEKS_CNI_Policy"
+ } : k => v if var.iam_role_attach_cni_policy && var.cluster_ip_family == "ipv4" }
+ ipv6_cni_policy = { for k, v in {
+ AmazonEKS_CNI_IPv6_Policy = "arn:${local.partition}:iam::${local.account_id}:policy/AmazonEKS_CNI_IPv6_Policy"
+ } : k => v if var.iam_role_attach_cni_policy && var.cluster_ip_family == "ipv6" }
+}
+
+data "aws_iam_policy_document" "assume_role_policy" {
+ count = local.create_iam_instance_profile ? 1 : 0
+
+ statement {
+ sid = "EKSNodeAssumeRole"
+ actions = ["sts:AssumeRole"]
+
+ principals {
+ type = "Service"
+ identifiers = ["ec2.amazonaws.com"]
+ }
+ }
+}
+
+resource "aws_iam_role" "this" {
+ count = local.create_iam_instance_profile ? 1 : 0
+
+ name = var.iam_role_use_name_prefix ? null : local.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
+ path = var.iam_role_path
+ description = var.iam_role_description
+
+ assume_role_policy = data.aws_iam_policy_document.assume_role_policy[0].json
+ permissions_boundary = var.iam_role_permissions_boundary
+ force_detach_policies = true
+
+ tags = merge(var.tags, var.iam_role_tags)
+}
+
+# Policies attached ref https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_node_group
+resource "aws_iam_role_policy_attachment" "this" {
+ for_each = { for k, v in merge(
+ {
+ AmazonEKSWorkerNodePolicy = "${local.iam_role_policy_prefix}/AmazonEKSWorkerNodePolicy"
+ AmazonEC2ContainerRegistryReadOnly = "${local.iam_role_policy_prefix}/AmazonEC2ContainerRegistryReadOnly"
+ },
+ local.ipv4_cni_policy,
+ local.ipv6_cni_policy
+ ) : k => v if local.create_iam_instance_profile }
+
+ policy_arn = each.value
+ role = aws_iam_role.this[0].name
+}
+
+resource "aws_iam_role_policy_attachment" "additional" {
+ for_each = { for k, v in var.iam_role_additional_policies : k => v if local.create_iam_instance_profile }
+
+ policy_arn = each.value
+ role = aws_iam_role.this[0].name
+}
+
+resource "aws_iam_instance_profile" "this" {
+ count = local.create_iam_instance_profile ? 1 : 0
+
+ role = aws_iam_role.this[0].name
+
+ name = var.iam_role_use_name_prefix ? null : local.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
+ path = var.iam_role_path
+
+ tags = merge(var.tags, var.iam_role_tags)
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+################################################################################
+# IAM Role Policy
+################################################################################
+
+locals {
+ create_iam_role_policy = local.create_iam_instance_profile && var.create_iam_role_policy && var.iam_role_policy_statements != null
+}
+
+data "aws_iam_policy_document" "role" {
+ count = local.create_iam_role_policy ? 1 : 0
+
+ dynamic "statement" {
+ for_each = var.iam_role_policy_statements != null ? var.iam_role_policy_statements : []
+
+ content {
+ sid = statement.value.sid
+ actions = statement.value.actions
+ not_actions = statement.value.not_actions
+ effect = statement.value.effect
+ resources = statement.value.resources
+ not_resources = statement.value.not_resources
+
+ dynamic "principals" {
+ for_each = statement.value.principals != null ? statement.value.principals : []
+
+ content {
+ type = principals.value.type
+ identifiers = principals.value.identifiers
+ }
+ }
+
+ dynamic "not_principals" {
+ for_each = statement.value.not_principals != null ? statement.value.not_principals : []
+
+ content {
+ type = not_principals.value.type
+ identifiers = not_principals.value.identifiers
+ }
+ }
+
+ dynamic "condition" {
+ for_each = statement.value.condition != null ? statement.value.condition : []
+
+ content {
+ test = condition.value.test
+ values = condition.value.values
+ variable = condition.value.variable
+ }
+ }
+ }
+ }
+}
+
+resource "aws_iam_role_policy" "this" {
+ count = local.create_iam_role_policy ? 1 : 0
+
+ name = var.iam_role_use_name_prefix ? null : local.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
+ policy = data.aws_iam_policy_document.role[0].json
+ role = aws_iam_role.this[0].id
+}
+
+################################################################################
+# Placement Group
+################################################################################
+
+locals {
+ create_placement_group = var.create && (local.enable_efa_support || var.create_placement_group)
+}
+
+resource "aws_placement_group" "this" {
+ count = local.create_placement_group ? 1 : 0
+
+ region = var.region
+
+ name = "${var.cluster_name}-${var.name}"
+ strategy = "cluster"
+
+ tags = var.tags
+}
+
+################################################################################
+# Access Entry
+################################################################################
+
+resource "aws_eks_access_entry" "this" {
+ count = var.create && var.create_access_entry ? 1 : 0
+
+ region = var.region
+
+ cluster_name = var.cluster_name
+ principal_arn = var.create_iam_instance_profile ? aws_iam_role.this[0].arn : var.iam_role_arn
+ type = startswith(var.ami_type, "WINDOWS_") ? "EC2_WINDOWS" : "EC2_LINUX"
+
+ tags = var.tags
+}
+
+################################################################################
+# Security Group
+################################################################################
+
+locals {
+ create_security_group = var.create && var.create_security_group && length(merge(local.security_group_ingress_rules, local.security_group_egress_rules)) > 0
+ security_group_name = coalesce(var.security_group_name, "${var.cluster_name}-${var.name}")
+
+ security_group_ingress_rules = merge({ for k, v in
+ {
+ all_self_efa = {
+ description = "Node to node EFA"
+ ip_protocol = "-1"
+ self = true
+
+ # Null out due to variable type and not using `try()` in resource
+ cidr_ipv4 = null
+ cidr_ipv6 = null
+ from_port = null
+ name = null
+ prefix_list_id = null
+ tags = {}
+ }
+ } : k => v if var.enable_efa_support
+ },
+ var.security_group_ingress_rules
+ )
+ security_group_egress_rules = merge({ for k, v in
+ {
+ all_self_efa = {
+ description = "Node to node EFA"
+ ip_protocol = "-1"
+ self = true
+
+ # Null out due to variable type and not using `try()` in resource
+ cidr_ipv4 = null
+ cidr_ipv6 = null
+ to_port = null
+ name = null
+ prefix_list_id = null
+ tags = {}
+ }
+ } : k => v if var.enable_efa_support
+ },
+ var.security_group_egress_rules
+ )
+}
+
+data "aws_subnet" "this" {
+ count = local.create_security_group ? 1 : 0
+
+ region = var.region
+
+ id = element(var.subnet_ids, 0)
+}
+
+resource "aws_security_group" "this" {
+ count = local.create_security_group ? 1 : 0
+
+ region = var.region
+
+ name = var.security_group_use_name_prefix ? null : local.security_group_name
+ name_prefix = var.security_group_use_name_prefix ? "${local.security_group_name}-" : null
+ description = var.security_group_description
+ vpc_id = data.aws_subnet.this[0].vpc_id
+
+ tags = merge(
+ var.tags,
+ { "Name" = local.security_group_name },
+ var.security_group_tags
+ )
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+resource "aws_vpc_security_group_ingress_rule" "this" {
+ for_each = { for k, v in local.security_group_ingress_rules : k => v if length(local.security_group_ingress_rules) > 0 && local.create_security_group }
+
+ region = var.region
+
+ cidr_ipv4 = each.value.cidr_ipv4
+ cidr_ipv6 = each.value.cidr_ipv6
+ description = each.value.description
+ from_port = each.value.from_port
+ ip_protocol = each.value.ip_protocol
+ prefix_list_id = each.value.prefix_list_id
+ referenced_security_group_id = each.value.self ? aws_security_group.this[0].id : each.value.referenced_security_group_id
+ security_group_id = aws_security_group.this[0].id
+ tags = merge(
+ var.tags,
+ var.security_group_tags,
+ { "Name" = coalesce(each.value.name, "${local.security_group_name}-${each.key}") },
+ each.value.tags
+ )
+ to_port = try(coalesce(each.value.to_port, each.value.from_port), null)
+}
+
+resource "aws_vpc_security_group_egress_rule" "this" {
+ for_each = { for k, v in local.security_group_egress_rules : k => v if length(local.security_group_egress_rules) > 0 && local.create_security_group }
+
+ region = var.region
+
+ cidr_ipv4 = each.value.cidr_ipv4
+ cidr_ipv6 = each.value.cidr_ipv6
+ description = each.value.description
+ from_port = try(coalesce(each.value.from_port, each.value.to_port), null)
+ ip_protocol = each.value.ip_protocol
+ prefix_list_id = each.value.prefix_list_id
+ referenced_security_group_id = each.value.self ? aws_security_group.this[0].id : each.value.referenced_security_group_id
+ security_group_id = aws_security_group.this[0].id
+ tags = merge(
+ var.tags,
+ var.security_group_tags,
+ { "Name" = coalesce(each.value.name, "${local.security_group_name}-${each.key}") },
+ each.value.tags
+ )
+ to_port = each.value.to_port
+}
diff --git a/modules/self-managed-node-group/migrations.tf b/modules/self-managed-node-group/migrations.tf
new file mode 100644
index 0000000000..5d51a7208a
--- /dev/null
+++ b/modules/self-managed-node-group/migrations.tf
@@ -0,0 +1,20 @@
+################################################################################
+# Migrations: v20.7 -> v20.8
+################################################################################
+
+# Node IAM role policy attachment
+# Commercial partition only - `moved` does now allow multiple moves to same target
+moved {
+ from = aws_iam_role_policy_attachment.this["arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"]
+ to = aws_iam_role_policy_attachment.this["AmazonEKSWorkerNodePolicy"]
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.this["arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"]
+ to = aws_iam_role_policy_attachment.this["AmazonEC2ContainerRegistryReadOnly"]
+}
+
+moved {
+ from = aws_iam_role_policy_attachment.this["arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"]
+ to = aws_iam_role_policy_attachment.this["AmazonEKS_CNI_Policy"]
+}
diff --git a/modules/self-managed-node-group/outputs.tf b/modules/self-managed-node-group/outputs.tf
new file mode 100644
index 0000000000..ad8710b890
--- /dev/null
+++ b/modules/self-managed-node-group/outputs.tf
@@ -0,0 +1,157 @@
+################################################################################
+# Launch template
+################################################################################
+
+output "launch_template_id" {
+ description = "The ID of the launch template"
+ value = try(aws_launch_template.this[0].id, null)
+}
+
+output "launch_template_arn" {
+ description = "The ARN of the launch template"
+ value = try(aws_launch_template.this[0].arn, null)
+}
+
+output "launch_template_latest_version" {
+ description = "The latest version of the launch template"
+ value = try(aws_launch_template.this[0].latest_version, null)
+}
+
+output "launch_template_name" {
+ description = "The name of the launch template"
+ value = try(aws_launch_template.this[0].name, null)
+}
+
+################################################################################
+# autoscaling group
+################################################################################
+
+output "autoscaling_group_arn" {
+ description = "The ARN for this autoscaling group"
+ value = try(aws_autoscaling_group.this[0].arn, null)
+}
+
+output "autoscaling_group_id" {
+ description = "The autoscaling group id"
+ value = try(aws_autoscaling_group.this[0].id, null)
+}
+
+output "autoscaling_group_name" {
+ description = "The autoscaling group name"
+ value = try(aws_autoscaling_group.this[0].name, null)
+}
+
+output "autoscaling_group_min_size" {
+ description = "The minimum size of the autoscaling group"
+ value = try(aws_autoscaling_group.this[0].min_size, null)
+}
+
+output "autoscaling_group_max_size" {
+ description = "The maximum size of the autoscaling group"
+ value = try(aws_autoscaling_group.this[0].max_size, null)
+}
+
+output "autoscaling_group_desired_capacity" {
+ description = "The number of Amazon EC2 instances that should be running in the group"
+ value = try(aws_autoscaling_group.this[0].desired_capacity, null)
+}
+
+output "autoscaling_group_default_cooldown" {
+ description = "Time between a scaling activity and the succeeding scaling activity"
+ value = try(aws_autoscaling_group.this[0].default_cooldown, null)
+}
+
+output "autoscaling_group_health_check_grace_period" {
+ description = "Time after instance comes into service before checking health"
+ value = try(aws_autoscaling_group.this[0].health_check_grace_period, null)
+}
+
+output "autoscaling_group_health_check_type" {
+ description = "EC2 or ELB. Controls how health checking is done"
+ value = try(aws_autoscaling_group.this[0].health_check_type, null)
+}
+
+output "autoscaling_group_availability_zones" {
+ description = "The availability zones of the autoscaling group"
+ value = try(aws_autoscaling_group.this[0].availability_zones, null)
+}
+
+output "autoscaling_group_vpc_zone_identifier" {
+ description = "The VPC zone identifier"
+ value = try(aws_autoscaling_group.this[0].vpc_zone_identifier, null)
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+output "iam_role_name" {
+ description = "The name of the IAM role"
+ value = try(aws_iam_role.this[0].name, null)
+}
+
+output "iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
+ value = try(aws_iam_role.this[0].arn, null)
+}
+
+output "iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = try(aws_iam_role.this[0].unique_id, null)
+}
+
+################################################################################
+# IAM Instance Profile
+################################################################################
+
+output "iam_instance_profile_arn" {
+ description = "ARN assigned by AWS to the instance profile"
+ value = try(aws_iam_instance_profile.this[0].arn, var.iam_instance_profile_arn)
+}
+
+output "iam_instance_profile_id" {
+ description = "Instance profile's ID"
+ value = try(aws_iam_instance_profile.this[0].id, null)
+}
+
+output "iam_instance_profile_unique" {
+ description = "Stable and unique string identifying the IAM instance profile"
+ value = try(aws_iam_instance_profile.this[0].unique_id, null)
+}
+
+################################################################################
+# Access Entry
+################################################################################
+
+output "access_entry_arn" {
+ description = "Amazon Resource Name (ARN) of the Access Entry"
+ value = try(aws_eks_access_entry.this[0].access_entry_arn, null)
+}
+
+################################################################################
+# Additional
+################################################################################
+
+output "image_id" {
+ description = "ID of the image"
+ value = try(aws_launch_template.this[0].image_id, null)
+}
+
+output "user_data" {
+ description = "Base64 encoded user data"
+ value = try(module.user_data.user_data, null)
+}
+
+################################################################################
+# Security Group
+################################################################################
+
+output "security_group_arn" {
+ description = "Amazon Resource Name (ARN) of the security group"
+ value = try(aws_security_group.this[0].arn, null)
+}
+
+output "security_group_id" {
+ description = "ID of the security group"
+ value = try(aws_security_group.this[0].id, null)
+}
diff --git a/modules/self-managed-node-group/variables.tf b/modules/self-managed-node-group/variables.tf
new file mode 100644
index 0000000000..6e42508c68
--- /dev/null
+++ b/modules/self-managed-node-group/variables.tf
@@ -0,0 +1,1029 @@
+variable "create" {
+ description = "Determines whether to create self managed node group or not"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "tags" {
+ description = "A map of tags to add to all resources"
+ type = map(string)
+ default = {}
+}
+
+variable "region" {
+ description = "Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration"
+ type = string
+ default = null
+}
+
+variable "partition" {
+ description = "The AWS partition - pass through value to reduce number of GET requests from data sources"
+ type = string
+ default = ""
+}
+
+variable "account_id" {
+ description = "The AWS account ID - pass through value to reduce number of GET requests from data sources"
+ type = string
+ default = ""
+}
+
+################################################################################
+# User Data
+################################################################################
+
+variable "cluster_name" {
+ description = "Name of associated EKS cluster"
+ type = string
+ default = ""
+}
+
+variable "cluster_endpoint" {
+ description = "Endpoint of associated EKS cluster"
+ type = string
+ default = null
+}
+
+variable "cluster_auth_base64" {
+ description = "Base64 encoded CA of associated EKS cluster"
+ type = string
+ default = null
+}
+
+variable "cluster_service_cidr" {
+ description = "The CIDR block (IPv4 or IPv6) used by the cluster to assign Kubernetes service IP addresses. This is derived from the cluster itself"
+ type = string
+ default = null
+}
+
+variable "cluster_ip_family" {
+ description = "The IP family used to assign Kubernetes pod and service addresses. Valid values are `ipv4` (default) and `ipv6`"
+ type = string
+ default = null
+}
+
+variable "additional_cluster_dns_ips" {
+ description = "Additional DNS IP addresses to use for the cluster. Only used when `ami_type` = `BOTTLEROCKET_*`"
+ type = list(string)
+ default = null
+}
+
+variable "pre_bootstrap_user_data" {
+ description = "User data that is injected into the user data script ahead of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*`"
+ type = string
+ default = null
+}
+
+variable "post_bootstrap_user_data" {
+ description = "User data that is appended to the user data script after of the EKS bootstrap script. Not used when `ami_type` = `BOTTLEROCKET_*`"
+ type = string
+ default = null
+}
+
+variable "bootstrap_extra_args" {
+ description = "Additional arguments passed to the bootstrap script. When `ami_type` = `BOTTLEROCKET_*`; these are additional [settings](https://github.com/bottlerocket-os/bottlerocket#settings) that are provided to the Bottlerocket user data"
+ type = string
+ default = null
+}
+
+variable "user_data_template_path" {
+ description = "Path to a local, custom user data template file to use when rendering user data"
+ type = string
+ default = null
+}
+
+variable "cloudinit_pre_nodeadm" {
+ description = "Array of cloud-init document parts that are created before the nodeadm document part"
+ type = list(object({
+ content = string
+ content_type = optional(string)
+ filename = optional(string)
+ merge_type = optional(string)
+ }))
+ default = null
+}
+
+variable "cloudinit_post_nodeadm" {
+ description = "Array of cloud-init document parts that are created after the nodeadm document part"
+ type = list(object({
+ content = string
+ content_type = optional(string)
+ filename = optional(string)
+ merge_type = optional(string)
+ }))
+ default = null
+}
+
+################################################################################
+# Launch template
+################################################################################
+
+variable "create_launch_template" {
+ description = "Determines whether to create launch template or not"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "launch_template_id" {
+ description = "The ID of an existing launch template to use. Required when `create_launch_template` = `false`"
+ type = string
+ default = ""
+}
+
+variable "launch_template_name" {
+ description = "Name of launch template to be created"
+ type = string
+ default = null
+}
+
+variable "launch_template_use_name_prefix" {
+ description = "Determines whether to use `launch_template_name` as is or create a unique name beginning with the `launch_template_name` as the prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "launch_template_description" {
+ description = "Description of the launch template"
+ type = string
+ default = null
+}
+
+variable "launch_template_default_version" {
+ description = "Default Version of the launch template"
+ type = string
+ default = null
+}
+
+variable "update_launch_template_default_version" {
+ description = "Whether to update Default Version each update. Conflicts with `launch_template_default_version`"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "disable_api_termination" {
+ description = "If true, enables EC2 instance termination protection"
+ type = bool
+ default = null
+}
+
+variable "instance_initiated_shutdown_behavior" {
+ description = "Shutdown behavior for the instance. Can be `stop` or `terminate`. (Default: `stop`)"
+ type = string
+ default = null
+}
+
+variable "kernel_id" {
+ description = "The kernel ID"
+ type = string
+ default = null
+}
+
+variable "ram_disk_id" {
+ description = "The ID of the ram disk"
+ type = string
+ default = null
+}
+
+variable "block_device_mappings" {
+ description = "Specify volumes to attach to the instance besides the volumes specified by the AMI"
+ type = map(object({
+ device_name = optional(string)
+ ebs = optional(object({
+ delete_on_termination = optional(bool)
+ encrypted = optional(bool)
+ iops = optional(number)
+ kms_key_id = optional(string)
+ snapshot_id = optional(string)
+ throughput = optional(number)
+ volume_initialization_rate = optional(number)
+ volume_size = optional(number)
+ volume_type = optional(string)
+ }))
+ no_device = optional(string)
+ virtual_name = optional(string)
+ }))
+ default = null
+}
+
+variable "capacity_reservation_specification" {
+ description = "Targeting for EC2 capacity reservations"
+ type = object({
+ capacity_reservation_preference = optional(string)
+ capacity_reservation_target = optional(object({
+ capacity_reservation_id = optional(string)
+ capacity_reservation_resource_group_arn = optional(string)
+ }))
+ })
+ default = null
+}
+
+variable "cpu_options" {
+ description = "The CPU options for the instance"
+ type = object({
+ amd_sev_snp = optional(string)
+ core_count = optional(number)
+ threads_per_core = optional(number)
+ })
+ default = null
+}
+
+variable "credit_specification" {
+ description = "Customize the credit specification of the instance"
+ type = object({
+ cpu_credits = optional(string)
+ })
+ default = null
+}
+
+variable "enclave_options" {
+ description = "Enable Nitro Enclaves on launched instances"
+ type = object({
+ enabled = optional(bool)
+ })
+ default = null
+}
+
+variable "instance_market_options" {
+ description = "The market (purchasing) option for the instance"
+ type = object({
+ market_type = optional(string)
+ spot_options = optional(object({
+ block_duration_minutes = optional(number)
+ instance_interruption_behavior = optional(string)
+ max_price = optional(string)
+ spot_instance_type = optional(string)
+ valid_until = optional(string)
+ }))
+ })
+ default = null
+}
+
+variable "maintenance_options" {
+ description = "The maintenance options for the instance"
+ type = object({
+ auto_recovery = optional(string)
+ })
+ default = null
+}
+
+variable "license_specifications" {
+ description = "A list of license specifications to associate with"
+ type = list(object({
+ license_configuration_arn = string
+ }))
+ default = null
+}
+
+variable "network_interfaces" {
+ description = "Customize network interfaces to be attached at instance boot time"
+ type = list(object({
+ associate_carrier_ip_address = optional(bool)
+ associate_public_ip_address = optional(bool)
+ connection_tracking_specification = optional(object({
+ tcp_established_timeout = optional(number)
+ udp_stream_timeout = optional(number)
+ udp_timeout = optional(number)
+ }))
+ delete_on_termination = optional(bool)
+ description = optional(string)
+ device_index = optional(number)
+ ena_srd_specification = optional(object({
+ ena_srd_enabled = optional(bool)
+ ena_srd_udp_specification = optional(object({
+ ena_srd_udp_enabled = optional(bool)
+ }))
+ }))
+ interface_type = optional(string)
+ ipv4_address_count = optional(number)
+ ipv4_addresses = optional(list(string))
+ ipv4_prefix_count = optional(number)
+ ipv4_prefixes = optional(list(string))
+ ipv6_address_count = optional(number)
+ ipv6_addresses = optional(list(string))
+ ipv6_prefix_count = optional(number)
+ ipv6_prefixes = optional(list(string))
+ network_card_index = optional(number)
+ network_interface_id = optional(string)
+ primary_ipv6 = optional(bool)
+ private_ip_address = optional(string)
+ security_groups = optional(list(string), [])
+ }))
+ default = []
+ nullable = false
+}
+
+variable "placement" {
+ description = "The placement of the instance"
+ type = object({
+ affinity = optional(string)
+ availability_zone = optional(string)
+ group_name = optional(string)
+ host_id = optional(string)
+ host_resource_group_arn = optional(string)
+ partition_number = optional(number)
+ spread_domain = optional(string)
+ tenancy = optional(string)
+ })
+ default = null
+}
+
+variable "create_placement_group" {
+ description = "Determines whether a placement group is created & used by the node group"
+ type = bool
+ default = false
+ nullable = false
+}
+
+variable "private_dns_name_options" {
+ description = "The options for the instance hostname. The default values are inherited from the subnet"
+ type = object({
+ enable_resource_name_dns_aaaa_record = optional(bool)
+ enable_resource_name_dns_a_record = optional(bool)
+ hostname_type = optional(string)
+ })
+ default = null
+}
+
+variable "ebs_optimized" {
+ description = "If true, the launched EC2 instance will be EBS-optimized"
+ type = bool
+ default = null
+}
+
+variable "ami_id" {
+ description = "The AMI from which to launch the instance"
+ type = string
+ default = ""
+ nullable = false
+}
+
+variable "ami_type" {
+ description = "Type of Amazon Machine Image (AMI) associated with the node group. See the [AWS documentation](https://docs.aws.amazon.com/eks/latest/APIReference/API_Nodegroup.html#AmazonEKS-Type-Nodegroup-amiType) for valid values"
+ type = string
+ default = "AL2023_x86_64_STANDARD"
+ nullable = false
+}
+
+variable "kubernetes_version" {
+ description = "Kubernetes cluster version - used to lookup default AMI ID if one is not provided"
+ type = string
+ default = null
+}
+
+variable "instance_requirements" {
+ description = "The attribute requirements for the type of instance. If present then `instance_type` cannot be present"
+ type = object({
+ accelerator_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ accelerator_manufacturers = optional(list(string))
+ accelerator_names = optional(list(string))
+ accelerator_total_memory_mib = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ accelerator_types = optional(list(string))
+ allowed_instance_types = optional(list(string))
+ bare_metal = optional(string)
+ baseline_ebs_bandwidth_mbps = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ burstable_performance = optional(string)
+ cpu_manufacturers = optional(list(string))
+ excluded_instance_types = optional(list(string))
+ instance_generations = optional(list(string))
+ local_storage = optional(string)
+ local_storage_types = optional(list(string))
+ max_spot_price_as_percentage_of_optimal_on_demand_price = optional(number)
+ memory_gib_per_vcpu = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ memory_mib = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ network_bandwidth_gbps = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ network_interface_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ on_demand_max_price_percentage_over_lowest_price = optional(number)
+ require_hibernate_support = optional(bool)
+ spot_max_price_percentage_over_lowest_price = optional(number)
+ total_local_storage_gb = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ vcpu_count = optional(object({
+ max = optional(number)
+ min = string
+ }))
+ })
+ default = null
+}
+
+variable "instance_type" {
+ description = "The type of the instance to launch"
+ type = string
+ default = "m6i.large"
+ nullable = false
+}
+
+variable "key_name" {
+ description = "The key name that should be used for the instance"
+ type = string
+ default = null
+}
+
+variable "vpc_security_group_ids" {
+ description = "A list of security group IDs to associate"
+ type = list(string)
+ default = []
+ nullable = false
+}
+
+variable "cluster_primary_security_group_id" {
+ description = "The ID of the EKS cluster primary security group to associate with the instance(s). This is the security group that is automatically created by the EKS service"
+ type = string
+ default = null
+}
+
+variable "enable_monitoring" {
+ description = "Enables/disables detailed monitoring"
+ type = bool
+ default = false
+ nullable = false
+}
+
+variable "enable_efa_support" {
+ description = "Determines whether to enable Elastic Fabric Adapter (EFA) support"
+ type = bool
+ default = false
+ nullable = false
+}
+
+variable "enable_efa_only" {
+ description = "Determines whether to enable EFA (`false`, default) or EFA and EFA-only (`true`) network interfaces. Note: requires vpc-cni version `v1.18.4` or later"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "efa_indices" {
+ description = "The indices of the network interfaces that should be EFA-enabled. Only valid when `enable_efa_support` = `true`"
+ type = list(number)
+ default = [0]
+ nullable = false
+}
+
+variable "metadata_options" {
+ description = "Customize the metadata options for the instance"
+ type = object({
+ http_endpoint = optional(string, "enabled")
+ http_protocol_ipv6 = optional(string)
+ http_put_response_hop_limit = optional(number, 1)
+ http_tokens = optional(string, "required")
+ instance_metadata_tags = optional(string)
+ })
+ default = {
+ http_endpoint = "enabled"
+ http_put_response_hop_limit = 1
+ http_tokens = "required"
+ }
+ nullable = false
+}
+
+variable "launch_template_tags" {
+ description = "A map of additional tags to add to the tag_specifications of launch template created"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+variable "tag_specifications" {
+ description = "The tags to apply to the resources during launch"
+ type = list(string)
+ default = ["instance", "volume", "network-interface"]
+ nullable = false
+}
+
+################################################################################
+# Autoscaling group
+################################################################################
+
+variable "create_autoscaling_group" {
+ description = "Determines whether to create autoscaling group or not"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "name" {
+ description = "Name of the Self managed Node Group"
+ type = string
+ default = ""
+}
+
+variable "use_name_prefix" {
+ description = "Determines whether to use `name` as is or create a unique name beginning with the `name` as the prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "launch_template_version" {
+ description = "Launch template version. Can be version number, `$Latest`, or `$Default`"
+ type = string
+ default = null
+}
+
+variable "availability_zones" {
+ description = "A list of one or more availability zones for the group. Used for EC2-Classic and default subnets when not specified with `subnet_ids` argument. Conflicts with `subnet_ids`"
+ type = list(string)
+ default = null
+}
+
+variable "subnet_ids" {
+ description = "A list of subnet IDs to launch resources in. Subnets automatically determine which availability zones the group will reside. Conflicts with `availability_zones`"
+ type = list(string)
+ default = null
+}
+
+variable "min_size" {
+ description = "The minimum size of the autoscaling group"
+ type = number
+ default = 1
+ nullable = false
+}
+
+variable "max_size" {
+ description = "The maximum size of the autoscaling group"
+ type = number
+ default = 3
+ nullable = false
+}
+
+variable "desired_size" {
+ description = "The number of Amazon EC2 instances that should be running in the autoscaling group"
+ type = number
+ default = 1
+ nullable = false
+}
+
+variable "desired_size_type" {
+ description = "The unit of measurement for the value specified for `desired_size`. Supported for attribute-based instance type selection only. Valid values: `units`, `vcpu`, `memory-mib`"
+ type = string
+ default = null
+}
+
+variable "ignore_failed_scaling_activities" {
+ description = "Whether to ignore failed Auto Scaling scaling activities while waiting for capacity"
+ type = bool
+ default = null
+}
+
+variable "context" {
+ description = "Reserved"
+ type = string
+ default = null
+}
+
+variable "capacity_rebalance" {
+ description = "Indicates whether capacity rebalance is enabled"
+ type = bool
+ default = null
+}
+
+variable "default_instance_warmup" {
+ description = "Amount of time, in seconds, until a newly launched instance can contribute to the Amazon CloudWatch metrics. This delay lets an instance finish initializing before Amazon EC2 Auto Scaling aggregates instance metrics, resulting in more reliable usage data"
+ type = number
+ default = null
+}
+
+variable "protect_from_scale_in" {
+ description = "Allows setting instance protection. The autoscaling group will not select instances with this setting for termination during scale in events"
+ type = bool
+ default = false
+ nullable = false
+}
+
+variable "placement_group" {
+ description = "The name of the placement group into which you'll launch your instances"
+ type = string
+ default = null
+}
+
+variable "health_check_type" {
+ description = "`EC2` or `ELB`. Controls how health checking is done"
+ type = string
+ default = null
+}
+
+variable "health_check_grace_period" {
+ description = "Time (in seconds) after instance comes into service before checking health"
+ type = number
+ default = null
+}
+
+variable "force_delete" {
+ description = "Allows deleting the Auto Scaling Group without waiting for all instances in the pool to terminate. You can force an Auto Scaling Group to delete even if it's in the process of scaling a resource. Normally, Terraform drains all the instances before deleting the group. This bypasses that behavior and potentially leaves resources dangling"
+ type = bool
+ default = null
+}
+
+variable "termination_policies" {
+ description = "A list of policies to decide how the instances in the Auto Scaling Group should be terminated. The allowed values are `OldestInstance`, `NewestInstance`, `OldestLaunchConfiguration`, `ClosestToNextInstanceHour`, `OldestLaunchTemplate`, `AllocationStrategy`, `Default`"
+ type = list(string)
+ default = []
+ nullable = false
+}
+
+variable "suspended_processes" {
+ description = "A list of processes to suspend for the Auto Scaling Group. The allowed values are `Launch`, `Terminate`, `HealthCheck`, `ReplaceUnhealthy`, `AZRebalance`, `AlarmNotification`, `ScheduledActions`, `AddToLoadBalancer`. Note that if you suspend either the `Launch` or `Terminate` process types, it can prevent your Auto Scaling Group from functioning properly"
+ type = list(string)
+ default = []
+ nullable = false
+}
+
+variable "max_instance_lifetime" {
+ description = "The maximum amount of time, in seconds, that an instance can be in service, values must be either equal to 0 or between 604800 and 31536000 seconds"
+ type = number
+ default = null
+}
+
+variable "enabled_metrics" {
+ description = "A list of metrics to collect. The allowed values are `GroupDesiredCapacity`, `GroupInServiceCapacity`, `GroupPendingCapacity`, `GroupMinSize`, `GroupMaxSize`, `GroupInServiceInstances`, `GroupPendingInstances`, `GroupStandbyInstances`, `GroupStandbyCapacity`, `GroupTerminatingCapacity`, `GroupTerminatingInstances`, `GroupTotalCapacity`, `GroupTotalInstances`"
+ type = list(string)
+ default = []
+ nullable = false
+}
+
+variable "metrics_granularity" {
+ description = "The granularity to associate with the metrics to collect. The only valid value is `1Minute`"
+ type = string
+ default = null
+}
+
+variable "initial_lifecycle_hooks" {
+ description = "One or more Lifecycle Hooks to attach to the Auto Scaling Group before instances are launched. The syntax is exactly the same as the separate `aws_autoscaling_lifecycle_hook` resource, without the `autoscaling_group_name` attribute. Please note that this will only work when creating a new Auto Scaling Group. For all other use-cases, please use `aws_autoscaling_lifecycle_hook` resource"
+ type = list(object({
+ default_result = optional(string)
+ heartbeat_timeout = optional(number)
+ lifecycle_transition = string
+ name = string
+ notification_metadata = optional(string)
+ notification_target_arn = optional(string)
+ role_arn = optional(string)
+ }))
+ default = null
+}
+
+variable "instance_maintenance_policy" {
+ description = "If this block is configured, add a instance maintenance policy to the specified Auto Scaling group"
+ type = object({
+ max_healthy_percentage = number
+ min_healthy_percentage = number
+ })
+ default = null
+}
+
+variable "instance_refresh" {
+ description = "If this block is configured, start an Instance Refresh when this Auto Scaling Group is updated"
+ type = object({
+ preferences = optional(object({
+ alarm_specification = optional(object({
+ alarms = optional(list(string))
+ }))
+ auto_rollback = optional(bool)
+ checkpoint_delay = optional(number)
+ checkpoint_percentages = optional(list(number))
+ instance_warmup = optional(number)
+ max_healthy_percentage = optional(number)
+ min_healthy_percentage = optional(number)
+ scale_in_protected_instances = optional(string)
+ skip_matching = optional(bool)
+ standby_instances = optional(string)
+ }))
+ strategy = optional(string)
+ triggers = optional(list(string))
+ })
+ default = {
+ strategy = "Rolling"
+ preferences = {
+ min_healthy_percentage = 66
+ }
+ }
+ nullable = false
+}
+
+variable "use_mixed_instances_policy" {
+ description = "Determines whether to use a mixed instances policy in the autoscaling group or not"
+ type = bool
+ default = false
+ nullable = false
+}
+
+variable "mixed_instances_policy" {
+ description = "Configuration block containing settings to define launch targets for Auto Scaling groups"
+ type = object({
+ instances_distribution = optional(object({
+ on_demand_allocation_strategy = optional(string)
+ on_demand_base_capacity = optional(number)
+ on_demand_percentage_above_base_capacity = optional(number)
+ spot_allocation_strategy = optional(string)
+ spot_instance_pools = optional(number)
+ spot_max_price = optional(string)
+ }))
+ launch_template = object({
+ override = optional(list(object({
+ instance_requirements = optional(object({
+ accelerator_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ accelerator_manufacturers = optional(list(string))
+ accelerator_names = optional(list(string))
+ accelerator_total_memory_mib = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ accelerator_types = optional(list(string))
+ allowed_instance_types = optional(list(string))
+ bare_metal = optional(string)
+ baseline_ebs_bandwidth_mbps = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ burstable_performance = optional(string)
+ cpu_manufacturers = optional(list(string))
+ excluded_instance_types = optional(list(string))
+ instance_generations = optional(list(string))
+ local_storage = optional(string)
+ local_storage_types = optional(list(string))
+ max_spot_price_as_percentage_of_optimal_on_demand_price = optional(number)
+ memory_gib_per_vcpu = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ memory_mib = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ network_bandwidth_gbps = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ network_interface_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ on_demand_max_price_percentage_over_lowest_price = optional(number)
+ require_hibernate_support = optional(bool)
+ spot_max_price_percentage_over_lowest_price = optional(number)
+ total_local_storage_gb = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ vcpu_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ }))
+ instance_type = optional(string)
+ launch_template_specification = optional(object({
+ launch_template_id = optional(string)
+ launch_template_name = optional(string)
+ version = optional(string)
+ }))
+ weighted_capacity = optional(string)
+ })))
+ })
+ })
+ default = null
+}
+
+variable "timeouts" {
+ description = "Timeout configurations for the autoscaling group"
+ type = object({
+ delete = optional(string)
+ })
+ default = null
+}
+
+variable "autoscaling_group_tags" {
+ description = "A map of additional tags to add to the autoscaling group created. Tags are applied to the autoscaling group only and are NOT propagated to instances"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+################################################################################
+# IAM Role
+################################################################################
+
+variable "create_iam_instance_profile" {
+ description = "Determines whether an IAM instance profile is created or to use an existing IAM instance profile"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_instance_profile_arn" {
+ description = "Amazon Resource Name (ARN) of an existing IAM instance profile that provides permissions for the node group. Required if `create_iam_instance_profile` = `false`"
+ type = string
+ default = null
+}
+
+variable "iam_role_name" {
+ description = "Name to use on IAM role created"
+ type = string
+ default = null
+}
+
+variable "iam_role_use_name_prefix" {
+ description = "Determines whether cluster IAM role name (`iam_role_name`) is used as a prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_role_path" {
+ description = "IAM role path"
+ type = string
+ default = null
+}
+
+variable "iam_role_description" {
+ description = "Description of the role"
+ type = string
+ default = "Self managed node group IAM role"
+ nullable = false
+}
+
+variable "iam_role_permissions_boundary" {
+ description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
+ type = string
+ default = null
+}
+
+variable "iam_role_attach_cni_policy" {
+ description = "Whether to attach the `AmazonEKS_CNI_Policy`/`AmazonEKS_CNI_IPv6_Policy` IAM policy to the IAM IAM role. WARNING: If set `false` the permissions must be assigned to the `aws-node` DaemonSet pods via another method or nodes will not be able to join the cluster"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_role_additional_policies" {
+ description = "Additional policies to be added to the IAM role"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+variable "iam_role_tags" {
+ description = "A map of additional tags to add to the IAM role created"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+################################################################################
+# IAM Role Policy
+################################################################################
+
+variable "create_iam_role_policy" {
+ description = "Determines whether an IAM role policy is created or not"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_role_policy_statements" {
+ description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed"
+ type = list(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string)
+ resources = optional(list(string))
+ not_resources = optional(list(string))
+ principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ not_principals = optional(list(object({
+ type = string
+ identifiers = list(string)
+ })))
+ condition = optional(list(object({
+ test = string
+ values = list(string)
+ variable = string
+ })))
+ }))
+ default = null
+}
+
+################################################################################
+# Access Entry
+################################################################################
+
+variable "create_access_entry" {
+ description = "Determines whether an access entry is created for the IAM role used by the node group"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "iam_role_arn" {
+ description = "ARN of the IAM role used by the instance profile. Required when `create_access_entry = true` and `create_iam_instance_profile = false`"
+ type = string
+ default = null
+}
+
+################################################################################
+# Security Group
+################################################################################
+
+variable "create_security_group" {
+ description = "Determines if a security group is created"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "security_group_name" {
+ description = "Name to use on security group created"
+ type = string
+ default = null
+}
+
+variable "security_group_use_name_prefix" {
+ description = "Determines whether the security group name (`security_group_name`) is used as a prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "security_group_description" {
+ description = "Description of the security group created"
+ type = string
+ default = null
+}
+
+variable "security_group_ingress_rules" {
+ description = "Security group ingress rules to add to the security group created"
+ type = map(object({
+ name = optional(string)
+
+ cidr_ipv4 = optional(string)
+ cidr_ipv6 = optional(string)
+ description = optional(string)
+ from_port = optional(string)
+ ip_protocol = optional(string, "tcp")
+ prefix_list_id = optional(string)
+ referenced_security_group_id = optional(string)
+ self = optional(bool, false)
+ tags = optional(map(string), {})
+ to_port = optional(string)
+ }))
+ default = {}
+ nullable = false
+}
+
+variable "security_group_egress_rules" {
+ description = "Security group egress rules to add to the security group created"
+ type = map(object({
+ name = optional(string)
+
+ cidr_ipv4 = optional(string)
+ cidr_ipv6 = optional(string)
+ description = optional(string)
+ from_port = optional(string)
+ ip_protocol = optional(string, "tcp")
+ prefix_list_id = optional(string)
+ referenced_security_group_id = optional(string)
+ self = optional(bool, false)
+ tags = optional(map(string), {})
+ to_port = optional(string)
+ }))
+ default = {}
+ nullable = false
+}
+
+variable "security_group_tags" {
+ description = "A map of additional tags to add to the security group created"
+ type = map(string)
+ default = {}
+ nullable = false
+}
diff --git a/modules/self-managed-node-group/versions.tf b/modules/self-managed-node-group/versions.tf
new file mode 100644
index 0000000000..af99edcc27
--- /dev/null
+++ b/modules/self-managed-node-group/versions.tf
@@ -0,0 +1,16 @@
+terraform {
+ required_version = ">= 1.5.7"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 6.28"
+ }
+ }
+
+ provider_meta "aws" {
+ user_agent = [
+ "github.com/terraform-aws-modules/terraform-aws-eks"
+ ]
+ }
+}
diff --git a/node_groups.tf b/node_groups.tf
new file mode 100644
index 0000000000..f43a3e325d
--- /dev/null
+++ b/node_groups.tf
@@ -0,0 +1,545 @@
+locals {
+ kubernetes_network_config = try(aws_eks_cluster.this[0].kubernetes_network_config[0], {})
+}
+
+# This sleep resource is used to provide a timed gap between the cluster creation and the downstream dependencies
+# that consume the outputs from here. Any of the values that are used as triggers can be used in dependencies
+# to ensure that the downstream resources are created after both the cluster is ready and the sleep time has passed.
+# This was primarily added to give addons that need to be configured BEFORE data plane compute resources
+# enough time to create and configure themselves before the data plane compute resources are created.
+resource "time_sleep" "this" {
+ count = var.create ? 1 : 0
+
+ create_duration = var.dataplane_wait_duration
+
+ triggers = {
+ name = aws_eks_cluster.this[0].id
+ endpoint = aws_eks_cluster.this[0].endpoint
+ kubernetes_version = aws_eks_cluster.this[0].version
+ service_cidr = var.ip_family == "ipv6" ? try(local.kubernetes_network_config.service_ipv6_cidr, "") : try(local.kubernetes_network_config.service_ipv4_cidr, "")
+
+ certificate_authority_data = aws_eks_cluster.this[0].certificate_authority[0].data
+ }
+}
+
+################################################################################
+# EKS IPV6 CNI Policy
+# https://docs.aws.amazon.com/eks/latest/userguide/cni-iam-role.html#cni-iam-role-create-ipv6-policy
+################################################################################
+
+data "aws_iam_policy_document" "cni_ipv6_policy" {
+ count = var.create && var.create_cni_ipv6_iam_policy ? 1 : 0
+
+ statement {
+ sid = "AssignDescribe"
+ actions = [
+ "ec2:AssignIpv6Addresses",
+ "ec2:DescribeInstances",
+ "ec2:DescribeTags",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DescribeInstanceTypes"
+ ]
+ resources = ["*"]
+ }
+
+ statement {
+ sid = "CreateTags"
+ actions = ["ec2:CreateTags"]
+ resources = ["arn:${local.partition}:ec2:*:*:network-interface/*"]
+ }
+}
+
+# Note - we are keeping this to a minimum in hopes that its soon replaced with an AWS managed policy like `AmazonEKS_CNI_Policy`
+resource "aws_iam_policy" "cni_ipv6_policy" {
+ count = var.create && var.create_cni_ipv6_iam_policy ? 1 : 0
+
+ # Will cause conflicts if trying to create on multiple clusters but necessary to reference by exact name in sub-modules
+ name = "AmazonEKS_CNI_IPv6_Policy"
+ description = "IAM policy for EKS CNI to assign IPV6 addresses"
+ policy = data.aws_iam_policy_document.cni_ipv6_policy[0].json
+
+ tags = var.tags
+}
+
+################################################################################
+# Node Security Group
+# Defaults follow https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html
+# Plus NTP/HTTPS (otherwise nodes fail to launch)
+################################################################################
+
+locals {
+ node_sg_name = coalesce(var.node_security_group_name, "${var.name}-node")
+ create_node_sg = var.create && var.create_node_security_group
+
+ node_security_group_id = local.create_node_sg ? aws_security_group.node[0].id : var.node_security_group_id
+
+ node_security_group_rules = {
+ ingress_cluster_443 = {
+ description = "Cluster API to node groups"
+ protocol = "tcp"
+ from_port = 443
+ to_port = 443
+ type = "ingress"
+ source_cluster_security_group = true
+ }
+ ingress_cluster_kubelet = {
+ description = "Cluster API to node kubelets"
+ protocol = "tcp"
+ from_port = 10250
+ to_port = 10250
+ type = "ingress"
+ source_cluster_security_group = true
+ }
+ ingress_self_coredns_tcp = {
+ description = "Node to node CoreDNS"
+ protocol = "tcp"
+ from_port = 53
+ to_port = 53
+ type = "ingress"
+ self = true
+ }
+ ingress_self_coredns_udp = {
+ description = "Node to node CoreDNS UDP"
+ protocol = "udp"
+ from_port = 53
+ to_port = 53
+ type = "ingress"
+ self = true
+ }
+ }
+
+ node_security_group_recommended_rules = { for k, v in {
+ ingress_nodes_ephemeral = {
+ description = "Node to node ingress on ephemeral ports"
+ protocol = "tcp"
+ from_port = 1025
+ to_port = 65535
+ type = "ingress"
+ self = true
+ }
+ # metrics-server, legacy port - TODO: remove this on the next breaking change at v22
+ ingress_cluster_4443_webhook = {
+ description = "Cluster API to node 4443/tcp webhook"
+ protocol = "tcp"
+ from_port = 4443
+ to_port = 4443
+ type = "ingress"
+ source_cluster_security_group = true
+ }
+ # metrics-server, current EKS default port
+ ingress_cluster_10251_webhook = {
+ description = "Cluster API to node 10251/tcp webhook"
+ protocol = "tcp"
+ from_port = 10251
+ to_port = 10251
+ type = "ingress"
+ source_cluster_security_group = true
+ }
+ # prometheus-adapter
+ ingress_cluster_6443_webhook = {
+ description = "Cluster API to node 6443/tcp webhook"
+ protocol = "tcp"
+ from_port = 6443
+ to_port = 6443
+ type = "ingress"
+ source_cluster_security_group = true
+ }
+ # Karpenter
+ ingress_cluster_8443_webhook = {
+ description = "Cluster API to node 8443/tcp webhook"
+ protocol = "tcp"
+ from_port = 8443
+ to_port = 8443
+ type = "ingress"
+ source_cluster_security_group = true
+ }
+ # ALB controller, NGINX
+ ingress_cluster_9443_webhook = {
+ description = "Cluster API to node 9443/tcp webhook"
+ protocol = "tcp"
+ from_port = 9443
+ to_port = 9443
+ type = "ingress"
+ source_cluster_security_group = true
+ }
+ egress_all = {
+ description = "Allow all egress"
+ protocol = "-1"
+ from_port = 0
+ to_port = 0
+ type = "egress"
+ cidr_blocks = ["0.0.0.0/0"]
+ ipv6_cidr_blocks = var.ip_family == "ipv6" ? ["::/0"] : null
+ }
+ } : k => v if var.node_security_group_enable_recommended_rules }
+}
+
+resource "aws_security_group" "node" {
+ count = local.create_node_sg ? 1 : 0
+
+ region = var.region
+
+ name = var.node_security_group_use_name_prefix ? null : local.node_sg_name
+ name_prefix = var.node_security_group_use_name_prefix ? "${local.node_sg_name}${var.prefix_separator}" : null
+ description = var.node_security_group_description
+ vpc_id = var.vpc_id
+
+ tags = merge(
+ var.tags,
+ {
+ "Name" = local.node_sg_name
+ "kubernetes.io/cluster/${var.name}" = "owned"
+ },
+ var.node_security_group_tags
+ )
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+resource "aws_security_group_rule" "node" {
+ for_each = { for k, v in merge(
+ local.node_security_group_rules,
+ local.node_security_group_recommended_rules,
+ var.node_security_group_additional_rules,
+ ) : k => v if local.create_node_sg }
+
+ region = var.region
+
+ security_group_id = aws_security_group.node[0].id
+ protocol = each.value.protocol
+ from_port = each.value.from_port
+ to_port = each.value.to_port
+ type = each.value.type
+ description = try(each.value.description, null)
+ cidr_blocks = try(each.value.cidr_blocks, null)
+ ipv6_cidr_blocks = try(each.value.ipv6_cidr_blocks, null)
+ prefix_list_ids = try(each.value.prefix_list_ids, null)
+ self = try(each.value.self, null)
+ source_security_group_id = try(each.value.source_cluster_security_group, false) ? local.security_group_id : try(each.value.source_security_group_id, null)
+}
+
+################################################################################
+# Fargate Profile
+################################################################################
+
+module "fargate_profile" {
+ source = "./modules/fargate-profile"
+
+ for_each = var.create && !local.create_outposts_local_cluster && var.fargate_profiles != null ? var.fargate_profiles : {}
+
+ create = each.value.create
+
+ region = var.region
+
+ # Pass through values to reduce GET requests from data sources
+ partition = local.partition
+ account_id = local.account_id
+
+ # Fargate Profile
+ cluster_name = time_sleep.this[0].triggers["name"]
+ cluster_ip_family = var.ip_family
+ name = coalesce(each.value.name, each.key)
+ subnet_ids = coalesce(each.value.subnet_ids, var.subnet_ids)
+ selectors = each.value.selectors
+ timeouts = each.value.timeouts
+
+ # IAM role
+ create_iam_role = each.value.create_iam_role
+ iam_role_arn = each.value.iam_role_arn
+ iam_role_name = each.value.iam_role_name
+ iam_role_use_name_prefix = each.value.iam_role_use_name_prefix
+ iam_role_path = each.value.iam_role_path
+ iam_role_description = each.value.iam_role_description
+ iam_role_permissions_boundary = each.value.iam_role_permissions_boundary
+ iam_role_tags = each.value.iam_role_tags
+ iam_role_attach_cni_policy = each.value.iam_role_attach_cni_policy
+ iam_role_additional_policies = lookup(each.value, "iam_role_additional_policies", null)
+ create_iam_role_policy = each.value.create_iam_role_policy
+ iam_role_policy_statements = each.value.iam_role_policy_statements
+
+ tags = merge(
+ var.tags,
+ each.value.tags,
+ )
+}
+
+################################################################################
+# EKS Managed Node Group
+################################################################################
+
+module "eks_managed_node_group" {
+ source = "./modules/eks-managed-node-group"
+
+ for_each = var.create && !local.create_outposts_local_cluster && var.eks_managed_node_groups != null ? var.eks_managed_node_groups : {}
+
+ create = each.value.create
+
+ region = var.region
+
+ # Pass through values to reduce GET requests from data sources
+ partition = local.partition
+ account_id = local.account_id
+
+ cluster_name = time_sleep.this[0].triggers["name"]
+ kubernetes_version = each.value.kubernetes_version != null ? each.value.kubernetes_version : time_sleep.this[0].triggers["kubernetes_version"]
+
+
+ # EKS Managed Node Group
+ name = coalesce(each.value.name, each.key)
+ use_name_prefix = each.value.use_name_prefix
+
+ subnet_ids = coalesce(each.value.subnet_ids, var.subnet_ids)
+
+ min_size = each.value.min_size
+ max_size = each.value.max_size
+ desired_size = each.value.desired_size
+
+ ami_id = each.value.ami_id
+ ami_type = each.value.ami_type
+ ami_release_version = each.value.ami_release_version
+ use_latest_ami_release_version = each.value.use_latest_ami_release_version
+
+ capacity_type = each.value.capacity_type
+ disk_size = each.value.disk_size
+ force_update_version = each.value.force_update_version
+ instance_types = each.value.instance_types
+ labels = each.value.labels
+ node_repair_config = each.value.node_repair_config
+ remote_access = each.value.remote_access
+ taints = each.value.taints
+ update_config = each.value.update_config
+ timeouts = each.value.timeouts
+
+ # User data
+ cluster_endpoint = try(time_sleep.this[0].triggers["endpoint"], "")
+ cluster_auth_base64 = try(time_sleep.this[0].triggers["certificate_authority_data"], "")
+ cluster_ip_family = var.ip_family
+ cluster_service_cidr = try(time_sleep.this[0].triggers["service_cidr"], "")
+ enable_bootstrap_user_data = each.value.enable_bootstrap_user_data
+ pre_bootstrap_user_data = each.value.pre_bootstrap_user_data
+ post_bootstrap_user_data = each.value.post_bootstrap_user_data
+ bootstrap_extra_args = each.value.bootstrap_extra_args
+ user_data_template_path = each.value.user_data_template_path
+ cloudinit_pre_nodeadm = each.value.cloudinit_pre_nodeadm
+ cloudinit_post_nodeadm = each.value.cloudinit_post_nodeadm
+
+ # Launch Template
+ create_launch_template = each.value.create_launch_template
+ use_custom_launch_template = each.value.use_custom_launch_template
+ launch_template_id = each.value.launch_template_id
+ launch_template_name = coalesce(each.value.launch_template_name, each.key)
+ launch_template_use_name_prefix = each.value.launch_template_use_name_prefix
+ launch_template_version = each.value.launch_template_version
+ launch_template_default_version = each.value.launch_template_default_version
+ update_launch_template_default_version = each.value.update_launch_template_default_version
+ launch_template_description = coalesce(each.value.launch_template_description, "Custom launch template for ${coalesce(each.value.name, each.key)} EKS managed node group")
+ launch_template_tags = each.value.launch_template_tags
+ tag_specifications = each.value.tag_specifications
+
+ ebs_optimized = each.value.ebs_optimized
+ key_name = each.value.key_name
+ disable_api_termination = each.value.disable_api_termination
+ kernel_id = each.value.kernel_id
+ ram_disk_id = each.value.ram_disk_id
+
+ block_device_mappings = each.value.block_device_mappings
+ capacity_reservation_specification = each.value.capacity_reservation_specification
+ cpu_options = each.value.cpu_options
+ credit_specification = each.value.credit_specification
+ enclave_options = each.value.enclave_options
+ instance_market_options = each.value.instance_market_options
+ license_specifications = each.value.license_specifications
+ metadata_options = each.value.metadata_options
+ enable_monitoring = each.value.enable_monitoring
+ enable_efa_support = each.value.enable_efa_support
+ enable_efa_only = each.value.enable_efa_only
+ efa_indices = each.value.efa_indices
+ create_placement_group = each.value.create_placement_group
+ placement = each.value.placement
+ network_interfaces = each.value.network_interfaces
+ maintenance_options = each.value.maintenance_options
+ private_dns_name_options = each.value.private_dns_name_options
+
+ # IAM role
+ create_iam_role = each.value.create_iam_role
+ iam_role_arn = each.value.iam_role_arn
+ iam_role_name = each.value.iam_role_name
+ iam_role_use_name_prefix = each.value.iam_role_use_name_prefix
+ iam_role_path = each.value.iam_role_path
+ iam_role_description = each.value.iam_role_description
+ iam_role_permissions_boundary = each.value.iam_role_permissions_boundary
+ iam_role_tags = each.value.iam_role_tags
+ iam_role_attach_cni_policy = each.value.iam_role_attach_cni_policy
+ iam_role_additional_policies = lookup(each.value, "iam_role_additional_policies", null)
+ create_iam_role_policy = each.value.create_iam_role_policy
+ iam_role_policy_statements = each.value.iam_role_policy_statements
+
+ # Security group
+ vpc_security_group_ids = compact(concat([local.node_security_group_id], each.value.vpc_security_group_ids))
+ cluster_primary_security_group_id = each.value.attach_cluster_primary_security_group ? aws_eks_cluster.this[0].vpc_config[0].cluster_security_group_id : null
+ create_security_group = each.value.create_security_group
+ security_group_name = each.value.security_group_name
+ security_group_use_name_prefix = each.value.security_group_use_name_prefix
+ security_group_description = each.value.security_group_description
+ security_group_ingress_rules = each.value.security_group_ingress_rules
+ security_group_egress_rules = each.value.security_group_egress_rules
+ security_group_tags = each.value.security_group_tags
+
+ tags = merge(
+ var.tags,
+ each.value.tags,
+ )
+}
+
+################################################################################
+# Self Managed Node Group
+################################################################################
+
+module "self_managed_node_group" {
+ source = "./modules/self-managed-node-group"
+
+ for_each = var.create && var.self_managed_node_groups != null ? var.self_managed_node_groups : {}
+
+ create = each.value.create
+
+ region = var.region
+
+ # Pass through values to reduce GET requests from data sources
+ partition = local.partition
+ account_id = local.account_id
+
+ cluster_name = time_sleep.this[0].triggers["name"]
+
+ # Autoscaling Group
+ create_autoscaling_group = each.value.create_autoscaling_group
+
+ name = coalesce(each.value.name, each.key)
+ use_name_prefix = each.value.use_name_prefix
+
+ availability_zones = each.value.availability_zones
+ subnet_ids = coalesce(each.value.subnet_ids, var.subnet_ids)
+
+ min_size = each.value.min_size
+ max_size = each.value.max_size
+ desired_size = each.value.desired_size
+ desired_size_type = each.value.desired_size_type
+ capacity_rebalance = each.value.capacity_rebalance
+ default_instance_warmup = each.value.default_instance_warmup
+ protect_from_scale_in = each.value.protect_from_scale_in
+ context = each.value.context
+
+ create_placement_group = each.value.create_placement_group
+ placement_group = each.value.placement_group
+ health_check_type = each.value.health_check_type
+ health_check_grace_period = each.value.health_check_grace_period
+
+ ignore_failed_scaling_activities = each.value.ignore_failed_scaling_activities
+
+ force_delete = each.value.force_delete
+ termination_policies = each.value.termination_policies
+ suspended_processes = each.value.suspended_processes
+ max_instance_lifetime = each.value.max_instance_lifetime
+
+ enabled_metrics = each.value.enabled_metrics
+ metrics_granularity = each.value.metrics_granularity
+
+ initial_lifecycle_hooks = each.value.initial_lifecycle_hooks
+ instance_maintenance_policy = each.value.instance_maintenance_policy
+ instance_refresh = each.value.instance_refresh
+ use_mixed_instances_policy = each.value.use_mixed_instances_policy
+ mixed_instances_policy = each.value.mixed_instances_policy
+
+ timeouts = each.value.timeouts
+ autoscaling_group_tags = each.value.autoscaling_group_tags
+
+ # User data
+ ami_type = each.value.ami_type
+ cluster_endpoint = try(time_sleep.this[0].triggers["endpoint"], "")
+ cluster_auth_base64 = try(time_sleep.this[0].triggers["certificate_authority_data"], "")
+ cluster_service_cidr = try(time_sleep.this[0].triggers["service_cidr"], "")
+ additional_cluster_dns_ips = each.value.additional_cluster_dns_ips
+ cluster_ip_family = var.ip_family
+ pre_bootstrap_user_data = each.value.pre_bootstrap_user_data
+ post_bootstrap_user_data = each.value.post_bootstrap_user_data
+ bootstrap_extra_args = each.value.bootstrap_extra_args
+ user_data_template_path = each.value.user_data_template_path
+ cloudinit_pre_nodeadm = each.value.cloudinit_pre_nodeadm
+ cloudinit_post_nodeadm = each.value.cloudinit_post_nodeadm
+
+ # Launch Template
+ create_launch_template = each.value.create_launch_template
+ launch_template_id = each.value.launch_template_id
+ launch_template_name = coalesce(each.value.launch_template_name, each.key)
+ launch_template_use_name_prefix = each.value.launch_template_use_name_prefix
+ launch_template_version = each.value.launch_template_version
+ launch_template_default_version = each.value.launch_template_default_version
+ update_launch_template_default_version = each.value.update_launch_template_default_version
+ launch_template_description = coalesce(each.value.launch_template_description, "Custom launch template for ${coalesce(each.value.name, each.key)} self managed node group")
+ launch_template_tags = each.value.launch_template_tags
+ tag_specifications = each.value.tag_specifications
+
+ ebs_optimized = each.value.ebs_optimized
+ ami_id = each.value.ami_id
+ kubernetes_version = each.value.kubernetes_version != null ? each.value.kubernetes_version : time_sleep.this[0].triggers["kubernetes_version"]
+ instance_type = each.value.instance_type
+ key_name = each.value.key_name
+
+ disable_api_termination = each.value.disable_api_termination
+ instance_initiated_shutdown_behavior = each.value.instance_initiated_shutdown_behavior
+ kernel_id = each.value.kernel_id
+ ram_disk_id = each.value.ram_disk_id
+
+ block_device_mappings = each.value.block_device_mappings
+ capacity_reservation_specification = each.value.capacity_reservation_specification
+ cpu_options = each.value.cpu_options
+ credit_specification = each.value.credit_specification
+ enclave_options = each.value.enclave_options
+ instance_requirements = each.value.instance_requirements
+ instance_market_options = each.value.instance_market_options
+ license_specifications = each.value.license_specifications
+ metadata_options = each.value.metadata_options
+ enable_monitoring = each.value.enable_monitoring
+ enable_efa_support = each.value.enable_efa_support
+ enable_efa_only = each.value.enable_efa_only
+ efa_indices = each.value.efa_indices
+ network_interfaces = each.value.network_interfaces
+ placement = each.value.placement
+ maintenance_options = each.value.maintenance_options
+ private_dns_name_options = each.value.private_dns_name_options
+
+ # IAM role
+ create_iam_instance_profile = each.value.create_iam_instance_profile
+ iam_instance_profile_arn = each.value.iam_instance_profile_arn
+ iam_role_name = each.value.iam_role_name
+ iam_role_use_name_prefix = each.value.iam_role_use_name_prefix
+ iam_role_path = each.value.iam_role_path
+ iam_role_description = each.value.iam_role_description
+ iam_role_permissions_boundary = each.value.iam_role_permissions_boundary
+ iam_role_tags = each.value.iam_role_tags
+ iam_role_attach_cni_policy = each.value.iam_role_attach_cni_policy
+ iam_role_additional_policies = lookup(each.value, "iam_role_additional_policies", null)
+ create_iam_role_policy = each.value.create_iam_role_policy
+ iam_role_policy_statements = each.value.iam_role_policy_statements
+
+ # Access entry
+ create_access_entry = each.value.create_access_entry
+ iam_role_arn = each.value.iam_role_arn
+
+ # Security group
+ vpc_security_group_ids = compact(concat([local.node_security_group_id], each.value.vpc_security_group_ids))
+ cluster_primary_security_group_id = each.value.attach_cluster_primary_security_group ? aws_eks_cluster.this[0].vpc_config[0].cluster_security_group_id : null
+ create_security_group = each.value.create_security_group
+ security_group_name = each.value.security_group_name
+ security_group_use_name_prefix = each.value.security_group_use_name_prefix
+ security_group_description = each.value.security_group_description
+ security_group_ingress_rules = each.value.security_group_ingress_rules
+ security_group_egress_rules = each.value.security_group_egress_rules
+ security_group_tags = each.value.security_group_tags
+
+ tags = merge(
+ var.tags,
+ each.value.tags,
+ )
+}
diff --git a/outputs.tf b/outputs.tf
index 1521d5c379..027e35d051 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -1,90 +1,281 @@
-output "cluster_id" {
- description = "The name/id of the EKS cluster."
- value = "${aws_eks_cluster.this.id}"
-}
+################################################################################
+# Cluster
+################################################################################
-# Though documented, not yet supported
-# output "cluster_arn" {
-# description = "The Amazon Resource Name (ARN) of the cluster."
-# value = "${aws_eks_cluster.this.arn}"
-# }
+output "cluster_arn" {
+ description = "The Amazon Resource Name (ARN) of the cluster"
+ value = try(aws_eks_cluster.this[0].arn, null)
+
+ depends_on = [
+ aws_eks_access_entry.this,
+ aws_eks_access_policy_association.this,
+ ]
+}
output "cluster_certificate_authority_data" {
- description = "Nested attribute containing certificate-authority-data for your cluster. This is the base64 encoded certificate data required to communicate with your cluster."
- value = "${aws_eks_cluster.this.certificate_authority.0.data}"
+ description = "Base64 encoded certificate data required to communicate with the cluster"
+ value = try(aws_eks_cluster.this[0].certificate_authority[0].data, null)
+
+ depends_on = [
+ aws_eks_access_entry.this,
+ aws_eks_access_policy_association.this,
+ ]
}
output "cluster_endpoint" {
- description = "The endpoint for your EKS Kubernetes API."
- value = "${aws_eks_cluster.this.endpoint}"
+ description = "Endpoint for your Kubernetes API server"
+ value = try(aws_eks_cluster.this[0].endpoint, null)
+
+ depends_on = [
+ aws_eks_access_entry.this,
+ aws_eks_access_policy_association.this,
+ ]
+}
+
+output "cluster_id" {
+ description = "The ID of the EKS cluster. Note: currently a value is returned only for local EKS clusters created on Outposts"
+ value = try(aws_eks_cluster.this[0].cluster_id, "")
+}
+
+output "cluster_name" {
+ description = "The name of the EKS cluster"
+ value = try(aws_eks_cluster.this[0].name, "")
+
+ depends_on = [
+ aws_eks_access_entry.this,
+ aws_eks_access_policy_association.this,
+ ]
+}
+
+output "cluster_oidc_issuer_url" {
+ description = "The URL on the EKS cluster for the OpenID Connect identity provider"
+ value = try(aws_eks_cluster.this[0].identity[0].oidc[0].issuer, null)
+}
+
+output "cluster_dualstack_oidc_issuer_url" {
+ description = "Dual-stack compatible URL on the EKS cluster for the OpenID Connect identity provider"
+ # https://github.com/aws/containers-roadmap/issues/2038#issuecomment-2278450601
+ value = try(replace(replace(aws_eks_cluster.this[0].identity[0].oidc[0].issuer, "https://oidc.eks.", "https://oidc-eks."), ".amazonaws.com/", ".api.aws/"), null)
}
output "cluster_version" {
- description = "The Kubernetes server version for the EKS cluster."
- value = "${aws_eks_cluster.this.version}"
+ description = "The Kubernetes version for the cluster"
+ value = try(aws_eks_cluster.this[0].version, null)
+}
+
+output "cluster_platform_version" {
+ description = "Platform version for the cluster"
+ value = try(aws_eks_cluster.this[0].platform_version, null)
+}
+
+output "cluster_status" {
+ description = "Status of the EKS cluster. One of `CREATING`, `ACTIVE`, `DELETING`, `FAILED`"
+ value = try(aws_eks_cluster.this[0].status, null)
+}
+
+output "cluster_primary_security_group_id" {
+ description = "Cluster security group that was created by Amazon EKS for the cluster. Managed node groups use this security group for control-plane-to-data-plane communication. Referred to as 'Cluster security group' in the EKS console"
+ value = try(aws_eks_cluster.this[0].vpc_config[0].cluster_security_group_id, null)
+}
+
+output "cluster_service_cidr" {
+ description = "The CIDR block where Kubernetes pod and service IP addresses are assigned from"
+ value = var.ip_family == "ipv6" ? try(aws_eks_cluster.this[0].kubernetes_network_config[0].service_ipv6_cidr, null) : try(aws_eks_cluster.this[0].kubernetes_network_config[0].service_ipv4_cidr, null)
+}
+
+output "cluster_ip_family" {
+ description = "The IP family used by the cluster (e.g. `ipv4` or `ipv6`)"
+ value = try(aws_eks_cluster.this[0].kubernetes_network_config[0].ip_family, null)
+}
+
+output "cluster_control_plane_scaling_tier" {
+ description = "The EKS Provisioned Control Plane scaling tier for the cluster"
+ value = try(aws_eks_cluster.this[0].control_plane_scaling_config[0].tier, null)
+}
+
+################################################################################
+# Access Entry
+################################################################################
+
+output "access_entries" {
+ description = "Map of access entries created and their attributes"
+ value = aws_eks_access_entry.this
+}
+
+output "access_policy_associations" {
+ description = "Map of eks cluster access policy associations created and their attributes"
+ value = aws_eks_access_policy_association.this
+}
+
+################################################################################
+# KMS Key
+################################################################################
+
+output "kms_key_arn" {
+ description = "The Amazon Resource Name (ARN) of the key"
+ value = module.kms.key_arn
+}
+
+output "kms_key_id" {
+ description = "The globally unique identifier for the key"
+ value = module.kms.key_id
+}
+
+output "kms_key_policy" {
+ description = "The IAM resource policy set on the key"
+ value = module.kms.key_policy
+}
+
+################################################################################
+# Cluster Security Group
+################################################################################
+
+output "cluster_security_group_arn" {
+ description = "Amazon Resource Name (ARN) of the cluster security group"
+ value = try(aws_security_group.cluster[0].arn, null)
}
output "cluster_security_group_id" {
- description = "Security group ID attached to the EKS cluster."
- value = "${local.cluster_security_group_id}"
+ description = "ID of the cluster security group"
+ value = try(aws_security_group.cluster[0].id, null)
+}
+
+################################################################################
+# Node Security Group
+################################################################################
+
+output "node_security_group_arn" {
+ description = "Amazon Resource Name (ARN) of the node shared security group"
+ value = try(aws_security_group.node[0].arn, null)
+}
+
+output "node_security_group_id" {
+ description = "ID of the node shared security group"
+ value = try(aws_security_group.node[0].id, null)
+}
+
+################################################################################
+# IRSA
+################################################################################
+
+output "oidc_provider" {
+ description = "The OpenID Connect identity provider (issuer URL without leading `https://`)"
+ value = try(replace(aws_eks_cluster.this[0].identity[0].oidc[0].issuer, "https://", ""), null)
+}
+
+output "oidc_provider_arn" {
+ description = "The ARN of the OIDC Provider if `enable_irsa = true`"
+ value = try(aws_iam_openid_connect_provider.oidc_provider[0].arn, null)
}
-output "config_map_aws_auth" {
- description = "A kubernetes configuration to authenticate to this EKS cluster."
- value = "${data.template_file.config_map_aws_auth.rendered}"
+output "cluster_tls_certificate_sha1_fingerprint" {
+ description = "The SHA1 fingerprint of the public key of the cluster's certificate"
+ value = try(data.tls_certificate.this[0].certificates[0].sha1_fingerprint, null)
}
+################################################################################
+# IAM Role
+################################################################################
+
output "cluster_iam_role_name" {
- description = "IAM role name of the EKS cluster."
- value = "${aws_iam_role.cluster.name}"
+ description = "Cluster IAM role name"
+ value = try(aws_iam_role.this[0].name, null)
}
output "cluster_iam_role_arn" {
- description = "IAM role ARN of the EKS cluster."
- value = "${aws_iam_role.cluster.arn}"
+ description = "Cluster IAM role ARN"
+ value = try(aws_iam_role.this[0].arn, null)
}
-output "kubeconfig" {
- description = "kubectl config file contents for this EKS cluster."
- value = "${data.template_file.kubeconfig.rendered}"
+output "cluster_iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = try(aws_iam_role.this[0].unique_id, null)
}
-output "kubeconfig_filename" {
- description = "The filename of the generated kubectl config."
- value = "${element(concat(local_file.kubeconfig.*.filename, list("")), 0)}"
+################################################################################
+# EKS Auto Node IAM Role
+################################################################################
+
+output "node_iam_role_name" {
+ description = "EKS Auto node IAM role name"
+ value = try(aws_iam_role.eks_auto[0].name, null)
}
-output "workers_asg_arns" {
- description = "IDs of the autoscaling groups containing workers."
- value = "${concat(aws_autoscaling_group.workers.*.arn, aws_autoscaling_group.workers_launch_template.*.arn)}"
+output "node_iam_role_arn" {
+ description = "EKS Auto node IAM role ARN"
+ value = try(aws_iam_role.eks_auto[0].arn, null)
}
-output "workers_asg_names" {
- description = "Names of the autoscaling groups containing workers."
- value = "${concat(aws_autoscaling_group.workers.*.id, aws_autoscaling_group.workers_launch_template.*.id)}"
+output "node_iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = try(aws_iam_role.eks_auto[0].unique_id, null)
}
-output "worker_security_group_id" {
- description = "Security group ID attached to the EKS workers."
- value = "${local.worker_security_group_id}"
+################################################################################
+# EKS Addons
+################################################################################
+
+output "cluster_addons" {
+ description = "Map of attribute maps for all EKS cluster addons enabled"
+ value = merge(aws_eks_addon.this, aws_eks_addon.before_compute)
}
-output "worker_iam_instance_profile_arns" {
- description = "default IAM instance profile ARN for EKS worker groups"
- value = "${aws_iam_instance_profile.workers.*.arn}"
+################################################################################
+# EKS Identity Provider
+################################################################################
+
+output "cluster_identity_providers" {
+ description = "Map of attribute maps for all EKS identity providers enabled"
+ value = aws_eks_identity_provider_config.this
}
-output "worker_iam_instance_profile_names" {
- description = "default IAM instance profile name for EKS worker groups"
- value = "${aws_iam_instance_profile.workers.*.name}"
+################################################################################
+# CloudWatch Log Group
+################################################################################
+
+output "cloudwatch_log_group_name" {
+ description = "Name of cloudwatch log group created"
+ value = try(aws_cloudwatch_log_group.this[0].name, null)
+}
+
+output "cloudwatch_log_group_arn" {
+ description = "Arn of cloudwatch log group created"
+ value = try(aws_cloudwatch_log_group.this[0].arn, null)
}
-output "worker_iam_role_name" {
- description = "default IAM role name for EKS worker groups"
- value = "${aws_iam_role.workers.name}"
+################################################################################
+# Fargate Profile
+################################################################################
+
+output "fargate_profiles" {
+ description = "Map of attribute maps for all EKS Fargate Profiles created"
+ value = module.fargate_profile
+}
+
+################################################################################
+# EKS Managed Node Group
+################################################################################
+
+output "eks_managed_node_groups" {
+ description = "Map of attribute maps for all EKS managed node groups created"
+ value = module.eks_managed_node_group
+}
+
+output "eks_managed_node_groups_autoscaling_group_names" {
+ description = "List of the autoscaling group names created by EKS managed node groups"
+ value = compact(flatten([for group in module.eks_managed_node_group : group.node_group_autoscaling_group_names]))
+}
+
+################################################################################
+# Self Managed Node Group
+################################################################################
+
+output "self_managed_node_groups" {
+ description = "Map of attribute maps for all self managed node groups created"
+ value = module.self_managed_node_group
}
-output "worker_iam_role_arn" {
- description = "default IAM role ARN for EKS worker groups"
- value = "${aws_iam_role.workers.arn}"
+output "self_managed_node_groups_autoscaling_group_names" {
+ description = "List of the autoscaling group names created by self-managed node groups"
+ value = compact([for group in module.self_managed_node_group : group.autoscaling_group_name])
}
diff --git a/templates/al2023_user_data.tpl b/templates/al2023_user_data.tpl
new file mode 100644
index 0000000000..cc360e6d65
--- /dev/null
+++ b/templates/al2023_user_data.tpl
@@ -0,0 +1,11 @@
+%{ if enable_bootstrap_user_data ~}
+---
+apiVersion: node.eks.aws/v1alpha1
+kind: NodeConfig
+spec:
+ cluster:
+ name: ${cluster_name}
+ apiServerEndpoint: ${cluster_endpoint}
+ certificateAuthority: ${cluster_auth_base64}
+ cidr: ${cluster_service_cidr}
+%{ endif ~}
diff --git a/templates/al2_user_data.tpl b/templates/al2_user_data.tpl
new file mode 100644
index 0000000000..d75d549ccc
--- /dev/null
+++ b/templates/al2_user_data.tpl
@@ -0,0 +1,12 @@
+%{ if enable_bootstrap_user_data ~}
+#!/bin/bash
+set -e
+%{ endif ~}
+${pre_bootstrap_user_data ~}
+%{ if enable_bootstrap_user_data ~}
+B64_CLUSTER_CA=${cluster_auth_base64}
+API_SERVER_URL=${cluster_endpoint}
+/etc/eks/bootstrap.sh ${cluster_name} ${bootstrap_extra_args} --b64-cluster-ca $B64_CLUSTER_CA --apiserver-endpoint $API_SERVER_URL \
+ --ip-family ${cluster_ip_family} --service-${cluster_ip_family}-cidr ${cluster_service_cidr}
+${post_bootstrap_user_data ~}
+%{ endif ~}
diff --git a/templates/bottlerocket_user_data.tpl b/templates/bottlerocket_user_data.tpl
new file mode 100644
index 0000000000..666d666069
--- /dev/null
+++ b/templates/bottlerocket_user_data.tpl
@@ -0,0 +1,8 @@
+%{ if enable_bootstrap_user_data ~}
+[settings.kubernetes]
+"cluster-name" = "${cluster_name}"
+"api-server" = "${cluster_endpoint}"
+"cluster-certificate" = "${cluster_auth_base64}"
+"cluster-dns-ip" = ${cluster_dns_ips}
+%{ endif ~}
+${bootstrap_extra_args ~}
diff --git a/templates/config-map-aws-auth-map_accounts.yaml.tpl b/templates/config-map-aws-auth-map_accounts.yaml.tpl
deleted file mode 100644
index 26dc5078f4..0000000000
--- a/templates/config-map-aws-auth-map_accounts.yaml.tpl
+++ /dev/null
@@ -1 +0,0 @@
- - "${account_number}"
diff --git a/templates/config-map-aws-auth-map_roles.yaml.tpl b/templates/config-map-aws-auth-map_roles.yaml.tpl
deleted file mode 100644
index 9f321b7be6..0000000000
--- a/templates/config-map-aws-auth-map_roles.yaml.tpl
+++ /dev/null
@@ -1,4 +0,0 @@
- - rolearn: ${role_arn}
- username: ${username}
- groups:
- - ${group}
diff --git a/templates/config-map-aws-auth-map_users.yaml.tpl b/templates/config-map-aws-auth-map_users.yaml.tpl
deleted file mode 100644
index 92499de41c..0000000000
--- a/templates/config-map-aws-auth-map_users.yaml.tpl
+++ /dev/null
@@ -1,4 +0,0 @@
- - userarn: ${user_arn}
- username: ${username}
- groups:
- - ${group}
diff --git a/templates/config-map-aws-auth.yaml.tpl b/templates/config-map-aws-auth.yaml.tpl
deleted file mode 100644
index 86f4f5f998..0000000000
--- a/templates/config-map-aws-auth.yaml.tpl
+++ /dev/null
@@ -1,13 +0,0 @@
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: aws-auth
- namespace: kube-system
-data:
- mapRoles: |
-${worker_role_arn}
-${map_roles}
- mapUsers: |
-${map_users}
- mapAccounts: |
-${map_accounts}
diff --git a/templates/kubeconfig.tpl b/templates/kubeconfig.tpl
deleted file mode 100644
index 1696391e89..0000000000
--- a/templates/kubeconfig.tpl
+++ /dev/null
@@ -1,28 +0,0 @@
-apiVersion: v1
-preferences: {}
-kind: Config
-
-clusters:
-- cluster:
- server: ${endpoint}
- certificate-authority-data: ${cluster_auth_base64}
- name: ${kubeconfig_name}
-
-contexts:
-- context:
- cluster: ${kubeconfig_name}
- user: ${kubeconfig_name}
- name: ${kubeconfig_name}
-
-current-context: ${kubeconfig_name}
-
-users:
-- name: ${kubeconfig_name}
- user:
- exec:
- apiVersion: client.authentication.k8s.io/v1alpha1
- command: ${aws_authenticator_command}
- args:
-${aws_authenticator_command_args}
-${aws_authenticator_additional_args}
-${aws_authenticator_env_variables}
diff --git a/templates/userdata.sh.tpl b/templates/userdata.sh.tpl
deleted file mode 100644
index ba8ea2800b..0000000000
--- a/templates/userdata.sh.tpl
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash -xe
-
-# Allow user supplied pre userdata code
-${pre_userdata}
-
-# Bootstrap and join the cluster
-/etc/eks/bootstrap.sh --b64-cluster-ca '${cluster_auth_base64}' --apiserver-endpoint '${endpoint}' ${bootstrap_extra_args} --kubelet-extra-args '${kubelet_extra_args}' '${cluster_name}'
-
-# Allow user supplied userdata code
-${additional_userdata}
diff --git a/templates/windows_user_data.tpl b/templates/windows_user_data.tpl
new file mode 100644
index 0000000000..9721d3cc33
--- /dev/null
+++ b/templates/windows_user_data.tpl
@@ -0,0 +1,13 @@
+%{ if enable_bootstrap_user_data ~}
+