diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bfddd4b673..b489ccee0d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.99.4 + rev: v1.99.5 hooks: - id: terraform_fmt - id: terraform_docs diff --git a/README.md b/README.md index 4767a624e1..235307d38e 100644 --- a/README.md +++ b/README.md @@ -30,18 +30,18 @@ Please note that we strive to provide a comprehensive suite of documentation for ```hcl module "eks" { source = "terraform-aws-modules/eks/aws" - version = "~> 20.31" + version = "~> 21.0" - cluster_name = "example" - cluster_version = "1.33" + name = "example" + kubernetes_version = "1.33" # Optional - cluster_endpoint_public_access = true + endpoint_public_access = true # Optional: Adds the current caller identity as an administrator via cluster access entry enable_cluster_creator_admin_permissions = true - cluster_compute_config = { + compute_config = { enabled = true node_pools = ["general-purpose"] } @@ -56,6 +56,89 @@ module "eks" { } ``` +### EKS Managed Node Group + +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 21.0" + + name = "my-cluster" + kubernetes_version = "1.33" + + addons = { + coredns = {} + eks-pod-identity-agent = { + before_compute = true + } + kube-proxy = {} + vpc-cni = { + before_compute = true + } + } + + # Optional + endpoint_public_access = true + + # Optional: Adds the current caller identity as an administrator via cluster access entry + enable_cluster_creator_admin_permissions = true + + vpc_id = "vpc-1234556abcdef" + subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] + control_plane_subnet_ids = ["subnet-xyzde987", "subnet-slkjf456", "subnet-qeiru789"] + + # 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"] + + min_size = 2 + max_size = 10 + desired_size = 2 + } + } + + tags = { + Environment = "dev" + Terraform = "true" + } +} +``` + +### Cluster Access Entry + +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. + +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. + +```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 @@ -69,7 +152,7 @@ locals { # 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 = "~> 20.31" + version = "~> 21.0" tags = { Environment = "dev" @@ -79,25 +162,25 @@ module "eks_hybrid_node_role" { module "eks" { source = "terraform-aws-modules/eks/aws" - version = "~> 20.31" + version = "~> 21.0" - cluster_name = "example" - cluster_version = "1.33" + name = "example" + kubernetes_version = "1.33" - cluster_addons = { + addons = { coredns = {} eks-pod-identity-agent = {} kube-proxy = {} } # Optional - cluster_endpoint_public_access = true + 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 - cluster_security_group_additional_rules = { + security_group_additional_rules = { hybrid-all = { cidr_blocks = [local.remote_network_cidr] description = "Allow all traffic from remote node/pod network" @@ -109,7 +192,7 @@ module "eks" { } # Optional - cluster_compute_config = { + compute_config = { enabled = true node_pools = ["system"] } @@ -124,7 +207,7 @@ module "eks" { vpc_id = "vpc-1234556abcdef" subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] - cluster_remote_network_config = { + remote_network_config = { remote_node_networks = { cidrs = [local.remote_node_cidr] } @@ -141,90 +224,6 @@ module "eks" { } ``` -### EKS Managed Node Group - -```hcl -module "eks" { - source = "terraform-aws-modules/eks/aws" - version = "~> 20.0" - - cluster_name = "my-cluster" - cluster_version = "1.33" - - bootstrap_self_managed_addons = false - cluster_addons = { - coredns = {} - eks-pod-identity-agent = {} - kube-proxy = {} - vpc-cni = {} - } - - # Optional - cluster_endpoint_public_access = true - - # Optional: Adds the current caller identity as an administrator via cluster access entry - enable_cluster_creator_admin_permissions = true - - vpc_id = "vpc-1234556abcdef" - subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] - control_plane_subnet_ids = ["subnet-xyzde987", "subnet-slkjf456", "subnet-qeiru789"] - - # EKS Managed Node Group(s) - eks_managed_node_group_defaults = { - instance_types = ["m6i.large", "m5.large", "m5n.large", "m5zn.large"] - } - - 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"] - - min_size = 2 - max_size = 10 - desired_size = 2 - } - } - - tags = { - Environment = "dev" - Terraform = "true" - } -} -``` - -### Cluster Access Entry - -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. - -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. - -```hcl -module "eks" { - source = "terraform-aws-modules/eks/aws" - version = "~> 20.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" - } - } - } - } - } -} -``` - ### 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` @@ -251,7 +250,7 @@ In order to enable EFA support, you will have to specify `enable_efa_support = t ```hcl module "eks" { source = "terraform-aws-modules/eks/aws" - version = "~> 20.0" + version = "~> 21.0" # Truncated for brevity ... @@ -317,18 +316,18 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | | [time](#requirement\_time) | >= 0.9 | -| [tls](#requirement\_tls) | >= 3.0 | +| [tls](#requirement\_tls) | >= 4.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.95, < 6.0.0 | +| [aws](#provider\_aws) | >= 6.0 | | [time](#provider\_time) | >= 0.9 | -| [tls](#provider\_tls) | >= 3.0 | +| [tls](#provider\_tls) | >= 4.0 | ## Modules @@ -336,7 +335,7 @@ We are grateful to the community for contributing bugfixes and improvements! Ple |------|--------|---------| | [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 | 2.1.0 | +| [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 @@ -382,68 +381,47 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [access\_entries](#input\_access\_entries) | Map of access entries to add to the cluster | `any` | `{}` | no | -| [attach\_cluster\_encryption\_policy](#input\_attach\_cluster\_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 | +| [access\_entries](#input\_access\_entries) | Map of access entries to add to the cluster |
map(object({| `{}` | 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` |
# 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
})
})))
}))
map(object({| `null` | no | +| [addons\_timeouts](#input\_addons\_timeouts) | Create, update, and delete timeout configurations for the cluster addons |
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), {})
}))
object({| `null` | 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 | -| [bootstrap\_self\_managed\_addons](#input\_bootstrap\_self\_managed\_addons) | Indicates whether or not to bootstrap self-managed addons after the cluster has been created | `bool` | `null` | 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\_additional\_security\_group\_ids](#input\_cluster\_additional\_security\_group\_ids) | List of additional, externally created security group IDs to attach to the cluster control plane | `list(string)` | `[]` | no | -| [cluster\_addons](#input\_cluster\_addons) | Map of cluster addon configurations to enable for the cluster. Addon name can be the map keys or set with `name` | `any` | `{}` | no | -| [cluster\_addons\_timeouts](#input\_cluster\_addons\_timeouts) | Create, update, and delete timeout configurations for the cluster addons | `map(string)` | `{}` | no | -| [cluster\_compute\_config](#input\_cluster\_compute\_config) | Configuration block for the cluster compute configuration | `any` | `{}` | no | -| [cluster\_enabled\_log\_types](#input\_cluster\_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)` |
create = optional(string)
update = optional(string)
delete = optional(string)
})
[| no | -| [cluster\_encryption\_config](#input\_cluster\_encryption\_config) | Configuration block with encryption configuration for the cluster. To disable secret encryption, set this value to `{}` | `any` |
"audit",
"api",
"authenticator"
]
{| no | -| [cluster\_encryption\_policy\_description](#input\_cluster\_encryption\_policy\_description) | Description of the cluster encryption policy created | `string` | `"Cluster encryption policy to allow cluster role to utilize CMK provided"` | no | -| [cluster\_encryption\_policy\_name](#input\_cluster\_encryption\_policy\_name) | Name to use on cluster encryption policy created | `string` | `null` | no | -| [cluster\_encryption\_policy\_path](#input\_cluster\_encryption\_policy\_path) | Cluster encryption policy path | `string` | `null` | no | -| [cluster\_encryption\_policy\_tags](#input\_cluster\_encryption\_policy\_tags) | A map of additional tags to add to the cluster encryption policy created | `map(string)` | `{}` | no | -| [cluster\_encryption\_policy\_use\_name\_prefix](#input\_cluster\_encryption\_policy\_use\_name\_prefix) | Determines whether cluster encryption policy name (`cluster_encryption_policy_name`) is used as a prefix | `bool` | `true` | no | -| [cluster\_endpoint\_private\_access](#input\_cluster\_endpoint\_private\_access) | Indicates whether or not the Amazon EKS private API server endpoint is enabled | `bool` | `true` | no | -| [cluster\_endpoint\_public\_access](#input\_cluster\_endpoint\_public\_access) | Indicates whether or not the Amazon EKS public API server endpoint is enabled | `bool` | `false` | no | -| [cluster\_endpoint\_public\_access\_cidrs](#input\_cluster\_endpoint\_public\_access\_cidrs) | List of CIDR blocks which can access the Amazon EKS public API server endpoint | `list(string)` |
"resources": [
"secrets"
]
}
[| no | -| [cluster\_force\_update\_version](#input\_cluster\_force\_update\_version) | Force version update by overriding upgrade-blocking readiness checks when updating a cluster | `bool` | `null` | no | -| [cluster\_identity\_providers](#input\_cluster\_identity\_providers) | Map of cluster identity provider configurations to enable for the cluster. Note - this is different/separate from IRSA | `any` | `{}` | 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`. 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 | -| [cluster\_name](#input\_cluster\_name) | Name of the EKS cluster | `string` | `""` | no | -| [cluster\_remote\_network\_config](#input\_cluster\_remote\_network\_config) | Configuration block for the cluster remote network configuration | `any` | `{}` | no | -| [cluster\_security\_group\_additional\_rules](#input\_cluster\_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 | `any` | `{}` | no | -| [cluster\_security\_group\_description](#input\_cluster\_security\_group\_description) | Description of the cluster security group created | `string` | `"EKS cluster security group"` | no | -| [cluster\_security\_group\_id](#input\_cluster\_security\_group\_id) | Existing security group ID to be attached to the cluster | `string` | `""` | no | -| [cluster\_security\_group\_name](#input\_cluster\_security\_group\_name) | Name to use on cluster security group created | `string` | `null` | no | -| [cluster\_security\_group\_tags](#input\_cluster\_security\_group\_tags) | A map of additional tags to add to the cluster security group created | `map(string)` | `{}` | no | -| [cluster\_security\_group\_use\_name\_prefix](#input\_cluster\_security\_group\_use\_name\_prefix) | Determines whether cluster security group name (`cluster_security_group_name`) is used as a prefix | `bool` | `true` | no | -| [cluster\_service\_ipv4\_cidr](#input\_cluster\_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 | -| [cluster\_service\_ipv6\_cidr](#input\_cluster\_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 | | [cluster\_tags](#input\_cluster\_tags) | A map of additional tags to add to the cluster | `map(string)` | `{}` | no | -| [cluster\_timeouts](#input\_cluster\_timeouts) | Create, update, and delete timeout configurations for the cluster | `map(string)` | `{}` | no | -| [cluster\_upgrade\_policy](#input\_cluster\_upgrade\_policy) | Configuration block for the cluster upgrade policy | `any` | `{}` | no | -| [cluster\_version](#input\_cluster\_version) | Kubernetes `
"0.0.0.0/0"
]
object({| `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\_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\_cluster\_primary\_security\_group\_tags](#input\_create\_cluster\_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\_cluster\_security\_group](#input\_create\_cluster\_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 | | [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 | -| [eks\_managed\_node\_group\_defaults](#input\_eks\_managed\_node\_group\_defaults) | Map of EKS managed node group default configurations | `any` | `{}` | no | -| [eks\_managed\_node\_groups](#input\_eks\_managed\_node\_groups) | Map of EKS managed node group definitions to create | `any` | `{}` | no | +| [eks\_managed\_node\_groups](#input\_eks\_managed\_node\_groups) | Map of EKS managed node group definitions to create |
enabled = optional(bool, false)
node_pools = optional(list(string))
node_role_arn = optional(string)
})
map(object({| `{}` | 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\_efa\_support](#input\_enable\_efa\_support) | Determines whether to enable Elastic Fabric Adapter (EFA) support | `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 | -| [enable\_security\_groups\_for\_pods](#input\_enable\_security\_groups\_for\_pods) | Determines whether to add the necessary IAM permission policy for security groups for pods | `bool` | `true` | no | -| [fargate\_profile\_defaults](#input\_fargate\_profile\_defaults) | Map of Fargate Profile default configurations | `any` | `{}` | no | -| [fargate\_profiles](#input\_fargate\_profiles) | Map of Fargate Profile definitions to create | `any` | `{}` | 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)` |
create = optional(bool, true)
kubernetes_version = optional(string)
# EKS Managed Node Group
name = optional(string) # Will fall back to map key
use_name_prefix = optional(bool, true)
subnet_ids = optional(list(string))
min_size = optional(number, 1)
max_size = optional(number, 3)
desired_size = optional(number, 1)
ami_id = optional(string, "")
ami_type = optional(string, "AL2023_x86_64_STANDARD")
ami_release_version = optional(string)
use_latest_ami_release_version = optional(bool, true)
capacity_type = optional(string, "ON_DEMAND")
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, true)
}))
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)
}), {
max_unavailable_percentage = 33
})
timeouts = optional(object({
create = optional(string)
update = optional(string)
delete = optional(string)
}))
# User data
enable_bootstrap_user_data = optional(bool, false)
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, true)
use_custom_launch_template = optional(bool, true)
launch_template_id = optional(string, "")
launch_template_name = optional(string) # Will fall back to map key
launch_template_use_name_prefix = optional(bool, true)
launch_template_version = optional(string)
update_launch_template_default_version = optional(bool, true)
launch_template_description = optional(string)
launch_template_tags = optional(map(string), {})
tag_specifications = optional(list(string), ["instance", "volume", "network-interface"])
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, "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"
})
enable_monitoring = optional(bool, false)
enable_efa_support = optional(bool, false)
enable_efa_only = optional(bool, true)
efa_indices = optional(list(string), [0])
create_placement_group = optional(bool, false)
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
creat_iam_role = optional(bool, true)
iam_role_arn = optional(string)
iam_role_name = optional(string)
iam_role_use_name_prefix = optional(bool, true)
iam_role_path = optional(string)
iam_role_description = optional(string, "EKS managed node group IAM role")
iam_role_permissions_boundary = optional(string)
iam_role_tags = optional(map(string), {})
iam_role_attach_cni_policy = optional(bool, true)
iam_role_additional_policies = optional(map(string), {})
create_iam_role_policy = optional(bool, true)
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, true)
security_group_name = optional(string)
security_group_use_name_prefix = optional(bool, true)
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, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool, false)
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, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool, false)
tags = optional(map(string), {})
to_port = optional(string)
})), {})
security_group_tags = optional(map(string), {})
tags = optional(map(string), {})
}))
[| no | +| [encryption\_config](#input\_encryption\_config) | Configuration block with encryption configuration for the cluster |
"audit",
"api",
"authenticator"
]
object({| `{}` | 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)` |
provider_key_arn = optional(string)
resources = optional(list(string), ["secrets"])
})
[| no | +| [fargate\_profiles](#input\_fargate\_profiles) | Map of Fargate Profile definitions to create |
"0.0.0.0/0"
]
map(object({| `{}` | 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 | @@ -452,7 +430,9 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [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 |
create = optional(bool, true)
# 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, true)
iam_role_arn = optional(string)
iam_role_name = optional(string)
iam_role_use_name_prefix = optional(bool, true)
iam_role_path = optional(string)
iam_role_description = optional(string, "Fargate profile IAM role")
iam_role_permissions_boundary = optional(string)
iam_role_tags = optional(map(string), {})
iam_role_attach_cni_policy = optional(bool, true)
iam_role_additional_policies = optional(map(string), {})
create_iam_role_policy = optional(bool, true)
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), {})
}))
map(object({| `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 | @@ -463,6 +443,8 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [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 `
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)
}))
map(object({| `{}` | 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 | @@ -478,14 +460,26 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [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 | `any` | `{}` | no | +| [outpost\_config](#input\_outpost\_config) | Configuration for the AWS Outpost to provision the cluster on |
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)
}))
object({| `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 | -| [self\_managed\_node\_group\_defaults](#input\_self\_managed\_node\_group\_defaults) | Map of self-managed node group default configurations | `any` | `{}` | no | -| [self\_managed\_node\_groups](#input\_self\_managed\_node\_groups) | Map of self-managed node group definitions to create | `any` | `{}` | 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 |
control_plane_instance_type = optional(string)
control_plane_placement = optional(object({
group_name = string
}))
outpost_arns = list(string)
})
object({| `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 |
remote_node_networks = object({
cidrs = optional(list(string))
})
remote_pod_networks = optional(object({
cidrs = optional(list(string))
}))
})
map(object({| `{}` | 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 |
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)
}))
map(object({| `{}` | 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 |
create = optional(bool, true)
# Autoscaling Group
create_autoscaling_group = optional(bool, true)
name = optional(string) # Will fall back to map key
use_name_prefix = optional(bool, true)
availability_zones = optional(list(string))
subnet_ids = optional(list(string))
min_size = optional(number, 1)
max_size = optional(number, 3)
desired_size = optional(number, 1)
desired_size_type = optional(string)
capacity_rebalance = optional(bool)
default_instance_warmup = optional(number)
protect_from_scale_in = optional(bool, false)
context = optional(string)
create_placement_group = optional(bool, false)
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, 66)
scale_in_protected_instances = optional(string)
skip_matching = optional(bool)
standby_instances = optional(string)
}))
strategy = optional(string, "Rolling")
triggers = optional(list(string))
}), {
strategy = "Rolling"
preferences = {
min_healthy_percentage = 66
}
})
use_mixed_instances_policy = optional(bool, false)
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, "AL2023_x86_64_STANDARD")
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, true)
use_custom_launch_template = optional(bool, true)
launch_template_id = optional(string, "")
launch_template_name = optional(string) # Will fall back to map key
launch_template_use_name_prefix = optional(bool, true)
launch_template_version = optional(string)
update_launch_template_default_version = optional(bool, true)
launch_template_description = optional(string)
launch_template_tags = optional(map(string), {})
tag_specifications = optional(list(string), ["instance", "volume", "network-interface"])
ebs_optimized = optional(bool)
ami_id = optional(string)
instance_type = optional(string, "m6i.large")
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, "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"
})
enable_monitoring = optional(bool, false)
enable_efa_support = optional(bool, false)
enable_efa_only = optional(bool, true)
efa_indices = optional(list(string), [0])
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, true)
iam_instance_profile_arn = optional(string)
iam_role_name = optional(string)
iam_role_use_name_prefix = optional(bool, true)
iam_role_path = optional(string)
iam_role_description = optional(string, "Self managed node group IAM role")
iam_role_permissions_boundary = optional(string)
iam_role_tags = optional(map(string), {})
iam_role_attach_cni_policy = optional(bool, true)
iam_role_additional_policies = optional(map(string), {})
create_iam_role_policy = optional(bool, true)
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, true)
iam_role_arn = optional(string)
# Security group
attach_cluster_primary_security_group = optional(bool, false)
create_security_group = optional(bool, true)
security_group_name = optional(string)
security_group_use_name_prefix = optional(bool, true)
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, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool, false)
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, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
self = optional(bool, false)
tags = optional(map(string), {})
to_port = optional(string)
})), {})
security_group_tags = optional(map(string), {})
tags = optional(map(string), {})
}))
object({| `null` | no | +| [upgrade\_policy](#input\_upgrade\_policy) | Configuration block for the cluster upgrade policy |
create = optional(string)
update = optional(string)
delete = optional(string)
})
object({| `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 |
support_type = optional(string)
})
object({| `null` | no | ## Outputs diff --git a/docs/README.md b/docs/README.md index 144826b4cf..960db18448 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,3 +11,4 @@ - [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) diff --git a/docs/UPGRADE-21.0.md b/docs/UPGRADE-21.0.md new file mode 100644 index 0000000000..695c8c80f2 --- /dev/null +++ b/docs/UPGRADE-21.0.md @@ -0,0 +1,328 @@ +# Upgrade from v20.x to v21.x + +If you have any questions regarding this upgrade process, please consult the [`examples`](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples) directory: +If you find a bug, please open an issue with supporting configuration to reproduce. + +## List of backwards incompatible changes + +- Terraform `v1.5.7` is now minimum supported version +- AWS provider `v6.0.0` is now minimum supported version +- TLS provider `v4.0.0` is now minimum supported version +- The `aws-auth` sub-module has been removed. Users who wish to utilize its functionality can continue to do so by specifying a `v20.x` version, or `~> v20.0` version constraint in their module source. +- `bootstrap_self_managed_addons` is now hardcoded to `false`. This is a legacy setting and instead users should utilize the EKS addons API, which is what this module does by default. In conjunction with this change, the `bootstrap_self_managed_addons` is now ignored by the module to aid in upgrading without disruption (otherwise it would require cluster re-creation). +- When enabling `enable_efa_support` or creating placement groups within a node group, users must now specify the correct `subnet_ids`; the module no longer tries to automatically select a suitable subnet. +- EKS managed node group: + - IMDS now default to a hop limit of 1 (previously was 2) + - `ami_type` now defaults to `AL2023_x86_64_STANDARD` + - `enable_monitoring` is now set to `false` by default + - `enable_efa_only` is now set to `true` by default + - `use_latest_ami_release_version` is now set to `true` by default + - Support for autoscaling group schedules has been removed +- Self-managed node group: + - IMDS now default to a hop limit of 1 (previously was 2) + - `ami_type` now defaults to `AL2023_x86_64_STANDARD` + - `enable_monitoring` is now set to `false` by default + - `enable_efa_only` is now set to `true` by default + - Support for autoscaling group schedules has been removed +- Karpenter: + - Native support for IAM roles for service accounts (IRSA) has been removed; EKS Pod Identity is now enabled by default + - Karpenter controller policy for prior to Karpenter `v1` have been removed (i.e. `v0.33`); the `v1` policy is now used by default + - `create_pod_identity_association` is now set to `true` by default +- `addons.resolve_conflicts_on_create` is now set to `"NONE"` by default (was `"OVERWRITE"`). +- `addons.most_recent` is now set to `true` by default (was `false`). +- `cluster_identity_providers.issuer_url` is now required to be set by users; the prior incorrect default has been removed. See https://github.com/terraform-aws-modules/terraform-aws-eks/pull/3055 and https://github.com/kubernetes/kubernetes/pull/123561 for more details. +- The OIDC issuer URL for IAM roles for service accounts (IRSA) has been changed to use the new dual stack`oidc-eks` endpoint instead of `oidc.eks`. This is to align with https://github.com/aws/containers-roadmap/issues/2038#issuecomment-2278450601 + +## Additional changes + +### Added + +- Support for `region` parameter to specify the AWS region for the resources created if different from the provider region. +- Both the EKS managed and self-managed node groups now support creating their own security groups (again). This is primarily motivated by the changes for EFA support; previously users would need to specify `enable_efa_support` both at the cluster level (to add the appropriate security group rules to the shared node security group) as well as the node group level. However, its not always desirable to have these rules across ALL node groups when they are really only required on the node group where EFA is utilized. And similarly for other use cases, users can create custom rules for a specific node group instead of apply across ALL node groups. + +### Modified + +- Variable definitions now contain detailed `object` types in place of the previously used any type. +- The embedded KMS key module definition has been updated to `v4.0` to support the same version requirements as well as the new `region` argument. + +### Variable and output changes + +1. Removed variables: + + - `enable_efa_support` - users only need to set this within the node group configuration, as the module no longer manages EFA support at the cluster level. + - `enable_security_groups_for_pods` - users can instead attach the `arn:aws:iam::aws:policy/AmazonEKSVPCResourceController` policy via `iam_role_additional_policies` if using security groups for pods. + - `eks-managed-node-group` sub-module + - `cluster_service_ipv4_cidr` - users should use `cluster_service_cidr` instead (for either IPv4 or IPv6). + - `elastic_gpu_specifications` + - `elastic_inference_accelerator` + - `platform` - this is superseded by `ami_type` + - `placement_group_strategy` - set to `cluster` by the module + - `placement_group_az` - users will need to specify the correct subnet in `subnet_ids` + - `create_schedule` + - `schedules` + - `self-managed-node-group` sub-module + - `elastic_gpu_specifications` + - `elastic_inference_accelerator` + - `platform` - this is superseded by `ami_type` + - `create_schedule` + - `schedules` + - `placement_group_az` - users will need to specify the correct subnet in `subnet_ids` + - `hibernation_options` - not valid in EKS + - `min_elb_capacity` - not valid in EKS + - `wait_for_elb_capacity` - not valid in EKS + - `wait_for_capacity_timeout` - not valid in EKS + - `default_cooldown` - not valid in EKS + - `target_group_arns` - not valid in EKS + - `service_linked_role_arn` - not valid in EKS + - `warm_pool` - not valid in EKS + - `fargate-profile` sub-module + - None + - `karpenter` sub-module + - `enable_v1_permissions` - v1 permissions are now the default + - `enable_irsa` + - `irsa_oidc_provider_arn` + - `irsa_namespace_service_accounts` + - `irsa_assume_role_condition_test` + +2. Renamed variables: + + - Variables prefixed with `cluster_*` have been stripped of the prefix to better match the underlying API: + - `cluster_name` -> `name` + - `cluster_version` -> `kubernetes_version` + - `cluster_enabled_log_types` -> `enabled_log_types` + - `cluster_force_update_version` -> `force_update_version` + - `cluster_compute_config` -> `compute_config` + - `cluster_upgrade_policy` -> `upgrade_policy` + - `cluster_remote_network_config` -> `remote_network_config` + - `cluster_zonal_shift_config` -> `zonal_shift_config` + - `cluster_additional_security_group_ids` -> `additional_security_group_ids` + - `cluster_endpoint_private_access` -> `endpoint_private_access` + - `cluster_endpoint_public_access` -> `endpoint_public_access` + - `cluster_endpoint_public_access_cidrs` -> `endpoint_public_access_cidrs` + - `cluster_ip_family` -> `ip_family` + - `cluster_service_ipv4_cidr` -> `service_ipv4_cidr` + - `cluster_service_ipv6_cidr` -> `service_ipv6_cidr` + - `cluster_encryption_config` -> `encryption_config` + - `create_cluster_primary_security_group_tags` -> `create_primary_security_group_tags` + - `cluster_timeouts` -> `timeouts` + - `create_cluster_security_group` -> `create_security_group` + - `cluster_security_group_id` -> `security_group_id` + - `cluster_security_group_name` -> `security_group_name` + - `cluster_security_group_use_name_prefix` -> `security_group_use_name_prefix` + - `cluster_security_group_description` -> `security_group_description` + - `cluster_security_group_additional_rules` -> `security_group_additional_rules` + - `cluster_security_group_tags` -> `security_group_tags` + - `cluster_encryption_policy_use_name_prefix` -> `encryption_policy_use_name_prefix` + - `cluster_encryption_policy_name` -> `encryption_policy_name` + - `cluster_encryption_policy_description` -> `encryption_policy_description` + - `cluster_encryption_policy_path` -> `encryption_policy_path` + - `cluster_encryption_policy_tags` -> `encryption_policy_tags` + - `cluster_addons` -> `addons` + - `cluster_addons_timeouts` -> `addons_timeouts` + - `cluster_identity_providers` -> `identity_providers` + - `eks-managed-node-group` sub-module + - `cluster_version` -> `kubernetes_version` + - `self-managed-node-group` sub-module + - `cluster_version` -> `kubernetes_version` + - `delete_timeout` -> `timeouts` + - `fargate-profile` sub-module + - None + - `karpenter` sub-module + - None + +3. Added variables: + + - `region` + - `eks-managed-node-group` sub-module + - `region` + - `partition` - added to reduce number of `GET` requests from data sources when possible + - `account_id` - added to reduce number of `GET` requests from data sources when possible + - `create_security_group` + - `security_group_name` + - `security_group_use_name_prefix` + - `security_group_description` + - `security_group_ingress_rules` + - `security_group_egress_rules` + - `security_group_tags` + - `self-managed-node-group` sub-module + - `region` + - `partition` - added to reduce number of `GET` requests from data sources when possible + - `account_id` - added to reduce number of `GET` requests from data sources when possible + - `create_security_group` + - `security_group_name` + - `security_group_use_name_prefix` + - `security_group_description` + - `security_group_ingress_rules` + - `security_group_egress_rules` + - `security_group_tags` + - `fargate-profile` sub-module + - `region` + - `partition` - added to reduce number of `GET` requests from data sources when possible + - `account_id` - added to reduce number of `GET` requests from data sources when possible + - `karpenter` sub-module + - `region` + +4. Removed outputs: + + - `eks-managed-node-group` sub-module + - `platform` - this is superseded by `ami_type` + - `autoscaling_group_schedule_arns` + - `self-managed-node-group` sub-module + - `platform` - this is superseded by `ami_type` + - `autoscaling_group_schedule_arns` + - `fargate-profile` sub-module + - None + - `karpenter` sub-module + - None + +5. Renamed outputs: + + - `eks-managed-node-group` sub-module + - None + - `self-managed-node-group` sub-module + - None + - `fargate-profile` sub-module + - None + - `karpenter` sub-module + - None + +6. Added outputs: + + - `eks-managed-node-group` sub-module + - `security_group_arn` + - `security_group_id` + - `self-managed-node-group` sub-module + - `security_group_arn` + - `security_group_id` + - `fargate-profile` sub-module + - None + - `karpenter` sub-module + - None + +## Upgrade Migrations + +### Before 20.x Example + +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 20.0" + + # Truncated for brevity ... + # Renamed variables are not shown here, please refer to the full list above. + + enable_efa_support = true + + eks_managed_node_group_defaults = { + iam_role_additional_policies = { + AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + } + } + + eks_managed_node_groups = { + efa = { + ami_type = "AL2023_x86_64_NVIDIA" + instance_types = ["p5e.48xlarge"] + + enable_efa_support = true + enable_efa_only = true + } + } + + self_managed_node_groups = { + example = { + use_mixed_instances_policy = true + mixed_instances_policy = { + instances_distribution = { + on_demand_base_capacity = 0 + on_demand_percentage_above_base_capacity = 0 + on_demand_allocation_strategy = "lowest-price" + spot_allocation_strategy = "price-capacity-optimized" + } + + # ASG configuration + override = [ + { + instance_requirements = { + cpu_manufacturers = ["intel"] + instance_generations = ["current", "previous"] + spot_max_price_percentage_over_lowest_price = 100 + + vcpu_count = { + min = 1 + } + + allowed_instance_types = ["t*", "m*"] + } + } + ] + } + } + } +} +``` + +### After 21.x Example + +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 21.0" + + # Truncated for brevity ... + # Renamed variables are not shown here, please refer to the full list above. + + eks_managed_node_groups = { + efa = { + ami_type = "AL2023_x86_64_NVIDIA" + instance_types = ["p5e.48xlarge"] + + iam_role_additional_policies = { + AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + } + + enable_efa_support = true + + subnet_ids = element(module.vpc.private_subnets, 0) + } + } + + self_managed_node_groups = { + example = { + use_mixed_instances_policy = true + mixed_instances_policy = { + instances_distribution = { + on_demand_base_capacity = 0 + on_demand_percentage_above_base_capacity = 0 + on_demand_allocation_strategy = "lowest-price" + spot_allocation_strategy = "price-capacity-optimized" + } + + # ASG configuration + # Need to wrap in `launch_template` now + launch_template = { + override = [ + { + instance_requirements = { + cpu_manufacturers = ["intel"] + instance_generations = ["current", "previous"] + spot_max_price_percentage_over_lowest_price = 100 + + vcpu_count = { + min = 1 + } + + allowed_instance_types = ["t*", "m*"] + } + } + ] + } + } + } + } +} +``` + +### State Changes + +No state changes required. diff --git a/docs/compute_resources.md b/docs/compute_resources.md index a7bd5b5e93..e7dee7660c 100644 --- a/docs/compute_resources.md +++ b/docs/compute_resources.md @@ -56,22 +56,34 @@ Refer to the [EKS Managed Node Group documentation](https://docs.aws.amazon.com/ ```hcl eks_managed_node_groups = { custom_ami = { - ami_id = "ami-0caf35bc73450c396" + ami_id = "ami-0caf35bc73450c396" + ami_type = "AL2023_x86_64_STANDARD" # By default, EKS managed node groups will not append bootstrap script; # this adds it back in using the default template provided by the module # Note: this assumes the AMI provided is an EKS optimized AMI derivative enable_bootstrap_user_data = true - pre_bootstrap_user_data = <<-EOT - export FOO=bar - EOT - - # Because we have full control over the user data supplied, we can also run additional - # scripts/configuration changes after the bootstrap script has been run - post_bootstrap_user_data = <<-EOT - echo "you are free little kubelet!" - EOT + cloudinit_pre_nodeadm = [{ + content = <<-EOT + --- + apiVersion: node.eks.aws/v1alpha1 + kind: NodeConfig + spec: + kubelet: + config: + shutdownGracePeriod: 30s + EOT + content_type = "application/node.eks.aws" + }] + + # This is only possible when `ami_id` is specified, indicating a custom AMI + cloudinit_post_nodeadm = [{ + content = <<-EOT + echo "All done" + EOT + content_type = "text/x-shellscript; charset=\"us-ascii\"" + }] } } ``` @@ -113,9 +125,9 @@ Refer to the [Self Managed Node Group documentation](https://docs.aws.amazon.com 1. The `self-managed-node-group` uses the latest AWS EKS Optimized AMI (Linux) for the given Kubernetes version by default: ```hcl - cluster_version = "1.33" + kubernetes_version = "1.33" - # This self managed node group will use the latest AWS EKS Optimized AMI for Kubernetes 1.27 + # This self managed node group will use the latest AWS EKS Optimized AMI for Kubernetes 1.33 self_managed_node_groups = { default = {} } @@ -124,7 +136,7 @@ Refer to the [Self Managed Node Group documentation](https://docs.aws.amazon.com 2. To use Bottlerocket, specify the `ami_type` as one of the respective `"BOTTLEROCKET_*" types` and supply a Bottlerocket OS AMI: ```hcl - cluster_version = "1.33" + kubernetes_version = "1.33" self_managed_node_groups = { bottlerocket = { @@ -139,42 +151,3 @@ See the [`examples/self-managed-node-group/` example](https://github.com/terrafo ### Fargate Profiles Fargate profiles are straightforward to use and therefore no further details are provided here. See the [`tests/fargate-profile/` tests](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/tests/fargate-profile) for a working example of various configurations. - -### Default Configurations - -Each type of compute resource (EKS managed node group, self managed node group, or Fargate profile) provides the option for users to specify a default configuration. These default configurations can be overridden from within the compute resource's individual definition. The order of precedence for configurations (from highest to least precedence): - -- Compute resource individual configuration - - Compute resource family default configuration (`eks_managed_node_group_defaults`, `self_managed_node_group_defaults`, `fargate_profile_defaults`) - - Module default configuration (see `variables.tf` and `node_groups.tf`) - -For example, the following creates 4 AWS EKS Managed Node Groups: - -```hcl - eks_managed_node_group_defaults = { - ami_type = "AL2_x86_64" - disk_size = 50 - instance_types = ["m6i.large", "m5.large", "m5n.large", "m5zn.large"] - } - - eks_managed_node_groups = { - # Uses module default configurations overridden by configuration above - default = {} - - # This further overrides the instance types used - compute = { - instance_types = ["c5.large", "c6i.large", "c6d.large"] - } - - # This further overrides the instance types and disk size used - persistent = { - disk_size = 1024 - instance_types = ["r5.xlarge", "r6i.xlarge", "r5b.xlarge"] - } - - # This overrides the OS used - bottlerocket = { - ami_type = "BOTTLEROCKET_x86_64" - } - } -``` diff --git a/docs/faq.md b/docs/faq.md index 2abd6695c2..69d8c90e7f 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -12,23 +12,44 @@ `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. If you wish to forgo the custom launch template route, you can set `use_custom_launch_template = false` and then you can set `disk_size` and `remote_access`. -### I received an error: `expect exactly one securityGroup tagged with kubernetes.io/cluster/
enabled = optional(bool)
})
list(object({| `[]` | no | | [cloudinit\_pre\_nodeadm](#input\_cloudinit\_pre\_nodeadm) | Array of cloud-init document parts that are created before the nodeadm document part |
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
}))
list(object({| `[]` | no | @@ -46,11 +46,9 @@ No modules. | [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 | -| [cluster\_service\_ipv4\_cidr](#input\_cluster\_service\_ipv4\_cidr) | [Deprecated] 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 | | [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 | -| [platform](#input\_platform) | [DEPRECATED - use `ami_type` instead. Will be removed in `v21.0`] Identifies the OS platform as `bottlerocket`, `linux` (AL2), `al2023`, or `windows` | `string` | `"linux"` | 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 | @@ -59,6 +57,5 @@ No modules. | Name | Description | |------|-------------| -| [platform](#output\_platform) | [DEPRECATED - Will be removed in `v21.0`] Identifies the OS platform as `bottlerocket`, `linux` (AL2), `al2023, or `windows | | [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 index c24074af15..c394421c4c 100644 --- a/modules/_user_data/main.tf +++ b/modules/_user_data/main.tf @@ -7,51 +7,47 @@ resource "null_resource" "validate_cluster_service_cidr" { 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(local.cluster_service_cidr) > 6 : true + condition = var.create ? length(var.cluster_service_cidr) > 6 : true error_message = "`cluster_service_cidr` is required when `create = true`." } } } locals { - # Converts AMI type into user data type that represents the underlying format (bash, toml, PS1, nodeadm) - # TODO - platform will be removed in v21.0 and only `ami_type` will be valid - ami_type_to_user_data_type = { - AL2_x86_64 = "linux" - AL2_x86_64_GPU = "linux" - AL2_ARM_64 = "linux" - BOTTLEROCKET_ARM_64 = "bottlerocket" - BOTTLEROCKET_x86_64 = "bottlerocket" - BOTTLEROCKET_ARM_64_FIPS = "bottlerocket" - BOTTLEROCKET_x86_64_FIPS = "bottlerocket" - BOTTLEROCKET_ARM_64_NVIDIA = "bottlerocket" - BOTTLEROCKET_x86_64_NVIDIA = "bottlerocket" - WINDOWS_CORE_2019_x86_64 = "windows" - WINDOWS_FULL_2019_x86_64 = "windows" - WINDOWS_CORE_2022_x86_64 = "windows" - WINDOWS_FULL_2022_x86_64 = "windows" - AL2023_x86_64_STANDARD = "al2023" - AL2023_ARM_64_STANDARD = "al2023" - AL2023_x86_64_NEURON = "al2023" - AL2023_x86_64_NVIDIA = "al2023" - AL2023_ARM_64_NVIDIA = "al2023" - } - # Try to use `ami_type` first, but fall back to current, default behavior - # TODO - will be removed in v21.0 - user_data_type = try(local.ami_type_to_user_data_type[var.ami_type], var.platform) - - template_path = { - al2023 = "${path.module}/../../templates/al2023_user_data.tpl" - bottlerocket = "${path.module}/../../templates/bottlerocket_user_data.tpl" - linux = "${path.module}/../../templates/linux_user_data.tpl" - windows = "${path.module}/../../templates/windows_user_data.tpl" + 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_service_cidr = try(coalesce(var.cluster_service_ipv4_cidr, var.cluster_service_cidr), "") - cluster_dns_ips = flatten(concat([try(cidrhost(local.cluster_service_cidr, 10), "")], var.additional_cluster_dns_ips)) + cluster_dns_ips = flatten(concat([try(cidrhost(var.cluster_service_cidr, 10), "")], var.additional_cluster_dns_ips)) - user_data = base64encode(templatefile( - coalesce(var.user_data_template_path, local.template_path[local.user_data_type]), + 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 @@ -61,7 +57,7 @@ locals { cluster_endpoint = var.cluster_endpoint cluster_auth_base64 = var.cluster_auth_base64 - cluster_service_cidr = local.cluster_service_cidr + cluster_service_cidr = var.cluster_service_cidr cluster_ip_family = var.cluster_ip_family # Bottlerocket @@ -72,22 +68,13 @@ locals { pre_bootstrap_user_data = var.pre_bootstrap_user_data post_bootstrap_user_data = var.post_bootstrap_user_data } - )) + )) : "" - user_data_type_to_rendered = { - al2023 = { - user_data = var.create ? try(data.cloudinit_config.al2023_eks_managed_node_group[0].rendered, local.user_data) : "" - } - bottlerocket = { - user_data = var.create && local.user_data_type == "bottlerocket" && (var.enable_bootstrap_user_data || var.user_data_template_path != "" || var.bootstrap_extra_args != "") ? local.user_data : "" - } - linux = { - user_data = var.create ? try(data.cloudinit_config.linux_eks_managed_node_group[0].rendered, local.user_data) : "" - } - windows = { - user_data = var.create && local.user_data_type == "windows" && (var.enable_bootstrap_user_data || var.user_data_template_path != "" || var.pre_bootstrap_user_data != "") ? local.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 @@ -97,8 +84,8 @@ locals { # 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" "linux_eks_managed_node_group" { - count = var.create && local.user_data_type == "linux" && var.is_eks_managed_node_group && !var.enable_bootstrap_user_data && var.pre_bootstrap_user_data != "" && var.user_data_template_path == "" ? 1 : 0 +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 @@ -129,7 +116,7 @@ locals { } data "cloudinit_config" "al2023_eks_managed_node_group" { - count = var.create && local.user_data_type == "al2023" && length(local.nodeadm_cloudinit) > 0 ? 1 : 0 + count = var.create && local.is_al2023 && length(local.nodeadm_cloudinit) > 0 ? 1 : 0 base64_encode = true gzip = false diff --git a/modules/_user_data/outputs.tf b/modules/_user_data/outputs.tf index 7bebb3f218..dda4b5195d 100644 --- a/modules/_user_data/outputs.tf +++ b/modules/_user_data/outputs.tf @@ -1,9 +1,4 @@ output "user_data" { description = "Base64 encoded user data rendered for the provided inputs" - value = try(local.user_data_type_to_rendered[local.user_data_type].user_data, null) -} - -output "platform" { - description = "[DEPRECATED - Will be removed in `v21.0`] Identifies the OS platform as `bottlerocket`, `linux` (AL2), `al2023, or `windows`" - value = local.user_data_type + value = local.user_data_type_to_rendered } diff --git a/modules/_user_data/variables.tf b/modules/_user_data/variables.tf index d5a1ef1bd3..ea3ad3620c 100644 --- a/modules/_user_data/variables.tf +++ b/modules/_user_data/variables.tf @@ -4,16 +4,10 @@ variable "create" { default = true } -variable "platform" { - description = "[DEPRECATED - use `ami_type` instead. Will be removed in `v21.0`] Identifies the OS platform as `bottlerocket`, `linux` (AL2), `al2023`, or `windows`" - type = string - default = "linux" -} - 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 = null + default = "AL2023_x86_64_STANDARD" } variable "enable_bootstrap_user_data" { @@ -64,13 +58,6 @@ variable "additional_cluster_dns_ips" { default = [] } -# TODO - remove at next breaking change -variable "cluster_service_ipv4_cidr" { - description = "[Deprecated] 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" - 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 diff --git a/modules/_user_data/versions.tf b/modules/_user_data/versions.tf index 9219addeda..a9802b0dea 100644 --- a/modules/_user_data/versions.tf +++ b/modules/_user_data/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { cloudinit = { diff --git a/modules/aws-auth/README.md b/modules/aws-auth/README.md deleted file mode 100644 index d4b97f14ca..0000000000 --- a/modules/aws-auth/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# `aws-auth` Module - -Configuration in this directory creates/updates the `aws-auth` ConfigMap. - -```hcl -module "eks" { - source = "terraform-aws-modules/eks/aws//modules/aws-auth" - version = "~> 20.0" - - manage_aws_auth_configmap = true - - aws_auth_roles = [ - { - rolearn = "arn:aws:iam::66666666666:role/role1" - username = "role1" - groups = ["system:masters"] - }, - ] - - aws_auth_users = [ - { - userarn = "arn:aws:iam::66666666666:user/user1" - username = "user1" - groups = ["system:masters"] - }, - { - userarn = "arn:aws:iam::66666666666:user/user2" - username = "user2" - groups = ["system:masters"] - }, - ] - - aws_auth_accounts = [ - "777777777777", - "888888888888", - ] -} -``` - -## Usage - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [kubernetes](#requirement\_kubernetes) | >= 2.20 | - -## Providers - -| Name | Version | -|------|---------| -| [kubernetes](#provider\_kubernetes) | >= 2.20 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [kubernetes_config_map.aws_auth](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map) | resource | -| [kubernetes_config_map_v1_data.aws_auth](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map_v1_data) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [aws\_auth\_accounts](#input\_aws\_auth\_accounts) | List of account maps to add to the aws-auth configmap | `list(any)` | `[]` | no | -| [aws\_auth\_roles](#input\_aws\_auth\_roles) | List of role maps to add to the aws-auth configmap | `list(any)` | `[]` | no | -| [aws\_auth\_users](#input\_aws\_auth\_users) | List of user maps to add to the aws-auth configmap | `list(any)` | `[]` | no | -| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | -| [create\_aws\_auth\_configmap](#input\_create\_aws\_auth\_configmap) | Determines whether to create the aws-auth configmap. NOTE - this is only intended for scenarios where the configmap does not exist (i.e. - when using only self-managed node groups). Most users should use `manage_aws_auth_configmap` | `bool` | `false` | no | -| [manage\_aws\_auth\_configmap](#input\_manage\_aws\_auth\_configmap) | Determines whether to manage the aws-auth configmap | `bool` | `true` | no | - -## Outputs - -No outputs. - diff --git a/modules/aws-auth/main.tf b/modules/aws-auth/main.tf deleted file mode 100644 index 2f7e9694a7..0000000000 --- a/modules/aws-auth/main.tf +++ /dev/null @@ -1,47 +0,0 @@ - -################################################################################ -# aws-auth configmap -################################################################################ - -locals { - aws_auth_configmap_data = { - mapRoles = yamlencode(var.aws_auth_roles) - mapUsers = yamlencode(var.aws_auth_users) - mapAccounts = yamlencode(var.aws_auth_accounts) - } -} - -resource "kubernetes_config_map" "aws_auth" { - count = var.create && var.create_aws_auth_configmap ? 1 : 0 - - metadata { - name = "aws-auth" - namespace = "kube-system" - } - - data = local.aws_auth_configmap_data - - lifecycle { - # We are ignoring the data here since we will manage it with the resource below - # This is only intended to be used in scenarios where the configmap does not exist - ignore_changes = [data, metadata[0].labels, metadata[0].annotations] - } -} - -resource "kubernetes_config_map_v1_data" "aws_auth" { - count = var.create && var.manage_aws_auth_configmap ? 1 : 0 - - force = true - - metadata { - name = "aws-auth" - namespace = "kube-system" - } - - data = local.aws_auth_configmap_data - - depends_on = [ - # Required for instances where the configmap does not exist yet to avoid race condition - kubernetes_config_map.aws_auth, - ] -} diff --git a/modules/aws-auth/outputs.tf b/modules/aws-auth/outputs.tf deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/modules/aws-auth/variables.tf b/modules/aws-auth/variables.tf deleted file mode 100644 index 3aaeb023e3..0000000000 --- a/modules/aws-auth/variables.tf +++ /dev/null @@ -1,39 +0,0 @@ -variable "create" { - description = "Controls if resources should be created (affects all resources)" - type = bool - default = true -} - -################################################################################ -# aws-auth ConfigMap -################################################################################ - -variable "create_aws_auth_configmap" { - description = "Determines whether to create the aws-auth configmap. NOTE - this is only intended for scenarios where the configmap does not exist (i.e. - when using only self-managed node groups). Most users should use `manage_aws_auth_configmap`" - type = bool - default = false -} - -variable "manage_aws_auth_configmap" { - description = "Determines whether to manage the aws-auth configmap" - type = bool - default = true -} - -variable "aws_auth_roles" { - description = "List of role maps to add to the aws-auth configmap" - type = list(any) - default = [] -} - -variable "aws_auth_users" { - description = "List of user maps to add to the aws-auth configmap" - type = list(any) - default = [] -} - -variable "aws_auth_accounts" { - description = "List of account maps to add to the aws-auth configmap" - type = list(any) - default = [] -} diff --git a/modules/aws-auth/versions.tf b/modules/aws-auth/versions.tf deleted file mode 100644 index f330045476..0000000000 --- a/modules/aws-auth/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.3.2" - - required_providers { - kubernetes = { - source = "hashicorp/kubernetes" - version = ">= 2.20" - } - } -} diff --git a/modules/eks-managed-node-group/README.md b/modules/eks-managed-node-group/README.md index 2eeb0c61c8..b7d3c7e8bf 100644 --- a/modules/eks-managed-node-group/README.md +++ b/modules/eks-managed-node-group/README.md @@ -63,14 +63,14 @@ module "eks_managed_node_group" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.95, < 6.0.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules @@ -82,7 +82,6 @@ module "eks_managed_node_group" { | Name | Type | |------|------| -| [aws_autoscaling_schedule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_schedule) | resource | | [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 | @@ -90,56 +89,56 @@ module "eks_managed_node_group" { | [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_ec2_instance_type_offerings.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_instance_type_offerings) | 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_subnets.placement_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnets) | 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` | `null` | no | -| [block\_device\_mappings](#input\_block\_device\_mappings) | Specify volumes to attach to the instance besides the volumes specified by the AMI | `any` | `{}` | 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 |
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
}))
map(object({| `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` | `""` | no | -| [capacity\_reservation\_specification](#input\_capacity\_reservation\_specification) | Targeting for EC2 capacity reservations | `any` | `{}` | no | +| [capacity\_reservation\_specification](#input\_capacity\_reservation\_specification) | Targeting for EC2 capacity reservations |
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)
}))
object({| `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 |
capacity_reservation_preference = optional(string)
capacity_reservation_target = optional(object({
capacity_reservation_id = optional(string)
capacity_reservation_resource_group_arn = optional(string)
}))
})
list(object({| `[]` | no | | [cloudinit\_pre\_nodeadm](#input\_cloudinit\_pre\_nodeadm) | Array of cloud-init document parts that are created before the nodeadm document part |
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
}))
list(object({| `[]` | 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 associated EKS cluster | `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` | `""` | no | -| [cluster\_service\_ipv4\_cidr](#input\_cluster\_service\_ipv4\_cidr) | [Deprecated] 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 | -| [cluster\_version](#input\_cluster\_version) | Kubernetes version. Defaults to EKS Cluster Kubernetes version | `string` | `null` | no | -| [cpu\_options](#input\_cpu\_options) | The CPU options for the instance | `map(string)` | `{}` | no | +| [cpu\_options](#input\_cpu\_options) | The CPU options for the instance |
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
}))
object({| `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\_schedule](#input\_create\_schedule) | Determines whether to create autoscaling group schedule or not | `bool` | `true` | no | -| [credit\_specification](#input\_credit\_specification) | Customize the credit specification of the instance | `map(string)` | `{}` | 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 |
amd_sev_snp = optional(string)
core_count = optional(number)
threads_per_core = optional(number)
})
object({| `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)` |
cpu_credits = optional(string)
})
[| no | -| [elastic\_gpu\_specifications](#input\_elastic\_gpu\_specifications) | The elastic GPU to attach to the instance | `any` | `{}` | no | -| [elastic\_inference\_accelerator](#input\_elastic\_inference\_accelerator) | Configuration block containing an Elastic Inference Accelerator to attach to the instance | `map(string)` | `{}` | 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` | `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` | `true` | no | -| [enclave\_options](#input\_enclave\_options) | Enable Nitro Enclaves on launched instances | `map(string)` | `{}` | 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({| `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 | @@ -148,13 +147,14 @@ module "eks_managed_node_group" { | [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 | `any` | `[]` | 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 |
enabled = optional(bool)
})
list(object({| `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 | `any` | `{}` | no | +| [instance\_market\_options](#input\_instance\_market\_options) | The market (purchasing) option for the instance |
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
})))
}))
object({| `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 | @@ -163,33 +163,37 @@ module "eks_managed_node_group" { | [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 map of license specifications to associate with | `any` | `{}` | no | -| [maintenance\_options](#input\_maintenance\_options) | The maintenance options for the instance | `any` | `{}` | no | +| [license\_specifications](#input\_license\_specifications) | A list of license specifications to associate with |
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)
}))
})
list(object({| `null` | no | +| [maintenance\_options](#input\_maintenance\_options) | The maintenance options for the instance |
license_configuration_arn = string
}))
object({| `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 | `map(string)` |
auto_recovery = optional(string)
})
{| no | -| [min\_size](#input\_min\_size) | Minimum number of instances/nodes | `number` | `0` | no | +| [metadata\_options](#input\_metadata\_options) | Customize the metadata options for the instance |
"http_endpoint": "enabled",
"http_put_response_hop_limit": 2,
"http_tokens": "required"
}
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)
})
{| 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(any)` | `[]` | no | +| [network\_interfaces](#input\_network\_interfaces) | Customize network interfaces to be attached at instance boot time |
"http_endpoint": "enabled",
"http_put_response_hop_limit": 1,
"http_tokens": "required"
}
list(object({| `[]` | no | | [node\_repair\_config](#input\_node\_repair\_config) | The node auto repair configuration for the node group |
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)
}))
object({| `null` | no | -| [placement](#input\_placement) | The placement of the instance | `map(string)` | `{}` | no | -| [placement\_group\_az](#input\_placement\_group\_az) | Availability zone where placement group is created (ex. `eu-west-1c`) | `string` | `null` | no | -| [placement\_group\_strategy](#input\_placement\_group\_strategy) | The placement group strategy | `string` | `"cluster"` | no | -| [platform](#input\_platform) | [DEPRECATED - use `ami_type` instead. Will be removed in `v21.0`] Identifies the OS platform as `bottlerocket`, `linux` (AL2), `al2023`, or `windows` | `string` | `"linux"` | 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 |
enabled = optional(bool, true)
})
object({| `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` | `""` | 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 | -| [private\_dns\_name\_options](#input\_private\_dns\_name\_options) | The options for the instance hostname. The default values are inherited from the subnet | `map(string)` | `{}` | no | +| [private\_dns\_name\_options](#input\_private\_dns\_name\_options) | The options for the instance hostname. The default values are inherited from the subnet |
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)
})
object({| `null` | no | | [ram\_disk\_id](#input\_ram\_disk\_id) | The ID of the ram disk | `string` | `null` | no | -| [remote\_access](#input\_remote\_access) | Configuration block with remote access settings. Only valid when `use_custom_launch_template` = `false` | `any` | `{}` | no | -| [schedules](#input\_schedules) | Map of autoscaling group schedule to create | `map(any)` | `{}` | 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` |
enable_resource_name_dns_aaaa_record = optional(bool)
enable_resource_name_dns_a_record = optional(bool)
hostname_type = optional(string)
})
object({| `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 |
ec2_ssh_key = optional(string)
source_security_group_ids = optional(list(string))
})
map(object({| `{}` | no | +| [security\_group\_ingress\_rules](#input\_security\_group\_ingress\_rules) | Security group ingress rules to add to the security group created |
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)
}))
map(object({| `{}` | 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)` |
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 | | [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 | `any` | `{}` | no | -| [timeouts](#input\_timeouts) | Create, update, and delete timeout configurations for the node group | `map(string)` | `{}` | no | -| [update\_config](#input\_update\_config) | Configuration block of settings for max unavailable resources during node group updates | `map(string)` |
"instance",
"volume",
"network-interface"
]
{| no | +| [taints](#input\_taints) | The Kubernetes taints to be applied to the nodes in the node group. Maximum of 50 taints per node group |
"max_unavailable_percentage": 33
}
map(object({| `null` | no | +| [timeouts](#input\_timeouts) | Create, update, and delete timeout configurations for the node group |
key = string
value = optional(string)
effect = string
}))
object({| `null` | no | +| [update\_config](#input\_update\_config) | Configuration block of settings for max unavailable resources during node group updates |
create = optional(string)
update = optional(string)
delete = optional(string)
})
object({|
max_unavailable = optional(number)
max_unavailable_percentage = optional(number)
})
{| 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 `cluster_version` must be supplied in order to enable this feature | `bool` | `false` | 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` | `""` | no | | [vpc\_security\_group\_ids](#input\_vpc\_security\_group\_ids) | A list of security group IDs to associate | `list(string)` | `[]` | no | @@ -198,7 +202,6 @@ module "eks_managed_node_group" { | Name | Description | |------|-------------| -| [autoscaling\_group\_schedule\_arns](#output\_autoscaling\_group\_schedule\_arns) | ARNs of autoscaling group schedules | | [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 | @@ -213,5 +216,6 @@ module "eks_managed_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 | -| [platform](#output\_platform) | [DEPRECATED - Will be removed in `v21.0`] Identifies the OS platform as `bottlerocket`, `linux` (AL2), `al2023`, or `windows` | +| [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 | diff --git a/modules/eks-managed-node-group/main.tf b/modules/eks-managed-node-group/main.tf index b9c9ba0e87..d07c38633d 100644 --- a/modules/eks-managed-node-group/main.tf +++ b/modules/eks-managed-node-group/main.tf @@ -1,5 +1,14 @@ -data "aws_partition" "current" {} -data "aws_caller_identity" "current" {} +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 @@ -9,14 +18,13 @@ module "user_data" { source = "../_user_data" create = var.create - platform = var.platform 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 = try(coalesce(var.cluster_service_cidr, var.cluster_service_ipv4_cidr), "") + 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 @@ -35,6 +43,8 @@ module "user_data" { data "aws_ec2_instance_type" "this" { count = var.create && var.enable_efa_support ? 1 : 0 + region = var.region + instance_type = local.efa_instance_type } @@ -64,71 +74,72 @@ locals { 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)) - - placement = local.create_placement_group ? { group_name = aws_placement_group.this[0].name } : var.placement + 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 + for_each = var.block_device_mappings != null ? var.block_device_mappings : {} content { - device_name = try(block_device_mappings.value.device_name, null) + device_name = block_device_mappings.value.device_name dynamic "ebs" { - for_each = try([block_device_mappings.value.ebs], []) + for_each = block_device_mappings.value.ebs != null ? [block_device_mappings.value.ebs] : [] content { - delete_on_termination = try(ebs.value.delete_on_termination, null) - encrypted = try(ebs.value.encrypted, null) - iops = try(ebs.value.iops, null) - kms_key_id = try(ebs.value.kms_key_id, null) - snapshot_id = try(ebs.value.snapshot_id, null) - throughput = try(ebs.value.throughput, null) - volume_size = try(ebs.value.volume_size, null) - volume_type = try(ebs.value.volume_type, null) + 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 = try(block_device_mappings.value.no_device, null) - virtual_name = try(block_device_mappings.value.virtual_name, null) + no_device = block_device_mappings.value.no_device + virtual_name = block_device_mappings.value.virtual_name } } dynamic "capacity_reservation_specification" { - for_each = length(var.capacity_reservation_specification) > 0 ? [var.capacity_reservation_specification] : [] + for_each = var.capacity_reservation_specification != null ? [var.capacity_reservation_specification] : [] content { - capacity_reservation_preference = try(capacity_reservation_specification.value.capacity_reservation_preference, null) + capacity_reservation_preference = capacity_reservation_specification.value.capacity_reservation_preference dynamic "capacity_reservation_target" { - for_each = try([capacity_reservation_specification.value.capacity_reservation_target], []) - + for_each = capacity_reservation_specification.value.capacity_reservation_target != null ? [capacity_reservation_specification.value.capacity_reservation_target] : [] content { - capacity_reservation_id = try(capacity_reservation_target.value.capacity_reservation_id, null) - capacity_reservation_resource_group_arn = try(capacity_reservation_target.value.capacity_reservation_resource_group_arn, null) + 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 = length(var.cpu_options) > 0 ? [var.cpu_options] : [] + for_each = var.cpu_options != null ? [var.cpu_options] : [] content { - core_count = try(cpu_options.value.core_count, null) - threads_per_core = try(cpu_options.value.threads_per_core, null) + 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 = length(var.credit_specification) > 0 ? [var.credit_specification] : [] + for_each = var.credit_specification != null ? [var.credit_specification] : [] content { - cpu_credits = try(credit_specification.value.cpu_credits, null) + cpu_credits = credit_specification.value.cpu_credits } } @@ -137,24 +148,8 @@ resource "aws_launch_template" "this" { disable_api_termination = var.disable_api_termination ebs_optimized = var.ebs_optimized - dynamic "elastic_gpu_specifications" { - for_each = var.elastic_gpu_specifications - - content { - type = elastic_gpu_specifications.value.type - } - } - - dynamic "elastic_inference_accelerator" { - for_each = length(var.elastic_inference_accelerator) > 0 ? [var.elastic_inference_accelerator] : [] - - content { - type = elastic_inference_accelerator.value.type - } - } - dynamic "enclave_options" { - for_each = length(var.enclave_options) > 0 ? [var.enclave_options] : [] + for_each = var.enclave_options != null ? [var.enclave_options] : [] content { enabled = enclave_options.value.enabled @@ -187,20 +182,20 @@ resource "aws_launch_template" "this" { # instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior dynamic "instance_market_options" { - for_each = length(var.instance_market_options) > 0 ? [var.instance_market_options] : [] + for_each = var.instance_market_options != null ? [var.instance_market_options] : [] content { - market_type = try(instance_market_options.value.market_type, null) + market_type = instance_market_options.value.market_type dynamic "spot_options" { - for_each = try([instance_market_options.value.spot_options], []) + for_each = instance_market_options.value.spot_options != null ? [instance_market_options.value.spot_options] : [] content { - block_duration_minutes = try(spot_options.value.block_duration_minutes, null) - instance_interruption_behavior = try(spot_options.value.instance_interruption_behavior, null) - max_price = try(spot_options.value.max_price, null) - spot_instance_type = try(spot_options.value.spot_instance_type, null) - valid_until = try(spot_options.value.valid_until, null) + 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 } } } @@ -213,7 +208,7 @@ resource "aws_launch_template" "this" { key_name = var.key_name dynamic "license_specification" { - for_each = length(var.license_specifications) > 0 ? var.license_specifications : {} + for_each = var.license_specifications != null ? var.license_specifications : [] content { license_configuration_arn = license_specification.value.license_configuration_arn @@ -221,22 +216,22 @@ resource "aws_launch_template" "this" { } dynamic "maintenance_options" { - for_each = length(var.maintenance_options) > 0 ? [var.maintenance_options] : [] + for_each = var.maintenance_options != null ? [var.maintenance_options] : [] content { - auto_recovery = try(maintenance_options.value.auto_recovery, null) + auto_recovery = maintenance_options.value.auto_recovery } } dynamic "metadata_options" { - for_each = length(var.metadata_options) > 0 ? [var.metadata_options] : [] + for_each = var.metadata_options != null ? [var.metadata_options] : [] content { - http_endpoint = try(metadata_options.value.http_endpoint, null) - http_protocol_ipv6 = try(metadata_options.value.http_protocol_ipv6, null) - http_put_response_hop_limit = try(metadata_options.value.http_put_response_hop_limit, null) - http_tokens = try(metadata_options.value.http_tokens, null) - instance_metadata_tags = try(metadata_options.value.instance_metadata_tags, null) + 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 } } @@ -252,29 +247,57 @@ resource "aws_launch_template" "this" { name_prefix = var.launch_template_use_name_prefix ? "${local.launch_template_name}-" : null dynamic "network_interfaces" { - for_each = local.network_interfaces + for_each = length(var.network_interfaces) > 0 ? var.network_interfaces : [] content { - associate_carrier_ip_address = try(network_interfaces.value.associate_carrier_ip_address, null) - associate_public_ip_address = try(network_interfaces.value.associate_public_ip_address, null) - delete_on_termination = try(network_interfaces.value.delete_on_termination, null) - description = try(network_interfaces.value.description, null) - device_index = try(network_interfaces.value.device_index, null) - interface_type = try(network_interfaces.value.interface_type, null) - ipv4_address_count = try(network_interfaces.value.ipv4_address_count, null) - ipv4_addresses = try(network_interfaces.value.ipv4_addresses, []) - ipv4_prefix_count = try(network_interfaces.value.ipv4_prefix_count, null) - ipv4_prefixes = try(network_interfaces.value.ipv4_prefixes, null) - ipv6_address_count = try(network_interfaces.value.ipv6_address_count, null) - ipv6_addresses = try(network_interfaces.value.ipv6_addresses, []) - ipv6_prefix_count = try(network_interfaces.value.ipv6_prefix_count, null) - ipv6_prefixes = try(network_interfaces.value.ipv6_prefixes, []) - network_card_index = try(network_interfaces.value.network_card_index, null) - network_interface_id = try(network_interfaces.value.network_interface_id, null) - primary_ipv6 = try(network_interfaces.value.primary_ipv6, null) - private_ip_address = try(network_interfaces.value.private_ip_address, null) + 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(try(network_interfaces.value.security_groups, []), local.security_group_ids)) + security_groups = compact(concat(network_interfaces.value.security_groups, var.vpc_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) @@ -282,14 +305,14 @@ resource "aws_launch_template" "this" { } dynamic "placement" { - for_each = length(local.placement) > 0 ? [local.placement] : [] + for_each = var.placement != null || local.create_placement_group ? [var.placement] : [] content { affinity = try(placement.value.affinity, null) - availability_zone = lookup(placement.value, "availability_zone", null) - group_name = lookup(placement.value, "group_name", null) - host_id = lookup(placement.value, "host_id", null) - host_resource_group_arn = lookup(placement.value, "host_resource_group_arn", 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) @@ -297,12 +320,12 @@ resource "aws_launch_template" "this" { } dynamic "private_dns_name_options" { - for_each = length(var.private_dns_name_options) > 0 ? [var.private_dns_name_options] : [] + for_each = var.private_dns_name_options != null ? [var.private_dns_name_options] : [] content { - enable_resource_name_dns_aaaa_record = try(private_dns_name_options.value.enable_resource_name_dns_aaaa_record, null) - enable_resource_name_dns_a_record = try(private_dns_name_options.value.enable_resource_name_dns_a_record, null) - hostname_type = try(private_dns_name_options.value.hostname_type, null) + 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 } } @@ -342,32 +365,41 @@ resource "aws_launch_template" "this" { # 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_cluster_version = var.cluster_version != null ? var.cluster_version : "" - ssm_ami_type = var.ami_type != null ? var.ami_type : "" + 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_cluster_version}/amazon-linux-2/recommended/release_version" - AL2_x86_64_GPU = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2-gpu/recommended/release_version" - AL2_ARM_64 = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2-arm64/recommended/release_version" + 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_cluster_version}/arm64/latest/image_version" - BOTTLEROCKET_x86_64 = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_version}/x86_64/latest/image_version" - BOTTLEROCKET_ARM_64_FIPS = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_version}-fips/arm64/latest/image_version" - BOTTLEROCKET_x86_64_FIPS = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_version}-fips/x86_64/latest/image_version" - BOTTLEROCKET_ARM_64_NVIDIA = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_version}-nvidia/arm64/latest/image_version" - BOTTLEROCKET_x86_64_NVIDIA = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_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_cluster_version}" - WINDOWS_FULL_2019_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2019-English-Core-EKS_Optimized-${local.ssm_cluster_version}" - WINDOWS_CORE_2022_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2022-English-Full-EKS_Optimized-${local.ssm_cluster_version}" - WINDOWS_FULL_2022_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2022-English-Core-EKS_Optimized-${local.ssm_cluster_version}" - AL2023_x86_64_STANDARD = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2023/x86_64/standard/recommended/release_version" - AL2023_ARM_64_STANDARD = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2023/arm64/standard/recommended/release_version" - AL2023_x86_64_NEURON = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2023/x86_64/neuron/recommended/release_version" - AL2023_x86_64_NVIDIA = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2023/x86_64/nvidia/recommended/release_version" - AL2023_ARM_64_NVIDIA = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2023/arm64/nvidia/recommended/release_version" + 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 @@ -379,6 +411,8 @@ locals { 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] } @@ -395,10 +429,12 @@ locals { 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 = local.create_placement_group ? data.aws_subnets.placement_group[0].ids : var.subnet_ids + subnet_ids = var.subnet_ids scaling_config { min_size = var.min_size @@ -413,7 +449,7 @@ resource "aws_eks_node_group" "this" { # 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.cluster_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 @@ -432,30 +468,30 @@ resource "aws_eks_node_group" "this" { } dynamic "remote_access" { - for_each = length(var.remote_access) > 0 ? [var.remote_access] : [] + for_each = var.remote_access != null ? [var.remote_access] : [] content { - ec2_ssh_key = try(remote_access.value.ec2_ssh_key, null) - source_security_group_ids = try(remote_access.value.source_security_group_ids, []) + 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 + for_each = var.taints != null ? var.taints : {} content { key = taint.value.key - value = try(taint.value.value, null) + value = taint.value.value effect = taint.value.effect } } dynamic "update_config" { - for_each = length(var.update_config) > 0 ? [var.update_config] : [] + for_each = var.update_config != null ? [var.update_config] : [] content { - max_unavailable_percentage = try(update_config.value.max_unavailable_percentage, null) - max_unavailable = try(update_config.value.max_unavailable, null) + max_unavailable_percentage = update_config.value.max_unavailable_percentage + max_unavailable = update_config.value.max_unavailable } } @@ -467,10 +503,14 @@ resource "aws_eks_node_group" "this" { } } - timeouts { - create = lookup(var.timeouts, "create", null) - update = lookup(var.timeouts, "update", null) - delete = lookup(var.timeouts, "delete", null) + dynamic "timeouts" { + for_each = var.timeouts != null ? [var.timeouts] : [] + + content { + create = var.timeouts.create + update = var.timeouts.update + delete = var.timeouts.delete + } } lifecycle { @@ -494,13 +534,13 @@ 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:${data.aws_partition.current.partition}:iam::aws:policy" + 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:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:policy/AmazonEKS_CNI_IPv6_Policy" + 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" } } @@ -560,25 +600,25 @@ resource "aws_iam_role_policy_attachment" "additional" { ################################################################################ locals { - create_iam_role_policy = local.create_iam_role && var.create_iam_role_policy && length(var.iam_role_policy_statements) > 0 + 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 + for_each = var.iam_role_policy_statements != null ? var.iam_role_policy_statements : [] content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) + 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 = try(statement.value.principals, []) + for_each = statement.value.principals != null ? statement.value.principals : [] content { type = principals.value.type @@ -587,7 +627,7 @@ data "aws_iam_policy_document" "role" { } dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] content { type = not_principals.value.type @@ -596,7 +636,7 @@ data "aws_iam_policy_document" "role" { } dynamic "condition" { - for_each = try(statement.value.conditions, []) + for_each = statement.value.condition != null ? statement.value.condition : [] content { test = condition.value.test @@ -628,84 +668,117 @@ locals { resource "aws_placement_group" "this" { count = local.create_placement_group ? 1 : 0 + region = var.region + name = "${var.cluster_name}-${var.name}" - strategy = var.placement_group_strategy + strategy = "cluster" tags = var.tags } ################################################################################ -# Instance AZ Lookup - -# Instances usually used in placement groups w/ EFA are only available in -# select availability zones. These data sources will cross reference the availability -# zones supported by the instance type with the subnets provided to ensure only -# AZs/subnets that are supported are used. +# Security Group ################################################################################ -# Find the availability zones supported by the instance type -# TODO - remove at next breaking change -# Force users to be explicit about which AZ to use when using placement groups, -# with or without EFA support -data "aws_ec2_instance_type_offerings" "this" { - count = local.enable_efa_support ? 1 : 0 - - filter { - name = "instance-type" - values = [local.efa_instance_type] - } +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}") - location_type = "availability-zone-id" + security_group_ingress_rules = merge({ for k, v in + { + all_self_efa = { + description = "Node to node EFA" + protocol = "-1" + from_port = 0 + self = true + } + } : 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" + protocol = "-1" + to_port = 0 + self = true + } + } : k => v if var.enable_efa_support + }, + var.security_group_egress_rules + ) } -# Reverse the lookup to find one of the subnets provided based on the availability -# availability zone ID of the queried instance type (supported) -data "aws_subnets" "placement_group" { - count = local.create_placement_group ? 1 : 0 +data "aws_subnet" "this" { + count = local.create_security_group ? 1 : 0 - filter { - name = "subnet-id" - values = var.subnet_ids - } + region = var.region - # The data source can lookup the first available AZ or you can specify an AZ (next filter) - dynamic "filter" { - for_each = var.enable_efa_support && var.placement_group_az == null ? [1] : [] + id = element(var.subnet_ids, 0) +} - content { - name = "availability-zone-id" - values = data.aws_ec2_instance_type_offerings.this[0].locations - } - } +resource "aws_security_group" "this" { + count = local.create_security_group ? 1 : 0 - dynamic "filter" { - for_each = var.placement_group_az != null ? [var.placement_group_az] : [] + region = var.region - content { - name = "availability-zone" - values = [filter.value] - } + 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 } } -################################################################################ -# Autoscaling Group Schedule -################################################################################ +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 -resource "aws_autoscaling_schedule" "this" { - for_each = { for k, v in var.schedules : k => v if var.create && var.create_schedule } + 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) +} - scheduled_action_name = each.key - autoscaling_group_name = aws_eks_node_group.this[0].resources[0].autoscaling_groups[0].name +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 } - min_size = try(each.value.min_size, -1) - max_size = try(each.value.max_size, -1) - desired_capacity = try(each.value.desired_size, -1) - start_time = try(each.value.start_time, null) - end_time = try(each.value.end_time, null) - time_zone = try(each.value.time_zone, null) + region = var.region - # [Minute] [Hour] [Day_of_Month] [Month_of_Year] [Day_of_Week] - # Cron examples: https://crontab.guru/examples.html - recurrence = try(each.value.recurrence, null) + 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/outputs.tf b/modules/eks-managed-node-group/outputs.tf index 8cab6e2c13..a7d6fcf62b 100644 --- a/modules/eks-managed-node-group/outputs.tf +++ b/modules/eks-managed-node-group/outputs.tf @@ -61,15 +61,6 @@ output "node_group_taints" { value = try(aws_eks_node_group.this[0].taint, []) } -################################################################################ -# Autoscaling Group Schedule -################################################################################ - -output "autoscaling_group_schedule_arns" { - description = "ARNs of autoscaling group schedules" - value = { for k, v in aws_autoscaling_schedule.this : k => v.arn } -} - ################################################################################ # IAM Role ################################################################################ @@ -90,10 +81,15 @@ output "iam_role_unique_id" { } ################################################################################ -# Additional +# Security Group ################################################################################ -output "platform" { - description = "[DEPRECATED - Will be removed in `v21.0`] Identifies the OS platform as `bottlerocket`, `linux` (AL2), `al2023`, or `windows`" - value = module.user_data.platform +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 index 54338542c9..96eccee9bd 100644 --- a/modules/eks-managed-node-group/variables.tf +++ b/modules/eks-managed-node-group/variables.tf @@ -2,6 +2,7 @@ variable "create" { description = "Determines whether to create EKS managed node group or not" type = bool default = true + nullable = false } variable "tags" { @@ -10,10 +11,22 @@ variable "tags" { default = {} } -variable "platform" { - description = "[DEPRECATED - use `ami_type` instead. Will be removed in `v21.0`] Identifies the OS platform as `bottlerocket`, `linux` (AL2), `al2023`, or `windows`" +variable "region" { + description = "Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration" type = string - default = "linux" + 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 = "" } ################################################################################ @@ -29,7 +42,8 @@ variable "enable_bootstrap_user_data" { variable "cluster_name" { description = "Name of associated EKS cluster" type = string - default = null + default = "" + nullable = false } variable "cluster_endpoint" { @@ -50,13 +64,6 @@ variable "cluster_service_cidr" { default = "" } -# TODO - remove at next breaking change -variable "cluster_service_ipv4_cidr" { - description = "[Deprecated] 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" - 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 @@ -111,6 +118,7 @@ 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" { @@ -205,79 +213,114 @@ variable "ram_disk_id" { variable "block_device_mappings" { description = "Specify volumes to attach to the instance besides the volumes specified by the AMI" - type = any - default = {} + 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 = any - default = {} + 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 = map(string) - default = {} + 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 = map(string) - default = {} -} - -variable "elastic_gpu_specifications" { - description = "The elastic GPU to attach to the instance" - type = any - default = {} -} - -variable "elastic_inference_accelerator" { - description = "Configuration block containing an Elastic Inference Accelerator to attach to the instance" - type = map(string) - default = {} + type = object({ + cpu_credits = optional(string) + }) + default = null } variable "enclave_options" { description = "Enable Nitro Enclaves on launched instances" - type = map(string) - default = {} + type = object({ + enabled = optional(bool) + }) + default = null } variable "instance_market_options" { description = "The market (purchasing) option for the instance" - type = any - default = {} + 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 = any - default = {} + type = object({ + auto_recovery = optional(string) + }) + default = null } variable "license_specifications" { - description = "A map of license specifications to associate with" - type = any - default = {} + 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 = map(string) + 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" - http_put_response_hop_limit = 2 } } -# TODO - make this false by default at next breaking change variable "enable_monitoring" { description = "Enables/disables detailed monitoring" type = bool - default = true + default = false } variable "enable_efa_support" { @@ -286,11 +329,10 @@ variable "enable_efa_support" { default = false } -# TODO - make this true by default at next breaking change (remove variable, only pass indices) 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 = false + default = true } variable "efa_indices" { @@ -301,33 +343,72 @@ variable "efa_indices" { variable "network_interfaces" { description = "Customize network interfaces to be attached at instance boot time" - type = list(any) - default = [] + 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), []) + subnet_id = optional(string) + })) + default = [] } variable "placement" { description = "The placement of the instance" - type = map(string) - default = {} + 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 -} - -# TODO - remove at next breaking change -variable "placement_group_strategy" { - description = "The placement group strategy" - type = string - default = "cluster" + nullable = false } variable "private_dns_name_options" { description = "The options for the instance hostname. The default values are inherited from the subnet" - type = map(string) - default = {} + 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" { @@ -352,16 +433,10 @@ variable "subnet_ids" { default = null } -variable "placement_group_az" { - description = "Availability zone where placement group is created (ex. `eu-west-1c`)" - type = string - default = null -} - variable "min_size" { description = "Minimum number of instances/nodes" type = number - default = 0 + default = 1 } variable "max_size" { @@ -380,6 +455,7 @@ variable "name" { description = "Name of the EKS managed node group" type = string default = "" + nullable = false } variable "use_name_prefix" { @@ -391,7 +467,7 @@ variable "use_name_prefix" { 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 = null + default = "AL2023_x86_64_STANDARD" } variable "ami_release_version" { @@ -401,9 +477,9 @@ variable "ami_release_version" { } 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 `cluster_version` must be supplied in order to enable this feature" + 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 = false + default = true } variable "capacity_type" { @@ -436,7 +512,7 @@ variable "labels" { default = null } -variable "cluster_version" { +variable "kubernetes_version" { description = "Kubernetes version. Defaults to EKS Cluster Kubernetes version" type = string default = null @@ -450,19 +526,29 @@ variable "launch_template_version" { variable "remote_access" { description = "Configuration block with remote access settings. Only valid when `use_custom_launch_template` = `false`" - type = any - default = {} + 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 = any - default = {} + 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 = map(string) + type = object({ + max_unavailable = optional(number) + max_unavailable_percentage = optional(number) + }) default = { max_unavailable_percentage = 33 } @@ -478,8 +564,12 @@ variable "node_repair_config" { variable "timeouts" { description = "Create, update, and delete timeout configurations for the node group" - type = map(string) - default = {} + type = object({ + create = optional(string) + update = optional(string) + delete = optional(string) + }) + default = null } ################################################################################ @@ -490,6 +580,7 @@ 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" { @@ -560,26 +651,104 @@ 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 = any - default = [] + 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 } ################################################################################ -# Autoscaling Group Schedule +# Security Group ################################################################################ -variable "create_schedule" { - description = "Determines whether to create autoscaling group schedule or not" +variable "create_security_group" { + description = "Determines if a security group is created" type = bool default = true + nullable = false } -variable "schedules" { - description = "Map of autoscaling group schedule to create" - type = map(any) +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 +} + +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 index 7884c4e1b2..db13b0a8d2 100644 --- a/modules/eks-managed-node-group/versions.tf +++ b/modules/eks-managed-node-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.95, < 6.0.0" + version = ">= 6.0" } } } diff --git a/modules/fargate-profile/README.md b/modules/fargate-profile/README.md index 9b4cb99dd3..bf199e1c41 100644 --- a/modules/fargate-profile/README.md +++ b/modules/fargate-profile/README.md @@ -28,14 +28,14 @@ module "fargate_profile" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.95, < 6.0.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules @@ -60,8 +60,9 @@ No modules. | 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` | `null` | 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 | @@ -72,14 +73,16 @@ No modules. | [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 | `any` | `[]` | 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 |
"max_unavailable_percentage": 33
}
list(object({| `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 | -| [selectors](#input\_selectors) | Configuration block(s) for selecting Kubernetes Pods to execute with this Fargate Profile | `any` | `[]` | 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 |
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
})))
}))
list(object({| `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 | `map(string)` | `{}` | no | +| [timeouts](#input\_timeouts) | Create and delete timeout configurations for the Fargate Profile |
labels = optional(map(string))
namespace = string
}))
object({| `null` | no | ## Outputs diff --git a/modules/fargate-profile/main.tf b/modules/fargate-profile/main.tf index ffbbfb8294..78c94357f4 100644 --- a/modules/fargate-profile/main.tf +++ b/modules/fargate-profile/main.tf @@ -1,18 +1,32 @@ -data "aws_partition" "current" {} -data "aws_caller_identity" "current" {} -data "aws_region" "current" {} +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:${data.aws_partition.current.partition}:iam::aws:policy" + 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:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:policy/AmazonEKS_CNI_IPv6_Policy" + 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" } } @@ -37,7 +51,7 @@ data "aws_iam_policy_document" "assume_role_policy" { variable = "aws:SourceArn" values = [ - "arn:${data.aws_partition.current.partition}:eks:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:fargateprofile/${var.cluster_name}/*", + "arn:${local.partition}:eks:${local.region}:${local.account_id}:fargateprofile/${var.cluster_name}/*", ] } } @@ -83,25 +97,25 @@ resource "aws_iam_role_policy_attachment" "additional" { ################################################################################ locals { - create_iam_role_policy = local.create_iam_role && var.create_iam_role_policy && length(var.iam_role_policy_statements) > 0 + 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 + for_each = var.iam_role_policy_statements != null ? var.iam_role_policy_statements : [] content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) + 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 = try(statement.value.principals, []) + for_each = statement.value.principals != null ? statement.value.principals : [] content { type = principals.value.type @@ -110,7 +124,7 @@ data "aws_iam_policy_document" "role" { } dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] content { type = not_principals.value.type @@ -119,7 +133,7 @@ data "aws_iam_policy_document" "role" { } dynamic "condition" { - for_each = try(statement.value.conditions, []) + for_each = statement.value.condition != null ? statement.value.condition : [] content { test = condition.value.test @@ -147,25 +161,28 @@ resource "aws_iam_role_policy" "this" { 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 + for_each = var.selectors != null ? var.selectors : [] content { namespace = selector.value.namespace - labels = lookup(selector.value, "labels", {}) + labels = selector.value.labels } } dynamic "timeouts" { - for_each = [var.timeouts] + for_each = var.timeouts != null ? [var.timeouts] : [] + content { - create = lookup(var.timeouts, "create", null) - delete = lookup(var.timeouts, "delete", null) + create = var.timeouts.create + delete = var.timeouts.delete } } diff --git a/modules/fargate-profile/variables.tf b/modules/fargate-profile/variables.tf index 3e37b8c151..64e210dbe0 100644 --- a/modules/fargate-profile/variables.tf +++ b/modules/fargate-profile/variables.tf @@ -2,6 +2,7 @@ variable "create" { description = "Determines whether to create Fargate profile or not" type = bool default = true + nullable = false } variable "tags" { @@ -10,6 +11,24 @@ variable "tags" { 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 = "" +} + ################################################################################ # IAM Role ################################################################################ @@ -18,6 +37,7 @@ 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" { @@ -88,12 +108,33 @@ 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 = any - default = [] + 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 } ################################################################################ @@ -103,13 +144,14 @@ variable "iam_role_policy_statements" { variable "cluster_name" { description = "Name of the EKS cluster" type = string - default = null + default = "" } variable "name" { description = "Name of the EKS Fargate Profile" type = string default = "" + nullable = false } variable "subnet_ids" { @@ -120,12 +162,18 @@ variable "subnet_ids" { variable "selectors" { description = "Configuration block(s) for selecting Kubernetes Pods to execute with this Fargate Profile" - type = any - default = [] + type = list(object({ + labels = optional(map(string)) + namespace = string + })) + default = null } variable "timeouts" { description = "Create and delete timeout configurations for the Fargate Profile" - type = map(string) - default = {} + type = object({ + create = optional(string) + delete = optional(string) + }) + default = null } diff --git a/modules/fargate-profile/versions.tf b/modules/fargate-profile/versions.tf index 7884c4e1b2..db13b0a8d2 100644 --- a/modules/fargate-profile/versions.tf +++ b/modules/fargate-profile/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.95, < 6.0.0" + version = ">= 6.0" } } } diff --git a/modules/hybrid-node-role/README.md b/modules/hybrid-node-role/README.md index 3119747f76..176aabcdf4 100644 --- a/modules/hybrid-node-role/README.md +++ b/modules/hybrid-node-role/README.md @@ -74,14 +74,14 @@ module "eks_hybrid_node_role" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.95, < 6.0.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules @@ -115,7 +115,7 @@ No modules. | [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 | `any` | `[]` | 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 |
create = optional(string)
delete = optional(string)
})
list(object({| `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 | @@ -129,7 +129,7 @@ No modules. | [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 | `any` | `[]` | no | +| [ira\_trust\_anchor\_notification\_settings](#input\_ira\_trust\_anchor\_notification\_settings) | Notification settings for the trust anchor |
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
})))
}))
list(object({| `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 | @@ -140,9 +140,9 @@ No modules. | [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 | `any` | `[]` | 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 |
channel = optional(string)
enabled = optional(bool)
event = optional(string)
threshold = optional(number)
}))
list(object({| `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(any)` | `{}` | 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 | diff --git a/modules/hybrid-node-role/main.tf b/modules/hybrid-node-role/main.tf index 5a2473f29b..1d14f41b40 100644 --- a/modules/hybrid-node-role/main.tf +++ b/modules/hybrid-node-role/main.tf @@ -3,7 +3,7 @@ data "aws_partition" "current" { } locals { - partition = try(data.aws_partition.current[0].partition, "aws") + partition = try(data.aws_partition.current[0].partition, "") } ################################################################################ @@ -117,18 +117,18 @@ data "aws_iam_policy_document" "this" { } dynamic "statement" { - for_each = var.policy_statements + for_each = var.policy_statements != null ? var.policy_statements : [] content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) + 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 = try(statement.value.principals, []) + for_each = statement.value.principals != null ? statement.value.principals : [] content { type = principals.value.type @@ -137,7 +137,7 @@ data "aws_iam_policy_document" "this" { } dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] content { type = not_principals.value.type @@ -146,7 +146,7 @@ data "aws_iam_policy_document" "this" { } dynamic "condition" { - for_each = try(statement.value.conditions, []) + for_each = statement.value.condition != null ? statement.value.condition : [] content { test = condition.value.test @@ -215,7 +215,7 @@ resource "aws_rolesanywhere_trust_anchor" "this" { name = try(coalesce(var.ira_trust_anchor_name, var.name), null) dynamic "notification_settings" { - for_each = var.ira_trust_anchor_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) @@ -297,18 +297,18 @@ data "aws_iam_policy_document" "intermediate" { } dynamic "statement" { - for_each = var.intermediate_policy_statements + for_each = var.intermediate_policy_statements != null ? var.intermediate_policy_statements : [] content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) + 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 = try(statement.value.principals, []) + for_each = statement.value.principals != null ? statement.value.principals : [] content { type = principals.value.type @@ -317,7 +317,7 @@ data "aws_iam_policy_document" "intermediate" { } dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] content { type = not_principals.value.type @@ -326,7 +326,7 @@ data "aws_iam_policy_document" "intermediate" { } dynamic "condition" { - for_each = try(statement.value.conditions, []) + for_each = statement.value.condition != null ? statement.value.condition : [] content { test = condition.value.test diff --git a/modules/hybrid-node-role/variables.tf b/modules/hybrid-node-role/variables.tf index 6e8d80465e..34e1247667 100644 --- a/modules/hybrid-node-role/variables.tf +++ b/modules/hybrid-node-role/variables.tf @@ -46,7 +46,7 @@ variable "permissions_boundary_arn" { variable "tags" { description = "A map of additional tags to add the the IAM role" - type = map(any) + type = map(string) default = {} } @@ -92,8 +92,28 @@ variable "policy_description" { 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 = any - default = [] + 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" { @@ -160,8 +180,13 @@ variable "ira_trust_anchor_name" { variable "ira_trust_anchor_notification_settings" { description = "Notification settings for the trust anchor" - type = any - default = [] + type = list(object({ + channel = optional(string) + enabled = optional(bool) + event = optional(string) + threshold = optional(number) + })) + default = null } variable "ira_trust_anchor_acm_pca_arn" { @@ -228,8 +253,28 @@ variable "intermediate_policy_use_name_prefix" { 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 = any - default = [] + 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" { diff --git a/modules/hybrid-node-role/versions.tf b/modules/hybrid-node-role/versions.tf index 7884c4e1b2..db13b0a8d2 100644 --- a/modules/hybrid-node-role/versions.tf +++ b/modules/hybrid-node-role/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.95, < 6.0.0" + version = ">= 6.0" } } } diff --git a/modules/karpenter/README.md b/modules/karpenter/README.md index 43cc3b560b..85e472b225 100644 --- a/modules/karpenter/README.md +++ b/modules/karpenter/README.md @@ -85,14 +85,14 @@ module "karpenter" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.95, < 6.0.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules @@ -121,8 +121,6 @@ No modules. | [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_iam_policy_document.v033](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.v1](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 | @@ -139,27 +137,23 @@ No modules. | [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` | `false` | no | -| [enable\_irsa](#input\_enable\_irsa) | Determines whether to enable support for IAM role for service accounts | `bool` | `false` | no | -| [enable\_pod\_identity](#input\_enable\_pod\_identity) | Determines whether to enable support for EKS pod identity | `bool` | `true` | no | +| [create\_pod\_identity\_association](#input\_create\_pod\_identity\_association) | Determines whether to create pod identity association | `bool` | `true` | no | | [enable\_spot\_termination](#input\_enable\_spot\_termination) | Determines whether to enable native spot termination handling | `bool` | `true` | no | -| [enable\_v1\_permissions](#input\_enable\_v1\_permissions) | Determines whether to enable permissions suitable for v1+ (`true`) or for v0.33.x-v0.37.x (`false`) | `bool` | `false` | 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 | `any` | `[]` | 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 |
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
})))
}))
list(object({| `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\_tags](#input\_iam\_role\_tags) | A map of additional tags to add the the IAM role | `map(any)` | `{}` | 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 | -| [irsa\_assume\_role\_condition\_test](#input\_irsa\_assume\_role\_condition\_test) | Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role | `string` | `"StringEquals"` | no | -| [irsa\_namespace\_service\_accounts](#input\_irsa\_namespace\_service\_accounts) | List of `namespace:serviceaccount`pairs to use in trust policy for IAM role for service accounts | `list(string)` |
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
})))
}))
[| no | -| [irsa\_oidc\_provider\_arn](#input\_irsa\_oidc\_provider\_arn) | OIDC provider arn used in trust policy for IAM role for service accounts | `string` | `""` | 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_iam_role` is set to `false` | `string` | `null` | no | @@ -175,6 +169,7 @@ No modules. | [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 | +| [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 | diff --git a/modules/karpenter/main.tf b/modules/karpenter/main.tf index d03dfa49f8..7eba2cb5d8 100644 --- a/modules/karpenter/main.tf +++ b/modules/karpenter/main.tf @@ -1,12 +1,20 @@ -data "aws_region" "current" {} -data "aws_partition" "current" {} -data "aws_caller_identity" "current" {} +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 +} locals { - account_id = data.aws_caller_identity.current.account_id - dns_suffix = data.aws_partition.current.dns_suffix - partition = data.aws_partition.current.partition - region = data.aws_region.current.name + account_id = try(data.aws_caller_identity.current[0].account_id, "") + dns_suffix = try(data.aws_partition.current[0].dns_suffix, "") + partition = try(data.aws_partition.current[0].partition, "") + region = try(data.aws_region.current[0].region, "") } ################################################################################ @@ -14,54 +22,26 @@ locals { ################################################################################ locals { - create_iam_role = var.create && var.create_iam_role - irsa_oidc_provider_url = replace(var.irsa_oidc_provider_arn, "/^(.*provider/)/", "") + create_iam_role = var.create && var.create_iam_role } data "aws_iam_policy_document" "controller_assume_role" { count = local.create_iam_role ? 1 : 0 - # Pod Identity - dynamic "statement" { - for_each = var.enable_pod_identity ? [1] : [] + override_policy_documents = var.iam_role_override_assume_policy_documents + source_policy_documents = var.iam_role_source_assume_policy_documents - content { - actions = [ - "sts:AssumeRole", - "sts:TagSession", - ] - - principals { - type = "Service" - identifiers = ["pods.eks.amazonaws.com"] - } - } - } - - # IAM Roles for Service Accounts (IRSA) - dynamic "statement" { - for_each = var.enable_irsa ? [1] : [] - - content { - actions = ["sts:AssumeRoleWithWebIdentity"] - - principals { - type = "Federated" - identifiers = [var.irsa_oidc_provider_arn] - } - - condition { - test = var.irsa_assume_role_condition_test - variable = "${local.irsa_oidc_provider_url}:sub" - values = [for sa in var.irsa_namespace_service_accounts : "system:serviceaccount:${sa}"] - } + # Pod Identity + statement { + sid = "PodIdentity" + actions = [ + "sts:AssumeRole", + "sts:TagSession", + ] - # https://aws.amazon.com/premiumsupport/knowledge-center/eks-troubleshoot-oidc-and-irsa/?nc1=h_ls - condition { - test = var.irsa_assume_role_condition_test - variable = "${local.irsa_oidc_provider_url}:aud" - values = ["sts.amazonaws.com"] - } + principals { + type = "Service" + identifiers = ["pods.eks.amazonaws.com"] } } } @@ -82,12 +62,6 @@ resource "aws_iam_role" "controller" { tags = merge(var.tags, var.iam_role_tags) } -data "aws_iam_policy_document" "controller" { - count = local.create_iam_role ? 1 : 0 - - source_policy_documents = var.enable_v1_permissions ? [data.aws_iam_policy_document.v1[0].json] : [data.aws_iam_policy_document.v033[0].json] -} - resource "aws_iam_policy" "controller" { count = local.create_iam_role ? 1 : 0 @@ -119,7 +93,9 @@ resource "aws_iam_role_policy_attachment" "controller_additional" { ################################################################################ resource "aws_eks_pod_identity_association" "karpenter" { - count = local.create_iam_role && var.enable_pod_identity && var.create_pod_identity_association ? 1 : 0 + count = local.create_iam_role && var.create_pod_identity_association ? 1 : 0 + + region = var.region cluster_name = var.cluster_name namespace = var.namespace @@ -142,6 +118,8 @@ locals { 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 @@ -175,7 +153,7 @@ data "aws_iam_policy_document" "queue" { ] resources = [aws_sqs_queue.this[0].arn] condition { - test = "StringEquals" + test = "Bool" variable = "aws:SecureTransport" values = [ "false" @@ -193,6 +171,8 @@ data "aws_iam_policy_document" "queue" { 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 } @@ -241,6 +221,8 @@ locals { 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) @@ -254,6 +236,8 @@ resource "aws_cloudwatch_event_rule" "this" { 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 @@ -274,7 +258,7 @@ locals { 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:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:policy/AmazonEKS_CNI_IPv6_Policy" + 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" } } @@ -337,6 +321,8 @@ resource "aws_iam_role_policy_attachment" "node_additional" { 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 diff --git a/modules/karpenter/policy.tf b/modules/karpenter/policy.tf index b9068d7b60..16f28fa61d 100644 --- a/modules/karpenter/policy.tf +++ b/modules/karpenter/policy.tf @@ -1,357 +1,4 @@ -################################################################################ -# v0.33.x - v0.37.x Controller IAM Policy -################################################################################ - -data "aws_iam_policy_document" "v033" { - count = local.create_iam_role ? 1 : 0 - - statement { - sid = "AllowScopedEC2InstanceActions" - resources = [ - "arn:${local.partition}:ec2:*::image/*", - "arn:${local.partition}:ec2:*::snapshot/*", - "arn:${local.partition}:ec2:*:*:spot-instances-request/*", - "arn:${local.partition}:ec2:*:*:security-group/*", - "arn:${local.partition}:ec2:*:*:subnet/*", - "arn:${local.partition}:ec2:*:*:launch-template/*", - ] - - actions = [ - "ec2:RunInstances", - "ec2:CreateFleet" - ] - } - - statement { - sid = "AllowScopedEC2InstanceActionsWithTags" - resources = [ - "arn:${local.partition}:ec2:*:*:fleet/*", - "arn:${local.partition}:ec2:*:*:instance/*", - "arn:${local.partition}:ec2:*:*:volume/*", - "arn:${local.partition}:ec2:*:*:network-interface/*", - "arn:${local.partition}:ec2:*:*:launch-template/*", - "arn:${local.partition}:ec2:*:*:spot-instances-request/*", - ] - actions = [ - "ec2:RunInstances", - "ec2:CreateFleet", - "ec2:CreateLaunchTemplate" - ] - - condition { - test = "StringEquals" - variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - condition { - test = "StringLike" - variable = "aws:RequestTag/karpenter.sh/nodepool" - values = ["*"] - } - } - - statement { - sid = "AllowScopedResourceCreationTagging" - resources = [ - "arn:${local.partition}:ec2:*:*:fleet/*", - "arn:${local.partition}:ec2:*:*:instance/*", - "arn:${local.partition}:ec2:*:*:volume/*", - "arn:${local.partition}:ec2:*:*:network-interface/*", - "arn:${local.partition}:ec2:*:*:launch-template/*", - "arn:${local.partition}:ec2:*:*:spot-instances-request/*", - ] - actions = ["ec2:CreateTags"] - - condition { - test = "StringEquals" - variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - 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:*:*: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 = "ForAllValues:StringEquals" - variable = "aws:TagKeys" - values = [ - "karpenter.sh/nodeclaim", - "Name", - ] - } - } - - statement { - sid = "AllowScopedDeletion" - resources = [ - "arn:${local.partition}:ec2:*:*:instance/*", - "arn:${local.partition}:ec2:*:*: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: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:GetQueueAttributes", - "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 = ["ec2.${local.dns_suffix}"] - } - } - - statement { - sid = "AllowScopedInstanceProfileCreationActions" - resources = ["*"] - actions = ["iam:CreateInstanceProfile"] - - condition { - test = "StringEquals" - variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - 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 = ["*"] - 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:ResourceTag/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 = ["*"] - 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 = ["*"] - actions = ["iam:GetInstanceProfile"] - } - - 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 - - content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) - - dynamic "principals" { - for_each = try(statement.value.principals, []) - - content { - type = principals.value.type - identifiers = principals.value.identifiers - } - } - - dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) - - content { - type = not_principals.value.type - identifiers = not_principals.value.identifiers - } - } - - dynamic "condition" { - for_each = try(statement.value.conditions, []) - - content { - test = condition.value.test - values = condition.value.values - variable = condition.value.variable - } - } - } - } -} - -################################################################################ -# v1.0.x Controller IAM Policy -################################################################################ - -data "aws_iam_policy_document" "v1" { +data "aws_iam_policy_document" "controller" { count = local.create_iam_role ? 1 : 0 statement { @@ -708,18 +355,18 @@ data "aws_iam_policy_document" "v1" { } dynamic "statement" { - for_each = var.iam_policy_statements + for_each = var.iam_policy_statements != null ? var.iam_policy_statements : [] content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) + 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 = try(statement.value.principals, []) + for_each = statement.value.principals != null ? statement.value.principals : [] content { type = principals.value.type @@ -728,7 +375,7 @@ data "aws_iam_policy_document" "v1" { } dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] content { type = not_principals.value.type @@ -737,7 +384,7 @@ data "aws_iam_policy_document" "v1" { } dynamic "condition" { - for_each = try(statement.value.conditions, []) + for_each = statement.value.condition != null ? statement.value.condition : [] content { test = condition.value.test diff --git a/modules/karpenter/variables.tf b/modules/karpenter/variables.tf index 71b2cbdf38..f0725f6a58 100644 --- a/modules/karpenter/variables.tf +++ b/modules/karpenter/variables.tf @@ -16,6 +16,12 @@ variable "cluster_name" { 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 ################################################################################ @@ -64,7 +70,7 @@ variable "iam_role_permissions_boundary_arn" { variable "iam_role_tags" { description = "A map of additional tags to add the the IAM role" - type = map(any) + type = map(string) default = {} } @@ -92,10 +98,42 @@ variable "iam_policy_description" { 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 = any - default = [] + 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 "iam_role_policies" { @@ -110,55 +148,14 @@ variable "ami_id_ssm_parameter_arns" { default = [] } -variable "enable_pod_identity" { - description = "Determines whether to enable support for EKS pod identity" - type = bool - default = true -} - -# TODO - make v1 permssions the default policy at next breaking change -variable "enable_v1_permissions" { - description = "Determines whether to enable permissions suitable for v1+ (`true`) or for v0.33.x-v0.37.x (`false`)" - type = bool - default = false -} - -################################################################################ -# IAM Role for Service Account (IRSA) -################################################################################ - -variable "enable_irsa" { - description = "Determines whether to enable support for IAM role for service accounts" - type = bool - default = false -} - -variable "irsa_oidc_provider_arn" { - description = "OIDC provider arn used in trust policy for IAM role for service accounts" - type = string - default = "" -} - -variable "irsa_namespace_service_accounts" { - description = "List of `namespace:serviceaccount`pairs to use in trust policy for IAM role for service accounts" - type = list(string) - default = ["karpenter:karpenter"] -} - -variable "irsa_assume_role_condition_test" { - description = "Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role" - type = string - default = "StringEquals" -} - ################################################################################ # Pod Identity Association ################################################################################ -# TODO - Change default to `true` at next breaking change + variable "create_pod_identity_association" { description = "Determines whether to create pod identity association" type = bool - default = false + default = true } variable "namespace" { diff --git a/modules/karpenter/versions.tf b/modules/karpenter/versions.tf index 7884c4e1b2..db13b0a8d2 100644 --- a/modules/karpenter/versions.tf +++ b/modules/karpenter/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.95, < 6.0.0" + version = ">= 6.0" } } } diff --git a/modules/self-managed-node-group/README.md b/modules/self-managed-node-group/README.md index 06747514f2..beaa7a9d7d 100644 --- a/modules/self-managed-node-group/README.md +++ b/modules/self-managed-node-group/README.md @@ -10,7 +10,7 @@ module "self_managed_node_group" { name = "separate-self-mng" cluster_name = "my-cluster" - cluster_version = "1.31" + kubernetes_version = "1.31" cluster_endpoint = "https://012345678903AB2BAE5D1E0BFE0E2B50.gr7.us-east-1.eks.amazonaws.com" cluster_auth_base64 = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKbXFqQ1VqNGdGR2w3ZW5PeWthWnZ2RjROOTVOUEZCM2o0cGhVZUsrWGFtN2ZSQnZya0d6OGxKZmZEZWF2b2plTwpQK2xOZFlqdHZncmxCUEpYdHZIZmFzTzYxVzdIZmdWQ2EvamdRM2w3RmkvL1dpQmxFOG9oWUZkdWpjc0s1SXM2CnNkbk5KTTNYUWN2TysrSitkV09NT2ZlNzlsSWdncmdQLzgvRU9CYkw3eUY1aU1hS3lsb1RHL1V3TlhPUWt3ZUcKblBNcjdiUmdkQ1NCZTlXYXowOGdGRmlxV2FOditsTDhsODBTdFZLcWVNVlUxbjQyejVwOVpQRTd4T2l6L0xTNQpYV2lXWkVkT3pMN0xBWGVCS2gzdkhnczFxMkI2d1BKZnZnS1NzWllQRGFpZTloT1NNOUJkNFNPY3JrZTRYSVBOCkVvcXVhMlYrUDRlTWJEQzhMUkVWRDdCdVZDdWdMTldWOTBoL3VJUy9WU2VOcEdUOGVScE5DakszSjc2aFlsWm8KWjNGRG5QWUY0MWpWTHhiOXF0U1ROdEp6amYwWXBEYnFWci9xZzNmQWlxbVorMzd3YWM1eHlqMDZ4cmlaRUgzZgpUM002d2lCUEVHYVlGeWN5TmNYTk5aYW9DWDJVL0N1d2JsUHAKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==" @@ -42,14 +42,14 @@ module "self_managed_node_group" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.95, < 6.0.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules @@ -62,7 +62,6 @@ module "self_managed_node_group" { | Name | Type | |------|------| | [aws_autoscaling_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group) | resource | -| [aws_autoscaling_schedule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_schedule) | 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 | @@ -71,28 +70,31 @@ module "self_managed_node_group" { | [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_ec2_instance_type_offerings.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_instance_type_offerings) | 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_subnets.placement_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnets) | 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)` | `[]` | 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` | `"AL2_x86_64"` | 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 | `any` | `{}` | no | +| [block\_device\_mappings](#input\_block\_device\_mappings) | Specify volumes to attach to the instance besides the volumes specified by the AMI |
"karpenter:karpenter"
]
map(object({| `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` | `""` | 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 | `any` | `{}` | no | +| [capacity\_reservation\_specification](#input\_capacity\_reservation\_specification) | Targeting for EC2 capacity reservations |
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)
}))
object({| `null` | no | | [cloudinit\_post\_nodeadm](#input\_cloudinit\_post\_nodeadm) | Array of cloud-init document parts that are created after the nodeadm document part |
capacity_reservation_preference = optional(string)
capacity_reservation_target = optional(object({
capacity_reservation_id = optional(string)
capacity_reservation_resource_group_arn = optional(string)
}))
})
list(object({| `[]` | no | | [cloudinit\_pre\_nodeadm](#input\_cloudinit\_pre\_nodeadm) | Array of cloud-init document parts that are created before the nodeadm document part |
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
}))
list(object({| `[]` | no | | [cluster\_auth\_base64](#input\_cluster\_auth\_base64) | Base64 encoded CA of associated EKS cluster | `string` | `""` | no | @@ -101,9 +103,8 @@ module "self_managed_node_group" { | [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` | `""` | no | -| [cluster\_version](#input\_cluster\_version) | Kubernetes cluster version - used to lookup default AMI ID if one is not provided | `string` | `null` | no | | [context](#input\_context) | Reserved | `string` | `null` | no | -| [cpu\_options](#input\_cpu\_options) | The CPU options for the instance | `map(string)` | `{}` | no | +| [cpu\_options](#input\_cpu\_options) | The CPU options for the instance |
content = string
content_type = optional(string)
filename = optional(string)
merge_type = optional(string)
}))
object({| `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 | @@ -111,28 +112,22 @@ module "self_managed_node_group" { | [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\_schedule](#input\_create\_schedule) | Determines whether to create autoscaling group schedule or not | `bool` | `true` | no | -| [credit\_specification](#input\_credit\_specification) | Customize the credit specification of the instance | `map(string)` | `{}` | no | -| [default\_cooldown](#input\_default\_cooldown) | The amount of time, in seconds, after a scaling activity completes before another scaling activity can start | `number` | `null` | 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 |
amd_sev_snp = optional(string)
core_count = optional(number)
threads_per_core = optional(number)
})
object({| `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 | -| [delete\_timeout](#input\_delete\_timeout) | Delete timeout to wait for destroying autoscaling group | `string` | `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)` |
cpu_credits = optional(string)
})
[| no | -| [elastic\_gpu\_specifications](#input\_elastic\_gpu\_specifications) | The elastic GPU to attach to the instance | `any` | `{}` | no | -| [elastic\_inference\_accelerator](#input\_elastic\_inference\_accelerator) | Configuration block containing an Elastic Inference Accelerator to attach to the instance | `map(string)` | `{}` | 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` | `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` | `true` | 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 | `map(string)` | `{}` | no | +| [enclave\_options](#input\_enclave\_options) | Enable Nitro Enclaves on launched instances |
0
]
object({| `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 | -| [force\_delete\_warm\_pool](#input\_force\_delete\_warm\_pool) | Allows deleting the Auto Scaling Group without waiting for all instances in the warm pool to terminate | `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 | -| [hibernation\_options](#input\_hibernation\_options) | The hibernation options for the instance | `map(string)` | `{}` | 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 | @@ -141,19 +136,20 @@ module "self_managed_node_group" { | [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 | `any` | `[]` | 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 |
enabled = optional(bool)
})
list(object({| `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(map(string))` | `[]` | 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 |
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
})))
}))
list(object({| `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 | `any` | `{}` | no | -| [instance\_market\_options](#input\_instance\_market\_options) | The market (purchasing) option for the instance | `any` | `{}` | no | -| [instance\_refresh](#input\_instance\_refresh) | If this block is configured, start an Instance Refresh when this Auto Scaling Group is updated | `any` |
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)
}))
{| no | -| [instance\_requirements](#input\_instance\_requirements) | The attribute requirements for the type of instance. If present then `instance_type` cannot be present | `any` | `{}` | no | -| [instance\_type](#input\_instance\_type) | The type of the instance to launch | `string` | `""` | no | +| [instance\_maintenance\_policy](#input\_instance\_maintenance\_policy) | If this block is configured, add a instance maintenance policy to the specified Auto Scaling group |
"preferences": {
"min_healthy_percentage": 66
},
"strategy": "Rolling"
}
object({| `null` | no | +| [instance\_market\_options](#input\_instance\_market\_options) | The market (purchasing) option for the instance |
max_healthy_percentage = number
min_healthy_percentage = number
})
object({| `null` | no | +| [instance\_refresh](#input\_instance\_refresh) | If this block is configured, start an Instance Refresh when this Auto Scaling Group is updated |
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)
}))
})
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, 33)
scale_in_protected_instances = optional(string)
skip_matching = optional(bool)
standby_instances = optional(string)
}))
strategy = optional(string, "Rolling")
triggers = optional(list(string))
})
{| no | +| [instance\_requirements](#input\_instance\_requirements) | The attribute requirements for the type of instance. If present then `instance_type` cannot be present |
"preferences": {
"min_healthy_percentage": 66
},
"strategy": "Rolling"
}
object({| `null` | no | +| [instance\_type](#input\_instance\_type) | The type of the instance to launch | `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 | `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 | @@ -161,42 +157,42 @@ module "self_managed_node_group" { | [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 map of license specifications to associate with | `any` | `{}` | no | -| [maintenance\_options](#input\_maintenance\_options) | The maintenance options for the instance | `any` | `{}` | no | +| [license\_specifications](#input\_license\_specifications) | A list of license specifications to associate with |
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
}))
})
list(object({| `null` | no | +| [maintenance\_options](#input\_maintenance\_options) | The maintenance options for the instance |
license_configuration_arn = string
}))
object({| `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 | `map(string)` |
auto_recovery = optional(string)
})
{| no | +| [metadata\_options](#input\_metadata\_options) | Customize the metadata options for the instance |
"http_endpoint": "enabled",
"http_put_response_hop_limit": 2,
"http_tokens": "required"
}
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)
})
{| 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\_elb\_capacity](#input\_min\_elb\_capacity) | Setting this causes Terraform to wait for this number of instances to show up healthy in the ELB only on creation. Updates will not wait on ELB instance number changes | `number` | `null` | no | -| [min\_size](#input\_min\_size) | The minimum size of the autoscaling group | `number` | `0` | no | -| [mixed\_instances\_policy](#input\_mixed\_instances\_policy) | Configuration block containing settings to define launch targets for Auto Scaling groups | `any` | `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 |
"http_endpoint": "enabled",
"http_put_response_hop_limit": 1,
"http_tokens": "required"
}
object({| `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(any)` | `[]` | no | -| [placement](#input\_placement) | The placement of the instance | `map(string)` | `{}` | no | -| [placement\_group](#input\_placement\_group) | The name of the placement group into which you'll launch your instances, if any | `string` | `null` | no | -| [placement\_group\_az](#input\_placement\_group\_az) | Availability zone where placement group is created (ex. `eu-west-1c`) | `string` | `null` | no | -| [platform](#input\_platform) | [DEPRECATED - must use `ami_type` instead. Will be removed in `v21.0`] | `string` | `null` | no | +| [network\_interfaces](#input\_network\_interfaces) | Customize network interfaces to be attached at instance boot time |
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)
})))
})
})
list(object({| `[]` | 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 |
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)
}))
object({| `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` | `""` | 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 | -| [private\_dns\_name\_options](#input\_private\_dns\_name\_options) | The options for the instance hostname. The default values are inherited from the subnet | `map(string)` | `{}` | 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 | +| [private\_dns\_name\_options](#input\_private\_dns\_name\_options) | The options for the instance hostname. The default values are inherited from the subnet |
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)
})
object({| `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 | -| [schedules](#input\_schedules) | Map of autoscaling group schedule to create | `map(any)` | `{}` | no | -| [service\_linked\_role\_arn](#input\_service\_linked\_role\_arn) | The ARN of the service-linked role that the ASG will use to call other AWS services | `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 |
enable_resource_name_dns_aaaa_record = optional(bool)
enable_resource_name_dns_a_record = optional(bool)
hostname_type = optional(string)
})
map(object({| `{}` | no | +| [security\_group\_ingress\_rules](#input\_security\_group\_ingress\_rules) | Security group ingress rules to add to the security group created |
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)
}))
map(object({| `{}` | 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)` |
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 | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | -| [target\_group\_arns](#input\_target\_group\_arns) | A set of `aws_alb_target_group` ARNs, for use with Application or Network Load Balancing | `list(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({| `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` | `""` | no | | [vpc\_security\_group\_ids](#input\_vpc\_security\_group\_ids) | A list of security group IDs to associate | `list(string)` | `[]` | no | -| [wait\_for\_capacity\_timeout](#input\_wait\_for\_capacity\_timeout) | A maximum duration that Terraform should wait for ASG instances to be healthy before timing out. (See also Waiting for Capacity below.) Setting this to '0' causes Terraform to skip all Capacity Waiting behavior. | `string` | `null` | no | -| [wait\_for\_elb\_capacity](#input\_wait\_for\_elb\_capacity) | Setting this will cause Terraform to wait for exactly this number of healthy instances in all attached load balancers on both create and update operations. Takes precedence over `min_elb_capacity` behavior. | `number` | `null` | no | -| [warm\_pool](#input\_warm\_pool) | If this block is configured, add a Warm Pool to the specified Auto Scaling group | `any` | `{}` | no | ## Outputs @@ -213,7 +209,6 @@ module "self_managed_node_group" { | [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\_schedule\_arns](#output\_autoscaling\_group\_schedule\_arns) | ARNs of autoscaling group schedules | | [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 | @@ -226,6 +221,7 @@ module "self_managed_node_group" { | [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 | -| [platform](#output\_platform) | [DEPRECATED - Will be removed in `v21.0`] Identifies the OS platform as `bottlerocket`, `linux` (AL2), `al2023`, or `windows` | +| [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 | diff --git a/modules/self-managed-node-group/main.tf b/modules/self-managed-node-group/main.tf index 24454ef74d..710d276602 100644 --- a/modules/self-managed-node-group/main.tf +++ b/modules/self-managed-node-group/main.tf @@ -1,5 +1,14 @@ -data "aws_partition" "current" {} -data "aws_caller_identity" "current" {} +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 @@ -7,58 +16,36 @@ data "aws_caller_identity" "current" {} locals { # Just to ensure templating doesn't fail when values are not provided - ssm_cluster_version = var.cluster_version != null ? var.cluster_version : "" - - # TODO - Temporary stopgap for backwards compatibility until v21.0 - ami_type_to_user_data_type = { - AL2_x86_64 = "linux" - AL2_x86_64_GPU = "linux" - AL2_ARM_64 = "linux" - BOTTLEROCKET_ARM_64 = "bottlerocket" - BOTTLEROCKET_x86_64 = "bottlerocket" - BOTTLEROCKET_ARM_64_FIPS = "bottlerocket" - BOTTLEROCKET_x86_64_FIPS = "bottlerocket" - BOTTLEROCKET_ARM_64_NVIDIA = "bottlerocket" - BOTTLEROCKET_x86_64_NVIDIA = "bottlerocket" - WINDOWS_CORE_2019_x86_64 = "windows" - WINDOWS_FULL_2019_x86_64 = "windows" - WINDOWS_CORE_2022_x86_64 = "windows" - WINDOWS_FULL_2022_x86_64 = "windows" - AL2023_x86_64_STANDARD = "al2023" - AL2023_ARM_64_STANDARD = "al2023" - AL2023_x86_64_NEURON = "al2023" - AL2023_x86_64_NVIDIA = "al2023" - AL2023_ARM_64_NVIDIA = "al2023" - } - - user_data_type = local.ami_type_to_user_data_type[var.ami_type] + 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_cluster_version}/amazon-linux-2/recommended/image_id" - AL2_x86_64_GPU = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2-gpu/recommended/image_id" - AL2_ARM_64 = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2-arm64/recommended/image_id" - BOTTLEROCKET_ARM_64 = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_version}/arm64/latest/image_id" - BOTTLEROCKET_x86_64 = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_version}/x86_64/latest/image_id" - BOTTLEROCKET_ARM_64_FIPS = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_version}-fips/arm64/latest/image_id" - BOTTLEROCKET_x86_64_FIPS = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_version}-fips/x86_64/latest/image_id" - BOTTLEROCKET_ARM_64_NVIDIA = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_version}-nvidia/arm64/latest/image_id" - BOTTLEROCKET_x86_64_NVIDIA = "/aws/service/bottlerocket/aws-k8s-${local.ssm_cluster_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_cluster_version}/image_id" - WINDOWS_FULL_2019_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2019-English-Core-EKS_Optimized-${local.ssm_cluster_version}/image_id" - WINDOWS_CORE_2022_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2022-English-Full-EKS_Optimized-${local.ssm_cluster_version}/image_id" - WINDOWS_FULL_2022_x86_64 = "/aws/service/ami-windows-latest/Windows_Server-2022-English-Core-EKS_Optimized-${local.ssm_cluster_version}/image_id" - AL2023_x86_64_STANDARD = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2023/x86_64/standard/recommended/image_id" - AL2023_ARM_64_STANDARD = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2023/arm64/standard/recommended/image_id" - AL2023_x86_64_NEURON = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2023/x86_64/neuron/recommended/image_id" - AL2023_x86_64_NVIDIA = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2023/x86_64/nvidia/recommended/image_id" - AL2023_ARM_64_NVIDIA = "/aws/service/eks/optimized-ami/${local.ssm_cluster_version}/amazon-linux-2023/arm64/nvidia/recommended/image_id" + 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] } @@ -70,7 +57,6 @@ module "user_data" { source = "../_user_data" create = var.create - platform = local.user_data_type ami_type = var.ami_type is_eks_managed_node_group = false @@ -98,6 +84,8 @@ module "user_data" { data "aws_ec2_instance_type" "this" { count = var.create && var.enable_efa_support ? 1 : 0 + region = var.region + instance_type = var.instance_type } @@ -128,70 +116,71 @@ locals { 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)) - - placement = local.enable_efa_support ? { group_name = aws_placement_group.this[0].name } : var.placement } 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 + for_each = var.block_device_mappings != null ? var.block_device_mappings : {} content { - device_name = try(block_device_mappings.value.device_name, null) + device_name = block_device_mappings.value.device_name dynamic "ebs" { - for_each = try([block_device_mappings.value.ebs], []) + for_each = block_device_mappings.value.ebs != null ? [block_device_mappings.value.ebs] : [] content { - delete_on_termination = try(ebs.value.delete_on_termination, null) - encrypted = try(ebs.value.encrypted, null) - iops = try(ebs.value.iops, null) - kms_key_id = try(ebs.value.kms_key_id, null) - snapshot_id = try(ebs.value.snapshot_id, null) - throughput = try(ebs.value.throughput, null) - volume_size = try(ebs.value.volume_size, null) - volume_type = try(ebs.value.volume_type, null) + 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 = try(block_device_mappings.value.no_device, null) - virtual_name = try(block_device_mappings.value.virtual_name, null) + no_device = block_device_mappings.value.no_device + virtual_name = block_device_mappings.value.virtual_name } } dynamic "capacity_reservation_specification" { - for_each = length(var.capacity_reservation_specification) > 0 ? [var.capacity_reservation_specification] : [] + for_each = var.capacity_reservation_specification != null ? [var.capacity_reservation_specification] : [] content { - capacity_reservation_preference = try(capacity_reservation_specification.value.capacity_reservation_preference, null) + capacity_reservation_preference = capacity_reservation_specification.value.capacity_reservation_preference dynamic "capacity_reservation_target" { - for_each = try([capacity_reservation_specification.value.capacity_reservation_target], []) - + for_each = capacity_reservation_specification.value.capacity_reservation_target != null ? [capacity_reservation_specification.value.capacity_reservation_target] : [] content { - capacity_reservation_id = try(capacity_reservation_target.value.capacity_reservation_id, null) - capacity_reservation_resource_group_arn = try(capacity_reservation_target.value.capacity_reservation_resource_group_arn, null) + 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 = length(var.cpu_options) > 0 ? [var.cpu_options] : [] + for_each = var.cpu_options != null ? [var.cpu_options] : [] content { - core_count = try(cpu_options.value.core_count, null) - threads_per_core = try(cpu_options.value.threads_per_core, null) + 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 = length(var.credit_specification) > 0 ? [var.credit_specification] : [] + for_each = var.credit_specification != null ? [var.credit_specification] : [] content { - cpu_credits = try(credit_specification.value.cpu_credits, null) + cpu_credits = credit_specification.value.cpu_credits } } @@ -200,38 +189,14 @@ resource "aws_launch_template" "this" { disable_api_termination = var.disable_api_termination ebs_optimized = var.ebs_optimized - dynamic "elastic_gpu_specifications" { - for_each = var.elastic_gpu_specifications - - content { - type = elastic_gpu_specifications.value.type - } - } - - dynamic "elastic_inference_accelerator" { - for_each = length(var.elastic_inference_accelerator) > 0 ? [var.elastic_inference_accelerator] : [] - - content { - type = elastic_inference_accelerator.value.type - } - } - dynamic "enclave_options" { - for_each = length(var.enclave_options) > 0 ? [var.enclave_options] : [] + for_each = var.enclave_options != null ? [var.enclave_options] : [] content { enabled = enclave_options.value.enabled } } - dynamic "hibernation_options" { - for_each = length(var.hibernation_options) > 0 ? [var.hibernation_options] : [] - - content { - configured = hibernation_options.value.configured - } - } - iam_instance_profile { arn = var.create_iam_instance_profile ? aws_iam_instance_profile.this[0].arn : var.iam_instance_profile_arn } @@ -240,125 +205,116 @@ resource "aws_launch_template" "this" { instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior dynamic "instance_market_options" { - for_each = length(var.instance_market_options) > 0 ? [var.instance_market_options] : [] + for_each = var.instance_market_options != null ? [var.instance_market_options] : [] content { - market_type = try(instance_market_options.value.market_type, null) + market_type = instance_market_options.value.market_type dynamic "spot_options" { - for_each = try([instance_market_options.value.spot_options], []) + for_each = instance_market_options.value.spot_options != null ? [instance_market_options.value.spot_options] : [] content { - block_duration_minutes = try(spot_options.value.block_duration_minutes, null) - instance_interruption_behavior = try(spot_options.value.instance_interruption_behavior, null) - max_price = try(spot_options.value.max_price, null) - spot_instance_type = try(spot_options.value.spot_instance_type, null) - valid_until = try(spot_options.value.valid_until, null) + 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 = length(var.instance_requirements) > 0 ? [var.instance_requirements] : [] + for_each = var.instance_requirements != null ? [var.instance_requirements] : [] content { - dynamic "accelerator_count" { - for_each = try([instance_requirements.value.accelerator_count], []) + for_each = instance_requirements.value.accelerator_count != null ? [instance_requirements.value.accelerator_count] : [] content { - max = try(accelerator_count.value.max, null) - min = try(accelerator_count.value.min, null) + max = accelerator_count.value.max + min = accelerator_count.value.min } } - accelerator_manufacturers = try(instance_requirements.value.accelerator_manufacturers, []) - accelerator_names = try(instance_requirements.value.accelerator_names, []) + accelerator_manufacturers = instance_requirements.value.accelerator_manufacturers + accelerator_names = instance_requirements.value.accelerator_names dynamic "accelerator_total_memory_mib" { - for_each = try([instance_requirements.value.accelerator_total_memory_mib], []) + for_each = instance_requirements.value.accelerator_total_memory_mib != null ? [instance_requirements.value.accelerator_total_memory_mib] : [] content { - max = try(accelerator_total_memory_mib.value.max, null) - min = try(accelerator_total_memory_mib.value.min, null) + max = accelerator_total_memory_mib.value.max + min = accelerator_total_memory_mib.value.min } } - accelerator_types = try(instance_requirements.value.accelerator_types, []) - allowed_instance_types = try(instance_requirements.value.allowed_instance_types, null) - bare_metal = try(instance_requirements.value.bare_metal, null) + 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 = try([instance_requirements.value.baseline_ebs_bandwidth_mbps], []) + for_each = instance_requirements.value.baseline_ebs_bandwidth_mbps != null ? [instance_requirements.value.baseline_ebs_bandwidth_mbps] : [] content { - max = try(baseline_ebs_bandwidth_mbps.value.max, null) - min = try(baseline_ebs_bandwidth_mbps.value.min, null) + max = baseline_ebs_bandwidth_mbps.value.max + min = baseline_ebs_bandwidth_mbps.value.min } } - burstable_performance = try(instance_requirements.value.burstable_performance, null) - cpu_manufacturers = try(instance_requirements.value.cpu_manufacturers, []) - excluded_instance_types = try(instance_requirements.value.excluded_instance_types, null) - instance_generations = try(instance_requirements.value.instance_generations, []) - local_storage = try(instance_requirements.value.local_storage, null) - local_storage_types = try(instance_requirements.value.local_storage_types, []) + 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 = try([instance_requirements.value.memory_gib_per_vcpu], []) + for_each = instance_requirements.value.memory_gib_per_vcpu != null ? [instance_requirements.value.memory_gib_per_vcpu] : [] content { - max = try(memory_gib_per_vcpu.value.max, null) - min = try(memory_gib_per_vcpu.value.min, null) + max = memory_gib_per_vcpu.value.max + min = memory_gib_per_vcpu.value.min } } dynamic "memory_mib" { - for_each = [instance_requirements.value.memory_mib] + for_each = instance_requirements.value.memory_mib != null ? [instance_requirements.value.memory_mib] : [] content { - max = try(memory_mib.value.max, null) + max = memory_mib.value.max min = memory_mib.value.min } } - dynamic "network_bandwidth_gbps" { - for_each = try([instance_requirements.value.network_bandwidth_gbps], []) - - content { - max = try(network_bandwidth_gbps.value.max, null) - min = try(network_bandwidth_gbps.value.min, null) - } - } - dynamic "network_interface_count" { - for_each = try([instance_requirements.value.network_interface_count], []) + for_each = instance_requirements.value.network_interface_count != null ? [instance_requirements.value.network_interface_count] : [] content { - max = try(network_interface_count.value.max, null) - min = try(network_interface_count.value.min, null) + max = network_interface_count.value.max + min = network_interface_count.value.min } } - on_demand_max_price_percentage_over_lowest_price = try(instance_requirements.value.on_demand_max_price_percentage_over_lowest_price, null) - require_hibernate_support = try(instance_requirements.value.require_hibernate_support, null) - spot_max_price_percentage_over_lowest_price = try(instance_requirements.value.spot_max_price_percentage_over_lowest_price, null) + 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 = try([instance_requirements.value.total_local_storage_gb], []) + for_each = instance_requirements.value.total_local_storage_gb != null ? [instance_requirements.value.total_local_storage_gb] : [] content { - max = try(total_local_storage_gb.value.max, null) - min = try(total_local_storage_gb.value.min, null) + max = total_local_storage_gb.value.max + min = total_local_storage_gb.value.min } } dynamic "vcpu_count" { - for_each = [instance_requirements.value.vcpu_count] + for_each = instance_requirements.value.vcpu_count != null ? [instance_requirements.value.vcpu_count] : [] content { - max = try(vcpu_count.value.max, null) + max = vcpu_count.value.max min = vcpu_count.value.min } } @@ -370,7 +326,7 @@ resource "aws_launch_template" "this" { key_name = var.key_name dynamic "license_specification" { - for_each = length(var.license_specifications) > 0 ? var.license_specifications : {} + for_each = var.license_specifications != null ? var.license_specifications : [] content { license_configuration_arn = license_specification.value.license_configuration_arn @@ -378,22 +334,22 @@ resource "aws_launch_template" "this" { } dynamic "maintenance_options" { - for_each = length(var.maintenance_options) > 0 ? [var.maintenance_options] : [] + for_each = var.maintenance_options != null ? [var.maintenance_options] : [] content { - auto_recovery = try(maintenance_options.value.auto_recovery, null) + auto_recovery = maintenance_options.value.auto_recovery } } dynamic "metadata_options" { - for_each = length(var.metadata_options) > 0 ? [var.metadata_options] : [] + for_each = var.metadata_options != null ? [var.metadata_options] : [] content { - http_endpoint = try(metadata_options.value.http_endpoint, null) - http_protocol_ipv6 = try(metadata_options.value.http_protocol_ipv6, null) - http_put_response_hop_limit = try(metadata_options.value.http_put_response_hop_limit, null) - http_tokens = try(metadata_options.value.http_tokens, null) - instance_metadata_tags = try(metadata_options.value.instance_metadata_tags, null) + 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 } } @@ -409,42 +365,72 @@ resource "aws_launch_template" "this" { name_prefix = var.launch_template_use_name_prefix ? "${local.launch_template_name}-" : null dynamic "network_interfaces" { - for_each = local.network_interfaces + for_each = length(var.network_interfaces) > 0 ? var.network_interfaces : [] content { - associate_carrier_ip_address = try(network_interfaces.value.associate_carrier_ip_address, null) - associate_public_ip_address = try(network_interfaces.value.associate_public_ip_address, null) - delete_on_termination = try(network_interfaces.value.delete_on_termination, null) - description = try(network_interfaces.value.description, null) - device_index = try(network_interfaces.value.device_index, null) - interface_type = try(network_interfaces.value.interface_type, null) - ipv4_address_count = try(network_interfaces.value.ipv4_address_count, null) - ipv4_addresses = try(network_interfaces.value.ipv4_addresses, []) - ipv4_prefix_count = try(network_interfaces.value.ipv4_prefix_count, null) - ipv4_prefixes = try(network_interfaces.value.ipv4_prefixes, null) - ipv6_address_count = try(network_interfaces.value.ipv6_address_count, null) - ipv6_addresses = try(network_interfaces.value.ipv6_addresses, []) - ipv6_prefix_count = try(network_interfaces.value.ipv6_prefix_count, null) - ipv6_prefixes = try(network_interfaces.value.ipv6_prefixes, []) - network_card_index = try(network_interfaces.value.network_card_index, null) - network_interface_id = try(network_interfaces.value.network_interface_id, null) - primary_ipv6 = try(network_interfaces.value.primary_ipv6, null) - private_ip_address = try(network_interfaces.value.private_ip_address, null) + 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(try(network_interfaces.value.security_groups, []), local.security_group_ids)) - subnet_id = try(network_interfaces.value.subnet_id, null) + security_groups = compact(concat(network_interfaces.value.security_groups, var.vpc_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 = length(local.placement) > 0 ? [local.placement] : [] + for_each = var.placement != null || local.create_placement_group ? [var.placement] : [] content { affinity = try(placement.value.affinity, null) - availability_zone = lookup(placement.value, "availability_zone", null) - group_name = lookup(placement.value, "group_name", null) - host_id = lookup(placement.value, "host_id", null) - host_resource_group_arn = lookup(placement.value, "host_resource_group_arn", 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) @@ -452,12 +438,12 @@ resource "aws_launch_template" "this" { } dynamic "private_dns_name_options" { - for_each = length(var.private_dns_name_options) > 0 ? [var.private_dns_name_options] : [] + for_each = var.private_dns_name_options != null ? [var.private_dns_name_options] : [] content { - enable_resource_name_dns_aaaa_record = try(private_dns_name_options.value.enable_resource_name_dns_aaaa_record, null) - enable_resource_name_dns_a_record = try(private_dns_name_options.value.enable_resource_name_dns_a_record, null) - hostname_type = try(private_dns_name_options.value.hostname_type, null) + 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 } } @@ -503,35 +489,35 @@ locals { 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_cooldown = var.default_cooldown 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 - force_delete_warm_pool = var.force_delete_warm_pool 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 + for_each = var.initial_lifecycle_hooks != null ? var.initial_lifecycle_hooks : [] content { - default_result = try(initial_lifecycle_hook.value.default_result, null) - heartbeat_timeout = try(initial_lifecycle_hook.value.heartbeat_timeout, null) + 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 = try(initial_lifecycle_hook.value.notification_metadata, null) - notification_target_arn = try(initial_lifecycle_hook.value.notification_target_arn, null) - role_arn = try(initial_lifecycle_hook.value.role_arn, null) + 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 = length(var.instance_maintenance_policy) > 0 ? [var.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 @@ -540,26 +526,35 @@ resource "aws_autoscaling_group" "this" { } dynamic "instance_refresh" { - for_each = length(var.instance_refresh) > 0 ? [var.instance_refresh] : [] + for_each = var.instance_refresh != null ? [var.instance_refresh] : [] content { dynamic "preferences" { - for_each = try([instance_refresh.value.preferences], []) + for_each = instance_refresh.value.preferences != null ? [instance_refresh.value.preferences] : [] content { - checkpoint_delay = try(preferences.value.checkpoint_delay, null) - checkpoint_percentages = try(preferences.value.checkpoint_percentages, null) - instance_warmup = try(preferences.value.instance_warmup, null) - max_healthy_percentage = try(preferences.value.max_healthy_percentage, null) - min_healthy_percentage = try(preferences.value.min_healthy_percentage, null) - scale_in_protected_instances = try(preferences.value.scale_in_protected_instances, null) - skip_matching = try(preferences.value.skip_matching, null) - standby_instances = try(preferences.value.standby_instances, null) + 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 = try(instance_refresh.value.triggers, null) + triggers = instance_refresh.value.triggers } } @@ -575,7 +570,6 @@ resource "aws_autoscaling_group" "this" { max_instance_lifetime = var.max_instance_lifetime max_size = var.max_size metrics_granularity = var.metrics_granularity - min_elb_capacity = var.min_elb_capacity min_size = var.min_size ignore_failed_scaling_activities = var.ignore_failed_scaling_activities @@ -585,149 +579,162 @@ resource "aws_autoscaling_group" "this" { content { dynamic "instances_distribution" { - for_each = try([mixed_instances_policy.value.instances_distribution], []) + for_each = mixed_instances_policy.value.instances_distribution != null ? [mixed_instances_policy.value.instances_distribution] : [] content { - on_demand_allocation_strategy = try(instances_distribution.value.on_demand_allocation_strategy, null) - on_demand_base_capacity = try(instances_distribution.value.on_demand_base_capacity, null) - on_demand_percentage_above_base_capacity = try(instances_distribution.value.on_demand_percentage_above_base_capacity, null) - spot_allocation_strategy = try(instances_distribution.value.spot_allocation_strategy, null) - spot_instance_pools = try(instances_distribution.value.spot_instance_pools, null) - spot_max_price = try(instances_distribution.value.spot_max_price, null) + 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 } } - launch_template { - launch_template_specification { - launch_template_id = local.launch_template_id - version = local.launch_template_version - } + dynamic "launch_template" { + for_each = [mixed_instances_policy.value.launch_template] - dynamic "override" { - for_each = try(mixed_instances_policy.value.override, []) + content { + launch_template_specification { + launch_template_id = local.launch_template_id + version = local.launch_template_version + } - content { - dynamic "instance_requirements" { - for_each = try([override.value.instance_requirements], []) + dynamic "override" { + for_each = launch_template.value.override != null ? launch_template.value.override : [] - content { + content { + dynamic "instance_requirements" { + for_each = override.value.instance_requirements != null ? [override.value.instance_requirements] : [] - dynamic "accelerator_count" { - for_each = try([instance_requirements.value.accelerator_count], []) + content { + dynamic "accelerator_count" { + for_each = instance_requirements.value.accelerator_count != null ? [instance_requirements.value.accelerator_count] : [] - content { - max = try(accelerator_count.value.max, null) - min = try(accelerator_count.value.min, null) + content { + max = accelerator_count.value.max + min = accelerator_count.value.min + } } - } - accelerator_manufacturers = try(instance_requirements.value.accelerator_manufacturers, []) - accelerator_names = try(instance_requirements.value.accelerator_names, []) + accelerator_manufacturers = instance_requirements.value.accelerator_manufacturers + accelerator_names = instance_requirements.value.accelerator_names - dynamic "accelerator_total_memory_mib" { - for_each = try([instance_requirements.value.accelerator_total_memory_mib], []) + dynamic "accelerator_total_memory_mib" { + for_each = instance_requirements.value.accelerator_total_memory_mib != null ? [instance_requirements.value.accelerator_total_memory_mib] : [] - content { - max = try(accelerator_total_memory_mib.value.max, null) - min = try(accelerator_total_memory_mib.value.min, null) + content { + max = accelerator_total_memory_mib.value.max + min = accelerator_total_memory_mib.value.min + } } - } - accelerator_types = try(instance_requirements.value.accelerator_types, []) - allowed_instance_types = try(instance_requirements.value.allowed_instance_types, null) - bare_metal = try(instance_requirements.value.bare_metal, null) + 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 = try([instance_requirements.value.baseline_ebs_bandwidth_mbps], []) + dynamic "baseline_ebs_bandwidth_mbps" { + for_each = instance_requirements.value.baseline_ebs_bandwidth_mbps != null ? [instance_requirements.value.baseline_ebs_bandwidth_mbps] : [] - content { - max = try(baseline_ebs_bandwidth_mbps.value.max, null) - min = try(baseline_ebs_bandwidth_mbps.value.min, null) + content { + max = baseline_ebs_bandwidth_mbps.value.max + min = baseline_ebs_bandwidth_mbps.value.min + } } - } - burstable_performance = try(instance_requirements.value.burstable_performance, null) - cpu_manufacturers = try(instance_requirements.value.cpu_manufacturers, []) - excluded_instance_types = try(instance_requirements.value.excluded_instance_types, []) - instance_generations = try(instance_requirements.value.instance_generations, []) - local_storage = try(instance_requirements.value.local_storage, null) - local_storage_types = try(instance_requirements.value.local_storage_types, []) + 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_gib_per_vcpu" { - for_each = try([instance_requirements.value.memory_gib_per_vcpu], []) + dynamic "memory_mib" { + for_each = instance_requirements.value.memory_mib != null ? [instance_requirements.value.memory_mib] : [] - content { - max = try(memory_gib_per_vcpu.value.max, null) - min = try(memory_gib_per_vcpu.value.min, null) + content { + max = memory_mib.value.max + min = memory_mib.value.min + } } - } - dynamic "memory_mib" { - for_each = [instance_requirements.value.memory_mib] + dynamic "network_bandwidth_gbps" { + for_each = instance_requirements.value.network_bandwidth_gbps != null ? [instance_requirements.value.network_bandwidth_gbps] : [] - content { - max = try(memory_mib.value.max, null) - min = memory_mib.value.min + content { + max = network_bandwidth_gbps.value.max + min = network_bandwidth_gbps.value.min + } } - } - dynamic "network_interface_count" { - for_each = try([instance_requirements.value.network_interface_count], []) + dynamic "network_interface_count" { + for_each = instance_requirements.value.network_interface_count != null ? [instance_requirements.value.network_interface_count] : [] - content { - max = try(network_interface_count.value.max, null) - min = try(network_interface_count.value.min, null) + content { + max = network_interface_count.value.max + min = network_interface_count.value.min + } } - } - on_demand_max_price_percentage_over_lowest_price = try(instance_requirements.value.on_demand_max_price_percentage_over_lowest_price, null) - require_hibernate_support = try(instance_requirements.value.require_hibernate_support, null) - spot_max_price_percentage_over_lowest_price = try(instance_requirements.value.spot_max_price_percentage_over_lowest_price, null) + 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 = try([instance_requirements.value.total_local_storage_gb], []) + dynamic "total_local_storage_gb" { + for_each = instance_requirements.value.total_local_storage_gb != null ? [instance_requirements.value.total_local_storage_gb] : [] - content { - max = try(total_local_storage_gb.value.max, null) - min = try(total_local_storage_gb.value.min, null) + 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] + dynamic "vcpu_count" { + for_each = instance_requirements.value.vcpu_count != null ? [instance_requirements.value.vcpu_count] : [] - content { - max = try(vcpu_count.value.max, null) - min = vcpu_count.value.min + content { + max = vcpu_count.value.max + min = vcpu_count.value.min + } } } } - } - instance_type = try(override.value.instance_type, null) + instance_type = override.value.instance_type - dynamic "launch_template_specification" { - for_each = try([override.value.launch_template_specification], []) + dynamic "launch_template_specification" { + for_each = override.value.launch_template_specification != null ? [override.value.launch_template_specification] : [] - content { - launch_template_id = try(launch_template_specification.value.launch_template_id, null) - version = try(launch_template_specification.value.version, null) + 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 = try(override.value.weighted_capacity, null) + 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 - service_linked_role_arn = var.service_linked_role_arn - suspended_processes = var.suspended_processes + 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( @@ -756,34 +763,17 @@ resource "aws_autoscaling_group" "this" { } } - target_group_arns = var.target_group_arns - termination_policies = var.termination_policies - vpc_zone_identifier = local.enable_efa_support ? data.aws_subnets.placement_group[0].ids : var.subnet_ids - wait_for_capacity_timeout = var.wait_for_capacity_timeout - wait_for_elb_capacity = var.wait_for_elb_capacity + termination_policies = var.termination_policies + vpc_zone_identifier = var.subnet_ids - dynamic "warm_pool" { - for_each = length(var.warm_pool) > 0 ? [var.warm_pool] : [] + dynamic "timeouts" { + for_each = var.timeouts != null ? [var.timeouts] : [] content { - dynamic "instance_reuse_policy" { - for_each = try([warm_pool.value.instance_reuse_policy], []) - - content { - reuse_on_scale_in = try(instance_reuse_policy.value.reuse_on_scale_in, null) - } - } - - max_group_prepared_capacity = try(warm_pool.value.max_group_prepared_capacity, null) - min_size = try(warm_pool.value.min_size, null) - pool_state = try(warm_pool.value.pool_state, null) + delete = var.timeouts.delete } } - timeouts { - delete = var.delete_timeout - } - lifecycle { create_before_destroy = true ignore_changes = [ @@ -800,13 +790,13 @@ 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:${data.aws_partition.current.partition}:iam::aws:policy" + 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:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:policy/AmazonEKS_CNI_IPv6_Policy" + 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" } } @@ -882,25 +872,25 @@ resource "aws_iam_instance_profile" "this" { ################################################################################ locals { - create_iam_role_policy = local.create_iam_instance_profile && var.create_iam_role_policy && length(var.iam_role_policy_statements) > 0 + 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 + for_each = var.iam_role_policy_statements != null ? var.iam_role_policy_statements : [] content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) + 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 = try(statement.value.principals, []) + for_each = statement.value.principals != null ? statement.value.principals : [] content { type = principals.value.type @@ -909,7 +899,7 @@ data "aws_iam_policy_document" "role" { } dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] content { type = not_principals.value.type @@ -918,7 +908,7 @@ data "aws_iam_policy_document" "role" { } dynamic "condition" { - for_each = try(statement.value.conditions, []) + for_each = statement.value.condition != null ? statement.value.condition : [] content { test = condition.value.test @@ -950,6 +940,8 @@ locals { resource "aws_placement_group" "this" { count = local.create_placement_group ? 1 : 0 + region = var.region + name = "${var.cluster_name}-${var.name}" strategy = "cluster" @@ -957,91 +949,124 @@ resource "aws_placement_group" "this" { } ################################################################################ -# Instance AZ Lookup - -# Instances usually used in placement groups w/ EFA are only available in -# select availability zones. These data sources will cross reference the availability -# zones supported by the instance type with the subnets provided to ensure only -# AZs/subnets that are supported are used. +# Access Entry ################################################################################ -# Find the availability zones supported by the instance type -# TODO - remove at next breaking change -# Force users to be explicit about which AZ to use when using placement groups, -# with or without EFA support -data "aws_ec2_instance_type_offerings" "this" { - count = local.enable_efa_support ? 1 : 0 +resource "aws_eks_access_entry" "this" { + count = var.create && var.create_access_entry ? 1 : 0 + + region = var.region - filter { - name = "instance-type" - values = [var.instance_type] - } + 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" - location_type = "availability-zone-id" + tags = var.tags } -# Reverse the lookup to find one of the subnets provided based on the availability -# availability zone ID of the queried instance type (supported) -data "aws_subnets" "placement_group" { - count = local.create_placement_group ? 1 : 0 +################################################################################ +# Security Group +################################################################################ - filter { - name = "subnet-id" - values = var.subnet_ids - } +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}") - # The data source can lookup the first available AZ or you can specify an AZ (next filter) - dynamic "filter" { - for_each = local.create_placement_group && var.placement_group_az == null ? [1] : [] + security_group_ingress_rules = merge({ for k, v in + { + all_self_efa = { + description = "Node to node EFA" + protocol = "-1" + from_port = 0 + self = true + } + } : 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" + protocol = "-1" + to_port = 0 + self = true + } + } : k => v if var.enable_efa_support + }, + var.security_group_egress_rules + ) +} - content { - name = "availability-zone-id" - values = data.aws_ec2_instance_type_offerings.this[0].locations - } - } +data "aws_subnet" "this" { + count = local.create_security_group ? 1 : 0 - dynamic "filter" { - for_each = var.placement_group_az != null ? [var.placement_group_az] : [] + region = var.region - content { - name = "availability-zone" - values = [filter.value] - } - } + id = element(var.subnet_ids, 0) } -################################################################################ -# Access Entry -################################################################################ - -resource "aws_eks_access_entry" "this" { - count = var.create && var.create_access_entry ? 1 : 0 +resource "aws_security_group" "this" { + count = local.create_security_group ? 1 : 0 - cluster_name = var.cluster_name - principal_arn = var.create_iam_instance_profile ? aws_iam_role.this[0].arn : var.iam_role_arn - type = local.user_data_type == "windows" ? "EC2_WINDOWS" : "EC2_LINUX" + region = var.region - tags = var.tags -} + 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 -################################################################################ -# Autoscaling group schedule -################################################################################ - -resource "aws_autoscaling_schedule" "this" { - for_each = { for k, v in var.schedules : k => v if var.create && var.create_schedule } + tags = merge( + var.tags, + { "Name" = local.security_group_name }, + var.security_group_tags + ) - scheduled_action_name = each.key - autoscaling_group_name = aws_autoscaling_group.this[0].name + lifecycle { + create_before_destroy = true + } +} - min_size = try(each.value.min_size, null) - max_size = try(each.value.max_size, null) - desired_capacity = try(each.value.desired_size, null) - start_time = try(each.value.start_time, null) - end_time = try(each.value.end_time, null) - time_zone = try(each.value.time_zone, null) +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) +} - # [Minute] [Hour] [Day_of_Month] [Month_of_Year] [Day_of_Week] - # Cron examples: https://crontab.guru/examples.html - recurrence = try(each.value.recurrence, 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/outputs.tf b/modules/self-managed-node-group/outputs.tf index 9607810ac3..ad8710b890 100644 --- a/modules/self-managed-node-group/outputs.tf +++ b/modules/self-managed-node-group/outputs.tf @@ -128,24 +128,10 @@ output "access_entry_arn" { value = try(aws_eks_access_entry.this[0].access_entry_arn, null) } -################################################################################ -# Autoscaling Group Schedule -################################################################################ - -output "autoscaling_group_schedule_arns" { - description = "ARNs of autoscaling group schedules" - value = { for k, v in aws_autoscaling_schedule.this : k => v.arn } -} - ################################################################################ # Additional ################################################################################ -output "platform" { - description = "[DEPRECATED - Will be removed in `v21.0`] Identifies the OS platform as `bottlerocket`, `linux` (AL2), `al2023`, or `windows`" - value = module.user_data.platform -} - output "image_id" { description = "ID of the image" value = try(aws_launch_template.this[0].image_id, null) @@ -155,3 +141,17 @@ 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 index 9076dab5af..0e805796a8 100644 --- a/modules/self-managed-node-group/variables.tf +++ b/modules/self-managed-node-group/variables.tf @@ -2,6 +2,7 @@ variable "create" { description = "Determines whether to create self managed node group or not" type = bool default = true + nullable = false } variable "tags" { @@ -10,16 +11,22 @@ variable "tags" { default = {} } -# tflint-ignore: terraform_unused_declarations -variable "platform" { - description = "[DEPRECATED - must use `ami_type` instead. Will be removed in `v21.0`]" +variable "region" { + description = "Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration" type = string default = null +} - validation { - condition = var.platform == null - error_message = "`platform` is no longer valid due to the number of OS choices. Please provide an [`ami_type`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-eks-nodegroup.html#cfn-eks-nodegroup-amitype) instead." - } +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 = "" } ################################################################################ @@ -116,6 +123,7 @@ variable "create_launch_template" { description = "Determines whether to create launch template or not" type = bool default = true + nullable = false } variable "launch_template_id" { @@ -180,92 +188,162 @@ variable "ram_disk_id" { variable "block_device_mappings" { description = "Specify volumes to attach to the instance besides the volumes specified by the AMI" - type = any - default = {} + 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 = any - default = {} + 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 = map(string) - default = {} + 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 = map(string) - default = {} -} - -variable "elastic_gpu_specifications" { - description = "The elastic GPU to attach to the instance" - type = any - default = {} -} - -variable "elastic_inference_accelerator" { - description = "Configuration block containing an Elastic Inference Accelerator to attach to the instance" - type = map(string) - default = {} + type = object({ + cpu_credits = optional(string) + }) + default = null } variable "enclave_options" { description = "Enable Nitro Enclaves on launched instances" - type = map(string) - default = {} -} - -variable "hibernation_options" { - description = "The hibernation options for the instance" - type = map(string) - default = {} + type = object({ + enabled = optional(bool) + }) + default = null } variable "instance_market_options" { description = "The market (purchasing) option for the instance" - type = any - default = {} + 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 = any - default = {} + type = object({ + auto_recovery = optional(string) + }) + default = null } variable "license_specifications" { - description = "A map of license specifications to associate with" - type = any - default = {} + 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(any) - default = [] + 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), []) + subnet_id = optional(string) + })) + default = [] } variable "placement" { description = "The placement of the instance" - type = map(string) - default = {} + 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 = map(string) - default = {} + 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" { @@ -283,10 +361,10 @@ variable "ami_id" { 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 = "AL2_x86_64" + default = "AL2023_x86_64_STANDARD" } -variable "cluster_version" { +variable "kubernetes_version" { description = "Kubernetes cluster version - used to lookup default AMI ID if one is not provided" type = string default = null @@ -294,14 +372,66 @@ variable "cluster_version" { variable "instance_requirements" { description = "The attribute requirements for the type of instance. If present then `instance_type` cannot be present" - type = any - default = {} + 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 = "" + default = null } variable "key_name" { @@ -334,11 +464,10 @@ variable "enable_efa_support" { default = false } -# TODO - make this true by default at next breaking change (remove variable, only pass indices) 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 = false + default = true } variable "efa_indices" { @@ -349,11 +478,17 @@ variable "efa_indices" { variable "metadata_options" { description = "Customize the metadata options for the instance" - type = map(string) + 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" - http_put_response_hop_limit = 2 } } @@ -377,6 +512,7 @@ variable "create_autoscaling_group" { description = "Determines whether to create autoscaling group or not" type = bool default = true + nullable = false } variable "name" { @@ -403,12 +539,6 @@ variable "availability_zones" { default = null } -variable "placement_group_az" { - description = "Availability zone where placement group is created (ex. `eu-west-1c`)" - type = 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) @@ -418,7 +548,7 @@ variable "subnet_ids" { variable "min_size" { description = "The minimum size of the autoscaling group" type = number - default = 0 + default = 1 } variable "max_size" { @@ -440,7 +570,7 @@ variable "desired_size_type" { } variable "ignore_failed_scaling_activities" { - description = "Whether to ignore failed Auto Scaling scaling activities while waiting for capacity." + description = "Whether to ignore failed Auto Scaling scaling activities while waiting for capacity" type = bool default = null } @@ -457,30 +587,6 @@ variable "capacity_rebalance" { default = null } -variable "min_elb_capacity" { - description = "Setting this causes Terraform to wait for this number of instances to show up healthy in the ELB only on creation. Updates will not wait on ELB instance number changes" - type = number - default = null -} - -variable "wait_for_elb_capacity" { - description = "Setting this will cause Terraform to wait for exactly this number of healthy instances in all attached load balancers on both create and update operations. Takes precedence over `min_elb_capacity` behavior." - type = number - default = null -} - -variable "wait_for_capacity_timeout" { - description = "A maximum duration that Terraform should wait for ASG instances to be healthy before timing out. (See also Waiting for Capacity below.) Setting this to '0' causes Terraform to skip all Capacity Waiting behavior." - type = string - default = null -} - -variable "default_cooldown" { - description = "The amount of time, in seconds, after a scaling activity completes before another scaling activity can start" - type = number - 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 @@ -488,19 +594,13 @@ variable "default_instance_warmup" { } 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." + 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 } -variable "target_group_arns" { - description = "A set of `aws_alb_target_group` ARNs, for use with Application or Network Load Balancing" - type = list(string) - default = [] -} - variable "placement_group" { - description = "The name of the placement group into which you'll launch your instances, if any" + description = "The name of the placement group into which you'll launch your instances" type = string default = null } @@ -523,12 +623,6 @@ variable "force_delete" { default = null } -variable "force_delete_warm_pool" { - description = "Allows deleting the Auto Scaling Group without waiting for all instances in the warm pool to terminate" - 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) @@ -559,27 +653,49 @@ variable "metrics_granularity" { default = null } -variable "service_linked_role_arn" { - description = "The ARN of the service-linked role that the ASG will use to call other AWS services" - 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(map(string)) - default = [] + 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 = any - default = {} + 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 = any + 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, 33) + scale_in_protected_instances = optional(string) + skip_matching = optional(bool) + standby_instances = optional(string) + })) + strategy = optional(string, "Rolling") + triggers = optional(list(string)) + }) default = { strategy = "Rolling" preferences = { @@ -596,20 +712,89 @@ variable "use_mixed_instances_policy" { variable "mixed_instances_policy" { description = "Configuration block containing settings to define launch targets for Auto Scaling groups" - type = any - default = null -} - -variable "warm_pool" { - description = "If this block is configured, add a Warm Pool to the specified Auto Scaling group" - type = any - default = {} -} - -variable "delete_timeout" { - description = "Delete timeout to wait for destroying autoscaling group" - type = string - default = null + 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" { @@ -626,6 +811,7 @@ 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" { @@ -690,12 +876,33 @@ 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 = any - default = [] + 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 } ################################################################################ @@ -706,6 +913,7 @@ 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" { @@ -715,17 +923,74 @@ variable "iam_role_arn" { } ################################################################################ -# Autoscaling group schedule +# Security Group ################################################################################ -variable "create_schedule" { - description = "Determines whether to create autoscaling group schedule or not" +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 } -variable "schedules" { - description = "Map of autoscaling group schedule to create" - type = map(any) +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/self-managed-node-group/versions.tf b/modules/self-managed-node-group/versions.tf index 7884c4e1b2..db13b0a8d2 100644 --- a/modules/self-managed-node-group/versions.tf +++ b/modules/self-managed-node-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.95, < 6.0.0" + version = ">= 6.0" } } } diff --git a/node_groups.tf b/node_groups.tf index de7411f226..9661e22629 100644 --- a/node_groups.tf +++ b/node_groups.tf @@ -1,23 +1,4 @@ locals { - metadata_options = { - http_endpoint = "enabled" - http_tokens = "required" - http_put_response_hop_limit = 2 - } - - # EKS managed node group - default_update_config = { - max_unavailable_percentage = 33 - } - - # Self-managed node group - default_instance_refresh = { - strategy = "Rolling" - preferences = { - min_healthy_percentage = 66 - } - } - kubernetes_network_config = try(aws_eks_cluster.this[0].kubernetes_network_config[0], {}) } @@ -32,12 +13,12 @@ resource "time_sleep" "this" { create_duration = var.dataplane_wait_duration triggers = { - cluster_name = aws_eks_cluster.this[0].id - cluster_endpoint = aws_eks_cluster.this[0].endpoint - cluster_version = aws_eks_cluster.this[0].version - cluster_service_cidr = var.cluster_ip_family == "ipv6" ? try(local.kubernetes_network_config.service_ipv6_cidr, "") : try(local.kubernetes_network_config.service_ipv4_cidr, "") + 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, "") - cluster_certificate_authority_data = aws_eks_cluster.this[0].certificate_authority[0].data + certificate_authority_data = aws_eks_cluster.this[0].certificate_authority[0].data } } @@ -87,7 +68,7 @@ resource "aws_iam_policy" "cni_ipv6_policy" { ################################################################################ locals { - node_sg_name = coalesce(var.node_security_group_name, "${var.cluster_name}-node") + 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 @@ -179,35 +160,16 @@ locals { to_port = 0 type = "egress" cidr_blocks = ["0.0.0.0/0"] - ipv6_cidr_blocks = var.cluster_ip_family == "ipv6" ? ["::/0"] : null + ipv6_cidr_blocks = var.ip_family == "ipv6" ? ["::/0"] : null } } : k => v if var.node_security_group_enable_recommended_rules } - - efa_security_group_rules = { for k, v in - { - ingress_all_self_efa = { - description = "Node to node EFA" - protocol = "-1" - from_port = 0 - to_port = 0 - type = "ingress" - self = true - } - egress_all_self_efa = { - description = "Node to node EFA" - protocol = "-1" - from_port = 0 - to_port = 0 - type = "egress" - self = true - } - } : k => v if var.enable_efa_support - } } 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 @@ -216,8 +178,8 @@ resource "aws_security_group" "node" { tags = merge( var.tags, { - "Name" = local.node_sg_name - "kubernetes.io/cluster/${var.cluster_name}" = "owned" + "Name" = local.node_sg_name + "kubernetes.io/cluster/${var.name}" = "owned" }, var.node_security_group_tags ) @@ -229,26 +191,24 @@ resource "aws_security_group" "node" { resource "aws_security_group_rule" "node" { for_each = { for k, v in merge( - local.efa_security_group_rules, local.node_security_group_rules, local.node_security_group_recommended_rules, var.node_security_group_additional_rules, ) : k => v if local.create_node_sg } - # Required - 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 - - # Optional - description = lookup(each.value, "description", null) - cidr_blocks = lookup(each.value, "cidr_blocks", null) - ipv6_cidr_blocks = lookup(each.value, "ipv6_cidr_blocks", null) - prefix_list_ids = lookup(each.value, "prefix_list_ids", []) - self = lookup(each.value, "self", null) - source_security_group_id = try(each.value.source_cluster_security_group, false) ? local.cluster_security_group_id : lookup(each.value, "source_security_group_id", null) + 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) } ################################################################################ @@ -258,35 +218,42 @@ resource "aws_security_group_rule" "node" { module "fargate_profile" { source = "./modules/fargate-profile" - for_each = { for k, v in var.fargate_profiles : k => v if var.create && !local.create_outposts_local_cluster } + for_each = var.create && !local.create_outposts_local_cluster && length(var.fargate_profiles) > 0 ? var.fargate_profiles : {} + + create = each.value.create + + region = var.region - create = try(each.value.create, true) + # 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["cluster_name"] - cluster_ip_family = var.cluster_ip_family - name = try(each.value.name, each.key) - subnet_ids = try(each.value.subnet_ids, var.fargate_profile_defaults.subnet_ids, var.subnet_ids) - selectors = try(each.value.selectors, var.fargate_profile_defaults.selectors, []) - timeouts = try(each.value.timeouts, var.fargate_profile_defaults.timeouts, {}) + 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 = try(each.value.selectors, null) + timeouts = try(each.value.timeouts, null) # IAM role - create_iam_role = try(each.value.create_iam_role, var.fargate_profile_defaults.create_iam_role, true) - iam_role_arn = try(each.value.iam_role_arn, var.fargate_profile_defaults.iam_role_arn, null) - iam_role_name = try(each.value.iam_role_name, var.fargate_profile_defaults.iam_role_name, null) - iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, var.fargate_profile_defaults.iam_role_use_name_prefix, true) - iam_role_path = try(each.value.iam_role_path, var.fargate_profile_defaults.iam_role_path, null) - iam_role_description = try(each.value.iam_role_description, var.fargate_profile_defaults.iam_role_description, "Fargate profile IAM role") - iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, var.fargate_profile_defaults.iam_role_permissions_boundary, null) - iam_role_tags = try(each.value.iam_role_tags, var.fargate_profile_defaults.iam_role_tags, {}) - iam_role_attach_cni_policy = try(each.value.iam_role_attach_cni_policy, var.fargate_profile_defaults.iam_role_attach_cni_policy, true) - # To better understand why this `lookup()` logic is required, see: - # https://github.com/hashicorp/terraform/issues/31646#issuecomment-1217279031 - iam_role_additional_policies = lookup(each.value, "iam_role_additional_policies", lookup(var.fargate_profile_defaults, "iam_role_additional_policies", {})) - create_iam_role_policy = try(each.value.create_iam_role_policy, var.fargate_profile_defaults.create_iam_role_policy, true) - iam_role_policy_statements = try(each.value.iam_role_policy_statements, var.fargate_profile_defaults.iam_role_policy_statements, []) - - tags = merge(var.tags, try(each.value.tags, var.fargate_profile_defaults.tags, {})) + create_iam_role = try(each.value.create_iam_role, null) + iam_role_arn = try(each.value.iam_role_arn, null) + iam_role_name = try(each.value.iam_role_name, null) + iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, null) + iam_role_path = try(each.value.iam_role_path, null) + iam_role_description = try(each.value.iam_role_description, null) + iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, null) + iam_role_tags = try(each.value.iam_role_tags, null) + iam_role_attach_cni_policy = try(each.value.iam_role_attach_cni_policy, null) + iam_role_additional_policies = lookup(each.value, "iam_role_additional_policies", null) + create_iam_role_policy = try(each.value.create_iam_role_policy, null) + iam_role_policy_statements = try(each.value.iam_role_policy_statements, null) + + tags = merge( + var.tags, + each.value.tags, + ) } ################################################################################ @@ -296,120 +263,124 @@ module "fargate_profile" { module "eks_managed_node_group" { source = "./modules/eks-managed-node-group" - for_each = { for k, v in var.eks_managed_node_groups : k => v if var.create && !local.create_outposts_local_cluster } + for_each = var.create && !local.create_outposts_local_cluster && length(var.eks_managed_node_groups) > 0 ? var.eks_managed_node_groups : {} + + create = each.value.create + + region = var.region - create = try(each.value.create, true) + # 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["cluster_name"] - cluster_version = try(each.value.cluster_version, var.eks_managed_node_group_defaults.cluster_version, time_sleep.this[0].triggers["cluster_version"]) + cluster_name = time_sleep.this[0].triggers["name"] + kubernetes_version = try(each.value.kubernetes_version, time_sleep.this[0].triggers["kubernetes_version"]) # EKS Managed Node Group - name = try(each.value.name, each.key) - use_name_prefix = try(each.value.use_name_prefix, var.eks_managed_node_group_defaults.use_name_prefix, true) - - subnet_ids = try(each.value.subnet_ids, var.eks_managed_node_group_defaults.subnet_ids, var.subnet_ids) - - min_size = try(each.value.min_size, var.eks_managed_node_group_defaults.min_size, 1) - max_size = try(each.value.max_size, var.eks_managed_node_group_defaults.max_size, 3) - desired_size = try(each.value.desired_size, var.eks_managed_node_group_defaults.desired_size, 1) - - ami_id = try(each.value.ami_id, var.eks_managed_node_group_defaults.ami_id, "") - ami_type = try(each.value.ami_type, var.eks_managed_node_group_defaults.ami_type, null) - ami_release_version = try(each.value.ami_release_version, var.eks_managed_node_group_defaults.ami_release_version, null) - use_latest_ami_release_version = try(each.value.use_latest_ami_release_version, var.eks_managed_node_group_defaults.use_latest_ami_release_version, false) - - capacity_type = try(each.value.capacity_type, var.eks_managed_node_group_defaults.capacity_type, null) - disk_size = try(each.value.disk_size, var.eks_managed_node_group_defaults.disk_size, null) - force_update_version = try(each.value.force_update_version, var.eks_managed_node_group_defaults.force_update_version, null) - instance_types = try(each.value.instance_types, var.eks_managed_node_group_defaults.instance_types, null) - labels = try(each.value.labels, var.eks_managed_node_group_defaults.labels, null) - node_repair_config = try(each.value.node_repair_config, var.eks_managed_node_group_defaults.node_repair_config, null) - remote_access = try(each.value.remote_access, var.eks_managed_node_group_defaults.remote_access, {}) - taints = try(each.value.taints, var.eks_managed_node_group_defaults.taints, {}) - update_config = try(each.value.update_config, var.eks_managed_node_group_defaults.update_config, local.default_update_config) - timeouts = try(each.value.timeouts, var.eks_managed_node_group_defaults.timeouts, {}) + name = coalesce(each.value.name, each.key) + use_name_prefix = try(each.value.use_name_prefix, null) + + subnet_ids = coalesce(each.value.subnet_ids, var.subnet_ids) + + min_size = try(each.value.min_size, null) + max_size = try(each.value.max_size, null) + desired_size = try(each.value.desired_size, null) + + ami_id = try(each.value.ami_id, null) + ami_type = try(each.value.ami_type, null) + ami_release_version = try(each.value.ami_release_version, null) + use_latest_ami_release_version = try(each.value.use_latest_ami_release_version, null) + + capacity_type = try(each.value.capacity_type, null) + disk_size = try(each.value.disk_size, null) + force_update_version = try(each.value.force_update_version, null) + instance_types = try(each.value.instance_types, null) + labels = try(each.value.labels, null) + node_repair_config = try(each.value.node_repair_config, null) + remote_access = try(each.value.remote_access, null) + taints = try(each.value.taints, null) + update_config = try(each.value.update_config, null) + timeouts = try(each.value.timeouts, null) # User data - platform = try(each.value.platform, var.eks_managed_node_group_defaults.platform, "linux") - cluster_endpoint = try(time_sleep.this[0].triggers["cluster_endpoint"], "") - cluster_auth_base64 = try(time_sleep.this[0].triggers["cluster_certificate_authority_data"], "") - cluster_service_ipv4_cidr = var.cluster_service_ipv4_cidr - cluster_ip_family = var.cluster_ip_family - cluster_service_cidr = try(time_sleep.this[0].triggers["cluster_service_cidr"], "") - enable_bootstrap_user_data = try(each.value.enable_bootstrap_user_data, var.eks_managed_node_group_defaults.enable_bootstrap_user_data, false) - pre_bootstrap_user_data = try(each.value.pre_bootstrap_user_data, var.eks_managed_node_group_defaults.pre_bootstrap_user_data, "") - post_bootstrap_user_data = try(each.value.post_bootstrap_user_data, var.eks_managed_node_group_defaults.post_bootstrap_user_data, "") - bootstrap_extra_args = try(each.value.bootstrap_extra_args, var.eks_managed_node_group_defaults.bootstrap_extra_args, "") - user_data_template_path = try(each.value.user_data_template_path, var.eks_managed_node_group_defaults.user_data_template_path, "") - cloudinit_pre_nodeadm = try(each.value.cloudinit_pre_nodeadm, var.eks_managed_node_group_defaults.cloudinit_pre_nodeadm, []) - cloudinit_post_nodeadm = try(each.value.cloudinit_post_nodeadm, var.eks_managed_node_group_defaults.cloudinit_post_nodeadm, []) + 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 = try(each.value.enable_bootstrap_user_data, null) + pre_bootstrap_user_data = try(each.value.pre_bootstrap_user_data, null) + post_bootstrap_user_data = try(each.value.post_bootstrap_user_data, null) + bootstrap_extra_args = try(each.value.bootstrap_extra_args, null) + user_data_template_path = try(each.value.user_data_template_path, null) + cloudinit_pre_nodeadm = try(each.value.cloudinit_pre_nodeadm, null) + cloudinit_post_nodeadm = try(each.value.cloudinit_post_nodeadm, null) # Launch Template - create_launch_template = try(each.value.create_launch_template, var.eks_managed_node_group_defaults.create_launch_template, true) - use_custom_launch_template = try(each.value.use_custom_launch_template, var.eks_managed_node_group_defaults.use_custom_launch_template, true) - launch_template_id = try(each.value.launch_template_id, var.eks_managed_node_group_defaults.launch_template_id, "") - launch_template_name = try(each.value.launch_template_name, var.eks_managed_node_group_defaults.launch_template_name, each.key) - launch_template_use_name_prefix = try(each.value.launch_template_use_name_prefix, var.eks_managed_node_group_defaults.launch_template_use_name_prefix, true) - launch_template_version = try(each.value.launch_template_version, var.eks_managed_node_group_defaults.launch_template_version, null) - launch_template_default_version = try(each.value.launch_template_default_version, var.eks_managed_node_group_defaults.launch_template_default_version, null) - update_launch_template_default_version = try(each.value.update_launch_template_default_version, var.eks_managed_node_group_defaults.update_launch_template_default_version, true) - launch_template_description = try(each.value.launch_template_description, var.eks_managed_node_group_defaults.launch_template_description, "Custom launch template for ${try(each.value.name, each.key)} EKS managed node group") - launch_template_tags = try(each.value.launch_template_tags, var.eks_managed_node_group_defaults.launch_template_tags, {}) - tag_specifications = try(each.value.tag_specifications, var.eks_managed_node_group_defaults.tag_specifications, ["instance", "volume", "network-interface"]) - - ebs_optimized = try(each.value.ebs_optimized, var.eks_managed_node_group_defaults.ebs_optimized, null) - key_name = try(each.value.key_name, var.eks_managed_node_group_defaults.key_name, null) - disable_api_termination = try(each.value.disable_api_termination, var.eks_managed_node_group_defaults.disable_api_termination, null) - kernel_id = try(each.value.kernel_id, var.eks_managed_node_group_defaults.kernel_id, null) - ram_disk_id = try(each.value.ram_disk_id, var.eks_managed_node_group_defaults.ram_disk_id, null) - - block_device_mappings = try(each.value.block_device_mappings, var.eks_managed_node_group_defaults.block_device_mappings, {}) - capacity_reservation_specification = try(each.value.capacity_reservation_specification, var.eks_managed_node_group_defaults.capacity_reservation_specification, {}) - cpu_options = try(each.value.cpu_options, var.eks_managed_node_group_defaults.cpu_options, {}) - credit_specification = try(each.value.credit_specification, var.eks_managed_node_group_defaults.credit_specification, {}) - elastic_gpu_specifications = try(each.value.elastic_gpu_specifications, var.eks_managed_node_group_defaults.elastic_gpu_specifications, {}) - elastic_inference_accelerator = try(each.value.elastic_inference_accelerator, var.eks_managed_node_group_defaults.elastic_inference_accelerator, {}) - enclave_options = try(each.value.enclave_options, var.eks_managed_node_group_defaults.enclave_options, {}) - instance_market_options = try(each.value.instance_market_options, var.eks_managed_node_group_defaults.instance_market_options, {}) - license_specifications = try(each.value.license_specifications, var.eks_managed_node_group_defaults.license_specifications, {}) - metadata_options = try(each.value.metadata_options, var.eks_managed_node_group_defaults.metadata_options, local.metadata_options) - enable_monitoring = try(each.value.enable_monitoring, var.eks_managed_node_group_defaults.enable_monitoring, true) - enable_efa_support = try(each.value.enable_efa_support, var.eks_managed_node_group_defaults.enable_efa_support, false) - enable_efa_only = try(each.value.enable_efa_only, var.eks_managed_node_group_defaults.enable_efa_only, false) - efa_indices = try(each.value.efa_indices, var.eks_managed_node_group_defaults.efa_indices, [0]) - create_placement_group = try(each.value.create_placement_group, var.eks_managed_node_group_defaults.create_placement_group, false) - placement = try(each.value.placement, var.eks_managed_node_group_defaults.placement, {}) - placement_group_az = try(each.value.placement_group_az, var.eks_managed_node_group_defaults.placement_group_az, null) - placement_group_strategy = try(each.value.placement_group_strategy, var.eks_managed_node_group_defaults.placement_group_strategy, "cluster") - network_interfaces = try(each.value.network_interfaces, var.eks_managed_node_group_defaults.network_interfaces, []) - maintenance_options = try(each.value.maintenance_options, var.eks_managed_node_group_defaults.maintenance_options, {}) - private_dns_name_options = try(each.value.private_dns_name_options, var.eks_managed_node_group_defaults.private_dns_name_options, {}) + create_launch_template = try(each.value.create_launch_template, null) + use_custom_launch_template = try(each.value.use_custom_launch_template, null) + launch_template_id = try(each.value.launch_template_id, null) + launch_template_name = coalesce(each.value.launch_template_name, each.key) + launch_template_use_name_prefix = try(each.value.launch_template_use_name_prefix, null) + launch_template_version = try(each.value.launch_template_version, null) + launch_template_default_version = try(each.value.launch_template_default_version, null) + update_launch_template_default_version = try(each.value.update_launch_template_default_version, null) + 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 = try(each.value.launch_template_tags, null) + tag_specifications = try(each.value.tag_specifications, null) + + ebs_optimized = try(each.value.ebs_optimized, null) + key_name = try(each.value.key_name, null) + disable_api_termination = try(each.value.disable_api_termination, null) + kernel_id = try(each.value.kernel_id, null) + ram_disk_id = try(each.value.ram_disk_id, null) + + block_device_mappings = try(each.value.block_device_mappings, null) + capacity_reservation_specification = try(each.value.capacity_reservation_specification, null) + cpu_options = try(each.value.cpu_options, null) + credit_specification = try(each.value.credit_specification, null) + enclave_options = try(each.value.enclave_options, null) + instance_market_options = try(each.value.instance_market_options, null) + license_specifications = try(each.value.license_specifications, null) + metadata_options = try(each.value.metadata_options, null) + enable_monitoring = try(each.value.enable_monitoring, null) + enable_efa_support = try(each.value.enable_efa_support, null) + enable_efa_only = try(each.value.enable_efa_only, null) + efa_indices = try(each.value.efa_indices, null) + create_placement_group = try(each.value.create_placement_group, null) + placement = try(each.value.placement, null) + network_interfaces = try(each.value.network_interfaces, null) + maintenance_options = try(each.value.maintenance_options, null) + private_dns_name_options = try(each.value.private_dns_name_options, null) # IAM role - create_iam_role = try(each.value.create_iam_role, var.eks_managed_node_group_defaults.create_iam_role, true) - iam_role_arn = try(each.value.iam_role_arn, var.eks_managed_node_group_defaults.iam_role_arn, null) - iam_role_name = try(each.value.iam_role_name, var.eks_managed_node_group_defaults.iam_role_name, null) - iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, var.eks_managed_node_group_defaults.iam_role_use_name_prefix, true) - iam_role_path = try(each.value.iam_role_path, var.eks_managed_node_group_defaults.iam_role_path, null) - iam_role_description = try(each.value.iam_role_description, var.eks_managed_node_group_defaults.iam_role_description, "EKS managed node group IAM role") - iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, var.eks_managed_node_group_defaults.iam_role_permissions_boundary, null) - iam_role_tags = try(each.value.iam_role_tags, var.eks_managed_node_group_defaults.iam_role_tags, {}) - iam_role_attach_cni_policy = try(each.value.iam_role_attach_cni_policy, var.eks_managed_node_group_defaults.iam_role_attach_cni_policy, true) - # To better understand why this `lookup()` logic is required, see: - # https://github.com/hashicorp/terraform/issues/31646#issuecomment-1217279031 - iam_role_additional_policies = lookup(each.value, "iam_role_additional_policies", lookup(var.eks_managed_node_group_defaults, "iam_role_additional_policies", {})) - create_iam_role_policy = try(each.value.create_iam_role_policy, var.eks_managed_node_group_defaults.create_iam_role_policy, true) - iam_role_policy_statements = try(each.value.iam_role_policy_statements, var.eks_managed_node_group_defaults.iam_role_policy_statements, []) - - # Autoscaling group schedule - create_schedule = try(each.value.create_schedule, var.eks_managed_node_group_defaults.create_schedule, true) - schedules = try(each.value.schedules, var.eks_managed_node_group_defaults.schedules, {}) + create_iam_role = try(each.value.create_iam_role, null) + iam_role_arn = try(each.value.iam_role_arn, null) + iam_role_name = try(each.value.iam_role_name, null) + iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, null) + iam_role_path = try(each.value.iam_role_path, null) + iam_role_description = try(each.value.iam_role_description, null) + iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, null) + iam_role_tags = try(each.value.iam_role_tags, null) + iam_role_attach_cni_policy = try(each.value.iam_role_attach_cni_policy, null) + iam_role_additional_policies = lookup(each.value, "iam_role_additional_policies", null) + create_iam_role_policy = try(each.value.create_iam_role_policy, true) + iam_role_policy_statements = try(each.value.iam_role_policy_statements, null) # Security group - vpc_security_group_ids = compact(concat([local.node_security_group_id], try(each.value.vpc_security_group_ids, var.eks_managed_node_group_defaults.vpc_security_group_ids, []))) - cluster_primary_security_group_id = try(each.value.attach_cluster_primary_security_group, var.eks_managed_node_group_defaults.attach_cluster_primary_security_group, false) ? aws_eks_cluster.this[0].vpc_config[0].cluster_security_group_id : null + vpc_security_group_ids = compact(concat([local.node_security_group_id], try(each.value.vpc_security_group_ids, []))) + cluster_primary_security_group_id = try(each.value.attach_cluster_primary_security_group, false) ? aws_eks_cluster.this[0].vpc_config[0].cluster_security_group_id : null + create_security_group = try(each.value.create_security_group, null) + security_group_name = try(each.value.security_group_name, null) + security_group_use_name_prefix = try(each.value.security_group_use_name_prefix, null) + security_group_description = try(each.value.security_group_description, null) + security_group_ingress_rules = try(each.value.security_group_ingress_rules, null) + security_group_egress_rules = try(each.value.security_group_egress_rules, null) + security_group_tags = try(each.value.security_group_tags, null) - tags = merge(var.tags, try(each.value.tags, var.eks_managed_node_group_defaults.tags, {})) + tags = merge( + var.tags, + each.value.tags, + ) } ################################################################################ @@ -419,150 +390,146 @@ module "eks_managed_node_group" { module "self_managed_node_group" { source = "./modules/self-managed-node-group" - for_each = { for k, v in var.self_managed_node_groups : k => v if var.create } + for_each = var.create && length(var.self_managed_node_groups) > 0 ? var.self_managed_node_groups : {} + + create = each.value.create + + region = var.region - create = try(each.value.create, true) + # 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["cluster_name"] + cluster_name = time_sleep.this[0].triggers["name"] # Autoscaling Group - create_autoscaling_group = try(each.value.create_autoscaling_group, var.self_managed_node_group_defaults.create_autoscaling_group, true) - - name = try(each.value.name, each.key) - use_name_prefix = try(each.value.use_name_prefix, var.self_managed_node_group_defaults.use_name_prefix, true) - - availability_zones = try(each.value.availability_zones, var.self_managed_node_group_defaults.availability_zones, null) - subnet_ids = try(each.value.subnet_ids, var.self_managed_node_group_defaults.subnet_ids, var.subnet_ids) - - min_size = try(each.value.min_size, var.self_managed_node_group_defaults.min_size, 0) - max_size = try(each.value.max_size, var.self_managed_node_group_defaults.max_size, 3) - desired_size = try(each.value.desired_size, var.self_managed_node_group_defaults.desired_size, 1) - desired_size_type = try(each.value.desired_size_type, var.self_managed_node_group_defaults.desired_size_type, null) - capacity_rebalance = try(each.value.capacity_rebalance, var.self_managed_node_group_defaults.capacity_rebalance, null) - min_elb_capacity = try(each.value.min_elb_capacity, var.self_managed_node_group_defaults.min_elb_capacity, null) - wait_for_elb_capacity = try(each.value.wait_for_elb_capacity, var.self_managed_node_group_defaults.wait_for_elb_capacity, null) - wait_for_capacity_timeout = try(each.value.wait_for_capacity_timeout, var.self_managed_node_group_defaults.wait_for_capacity_timeout, null) - default_cooldown = try(each.value.default_cooldown, var.self_managed_node_group_defaults.default_cooldown, null) - default_instance_warmup = try(each.value.default_instance_warmup, var.self_managed_node_group_defaults.default_instance_warmup, null) - protect_from_scale_in = try(each.value.protect_from_scale_in, var.self_managed_node_group_defaults.protect_from_scale_in, null) - context = try(each.value.context, var.self_managed_node_group_defaults.context, null) - - target_group_arns = try(each.value.target_group_arns, var.self_managed_node_group_defaults.target_group_arns, []) - create_placement_group = try(each.value.create_placement_group, var.self_managed_node_group_defaults.create_placement_group, false) - placement_group = try(each.value.placement_group, var.self_managed_node_group_defaults.placement_group, null) - placement_group_az = try(each.value.placement_group_az, var.self_managed_node_group_defaults.placement_group_az, null) - health_check_type = try(each.value.health_check_type, var.self_managed_node_group_defaults.health_check_type, null) - health_check_grace_period = try(each.value.health_check_grace_period, var.self_managed_node_group_defaults.health_check_grace_period, null) - - ignore_failed_scaling_activities = try(each.value.ignore_failed_scaling_activities, var.self_managed_node_group_defaults.ignore_failed_scaling_activities, null) - - force_delete = try(each.value.force_delete, var.self_managed_node_group_defaults.force_delete, null) - force_delete_warm_pool = try(each.value.force_delete_warm_pool, var.self_managed_node_group_defaults.force_delete_warm_pool, null) - termination_policies = try(each.value.termination_policies, var.self_managed_node_group_defaults.termination_policies, []) - suspended_processes = try(each.value.suspended_processes, var.self_managed_node_group_defaults.suspended_processes, []) - max_instance_lifetime = try(each.value.max_instance_lifetime, var.self_managed_node_group_defaults.max_instance_lifetime, null) - - enabled_metrics = try(each.value.enabled_metrics, var.self_managed_node_group_defaults.enabled_metrics, []) - metrics_granularity = try(each.value.metrics_granularity, var.self_managed_node_group_defaults.metrics_granularity, null) - service_linked_role_arn = try(each.value.service_linked_role_arn, var.self_managed_node_group_defaults.service_linked_role_arn, null) - - initial_lifecycle_hooks = try(each.value.initial_lifecycle_hooks, var.self_managed_node_group_defaults.initial_lifecycle_hooks, []) - instance_maintenance_policy = try(each.value.instance_maintenance_policy, var.self_managed_node_group_defaults.instance_maintenance_policy, {}) - instance_refresh = try(each.value.instance_refresh, var.self_managed_node_group_defaults.instance_refresh, local.default_instance_refresh) - use_mixed_instances_policy = try(each.value.use_mixed_instances_policy, var.self_managed_node_group_defaults.use_mixed_instances_policy, false) - mixed_instances_policy = try(each.value.mixed_instances_policy, var.self_managed_node_group_defaults.mixed_instances_policy, null) - warm_pool = try(each.value.warm_pool, var.self_managed_node_group_defaults.warm_pool, {}) - - delete_timeout = try(each.value.delete_timeout, var.self_managed_node_group_defaults.delete_timeout, null) - autoscaling_group_tags = try(each.value.autoscaling_group_tags, var.self_managed_node_group_defaults.autoscaling_group_tags, {}) + create_autoscaling_group = try(each.value.create_autoscaling_group, null) + + name = coalesce(each.value.name, each.key) + use_name_prefix = try(each.value.use_name_prefix, null) + + availability_zones = try(each.value.availability_zones, null) + subnet_ids = coalesce(each.value.subnet_ids, var.subnet_ids) + + min_size = try(each.value.min_size, null) + max_size = try(each.value.max_size, null) + desired_size = try(each.value.desired_size, null) + desired_size_type = try(each.value.desired_size_type, null) + capacity_rebalance = try(each.value.capacity_rebalance, null) + default_instance_warmup = try(each.value.default_instance_warmup, null) + protect_from_scale_in = try(each.value.protect_from_scale_in, null) + context = try(each.value.context, null) + + create_placement_group = try(each.value.create_placement_group, false) + placement_group = try(each.value.placement_group, null) + health_check_type = try(each.value.health_check_type, null) + health_check_grace_period = try(each.value.health_check_grace_period, null) + + ignore_failed_scaling_activities = try(each.value.ignore_failed_scaling_activities, null) + + force_delete = try(each.value.force_delete, null) + termination_policies = try(each.value.termination_policies, null) + suspended_processes = try(each.value.suspended_processes, null) + max_instance_lifetime = try(each.value.max_instance_lifetime, null) + + enabled_metrics = try(each.value.enabled_metrics, null) + metrics_granularity = try(each.value.metrics_granularity, null) + + initial_lifecycle_hooks = try(each.value.initial_lifecycle_hooks, null) + instance_maintenance_policy = try(each.value.instance_maintenance_policy, null) + instance_refresh = try(each.value.instance_refresh, null) + use_mixed_instances_policy = try(each.value.use_mixed_instances_policy, null) + mixed_instances_policy = try(each.value.mixed_instances_policy, null) + + timeouts = try(each.value.timeouts, null) + autoscaling_group_tags = try(each.value.autoscaling_group_tags, null) # User data - platform = try(each.value.platform, var.self_managed_node_group_defaults.platform, null) - # TODO - update this when `var.platform` is removed in v21.0 - ami_type = try(each.value.ami_type, var.self_managed_node_group_defaults.ami_type, "AL2_x86_64") - cluster_endpoint = try(time_sleep.this[0].triggers["cluster_endpoint"], "") - cluster_auth_base64 = try(time_sleep.this[0].triggers["cluster_certificate_authority_data"], "") - cluster_service_cidr = try(time_sleep.this[0].triggers["cluster_service_cidr"], "") - additional_cluster_dns_ips = try(each.value.additional_cluster_dns_ips, var.self_managed_node_group_defaults.additional_cluster_dns_ips, []) - cluster_ip_family = var.cluster_ip_family - pre_bootstrap_user_data = try(each.value.pre_bootstrap_user_data, var.self_managed_node_group_defaults.pre_bootstrap_user_data, "") - post_bootstrap_user_data = try(each.value.post_bootstrap_user_data, var.self_managed_node_group_defaults.post_bootstrap_user_data, "") - bootstrap_extra_args = try(each.value.bootstrap_extra_args, var.self_managed_node_group_defaults.bootstrap_extra_args, "") - user_data_template_path = try(each.value.user_data_template_path, var.self_managed_node_group_defaults.user_data_template_path, "") - cloudinit_pre_nodeadm = try(each.value.cloudinit_pre_nodeadm, var.self_managed_node_group_defaults.cloudinit_pre_nodeadm, []) - cloudinit_post_nodeadm = try(each.value.cloudinit_post_nodeadm, var.self_managed_node_group_defaults.cloudinit_post_nodeadm, []) + ami_type = try(each.value.ami_type, null) + 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 = try(each.value.additional_cluster_dns_ips, null) + cluster_ip_family = var.ip_family + pre_bootstrap_user_data = try(each.value.pre_bootstrap_user_data, null) + post_bootstrap_user_data = try(each.value.post_bootstrap_user_data, null) + bootstrap_extra_args = try(each.value.bootstrap_extra_args, null) + user_data_template_path = try(each.value.user_data_template_path, null) + cloudinit_pre_nodeadm = try(each.value.cloudinit_pre_nodeadm, null) + cloudinit_post_nodeadm = try(each.value.cloudinit_post_nodeadm, null) # Launch Template - create_launch_template = try(each.value.create_launch_template, var.self_managed_node_group_defaults.create_launch_template, true) - launch_template_id = try(each.value.launch_template_id, var.self_managed_node_group_defaults.launch_template_id, "") - launch_template_name = try(each.value.launch_template_name, var.self_managed_node_group_defaults.launch_template_name, each.key) - launch_template_use_name_prefix = try(each.value.launch_template_use_name_prefix, var.self_managed_node_group_defaults.launch_template_use_name_prefix, true) - launch_template_version = try(each.value.launch_template_version, var.self_managed_node_group_defaults.launch_template_version, null) - launch_template_default_version = try(each.value.launch_template_default_version, var.self_managed_node_group_defaults.launch_template_default_version, null) - update_launch_template_default_version = try(each.value.update_launch_template_default_version, var.self_managed_node_group_defaults.update_launch_template_default_version, true) - launch_template_description = try(each.value.launch_template_description, var.self_managed_node_group_defaults.launch_template_description, "Custom launch template for ${try(each.value.name, each.key)} self managed node group") - launch_template_tags = try(each.value.launch_template_tags, var.self_managed_node_group_defaults.launch_template_tags, {}) - tag_specifications = try(each.value.tag_specifications, var.self_managed_node_group_defaults.tag_specifications, ["instance", "volume", "network-interface"]) - - ebs_optimized = try(each.value.ebs_optimized, var.self_managed_node_group_defaults.ebs_optimized, null) - ami_id = try(each.value.ami_id, var.self_managed_node_group_defaults.ami_id, "") - cluster_version = try(each.value.cluster_version, var.self_managed_node_group_defaults.cluster_version, time_sleep.this[0].triggers["cluster_version"]) - instance_type = try(each.value.instance_type, var.self_managed_node_group_defaults.instance_type, "m6i.large") - key_name = try(each.value.key_name, var.self_managed_node_group_defaults.key_name, null) - - disable_api_termination = try(each.value.disable_api_termination, var.self_managed_node_group_defaults.disable_api_termination, null) - instance_initiated_shutdown_behavior = try(each.value.instance_initiated_shutdown_behavior, var.self_managed_node_group_defaults.instance_initiated_shutdown_behavior, null) - kernel_id = try(each.value.kernel_id, var.self_managed_node_group_defaults.kernel_id, null) - ram_disk_id = try(each.value.ram_disk_id, var.self_managed_node_group_defaults.ram_disk_id, null) - - block_device_mappings = try(each.value.block_device_mappings, var.self_managed_node_group_defaults.block_device_mappings, {}) - capacity_reservation_specification = try(each.value.capacity_reservation_specification, var.self_managed_node_group_defaults.capacity_reservation_specification, {}) - cpu_options = try(each.value.cpu_options, var.self_managed_node_group_defaults.cpu_options, {}) - credit_specification = try(each.value.credit_specification, var.self_managed_node_group_defaults.credit_specification, {}) - elastic_gpu_specifications = try(each.value.elastic_gpu_specifications, var.self_managed_node_group_defaults.elastic_gpu_specifications, {}) - elastic_inference_accelerator = try(each.value.elastic_inference_accelerator, var.self_managed_node_group_defaults.elastic_inference_accelerator, {}) - enclave_options = try(each.value.enclave_options, var.self_managed_node_group_defaults.enclave_options, {}) - hibernation_options = try(each.value.hibernation_options, var.self_managed_node_group_defaults.hibernation_options, {}) - instance_requirements = try(each.value.instance_requirements, var.self_managed_node_group_defaults.instance_requirements, {}) - instance_market_options = try(each.value.instance_market_options, var.self_managed_node_group_defaults.instance_market_options, {}) - license_specifications = try(each.value.license_specifications, var.self_managed_node_group_defaults.license_specifications, {}) - metadata_options = try(each.value.metadata_options, var.self_managed_node_group_defaults.metadata_options, local.metadata_options) - enable_monitoring = try(each.value.enable_monitoring, var.self_managed_node_group_defaults.enable_monitoring, true) - enable_efa_support = try(each.value.enable_efa_support, var.self_managed_node_group_defaults.enable_efa_support, false) - enable_efa_only = try(each.value.enable_efa_only, var.self_managed_node_group_defaults.enable_efa_only, false) - efa_indices = try(each.value.efa_indices, var.self_managed_node_group_defaults.efa_indices, [0]) - network_interfaces = try(each.value.network_interfaces, var.self_managed_node_group_defaults.network_interfaces, []) - placement = try(each.value.placement, var.self_managed_node_group_defaults.placement, {}) - maintenance_options = try(each.value.maintenance_options, var.self_managed_node_group_defaults.maintenance_options, {}) - private_dns_name_options = try(each.value.private_dns_name_options, var.self_managed_node_group_defaults.private_dns_name_options, {}) + create_launch_template = try(each.value.create_launch_template, null) + launch_template_id = try(each.value.launch_template_id, null) + launch_template_name = coalesce(each.value.launch_template_name, each.key) + launch_template_use_name_prefix = try(each.value.launch_template_use_name_prefix, null) + launch_template_version = try(each.value.launch_template_version, null) + launch_template_default_version = try(each.value.launch_template_default_version, null) + update_launch_template_default_version = try(each.value.update_launch_template_default_version, null) + 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 = try(each.value.launch_template_tags, null) + tag_specifications = try(each.value.tag_specifications, null) + + ebs_optimized = try(each.value.ebs_optimized, null) + ami_id = try(each.value.ami_id, null) + kubernetes_version = try(each.value.kubernetes_version, time_sleep.this[0].triggers["kubernetes_version"]) + instance_type = try(each.value.instance_type, null) + key_name = try(each.value.key_name, null) + + disable_api_termination = try(each.value.disable_api_termination, null) + instance_initiated_shutdown_behavior = try(each.value.instance_initiated_shutdown_behavior, null) + kernel_id = try(each.value.kernel_id, null) + ram_disk_id = try(each.value.ram_disk_id, null) + + block_device_mappings = try(each.value.block_device_mappings, null) + capacity_reservation_specification = try(each.value.capacity_reservation_specification, null) + cpu_options = try(each.value.cpu_options, null) + credit_specification = try(each.value.credit_specification, null) + enclave_options = try(each.value.enclave_options, null) + instance_requirements = try(each.value.instance_requirements, null) + instance_market_options = try(each.value.instance_market_options, null) + license_specifications = try(each.value.license_specifications, null) + metadata_options = try(each.value.metadata_options, null) + enable_monitoring = try(each.value.enable_monitoring, null) + enable_efa_support = try(each.value.enable_efa_support, null) + enable_efa_only = try(each.value.enable_efa_only, null) + efa_indices = try(each.value.efa_indices, null) + network_interfaces = try(each.value.network_interfaces, null) + placement = try(each.value.placement, null) + maintenance_options = try(each.value.maintenance_options, null) + private_dns_name_options = try(each.value.private_dns_name_options, null) # IAM role - create_iam_instance_profile = try(each.value.create_iam_instance_profile, var.self_managed_node_group_defaults.create_iam_instance_profile, true) - iam_instance_profile_arn = try(each.value.iam_instance_profile_arn, var.self_managed_node_group_defaults.iam_instance_profile_arn, null) - iam_role_name = try(each.value.iam_role_name, var.self_managed_node_group_defaults.iam_role_name, null) - iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, var.self_managed_node_group_defaults.iam_role_use_name_prefix, true) - iam_role_path = try(each.value.iam_role_path, var.self_managed_node_group_defaults.iam_role_path, null) - iam_role_description = try(each.value.iam_role_description, var.self_managed_node_group_defaults.iam_role_description, "Self managed node group IAM role") - iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, var.self_managed_node_group_defaults.iam_role_permissions_boundary, null) - iam_role_tags = try(each.value.iam_role_tags, var.self_managed_node_group_defaults.iam_role_tags, {}) - iam_role_attach_cni_policy = try(each.value.iam_role_attach_cni_policy, var.self_managed_node_group_defaults.iam_role_attach_cni_policy, true) - # To better understand why this `lookup()` logic is required, see: - # https://github.com/hashicorp/terraform/issues/31646#issuecomment-1217279031 - iam_role_additional_policies = lookup(each.value, "iam_role_additional_policies", lookup(var.self_managed_node_group_defaults, "iam_role_additional_policies", {})) - create_iam_role_policy = try(each.value.create_iam_role_policy, var.self_managed_node_group_defaults.create_iam_role_policy, true) - iam_role_policy_statements = try(each.value.iam_role_policy_statements, var.self_managed_node_group_defaults.iam_role_policy_statements, []) + create_iam_instance_profile = try(each.value.create_iam_instance_profile, null) + iam_instance_profile_arn = try(each.value.iam_instance_profile_arn, null) + iam_role_name = try(each.value.iam_role_name, null) + iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, true) + iam_role_path = try(each.value.iam_role_path, null) + iam_role_description = try(each.value.iam_role_description, null) + iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, null) + iam_role_tags = try(each.value.iam_role_tags, null) + iam_role_attach_cni_policy = try(each.value.iam_role_attach_cni_policy, null) + iam_role_additional_policies = lookup(each.value, "iam_role_additional_policies", null) + create_iam_role_policy = try(each.value.create_iam_role_policy, null) + iam_role_policy_statements = try(each.value.iam_role_policy_statements, null) # Access entry - create_access_entry = try(each.value.create_access_entry, var.self_managed_node_group_defaults.create_access_entry, true) - iam_role_arn = try(each.value.iam_role_arn, var.self_managed_node_group_defaults.iam_role_arn, null) - - # Autoscaling group schedule - create_schedule = try(each.value.create_schedule, var.self_managed_node_group_defaults.create_schedule, true) - schedules = try(each.value.schedules, var.self_managed_node_group_defaults.schedules, {}) + create_access_entry = try(each.value.create_access_entry, null) + iam_role_arn = try(each.value.iam_role_arn, null) # Security group - vpc_security_group_ids = compact(concat([local.node_security_group_id], try(each.value.vpc_security_group_ids, var.self_managed_node_group_defaults.vpc_security_group_ids, []))) - cluster_primary_security_group_id = try(each.value.attach_cluster_primary_security_group, var.self_managed_node_group_defaults.attach_cluster_primary_security_group, false) ? aws_eks_cluster.this[0].vpc_config[0].cluster_security_group_id : null + vpc_security_group_ids = compact(concat([local.node_security_group_id], try(each.value.vpc_security_group_ids, []))) + cluster_primary_security_group_id = try(each.value.attach_cluster_primary_security_group, false) ? aws_eks_cluster.this[0].vpc_config[0].cluster_security_group_id : null + create_security_group = try(each.value.create_security_group, null) + security_group_name = try(each.value.security_group_name, null) + security_group_use_name_prefix = try(each.value.security_group_use_name_prefix, null) + security_group_description = try(each.value.security_group_description, null) + security_group_ingress_rules = try(each.value.security_group_ingress_rules, null) + security_group_egress_rules = try(each.value.security_group_egress_rules, null) + security_group_tags = try(each.value.security_group_tags, null) - tags = merge(var.tags, try(each.value.tags, var.self_managed_node_group_defaults.tags, {})) + tags = merge( + var.tags, + each.value.tags, + ) } diff --git a/outputs.tf b/outputs.tf index 688076f604..95afd4365b 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,4 +1,5 @@ locals { + # https://github.com/aws/containers-roadmap/issues/2038#issuecomment-2278450601 dualstack_oidc_issuer_url = try(replace(replace(aws_eks_cluster.this[0].identity[0].oidc[0].issuer, "https://oidc.eks.", "https://oidc-eks."), ".amazonaws.com/", ".api.aws/"), null) } @@ -83,7 +84,7 @@ output "cluster_primary_security_group_id" { output "cluster_service_cidr" { description = "The CIDR block where Kubernetes pod and service IP addresses are assigned from" - value = var.cluster_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) + 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" { diff --git a/templates/linux_user_data.tpl b/templates/al2_user_data.tpl similarity index 100% rename from templates/linux_user_data.tpl rename to templates/al2_user_data.tpl diff --git a/tests/eks-fargate-profile/README.md b/tests/eks-fargate-profile/README.md index 419f601de8..2696041cea 100644 --- a/tests/eks-fargate-profile/README.md +++ b/tests/eks-fargate-profile/README.md @@ -17,14 +17,14 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.95, < 6.0.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules @@ -33,7 +33,7 @@ Note that this example may create resources which cost money. Run `terraform des | [disabled\_fargate\_profile](#module\_disabled\_fargate\_profile) | ../../modules/fargate-profile | n/a | | [eks](#module\_eks) | ../.. | n/a | | [fargate\_profile](#module\_fargate\_profile) | ../../modules/fargate-profile | n/a | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 6.0 | ## Resources diff --git a/tests/eks-fargate-profile/main.tf b/tests/eks-fargate-profile/main.tf index bf91d522a8..795e736fc7 100644 --- a/tests/eks-fargate-profile/main.tf +++ b/tests/eks-fargate-profile/main.tf @@ -11,9 +11,9 @@ data "aws_availability_zones" "available" { } locals { - name = "ex-${basename(path.cwd)}" - cluster_version = "1.33" - region = "eu-west-1" + name = "ex-${basename(path.cwd)}" + kubernetes_version = "1.33" + region = "eu-west-1" vpc_cidr = "10.0.0.0/16" azs = slice(data.aws_availability_zones.available.names, 0, 3) @@ -32,11 +32,11 @@ locals { module "eks" { source = "../.." - cluster_name = local.name - cluster_version = local.cluster_version - cluster_endpoint_public_access = true + name = local.name + kubernetes_version = local.kubernetes_version + endpoint_public_access = true - cluster_addons = { + addons = { kube-proxy = {} vpc-cni = {} coredns = { @@ -51,14 +51,8 @@ module "eks" { control_plane_subnet_ids = module.vpc.intra_subnets # Fargate profiles use the cluster primary security group so these are not utilized - create_cluster_security_group = false - create_node_security_group = false - - fargate_profile_defaults = { - iam_role_additional_policies = { - additional = aws_iam_policy.additional.arn - } - } + create_security_group = false + create_node_security_group = false fargate_profiles = { example = { @@ -78,6 +72,10 @@ module "eks" { } ] + iam_role_additional_policies = { + additional = aws_iam_policy.additional.arn + } + # Using specific subnets instead of the subnets supplied for the cluster itself subnet_ids = [module.vpc.private_subnets[1]] @@ -125,7 +123,7 @@ module "disabled_fargate_profile" { module "vpc" { source = "terraform-aws-modules/vpc/aws" - version = "~> 5.0" + version = "~> 6.0" name = local.name cidr = local.vpc_cidr diff --git a/tests/eks-fargate-profile/versions.tf b/tests/eks-fargate-profile/versions.tf index 7884c4e1b2..db13b0a8d2 100644 --- a/tests/eks-fargate-profile/versions.tf +++ b/tests/eks-fargate-profile/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.95, < 6.0.0" + version = ">= 6.0" } } } diff --git a/tests/eks-hybrid-nodes/README.md b/tests/eks-hybrid-nodes/README.md index 67bc82c7a9..efe26912ce 100644 --- a/tests/eks-hybrid-nodes/README.md +++ b/tests/eks-hybrid-nodes/README.md @@ -17,8 +17,8 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | | [tls](#requirement\_tls) | >= 4.0 | ## Providers diff --git a/tests/eks-hybrid-nodes/versions.tf b/tests/eks-hybrid-nodes/versions.tf index 20514e1bdd..3c2ec900b1 100644 --- a/tests/eks-hybrid-nodes/versions.tf +++ b/tests/eks-hybrid-nodes/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.95, < 6.0.0" + version = ">= 6.0" } tls = { source = "hashicorp/tls" diff --git a/tests/eks-managed-node-group/README.md b/tests/eks-managed-node-group/README.md index 96a5011da3..15a05fa9e6 100644 --- a/tests/eks-managed-node-group/README.md +++ b/tests/eks-managed-node-group/README.md @@ -17,14 +17,14 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.95, < 6.0.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules @@ -33,11 +33,11 @@ Note that this example may create resources which cost money. Run `terraform des | [aws\_vpc\_cni\_ipv6\_pod\_identity](#module\_aws\_vpc\_cni\_ipv6\_pod\_identity) | terraform-aws-modules/eks-pod-identity/aws | ~> 1.6 | | [disabled\_eks](#module\_disabled\_eks) | ../.. | n/a | | [disabled\_eks\_managed\_node\_group](#module\_disabled\_eks\_managed\_node\_group) | ../../modules/eks-managed-node-group | n/a | -| [ebs\_kms\_key](#module\_ebs\_kms\_key) | terraform-aws-modules/kms/aws | ~> 2.1 | +| [ebs\_kms\_key](#module\_ebs\_kms\_key) | terraform-aws-modules/kms/aws | ~> 4.0 | | [eks](#module\_eks) | ../.. | n/a | | [eks\_managed\_node\_group](#module\_eks\_managed\_node\_group) | ../../modules/eks-managed-node-group | n/a | | [key\_pair](#module\_key\_pair) | terraform-aws-modules/key-pair/aws | ~> 2.0 | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 6.0 | ## Resources diff --git a/tests/eks-managed-node-group/main.tf b/tests/eks-managed-node-group/main.tf index 010a55fa1b..76cd5cbd35 100644 --- a/tests/eks-managed-node-group/main.tf +++ b/tests/eks-managed-node-group/main.tf @@ -13,9 +13,9 @@ data "aws_availability_zones" "available" { } locals { - name = "ex-${replace(basename(path.cwd), "_", "-")}" - cluster_version = "1.33" - region = "eu-west-1" + name = "ex-${replace(basename(path.cwd), "_", "-")}" + kubernetes_version = "1.33" + region = "eu-west-1" vpc_cidr = "10.0.0.0/16" azs = slice(data.aws_availability_zones.available.names, 0, 3) @@ -34,21 +34,17 @@ locals { module "eks" { source = "../.." - cluster_name = local.name - cluster_version = local.cluster_version - cluster_endpoint_public_access = true + name = local.name + kubernetes_version = local.kubernetes_version + endpoint_public_access = true # IPV6 - cluster_ip_family = "ipv6" + ip_family = "ipv6" create_cni_ipv6_iam_policy = true enable_cluster_creator_admin_permissions = true - # Enable EFA support by adding necessary security group rules - # to the shared node security group - enable_efa_support = true - - cluster_addons = { + addons = { coredns = { most_recent = true } @@ -79,11 +75,11 @@ module "eks" { } } - cluster_upgrade_policy = { + upgrade_policy = { support_type = "STANDARD" } - cluster_zonal_shift_config = { + zonal_shift_config = { enabled = true } @@ -91,11 +87,6 @@ module "eks" { subnet_ids = module.vpc.private_subnets control_plane_subnet_ids = module.vpc.intra_subnets - eks_managed_node_group_defaults = { - ami_type = "AL2023_x86_64_STANDARD" - instance_types = ["m6i.large", "m5.large", "m5n.large", "m5zn.large"] - } - eks_managed_node_groups = { # Default node group - as provided by AWS EKS default_node_group = { @@ -114,8 +105,7 @@ module "eks" { placement_group = { create_placement_group = true - # forces the subnet lookup to be restricted to this availability zone - placement_group_az = element(local.azs, 3) + subnet_ids = slice(module.vpc.private_subnets, 0, 1) } # AL2023 node group utilizing new user data format which utilizes nodeadm @@ -135,8 +125,6 @@ module "eks" { kubelet: config: shutdownGracePeriod: 30s - featureGates: - DisableKubeletCloudCredentialProviders: true EOT } ] @@ -238,8 +226,6 @@ module "eks" { kubelet: config: shutdownGracePeriod: 30s - featureGates: - DisableKubeletCloudCredentialProviders: true EOT content_type = "application/node.eks.aws" }] @@ -359,7 +345,7 @@ module "eks" { # This will: # 1. Create a placement group to place the instances close to one another - # 2. Ignore subnets that reside in AZs that do not support the instance type + # 2. Create and attach the necessary security group rules (and security group) # 3. Expose all of the available EFA interfaces on the launch template enable_efa_support = true enable_efa_only = true @@ -476,7 +462,7 @@ module "disabled_eks_managed_node_group" { module "vpc" { source = "terraform-aws-modules/vpc/aws" - version = "~> 5.0" + version = "~> 6.0" name = local.name cidr = local.vpc_cidr @@ -523,7 +509,7 @@ module "aws_vpc_cni_ipv6_pod_identity" { module "ebs_kms_key" { source = "terraform-aws-modules/kms/aws" - version = "~> 2.1" + version = "~> 4.0" description = "Customer managed key to encrypt EKS managed node group volumes" @@ -605,7 +591,7 @@ data "aws_ami" "eks_default" { filter { name = "name" - values = ["amazon-eks-node-al2023-x86_64-standard-${local.cluster_version}-v*"] + values = ["amazon-eks-node-al2023-x86_64-standard-${local.kubernetes_version}-v*"] } } @@ -615,7 +601,7 @@ data "aws_ami" "eks_default_arm" { filter { name = "name" - values = ["amazon-eks-node-al2023-arm64-standard-${local.cluster_version}-v*"] + values = ["amazon-eks-node-al2023-arm64-standard-${local.kubernetes_version}-v*"] } } @@ -625,7 +611,7 @@ data "aws_ami" "eks_default_bottlerocket" { filter { name = "name" - values = ["bottlerocket-aws-k8s-${local.cluster_version}-x86_64-*"] + values = ["bottlerocket-aws-k8s-${local.kubernetes_version}-x86_64-*"] } } diff --git a/tests/eks-managed-node-group/versions.tf b/tests/eks-managed-node-group/versions.tf index 7884c4e1b2..db13b0a8d2 100644 --- a/tests/eks-managed-node-group/versions.tf +++ b/tests/eks-managed-node-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.95, < 6.0.0" + version = ">= 6.0" } } } diff --git a/tests/fast-addons/README.md b/tests/fast-addons/README.md deleted file mode 100644 index cd1be0dcd6..0000000000 --- a/tests/fast-addons/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# Fast Addons - -Refer to https://github.com/terraform-aws-modules/terraform-aws-eks/pull/3214 for additional information. - - - -## Usage - -To provision the provided configurations you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply --auto-approve -``` - -Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 5.95, < 6.0.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [eks](#module\_eks) | ../.. | n/a | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | - -## Resources - -| Name | Type | -|------|------| -| [aws_route_table_association.custom_network](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | -| [aws_subnet.custom_network](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | -| [aws_vpc_ipv4_cidr_block_association.custom_network](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipv4_cidr_block_association) | resource | -| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [access\_entries](#output\_access\_entries) | Map of access entries 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\_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) | IAM role ARN of the EKS cluster | -| [cluster\_iam\_role\_name](#output\_cluster\_iam\_role\_name) | IAM role name of the EKS cluster | -| [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 | -| [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\_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 | - diff --git a/tests/fast-addons/main.tf b/tests/fast-addons/main.tf deleted file mode 100644 index e7a5b9cc55..0000000000 --- a/tests/fast-addons/main.tf +++ /dev/null @@ -1,159 +0,0 @@ -provider "aws" { - region = local.region -} - -locals { - name = "ex-${basename(path.cwd)}" - cluster_version = "1.33" - region = "eu-west-1" - - tags = { - Test = local.name - GithubRepo = "terraform-aws-eks" - GithubOrg = "terraform-aws-modules" - } -} - -################################################################################ -# EKS Module -################################################################################ - -module "eks" { - source = "../.." - - cluster_name = local.name - cluster_version = local.cluster_version - cluster_endpoint_public_access = true - - enable_cluster_creator_admin_permissions = true - - # Disable the default self-managed addons to avoid the penalty of adopting them later - bootstrap_self_managed_addons = false - - # Addons will be provisioned net new via the EKS addon API - cluster_addons = { - coredns = { - most_recent = true - } - eks-pod-identity-agent = { - before_compute = true - most_recent = true - } - kube-proxy = { - most_recent = true - } - vpc-cni = { - most_recent = true - before_compute = true - configuration_values = jsonencode({ - env = { - # Use subnet tags to avoid the need to inject the ENIConfig - # which requires a live API server endpoint which leads to a dependency of: - # Control plane -> API request to create ENIConfig -> VPC CNI addon -> nodes/compute - # With the subnet discovery feature, we can avoid this dependency: - # Control plane -> VPC CNI addon -> nodes/compute - ENABLE_SUBNET_DISCOVERY = "true" - } - }) - } - } - - vpc_id = module.vpc.vpc_id - subnet_ids = module.vpc.private_subnets - - eks_managed_node_groups = { - example = { - instance_types = ["m6i.large"] - - min_size = 2 - max_size = 5 - desired_size = 2 - } - } - - tags = local.tags -} - -################################################################################ -# VPC -################################################################################ - -data "aws_availability_zones" "available" { - # Exclude local zones - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - -locals { - vpc_cidr = "10.0.0.0/16" - azs = slice(data.aws_availability_zones.available.names, 0, 3) -} - -module "vpc" { - source = "terraform-aws-modules/vpc/aws" - version = "~> 5.0" - - name = local.name - cidr = local.vpc_cidr - - azs = local.azs - private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] - public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] - - enable_nat_gateway = true - single_nat_gateway = true - - public_subnet_tags = { - "kubernetes.io/role/elb" = 1 - } - - tags = local.tags -} - -################################################################################ -# Custom Networking -################################################################################ - -locals { - custom_network_vpc_cidr = "10.99.0.0/16" - - custom_network_subnets = [for k, v in local.azs : cidrsubnet(local.custom_network_vpc_cidr, 4, k)] -} - -resource "aws_vpc_ipv4_cidr_block_association" "custom_network" { - vpc_id = module.vpc.vpc_id - cidr_block = local.custom_network_vpc_cidr -} - -resource "aws_subnet" "custom_network" { - count = length(local.custom_network_subnets) - - vpc_id = module.vpc.vpc_id - cidr_block = element(local.custom_network_subnets, count.index) - - tags = merge( - local.tags, - { - # Tag for subnet discovery - "kubernetes.io/role/cni" = 1 - "kubernetes.io/role/internal-elb" = 1 - } - ) - - depends_on = [ - aws_vpc_ipv4_cidr_block_association.custom_network - ] -} - -resource "aws_route_table_association" "custom_network" { - count = length(local.custom_network_subnets) - - subnet_id = element(aws_subnet.custom_network[*].id, count.index) - route_table_id = element(module.vpc.private_route_table_ids, 0) - - depends_on = [ - aws_vpc_ipv4_cidr_block_association.custom_network - ] -} diff --git a/tests/fast-addons/outputs.tf b/tests/fast-addons/outputs.tf deleted file mode 100644 index 9357464c29..0000000000 --- a/tests/fast-addons/outputs.tf +++ /dev/null @@ -1,226 +0,0 @@ -################################################################################ -# Cluster -################################################################################ - -output "cluster_arn" { - description = "The Amazon Resource Name (ARN) of the cluster" - value = module.eks.cluster_arn -} - -output "cluster_certificate_authority_data" { - description = "Base64 encoded certificate data required to communicate with the cluster" - value = module.eks.cluster_certificate_authority_data -} - -output "cluster_endpoint" { - description = "Endpoint for your Kubernetes API server" - value = module.eks.cluster_endpoint -} - -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 = module.eks.cluster_id -} - -output "cluster_name" { - description = "The name of the EKS cluster" - value = module.eks.cluster_name -} - -output "cluster_oidc_issuer_url" { - description = "The URL on the EKS cluster for the OpenID Connect identity provider" - value = module.eks.cluster_oidc_issuer_url -} - -output "cluster_dualstack_oidc_issuer_url" { - description = "Dual-stack compatible URL on the EKS cluster for the OpenID Connect identity provider" - value = module.eks.cluster_dualstack_oidc_issuer_url -} - -output "cluster_platform_version" { - description = "Platform version for the cluster" - value = module.eks.cluster_platform_version -} - -output "cluster_status" { - description = "Status of the EKS cluster. One of `CREATING`, `ACTIVE`, `DELETING`, `FAILED`" - value = module.eks.cluster_status -} - -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 = module.eks.cluster_primary_security_group_id -} - -output "cluster_service_cidr" { - description = "The CIDR block where Kubernetes pod and service IP addresses are assigned from" - value = module.eks.cluster_service_cidr -} - -output "cluster_ip_family" { - description = "The IP family used by the cluster (e.g. `ipv4` or `ipv6`)" - value = module.eks.cluster_ip_family -} - -################################################################################ -# Access Entry -################################################################################ - -output "access_entries" { - description = "Map of access entries created and their attributes" - value = module.eks.access_entries -} - -################################################################################ -# KMS Key -################################################################################ - -output "kms_key_arn" { - description = "The Amazon Resource Name (ARN) of the key" - value = module.eks.kms_key_arn -} - -output "kms_key_id" { - description = "The globally unique identifier for the key" - value = module.eks.kms_key_id -} - -output "kms_key_policy" { - description = "The IAM resource policy set on the key" - value = module.eks.kms_key_policy -} - -################################################################################ -# Security Group -################################################################################ - -output "cluster_security_group_arn" { - description = "Amazon Resource Name (ARN) of the cluster security group" - value = module.eks.cluster_security_group_arn -} - -output "cluster_security_group_id" { - description = "ID of the cluster security group" - value = module.eks.cluster_security_group_id -} - -################################################################################ -# Node Security Group -################################################################################ - -output "node_security_group_arn" { - description = "Amazon Resource Name (ARN) of the node shared security group" - value = module.eks.node_security_group_arn -} - -output "node_security_group_id" { - description = "ID of the node shared security group" - value = module.eks.node_security_group_id -} - -################################################################################ -# IRSA -################################################################################ - -output "oidc_provider" { - description = "The OpenID Connect identity provider (issuer URL without leading `https://`)" - value = module.eks.oidc_provider -} - -output "oidc_provider_arn" { - description = "The ARN of the OIDC Provider if `enable_irsa = true`" - value = module.eks.oidc_provider_arn -} - -output "cluster_tls_certificate_sha1_fingerprint" { - description = "The SHA1 fingerprint of the public key of the cluster's certificate" - value = module.eks.cluster_tls_certificate_sha1_fingerprint -} - -################################################################################ -# IAM Role -################################################################################ - -output "cluster_iam_role_name" { - description = "IAM role name of the EKS cluster" - value = module.eks.cluster_iam_role_name -} - -output "cluster_iam_role_arn" { - description = "IAM role ARN of the EKS cluster" - value = module.eks.cluster_iam_role_arn -} - -output "cluster_iam_role_unique_id" { - description = "Stable and unique string identifying the IAM role" - value = module.eks.cluster_iam_role_unique_id -} - -################################################################################ -# EKS Addons -################################################################################ - -output "cluster_addons" { - description = "Map of attribute maps for all EKS cluster addons enabled" - value = module.eks.cluster_addons -} - -################################################################################ -# EKS Identity Provider -################################################################################ - -output "cluster_identity_providers" { - description = "Map of attribute maps for all EKS identity providers enabled" - value = module.eks.cluster_identity_providers -} - -################################################################################ -# CloudWatch Log Group -################################################################################ - -output "cloudwatch_log_group_name" { - description = "Name of cloudwatch log group created" - value = module.eks.cloudwatch_log_group_name -} - -output "cloudwatch_log_group_arn" { - description = "Arn of cloudwatch log group created" - value = module.eks.cloudwatch_log_group_arn -} - -################################################################################ -# Fargate Profile -################################################################################ - -output "fargate_profiles" { - description = "Map of attribute maps for all EKS Fargate Profiles created" - value = module.eks.fargate_profiles -} - -################################################################################ -# EKS Managed Node Group -################################################################################ - -output "eks_managed_node_groups" { - description = "Map of attribute maps for all EKS managed node groups created" - value = module.eks.eks_managed_node_groups -} - -output "eks_managed_node_groups_autoscaling_group_names" { - description = "List of the autoscaling group names created by EKS managed node groups" - value = module.eks.eks_managed_node_groups_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.eks.self_managed_node_groups -} - -output "self_managed_node_groups_autoscaling_group_names" { - description = "List of the autoscaling group names created by self-managed node groups" - value = module.eks.self_managed_node_groups_autoscaling_group_names -} diff --git a/tests/fast-addons/variables.tf b/tests/fast-addons/variables.tf deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/fast-addons/versions.tf b/tests/fast-addons/versions.tf deleted file mode 100644 index 7884c4e1b2..0000000000 --- a/tests/fast-addons/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.3.2" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 5.95, < 6.0.0" - } - } -} diff --git a/tests/self-managed-node-group/README.md b/tests/self-managed-node-group/README.md index 655025789b..feee069124 100644 --- a/tests/self-managed-node-group/README.md +++ b/tests/self-managed-node-group/README.md @@ -17,14 +17,14 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.95, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.95, < 6.0.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules @@ -32,11 +32,11 @@ Note that this example may create resources which cost money. Run `terraform des |------|--------|---------| | [aws\_vpc\_cni\_ipv4\_pod\_identity](#module\_aws\_vpc\_cni\_ipv4\_pod\_identity) | terraform-aws-modules/eks-pod-identity/aws | ~> 1.6 | | [disabled\_self\_managed\_node\_group](#module\_disabled\_self\_managed\_node\_group) | ../../modules/self-managed-node-group | n/a | -| [ebs\_kms\_key](#module\_ebs\_kms\_key) | terraform-aws-modules/kms/aws | ~> 2.0 | +| [ebs\_kms\_key](#module\_ebs\_kms\_key) | terraform-aws-modules/kms/aws | ~> 4.0 | | [eks](#module\_eks) | ../.. | n/a | | [key\_pair](#module\_key\_pair) | terraform-aws-modules/key-pair/aws | ~> 2.0 | -| [kms](#module\_kms) | terraform-aws-modules/kms/aws | ~> 2.1 | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | +| [kms](#module\_kms) | terraform-aws-modules/kms/aws | ~> 4.0 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 6.0 | ## Resources diff --git a/tests/self-managed-node-group/main.tf b/tests/self-managed-node-group/main.tf index 351985f359..8629039e7d 100644 --- a/tests/self-managed-node-group/main.tf +++ b/tests/self-managed-node-group/main.tf @@ -13,9 +13,9 @@ data "aws_availability_zones" "available" { } locals { - name = "ex-${replace(basename(path.cwd), "_", "-")}" - cluster_version = "1.33" - region = "eu-west-1" + name = "ex-${replace(basename(path.cwd), "_", "-")}" + kubernetes_version = "1.33" + region = "eu-west-1" vpc_cidr = "10.0.0.0/16" azs = slice(data.aws_availability_zones.available.names, 0, 3) @@ -34,28 +34,26 @@ locals { module "eks" { source = "../.." - cluster_name = local.name - cluster_version = local.cluster_version - cluster_endpoint_public_access = true + name = local.name + kubernetes_version = local.kubernetes_version + endpoint_public_access = true enable_cluster_creator_admin_permissions = true - # Enable EFA support by adding necessary security group rules - # to the shared node security group - enable_efa_support = true - - cluster_addons = { + addons = { coredns = { most_recent = true } eks-pod-identity-agent = { - most_recent = true + before_compute = true + most_recent = true } kube-proxy = { most_recent = true } vpc-cni = { - most_recent = true + before_compute = true + most_recent = true pod_identity_association = [{ role_arn = module.aws_vpc_cni_ipv4_pod_identity.iam_role_arn service_account = "aws-node" @@ -69,25 +67,23 @@ module "eks" { # External encryption key create_kms_key = false - cluster_encryption_config = { + encryption_config = { resources = ["secrets"] provider_key_arn = module.kms.key_arn } - self_managed_node_group_defaults = { - ami_type = "AL2023_x86_64_STANDARD" - ami_id = data.aws_ami.eks_default.image_id - - # enable discovery of autoscaling groups by cluster-autoscaler - autoscaling_group_tags = { - "k8s.io/cluster-autoscaler/enabled" : true, - "k8s.io/cluster-autoscaler/${local.name}" : "owned", - } - } - self_managed_node_groups = { # Default node group - as provisioned by the module defaults - default_node_group = {} + default_node_group = { + ami_type = "AL2023_x86_64_STANDARD" + ami_id = data.aws_ami.eks_default.image_id + + # enable discovery of autoscaling groups by cluster-autoscaler + autoscaling_group_tags = { + "k8s.io/cluster-autoscaler/enabled" : true, + "k8s.io/cluster-autoscaler/${local.name}" : "owned", + } + } # Bottlerocket node group bottlerocket = { @@ -153,16 +149,18 @@ module "eks" { spot_allocation_strategy = "capacity-optimized" } - override = [ - { - instance_type = "m5.large" - weighted_capacity = "1" - }, - { - instance_type = "m6i.large" - weighted_capacity = "2" - }, - ] + launch_template = { + override = [ + { + instance_type = "m5.large" + weighted_capacity = "1" + }, + { + instance_type = "m6i.large" + weighted_capacity = "2" + }, + ] + } } } @@ -232,8 +230,6 @@ module "eks" { kubelet: config: shutdownGracePeriod: 30s - featureGates: - DisableKubeletCloudCredentialProviders: true EOT content_type = "application/node.eks.aws" }] @@ -263,28 +259,30 @@ module "eks" { } # ASG configuration - override = [ - { - instance_requirements = { - cpu_manufacturers = ["intel"] - instance_generations = ["current", "previous"] - spot_max_price_percentage_over_lowest_price = 100 - - vcpu_count = { - min = 1 + launch_template = { + override = [ + { + instance_requirements = { + cpu_manufacturers = ["intel"] + instance_generations = ["current", "previous"] + spot_max_price_percentage_over_lowest_price = 100 + + vcpu_count = { + min = 1 + } + + allowed_instance_types = ["t*", "m*"] } - - allowed_instance_types = ["t*", "m*"] } - } - ] + ] + } } } metadata_options = { http_endpoint = "enabled" http_tokens = "required" - http_put_response_hop_limit = 2 + http_put_response_hop_limit = 1 instance_metadata_tags = "disabled" } @@ -334,7 +332,7 @@ module "eks" { # This will: # 1. Create a placement group to place the instances close to one another - # 2. Ignore subnets that reside in AZs that do not support the instance type + # 2. Create and attach the necessary security group rules (and security group) # 3. Expose all of the available EFA interfaces on the launch template enable_efa_support = true enable_efa_only = true @@ -378,7 +376,7 @@ module "disabled_self_managed_node_group" { module "vpc" { source = "terraform-aws-modules/vpc/aws" - version = "~> 5.0" + version = "~> 6.0" name = local.name cidr = local.vpc_cidr @@ -420,7 +418,7 @@ data "aws_ami" "eks_default" { filter { name = "name" - values = ["amazon-eks-node-al2023-x86_64-standard-${local.cluster_version}-v*"] + values = ["amazon-eks-node-al2023-x86_64-standard-${local.kubernetes_version}-v*"] } } @@ -430,7 +428,7 @@ data "aws_ami" "eks_default_bottlerocket" { filter { name = "name" - values = ["bottlerocket-aws-k8s-${local.cluster_version}-x86_64-*"] + values = ["bottlerocket-aws-k8s-${local.kubernetes_version}-x86_64-*"] } } @@ -446,7 +444,7 @@ module "key_pair" { module "ebs_kms_key" { source = "terraform-aws-modules/kms/aws" - version = "~> 2.0" + version = "~> 4.0" description = "Customer managed key to encrypt EKS managed node group volumes" @@ -470,7 +468,7 @@ module "ebs_kms_key" { module "kms" { source = "terraform-aws-modules/kms/aws" - version = "~> 2.1" + version = "~> 4.0" aliases = ["eks/${local.name}"] description = "${local.name} cluster encryption key" diff --git a/tests/self-managed-node-group/versions.tf b/tests/self-managed-node-group/versions.tf index 7884c4e1b2..db13b0a8d2 100644 --- a/tests/self-managed-node-group/versions.tf +++ b/tests/self-managed-node-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.95, < 6.0.0" + version = ">= 6.0" } } } diff --git a/tests/user-data/README.md b/tests/user-data/README.md index 88988ab2f4..684a3a7648 100644 --- a/tests/user-data/README.md +++ b/tests/user-data/README.md @@ -17,7 +17,7 @@ $ terraform apply --auto-approve | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.2 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | | [local](#requirement\_local) | >= 2.4 | ## Providers diff --git a/tests/user-data/main.tf b/tests/user-data/main.tf index 9a55b3cf77..835d9fc347 100644 --- a/tests/user-data/main.tf +++ b/tests/user-data/main.tf @@ -15,13 +15,15 @@ locals { module "eks_mng_al2_disabled" { source = "../../modules/_user_data" - create = false + ami_type = "AL2_x86_64" + create = false } module "eks_mng_al2_no_op" { source = "../../modules/_user_data" # Hard requirement + ami_type = "AL2_x86_64" cluster_service_cidr = local.cluster_service_cidr } @@ -29,6 +31,7 @@ module "eks_mng_al2_additional" { source = "../../modules/_user_data" # Hard requirement + ami_type = "AL2_x86_64" cluster_service_cidr = local.cluster_service_cidr pre_bootstrap_user_data = <<-EOT @@ -39,6 +42,7 @@ module "eks_mng_al2_additional" { module "eks_mng_al2_custom_ami" { source = "../../modules/_user_data" + ami_type = "AL2_x86_64" cluster_name = local.name cluster_endpoint = local.cluster_endpoint cluster_auth_base64 = local.cluster_auth_base64 @@ -60,6 +64,7 @@ module "eks_mng_al2_custom_ami" { module "eks_mng_al2_custom_ami_ipv6" { source = "../../modules/_user_data" + ami_type = "AL2_x86_64" cluster_name = local.name cluster_endpoint = local.cluster_endpoint cluster_auth_base64 = local.cluster_auth_base64 @@ -82,6 +87,7 @@ module "eks_mng_al2_custom_ami_ipv6" { module "eks_mng_al2_custom_template" { source = "../../modules/_user_data" + ami_type = "AL2_x86_64" cluster_name = local.name cluster_endpoint = local.cluster_endpoint cluster_auth_base64 = local.cluster_auth_base64 @@ -364,12 +370,15 @@ module "self_mng_al2_no_op" { is_eks_managed_node_group = false # Hard requirement + ami_type = "AL2_x86_64" cluster_service_cidr = local.cluster_service_cidr } module "self_mng_al2_bootstrap" { source = "../../modules/_user_data" + ami_type = "AL2_x86_64" + enable_bootstrap_user_data = true is_eks_managed_node_group = false @@ -393,6 +402,8 @@ module "self_mng_al2_bootstrap" { module "self_mng_al2_bootstrap_ipv6" { source = "../../modules/_user_data" + ami_type = "AL2_x86_64" + enable_bootstrap_user_data = true is_eks_managed_node_group = false @@ -417,6 +428,8 @@ module "self_mng_al2_bootstrap_ipv6" { module "self_mng_al2_custom_template" { source = "../../modules/_user_data" + ami_type = "AL2_x86_64" + enable_bootstrap_user_data = true is_eks_managed_node_group = false diff --git a/tests/user-data/versions.tf b/tests/user-data/versions.tf index 31969d6c02..0d69a257f0 100644 --- a/tests/user-data/versions.tf +++ b/tests/user-data/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 1.3.2" + required_version = ">= 1.5.7" required_providers { local = { diff --git a/variables.tf b/variables.tf index b4881b2988..39dd07b992 100644 --- a/variables.tf +++ b/variables.tf @@ -4,41 +4,47 @@ variable "create" { default = true } -variable "tags" { - description = "A map of tags to add to all resources" - type = map(string) - default = {} -} - variable "prefix_separator" { description = "The separator to use between the prefix and the generated timestamp for resource names" 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 +} + +variable "tags" { + description = "A map of tags to add to all resources" + type = map(string) + default = {} +} + ################################################################################ # Cluster ################################################################################ -variable "cluster_name" { +variable "name" { description = "Name of the EKS cluster" type = string default = "" } -variable "cluster_version" { - description = "Kubernetes `
delete = optional(string)
})