diff --git a/README.md b/README.md index 5a074b966d..8f7cc2727b 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,124 @@ Please note that we strive to provide a comprehensive suite of documentation for ## Usage +### EKS Auto Mode + +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 20.31" + + cluster_name = "example" + cluster_version = "1.31" + + # 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 + + cluster_compute_config = { + enabled = true + node_pools = ["general-purpose"] + } + + vpc_id = "vpc-1234556abcdef" + subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] + + tags = { + Environment = "dev" + Terraform = "true" + } +} +``` + +### EKS Hybrid Nodes + +```hcl +locals { + # RFC 1918 IP ranges supported + remote_network_cidr = "172.16.0.0/16" + remote_node_cidr = cidrsubnet(local.remote_network_cidr, 2, 0) + remote_pod_cidr = cidrsubnet(local.remote_network_cidr, 2, 1) +} + +# 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" + + tags = { + Environment = "dev" + Terraform = "true" + } +} + +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 20.31" + + cluster_name = "example" + cluster_version = "1.31" + + cluster_addons = { + coredns = {} + eks-pod-identity-agent = {} + kube-proxy = {} + } + + # 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 + + create_node_security_group = false + cluster_security_group_additional_rules = { + hybrid-all = { + cidr_blocks = [local.remote_network_cidr] + description = "Allow all traffic from remote node/pod network" + from_port = 0 + to_port = 0 + protocol = "all" + type = "ingress" + } + } + + # Optional + cluster_compute_config = { + enabled = true + node_pools = ["system"] + } + + access_entries = { + hybrid-node-role = { + principal_arn = module.eks_hybrid_node_role.arn + type = "HYBRID_LINUX" + } + } + + vpc_id = "vpc-1234556abcdef" + subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] + + cluster_remote_network_config = { + remote_node_networks = { + cidrs = [local.remote_node_cidr] + } + # Required if running webhooks on Hybrid nodes + remote_pod_networks = { + cidrs = [local.remote_pod_cidr] + } + } + + tags = { + Environment = "dev" + Terraform = "true" + } +} +``` + +### EKS Managed Node Group + ```hcl module "eks" { source = "terraform-aws-modules/eks/aws" @@ -33,8 +151,7 @@ module "eks" { cluster_name = "my-cluster" cluster_version = "1.31" - cluster_endpoint_public_access = true - + bootstrap_self_managed_addons = false cluster_addons = { coredns = {} eks-pod-identity-agent = {} @@ -42,6 +159,12 @@ module "eks" { 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"] @@ -63,14 +186,30 @@ module "eks" { } } - # Cluster access entry - # To add the current caller identity as an administrator - enable_cluster_creator_admin_permissions = true + 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" + principal_arn = "arn:aws:iam::123456789012:role/something" policy_associations = { example = { @@ -83,20 +222,9 @@ module "eks" { } } } - - 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 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. - ### 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` @@ -133,17 +261,31 @@ module "eks" { eks_managed_node_groups = { example = { + # The EKS AL2023 NVIDIA AMI provides all of the necessary components + # for accelerated workloads w/ EFA + ami_type = "AL2023_x86_64_NVIDIA" instance_types = ["p5.48xlarge"] # Exposes all EFA interfaces on the launch template created by the node group(s) # This would expose all 32 EFA interfaces for the p5.48xlarge instance type enable_efa_support = true - pre_bootstrap_user_data = <<-EOT - # Mount NVME instance store volumes since they are typically - # available on instance types that support EFA - setup-local-disks raid0 - EOT + # Mount instance store volumes in RAID-0 for kubelet and containerd + # https://github.com/awslabs/amazon-eks-ami/blob/master/doc/USER_GUIDE.md#raid-0-for-kubelet-and-containerd-raid0 + cloudinit_pre_nodeadm = [ + { + content_type = "application/node.eks.aws" + content = <<-EOT + --- + apiVersion: node.eks.aws/v1alpha1 + kind: NodeConfig + spec: + instance: + localStorage: + strategy: RAID0 + EOT + } + ] # EFA should only be enabled when connecting 2 or more nodes # Do not use EFA on a single node workload @@ -157,9 +299,11 @@ module "eks" { ## Examples -- [EKS Managed Node Group](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/eks-managed-node-group): EKS Cluster using EKS managed node groups +- [EKS Auto Mode](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/eks-auto-mode): EKS Cluster with EKS Auto Mode +- [EKS Hybrid Nodes](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/eks-hybrid-nodes): EKS Cluster with EKS Hybrid nodes +- [EKS Managed Node Group](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/eks-managed-node-group): EKS Cluster with EKS managed node group(s) - [Karpenter](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/karpenter): EKS Cluster with [Karpenter](https://karpenter.sh/) provisioned for intelligent data plane management -- [Self Managed Node Group](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/self-managed-node-group): EKS Cluster using self-managed node groups +- [Self Managed Node Group](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples/self-managed-node-group): EKS Cluster with self-managed node group(s) ## Contributing @@ -174,7 +318,7 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.75 | +| [aws](#requirement\_aws) | >= 5.79 | | [time](#requirement\_time) | >= 0.9 | | [tls](#requirement\_tls) | >= 3.0 | @@ -182,7 +326,7 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.75 | +| [aws](#provider\_aws) | >= 5.79 | | [time](#provider\_time) | >= 0.9 | | [tls](#provider\_tls) | >= 3.0 | @@ -210,9 +354,14 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [aws_iam_openid_connect_provider.oidc_provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider) | resource | | [aws_iam_policy.cluster_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.cni_ipv6_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.eks_auto_custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.eks_auto](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.cluster_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.eks_auto](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.eks_auto_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.eks_auto_custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_security_group.cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_security_group.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | @@ -223,6 +372,8 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [aws_eks_addon_version.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks_addon_version) | data source | | [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.cni_ipv6_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.eks_auto_custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.node_assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_session_context.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_session_context) | data source | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | | [tls_certificate.this](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/data-sources/certificate) | data source | @@ -242,6 +393,7 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [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)` |
[
"audit",
"api",
"authenticator"
]
| 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` |
{
"resources": [
"secrets"
]
}
| 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 | @@ -255,6 +407,7 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [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 | @@ -274,8 +427,9 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [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 a an IAM role is created or to use an existing IAM role | `bool` | `true` | 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 | | [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 | @@ -285,13 +439,15 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [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\_node\_custom\_tags\_permissions](#input\_enable\_node\_custom\_tags\_permissions) | Determines whether to enable permissions for custom tags for the EKS Auto node IAM role | `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 | | [iam\_role\_additional\_policies](#input\_iam\_role\_additional\_policies) | Additional policies to be added to the IAM role | `map(string)` | `{}` | no | | [iam\_role\_arn](#input\_iam\_role\_arn) | Existing IAM role ARN for the cluster. Required if `create_iam_role` is set to `false` | `string` | `null` | no | | [iam\_role\_description](#input\_iam\_role\_description) | Description of the role | `string` | `null` | no | | [iam\_role\_name](#input\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no | -| [iam\_role\_path](#input\_iam\_role\_path) | Cluster IAM role path | `string` | `null` | no | +| [iam\_role\_path](#input\_iam\_role\_path) | The IAM role path | `string` | `null` | no | | [iam\_role\_permissions\_boundary](#input\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | | [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no | | [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no | @@ -306,6 +462,14 @@ 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 | +| [node\_iam\_role\_additional\_policies](#input\_node\_iam\_role\_additional\_policies) | Additional policies to be added to the EKS Auto node IAM role | `map(string)` | `{}` | no | +| [node\_iam\_role\_description](#input\_node\_iam\_role\_description) | Description of the EKS Auto node IAM role | `string` | `null` | no | +| [node\_iam\_role\_name](#input\_node\_iam\_role\_name) | Name to use on the EKS Auto node IAM role created | `string` | `null` | no | +| [node\_iam\_role\_path](#input\_node\_iam\_role\_path) | The EKS Auto node IAM role path | `string` | `null` | no | +| [node\_iam\_role\_permissions\_boundary](#input\_node\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the EKS Auto node IAM role | `string` | `null` | no | +| [node\_iam\_role\_policy\_statements](#input\_node\_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 | +| [node\_iam\_role\_tags](#input\_node\_iam\_role\_tags) | A map of additional tags to add to the EKS Auto node IAM role created | `map(string)` | `{}` | no | +| [node\_iam\_role\_use\_name\_prefix](#input\_node\_iam\_role\_use\_name\_prefix) | Determines whether the EKS Auto node IAM role name (`node_iam_role_name`) is used as a prefix | `bool` | `true` | no | | [node\_security\_group\_additional\_rules](#input\_node\_security\_group\_additional\_rules) | List of additional security group rules to add to the node security group created. Set `source_cluster_security_group = true` inside rules to set the `cluster_security_group` as source | `any` | `{}` | 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 | @@ -336,8 +500,8 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [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\_arn](#output\_cluster\_iam\_role\_arn) | Cluster IAM role ARN | +| [cluster\_iam\_role\_name](#output\_cluster\_iam\_role\_name) | Cluster IAM role name | | [cluster\_iam\_role\_unique\_id](#output\_cluster\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [cluster\_id](#output\_cluster\_id) | The ID of the EKS cluster. Note: currently a value is returned only for local EKS clusters created on Outposts | | [cluster\_identity\_providers](#output\_cluster\_identity\_providers) | Map of attribute maps for all EKS identity providers enabled | @@ -358,6 +522,9 @@ We are grateful to the community for contributing bugfixes and improvements! Ple | [kms\_key\_arn](#output\_kms\_key\_arn) | The Amazon Resource Name (ARN) of the key | | [kms\_key\_id](#output\_kms\_key\_id) | The globally unique identifier for the key | | [kms\_key\_policy](#output\_kms\_key\_policy) | The IAM resource policy set on the key | +| [node\_iam\_role\_arn](#output\_node\_iam\_role\_arn) | EKS Auto node IAM role ARN | +| [node\_iam\_role\_name](#output\_node\_iam\_role\_name) | EKS Auto node IAM role name | +| [node\_iam\_role\_unique\_id](#output\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [node\_security\_group\_arn](#output\_node\_security\_group\_arn) | Amazon Resource Name (ARN) of the node shared security group | | [node\_security\_group\_id](#output\_node\_security\_group\_id) | ID of the node shared security group | | [oidc\_provider](#output\_oidc\_provider) | The OpenID Connect identity provider (issuer URL without leading `https://`) | diff --git a/examples/eks-auto-mode/README.md b/examples/eks-auto-mode/README.md new file mode 100644 index 0000000000..d12605de93 --- /dev/null +++ b/examples/eks-auto-mode/README.md @@ -0,0 +1,96 @@ +# EKS Auto Mode + +## Usage + +To provision the provided configurations you need to execute: + +```bash +terraform init +terraform plan +terraform apply --auto-approve +``` + +Once the cluster has finished provisioning, you can use the `kubectl` command to interact with the cluster. For example, to deploy a sample deployment and see EKS Auto Mode in action, run: + +```bash +aws eks update-kubeconfig --name $(terraform output -raw cluster_name) +kubectl apply -f deployment.yaml +``` + +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.79 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 5.79 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [disabled\_eks](#module\_disabled\_eks) | ../.. | n/a | +| [eks](#module\_eks) | ../.. | n/a | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | + +## Resources + +| Name | Type | +|------|------| +| [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) | Cluster IAM role ARN | +| [cluster\_iam\_role\_name](#output\_cluster\_iam\_role\_name) | Cluster IAM role name | +| [cluster\_iam\_role\_unique\_id](#output\_cluster\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | +| [cluster\_id](#output\_cluster\_id) | The ID of the EKS cluster. Note: currently a value is returned only for local EKS clusters created on Outposts | +| [cluster\_identity\_providers](#output\_cluster\_identity\_providers) | Map of attribute maps for all EKS identity providers enabled | +| [cluster\_ip\_family](#output\_cluster\_ip\_family) | The IP family used by the cluster (e.g. `ipv4` or `ipv6`) | +| [cluster\_name](#output\_cluster\_name) | The name of the EKS cluster | +| [cluster\_oidc\_issuer\_url](#output\_cluster\_oidc\_issuer\_url) | The URL on the EKS cluster for the OpenID Connect identity provider | +| [cluster\_platform\_version](#output\_cluster\_platform\_version) | Platform version for the cluster | +| [cluster\_primary\_security\_group\_id](#output\_cluster\_primary\_security\_group\_id) | Cluster security group that was created by Amazon EKS for the cluster. Managed node groups use this security group for control-plane-to-data-plane communication. Referred to as 'Cluster security group' in the EKS console | +| [cluster\_security\_group\_arn](#output\_cluster\_security\_group\_arn) | Amazon Resource Name (ARN) of the cluster security group | +| [cluster\_security\_group\_id](#output\_cluster\_security\_group\_id) | ID of the cluster security group | +| [cluster\_service\_cidr](#output\_cluster\_service\_cidr) | The CIDR block where Kubernetes pod and service IP addresses are assigned from | +| [cluster\_status](#output\_cluster\_status) | Status of the EKS cluster. One of `CREATING`, `ACTIVE`, `DELETING`, `FAILED` | +| [cluster\_tls\_certificate\_sha1\_fingerprint](#output\_cluster\_tls\_certificate\_sha1\_fingerprint) | The SHA1 fingerprint of the public key of the cluster's certificate | +| [eks\_managed\_node\_groups](#output\_eks\_managed\_node\_groups) | Map of attribute maps for all EKS managed node groups created | +| [eks\_managed\_node\_groups\_autoscaling\_group\_names](#output\_eks\_managed\_node\_groups\_autoscaling\_group\_names) | List of the autoscaling group names created by EKS managed node groups | +| [fargate\_profiles](#output\_fargate\_profiles) | Map of attribute maps for all EKS Fargate Profiles created | +| [kms\_key\_arn](#output\_kms\_key\_arn) | The Amazon Resource Name (ARN) of the key | +| [kms\_key\_id](#output\_kms\_key\_id) | The globally unique identifier for the key | +| [kms\_key\_policy](#output\_kms\_key\_policy) | The IAM resource policy set on the key | +| [node\_iam\_role\_arn](#output\_node\_iam\_role\_arn) | EKS Auto node IAM role ARN | +| [node\_iam\_role\_name](#output\_node\_iam\_role\_name) | EKS Auto node IAM role name | +| [node\_iam\_role\_unique\_id](#output\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | +| [node\_security\_group\_arn](#output\_node\_security\_group\_arn) | Amazon Resource Name (ARN) of the node shared security group | +| [node\_security\_group\_id](#output\_node\_security\_group\_id) | ID of the node shared security group | +| [oidc\_provider](#output\_oidc\_provider) | The OpenID Connect identity provider (issuer URL without leading `https://`) | +| [oidc\_provider\_arn](#output\_oidc\_provider\_arn) | The ARN of the OIDC Provider if `enable_irsa = true` | +| [self\_managed\_node\_groups](#output\_self\_managed\_node\_groups) | Map of attribute maps for all self managed node groups created | +| [self\_managed\_node\_groups\_autoscaling\_group\_names](#output\_self\_managed\_node\_groups\_autoscaling\_group\_names) | List of the autoscaling group names created by self-managed node groups | + diff --git a/examples/eks-auto-mode/deployment.yaml b/examples/eks-auto-mode/deployment.yaml new file mode 100644 index 0000000000..a49fc35f4c --- /dev/null +++ b/examples/eks-auto-mode/deployment.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: inflate +spec: + replicas: 3 + selector: + matchLabels: + app: inflate + template: + metadata: + labels: + app: inflate + spec: + terminationGracePeriodSeconds: 0 + containers: + - name: inflate + image: public.ecr.aws/eks-distro/kubernetes/pause:3.10 + resources: + requests: + cpu: 1 diff --git a/examples/eks-auto-mode/main.tf b/examples/eks-auto-mode/main.tf new file mode 100644 index 0000000000..756437d297 --- /dev/null +++ b/examples/eks-auto-mode/main.tf @@ -0,0 +1,86 @@ +provider "aws" { + region = local.region +} + +data "aws_availability_zones" "available" { + # Exclude local zones + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +locals { + name = "ex-${basename(path.cwd)}" + cluster_version = "1.31" + region = "us-west-2" + + vpc_cidr = "10.0.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 3) + + 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 + + cluster_compute_config = { + enabled = true + node_pools = ["general-purpose"] + } + + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.private_subnets + + tags = local.tags +} + +module "disabled_eks" { + source = "../.." + + create = false +} + +################################################################################ +# Supporting Resources +################################################################################ + +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)] + intra_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 52)] + + enable_nat_gateway = true + single_nat_gateway = true + + public_subnet_tags = { + "kubernetes.io/role/elb" = 1 + } + + private_subnet_tags = { + "kubernetes.io/role/internal-elb" = 1 + } + + tags = local.tags +} diff --git a/tests/fargate-profile/outputs.tf b/examples/eks-auto-mode/outputs.tf similarity index 92% rename from tests/fargate-profile/outputs.tf rename to examples/eks-auto-mode/outputs.tf index 9357464c29..9ed8c27220 100644 --- a/tests/fargate-profile/outputs.tf +++ b/examples/eks-auto-mode/outputs.tf @@ -142,12 +142,12 @@ output "cluster_tls_certificate_sha1_fingerprint" { ################################################################################ output "cluster_iam_role_name" { - description = "IAM role name of the EKS cluster" + description = "Cluster IAM role name" value = module.eks.cluster_iam_role_name } output "cluster_iam_role_arn" { - description = "IAM role ARN of the EKS cluster" + description = "Cluster IAM role ARN" value = module.eks.cluster_iam_role_arn } @@ -156,6 +156,25 @@ output "cluster_iam_role_unique_id" { value = module.eks.cluster_iam_role_unique_id } +################################################################################ +# EKS Auto Node IAM Role +################################################################################ + +output "node_iam_role_name" { + description = "EKS Auto node IAM role name" + value = module.eks.node_iam_role_name +} + +output "node_iam_role_arn" { + description = "EKS Auto node IAM role ARN" + value = module.eks.node_iam_role_arn +} + +output "node_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.eks.node_iam_role_unique_id +} + ################################################################################ # EKS Addons ################################################################################ diff --git a/tests/fargate-profile/variables.tf b/examples/eks-auto-mode/variables.tf similarity index 100% rename from tests/fargate-profile/variables.tf rename to examples/eks-auto-mode/variables.tf diff --git a/tests/fargate-profile/versions.tf b/examples/eks-auto-mode/versions.tf similarity index 82% rename from tests/fargate-profile/versions.tf rename to examples/eks-auto-mode/versions.tf index 0099e6baaf..5b36c201aa 100644 --- a/tests/fargate-profile/versions.tf +++ b/examples/eks-auto-mode/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } } } diff --git a/examples/eks-hybrid-nodes/.gitignore b/examples/eks-hybrid-nodes/.gitignore new file mode 100644 index 0000000000..3a7d063a21 --- /dev/null +++ b/examples/eks-hybrid-nodes/.gitignore @@ -0,0 +1,2 @@ +*.pem +*.sh diff --git a/examples/eks-hybrid-nodes/README.md b/examples/eks-hybrid-nodes/README.md new file mode 100644 index 0000000000..df32f7d8ac --- /dev/null +++ b/examples/eks-hybrid-nodes/README.md @@ -0,0 +1,82 @@ +# EKS Hybrid Node IAM Role + +## Usage + +> [!NOTE] +> The [Packer CLI](https://developer.hashicorp.com/packer/tutorials/docker-get-started/get-started-install-cli) is required to build a custom AMI for the Hybrid node used in the example. + +To provision the provided configurations you need to execute: + +```bash +terraform init +terraform apply -target=module.remote_node_vpc -target=local_file.key_pem --auto-approve +cd ami && packer build -var 'ssh_keypair_name=hybrid-node' -var 'ssh_private_key_file=../key.pem' . && cd - +terraform apply --auto-approve +./join.sh +``` + +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.79 | +| [helm](#requirement\_helm) | >= 2.16 | +| [http](#requirement\_http) | >= 3.4 | +| [local](#requirement\_local) | >= 2.5 | +| [tls](#requirement\_tls) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 5.79 | +| [aws.remote](#provider\_aws.remote) | >= 5.79 | +| [helm](#provider\_helm) | >= 2.16 | +| [http](#provider\_http) | >= 3.4 | +| [local](#provider\_local) | >= 2.5 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [eks](#module\_eks) | ../.. | n/a | +| [eks\_hybrid\_node\_role](#module\_eks\_hybrid\_node\_role) | ../../modules/hybrid-node-role | n/a | +| [key\_pair](#module\_key\_pair) | terraform-aws-modules/key-pair/aws | ~> 2.0 | +| [remote\_node\_vpc](#module\_remote\_node\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | + +## Resources + +| Name | Type | +|------|------| +| [aws_instance.hybrid_node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | +| [aws_route.peer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.remote_node_private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.remote_node_public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_security_group.remote_node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_ssm_activation.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_activation) | resource | +| [aws_vpc_peering_connection.remote_node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_peering_connection) | resource | +| [aws_vpc_peering_connection_accepter.peer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_peering_connection_accepter) | resource | +| [aws_vpc_security_group_egress_rule.remote_node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | +| [aws_vpc_security_group_ingress_rule.remote_node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | +| [helm_release.cilium](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource | +| [local_file.join](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.key_pem](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.key_pub_pem](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [aws_ami.hybrid_node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | +| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | +| [aws_availability_zones.remote](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | +| [http_http.icanhazip](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) | data source | + +## Inputs + +No inputs. + +## Outputs + +No outputs. + diff --git a/examples/eks-hybrid-nodes/ami/amazon-eks-ubuntu.pkr.hcl b/examples/eks-hybrid-nodes/ami/amazon-eks-ubuntu.pkr.hcl new file mode 100644 index 0000000000..02c54ea647 --- /dev/null +++ b/examples/eks-hybrid-nodes/ami/amazon-eks-ubuntu.pkr.hcl @@ -0,0 +1,320 @@ +locals { + timestamp = regex_replace(timestamp(), "[- TZ:]", "") + + ami_name = "${var.ami_name_prefix}-${var.eks_version}-amd64-${local.timestamp}" + + tags = { + SourceAMI = "{{ .SourceAMI }}" + Name = local.ami_name + Architecture = "amd64" + } +} + +data "amazon-parameterstore" "this" { + name = "/aws/service/canonical/ubuntu/server-minimal/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id" + region = var.region +} + +################################################################################ +# EBS Source +################################################################################ + +source "amazon-ebs" "this" { + + # AMI Configuration + dynamic "ami_block_device_mappings" { + for_each = var.ami_block_device_mappings + + content { + delete_on_termination = try(ami_block_device_mappings.value.delete_on_termination, true) + device_name = try(ami_block_device_mappings.value.device_name, null) + encrypted = try(ami_block_device_mappings.value.encrypted, null) + iops = try(ami_block_device_mappings.value.iops, null) + no_device = try(ami_block_device_mappings.value.no_device, null) + snapshot_id = try(ami_block_device_mappings.value.snapshot_id, null) + throughput = try(ami_block_device_mappings.value.throughput, null) + virtual_name = try(ami_block_device_mappings.value.virtual_name, null) + volume_size = try(ami_block_device_mappings.value.volume_size, 4) + volume_type = try(ami_block_device_mappings.value.volume_type, "gp3") + kms_key_id = try(ami_block_device_mappings.value.kms_key_id, null) + } + } + + ami_description = var.ami_description + ami_groups = var.ami_groups + ami_name = local.ami_name + ami_org_arns = var.ami_org_arns + ami_ou_arns = var.ami_ou_arns + ami_regions = var.ami_regions + ami_users = var.ami_users + ami_virtualization_type = var.ami_virtualization_type + deprecate_at = var.deprecate_at + ena_support = var.ena_support + encrypt_boot = var.encrypt_boot + force_deregister = var.force_deregister + force_delete_snapshot = var.force_delete_snapshot + imds_support = var.imds_support + kms_key_id = var.kms_key_id + + dynamic "launch_block_device_mappings" { + for_each = length(var.launch_block_device_mappings) > 0 ? var.launch_block_device_mappings : var.ami_block_device_mappings + + content { + delete_on_termination = try(launch_block_device_mappings.value.delete_on_termination, true) + device_name = try(launch_block_device_mappings.value.device_name, null) + encrypted = try(launch_block_device_mappings.value.encrypted, null) + iops = try(launch_block_device_mappings.value.iops, null) + no_device = try(launch_block_device_mappings.value.no_device, null) + snapshot_id = try(launch_block_device_mappings.value.snapshot_id, null) + throughput = try(launch_block_device_mappings.value.throughput, null) + virtual_name = try(launch_block_device_mappings.value.virtual_name, null) + volume_size = try(launch_block_device_mappings.value.volume_size, 4) + volume_type = try(launch_block_device_mappings.value.volume_type, "gp3") + } + } + + region_kms_key_ids = var.region_kms_key_ids + run_volume_tags = var.run_volume_tags + skip_create_ami = var.skip_create_ami + skip_region_validation = var.skip_region_validation + skip_save_build_region = var.skip_save_build_region + sriov_support = var.sriov_support + snapshot_groups = var.snapshot_groups + snapshot_tags = var.snapshot_tags + snapshot_users = var.snapshot_users + tags = merge(local.tags, var.tags) + + # Access Configuration + access_key = var.access_key + + dynamic "assume_role" { + for_each = length(var.assume_role) > 0 ? [var.assume_role] : [] + + content { + duration_seconds = try(assume_role.value.duration_seconds, null) + external_id = try(assume_role.value.external_id, null) + policy = try(assume_role.value.policy, null) + policy_arns = try(assume_role.value.policy_arns, null) + role_arn = try(assume_role.value.role_arn, null) + session_name = try(assume_role.value.session_name, null) + tag = try(assume_role.value.tag, null) + transitive_tag_keys = try(assume_role.value.transitive_tag_keys, null) + } + } + + dynamic "aws_polling" { + for_each = length(var.aws_polling) > 0 ? [var.aws_polling] : [] + + content { + delay_seconds = try(aws_polling.value.delay_seconds, null) + max_attempts = try(aws_polling.value.max_attempts, null) + } + } + + custom_endpoint_ec2 = var.custom_endpoint_ec2 + decode_authorization_messages = var.decode_authorization_messages + insecure_skip_tls_verify = var.insecure_skip_tls_verify + max_retries = var.max_retries + mfa_code = var.mfa_code + profile = var.profile + region = var.region + secret_key = var.secret_key + shared_credentials_file = var.shared_credentials_file + skip_credential_validation = var.skip_credential_validation + skip_metadata_api_check = var.skip_metadata_api_check + token = var.token + + # Communicator + communicator = var.communicator + pause_before_connecting = var.pause_before_connecting + ssh_agent_auth = var.ssh_agent_auth + ssh_bastion_agent_auth = var.ssh_bastion_agent_auth + ssh_bastion_certificate_file = var.ssh_bastion_certificate_file + ssh_bastion_host = var.ssh_bastion_host + ssh_bastion_interactive = var.ssh_bastion_interactive + ssh_bastion_password = var.ssh_bastion_password + ssh_bastion_port = var.ssh_bastion_port + ssh_bastion_private_key_file = var.ssh_bastion_private_key_file + ssh_bastion_username = var.ssh_bastion_username + ssh_ciphers = var.ssh_ciphers + ssh_certificate_file = var.ssh_certificate_file + ssh_clear_authorized_keys = var.ssh_clear_authorized_keys + ssh_disable_agent_forwarding = var.ssh_disable_agent_forwarding + ssh_file_transfer_method = var.ssh_file_transfer_method + ssh_handshake_attempts = var.ssh_handshake_attempts + ssh_host = var.ssh_host + ssh_interface = var.ssh_interface # "public_dns" + ssh_keep_alive_interval = var.ssh_keep_alive_interval + ssh_key_exchange_algorithms = var.ssh_key_exchange_algorithms + ssh_keypair_name = var.ssh_keypair_name + ssh_local_tunnels = var.ssh_local_tunnels + ssh_password = var.ssh_password + ssh_port = var.ssh_port + ssh_private_key_file = var.ssh_private_key_file + ssh_proxy_host = var.ssh_proxy_host + ssh_proxy_password = var.ssh_proxy_password + ssh_proxy_port = var.ssh_proxy_port + ssh_proxy_username = var.ssh_proxy_username + ssh_pty = var.ssh_pty + ssh_read_write_timeout = var.ssh_read_write_timeout + ssh_remote_tunnels = var.ssh_remote_tunnels + ssh_timeout = var.ssh_timeout + ssh_username = var.ssh_username + temporary_key_pair_bits = var.temporary_key_pair_bits + temporary_key_pair_type = var.temporary_key_pair_type + + # Run Configuration + associate_public_ip_address = var.associate_public_ip_address + capacity_reservation_preference = var.capacity_reservation_preference + capacity_reservation_group_arn = var.capacity_reservation_group_arn + capacity_reservation_id = var.capacity_reservation_id + disable_stop_instance = var.disable_stop_instance + ebs_optimized = var.ebs_optimized + enable_nitro_enclave = var.enable_nitro_enclave + enable_unlimited_credits = var.enable_unlimited_credits + iam_instance_profile = var.iam_instance_profile + instance_type = var.instance_type + fleet_tags = var.fleet_tags + pause_before_ssm = var.pause_before_ssm + + dynamic "placement" { + for_each = length(var.placement) > 0 ? [var.placement] : [] + + content { + host_resource_group_arn = try(placement.value.host_resource_group_arn, null) + tenancy = try(placement.value.tenancy, null) + } + } + + run_tags = merge(local.tags, var.run_tags) + security_group_ids = var.security_group_ids + + dynamic "security_group_filter" { + for_each = length(var.security_group_filter) > 0 ? var.security_group_filter : [] + + content { + filters = try(security_group_filter.value.filters, null) + } + } + + session_manager_port = var.session_manager_port + shutdown_behavior = var.shutdown_behavior + skip_profile_validation = var.skip_profile_validation + source_ami = data.amazon-parameterstore.this.value + + dynamic "subnet_filter" { + for_each = length(var.subnet_filter) > 0 ? [var.subnet_filter] : [] + + content { + filters = try(subnet_filter.value.filters, null) + most_free = try(subnet_filter.value.most_free, null) + random = try(subnet_filter.value.random, null) + } + } + + subnet_id = var.subnet_id + + dynamic "temporary_iam_instance_profile_policy_document" { + for_each = length(var.temporary_iam_instance_profile_policy_document) > 0 ? [var.temporary_iam_instance_profile_policy_document] : [] + + content { + dynamic "Statement" { + for_each = temporary_iam_instance_profile_policy_document.value + + content { + Action = try(Statement.value.Action, []) + Effect = try(Statement.value.Effect, "Allow") + Resource = try(Statement.value.Resource, ["*"]) + } + } + Version = "2012-10-17" + } + } + + temporary_security_group_source_cidrs = var.temporary_security_group_source_cidrs + temporary_security_group_source_public_ip = var.temporary_security_group_source_public_ip + user_data = var.user_data + user_data_file = var.user_data_file + + dynamic "vpc_filter" { + for_each = length(var.vpc_filter) > 0 ? var.vpc_filter : [] + + content { + filters = try(vpc_filter.value.filters, null) + } + } + + vpc_id = var.vpc_id + + dynamic "metadata_options" { + for_each = length(var.metadata_options) > 0 ? [var.metadata_options] : [] + + content { + http_endpoint = try(metadata_options.value.http_endpoint, 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) + } + } +} + +################################################################################ +# Build +################################################################################ + +build { + sources = ["source.amazon-ebs.this"] + + provisioner "shell" { + execute_command = "echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'" + + env = { + DEBIAN_FRONTEND = "noninteractive" + } + + expect_disconnect = true + + inline = [ + "cloud-init status --wait", + "apt update", + "apt upgrade -y", + "apt install iptables conntrack -y", + "systemctl reboot", + ] + + pause_after = "15s" + } + + provisioner "shell" { + execute_command = "echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'" + + env = { + DEBIAN_FRONTEND = "noninteractive" + } + + inline = [ + + "snap install aws-cli --classic", + "snap switch --channel=candidate amazon-ssm-agent", + "curl -OL 'https://hybrid-assets.eks.amazonaws.com/releases/latest/bin/linux/amd64/nodeadm'", + "mv nodeadm /usr/bin/nodeadm", + "chmod +x /usr/bin/nodeadm", + "nodeadm install ${var.eks_version} --credential-provider ${var.credential_provider}", + ] + } + + provisioner "shell" { + execute_command = "echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'" + + env = { + DEBIAN_FRONTEND = "noninteractive" + } + + inline = [ + "apt --purge autoremove -y", + "cloud-init clean --logs --machine-id", + "mkdir -p /etc/amazon/ssm", + "cp $(find / -name '*seelog.xml.template') /etc/amazon/ssm/seelog.xml", + ] + } +} diff --git a/examples/eks-hybrid-nodes/ami/plugins.pkr.hcl b/examples/eks-hybrid-nodes/ami/plugins.pkr.hcl new file mode 100644 index 0000000000..24a7f5b8f5 --- /dev/null +++ b/examples/eks-hybrid-nodes/ami/plugins.pkr.hcl @@ -0,0 +1,8 @@ +packer { + required_plugins { + amazon = { + version = "~> 1.2" + source = "github.com/hashicorp/amazon" + } + } +} diff --git a/examples/eks-hybrid-nodes/ami/variables.pkr.hcl b/examples/eks-hybrid-nodes/ami/variables.pkr.hcl new file mode 100644 index 0000000000..89e1b27ffa --- /dev/null +++ b/examples/eks-hybrid-nodes/ami/variables.pkr.hcl @@ -0,0 +1,723 @@ +variable "ami_name_prefix" { + description = "The prefix to use when creating the AMI name. i.e. - `---" + type = string + default = "eks-hybrid-ubuntu" +} + +variable "eks_version" { + description = "The EKS cluster version associated with the AMI created" + type = string + default = "1.31" +} + +variable "credential_provider" { + description = "The credential provider to use with the Hybrid Node role" + type = string + default = "ssm" +} + +variable "cpu_architecture" { + description = "The CPU architecture. Either `amd64` or `arm64`" + type = string + default = "amd64" +} + +################################################################################ +# EBS Source +################################################################################ + +variable "ami_block_device_mappings" { + description = "The block device mappings attached when booting a new instance from the AMI created" + type = list(map(string)) + default = [ + { + device_name = "/dev/sda1" + volume_size = 24 + volume_type = "gp3" + delete_on_termination = true + }, + ] +} + +variable "ami_description" { + description = "The description to use when creating the AMI" + type = string + default = "EKS Hybrid Node demonstration AMI" +} + +variable "ami_groups" { + description = "A list of groups that have access to launch the resulting AMI(s). By default no groups have permission to launch the AMI. `all` will make the AMI publicly accessible. AWS currently doesn't accept any value other than `all`" + type = list(string) + default = null +} + +variable "ami_org_arns" { + description = "A list of Amazon Resource Names (ARN) of AWS Organizations that have access to launch the resulting AMI(s). By default no organizations have permission to launch the AMI" + type = list(string) + default = null +} + +variable "ami_ou_arns" { + description = "A list of Amazon Resource Names (ARN) of AWS Organizations organizational units (OU) that have access to launch the resulting AMI(s). By default no organizational units have permission to launch the AMI" + type = list(string) + default = null +} + +variable "ami_regions" { + description = "A list of regions to copy the AMI to. Tags and attributes are copied along with the AMI. AMI copying takes time depending on the size of the AMI, but will generally take many minutes" + type = list(string) + default = null +} + +variable "ami_users" { + description = "A list of account IDs that have access to launch the resulting AMI(s). By default no additional users other than the user creating the AMI has permissions to launch it" + type = list(string) + default = null +} + +variable "ami_virtualization_type" { + description = "The type of virtualization used to create the AMI. Can be one of `hvm` or `paravirtual`" + type = string + default = "hvm" +} + +variable "deprecate_at" { + description = "The date and time to deprecate the AMI, in UTC, in the following format: YYYY-MM-DDTHH:MM:SSZ. If you specify a value for seconds, Amazon EC2 rounds the seconds to the nearest minute" + type = string + default = null +} + +variable "ena_support" { + description = "Enable enhanced networking (ENA but not SriovNetSupport) on HVM-compatible AMIs" + type = bool + default = null +} + +variable "encrypt_boot" { + description = "Whether or not to encrypt the resulting AMI when copying a provisioned instance to an AMI. By default, Packer will keep the encryption setting to what it was in the source image" + type = bool + default = null +} + +variable "force_deregister" { + description = "Force Packer to first deregister an existing AMI if one with the same name already exists. Default `false`" + type = bool + default = null +} + +variable "force_delete_snapshot" { + description = "Force Packer to delete snapshots associated with AMIs, which have been deregistered by force_deregister. Default `false`" + type = bool + default = null +} + +variable "imds_support" { + description = "Enforce version of the Instance Metadata Service on the built AMI. Valid options are `unset` (legacy) and `v2.0`" + type = string + default = "v2.0" +} + +variable "kms_key_id" { + description = "ID, alias or ARN of the KMS key to use for AMI encryption. This only applies to the main `region` -- any regions the AMI gets copied to copied will be encrypted by the default EBS KMS key for that region, unless you set region-specific keys in `region_kms_key_ids`" + type = string + default = null +} + +variable "launch_block_device_mappings" { + description = "The block device mappings to use when creating the AMI. If you add instance store volumes or EBS volumes in addition to the root device volume, the created AMI will contain block device mapping information for those volumes. Amazon creates snapshots of the source instance's root volume and any other EBS volumes described here. When you launch an instance from this new AMI, the instance automatically launches with these additional volumes, and will restore them from snapshots taken from the source instance" + type = list(map(string)) + default = [ + { + device_name = "/dev/sda1" + volume_size = 24 + volume_type = "gp3" + delete_on_termination = true + }, + ] +} + +variable "region_kms_key_ids" { + description = "regions to copy the ami to, along with the custom kms key id (alias or arn) to use for encryption for that region. Keys must match the regions provided in `ami_regions`" + type = map(string) + default = null +} + +variable "run_volume_tags" { + description = "Tags to apply to the volumes that are launched to create the AMI. These tags are not applied to the resulting AMI" + type = map(string) + default = null +} + +variable "skip_create_ami" { + description = "If `true`, Packer will not create the AMI. Useful for setting to `true` during a build test stage. Default `false`" + type = bool + default = null +} + +variable "skip_region_validation" { + description = "Set to `true` if you want to skip validation of the `ami_regions` configuration option. Default `false`" + type = bool + default = null +} + +variable "skip_save_build_region" { + description = "If true, Packer will not check whether an AMI with the ami_name exists in the region it is building in. It will use an intermediary AMI name, which it will not convert to an AMI in the build region. Default `false`" + type = bool + default = null +} + +variable "sriov_support" { + description = "Enable enhanced networking (SriovNetSupport but not ENA) on HVM-compatible AMIs" + type = bool + default = null +} + +variable "snapshot_groups" { + description = "A list of groups that have access to create volumes from the snapshot(s). By default no groups have permission to create volumes from the snapshot(s). all will make the snapshot publicly accessible" + type = list(string) + default = null +} + +variable "snapshot_tags" { + description = "Key/value pair tags to apply to snapshot. They will override AMI tags if already applied to snapshot" + type = map(string) + default = null +} + +variable "snapshot_users" { + description = "A list of account IDs that have access to create volumes from the snapshot(s). By default no additional users other than the user creating the AMI has permissions to create volumes from the backing snapshot(s)" + type = list(string) + default = null +} + +variable "tags" { + description = "Key/value pair tags applied to the AMI" + type = map(string) + default = {} +} + +# Access Configuration + +variable "access_key" { + description = "The access key used to communicate with AWS" + type = string + default = null +} + +variable "assume_role" { + description = "If provided with a role ARN, Packer will attempt to assume this role using the supplied credentials" + type = map(string) + default = {} +} + +variable "aws_polling" { + description = "Polling configuration for the AWS waiter. Configures the waiter for resources creation or actions like attaching volumes or importing image" + type = map(string) + default = {} +} + +variable "custom_endpoint_ec2" { + description = "This option is useful if you use a cloud provider whose API is compatible with aws EC2" + type = string + default = null +} + +variable "decode_authorization_messages" { + description = "Enable automatic decoding of any encoded authorization (error) messages using the sts:DecodeAuthorizationMessage API" + type = bool + default = null +} + +variable "insecure_skip_tls_verify" { + description = "This allows skipping TLS verification of the AWS EC2 endpoint. The default is `false`" + type = bool + default = null +} + +variable "max_retries" { + description = "This is the maximum number of times an API call is retried, in the case where requests are being throttled or experiencing transient failures. The delay between the subsequent API calls increases exponentially" + type = number + default = null +} + +variable "mfa_code" { + description = "The MFA TOTP code. This should probably be a user variable since it changes all the time" + type = string + default = null +} + +variable "profile" { + description = "The profile to use in the shared credentials file for AWS" + type = string + default = null +} + +variable "region" { + description = "The name of the region, such as us-east-1, in which to launch the EC2 instance to create the AMI" + type = string + default = "us-east-1" +} + +variable "secret_key" { + description = "The secret key used to communicate with AWS" + type = string + default = null +} + +variable "shared_credentials_file" { + description = "Path to a credentials file to load credentials from" + type = string + default = null +} + +variable "skip_credential_validation" { + description = "Set to true if you want to skip validating AWS credentials before runtime" + type = bool + default = null +} + +variable "skip_metadata_api_check" { + description = "Skip Metadata Api Check" + type = bool + default = null +} + +variable "token" { + description = "The access token to use. This is different from the access key and secret key" + type = string + default = null +} + +# Communicator + +variable "communicator" { + description = "The communicator to use to communicate with the EC2 instance. Valid values are `none`, `ssh`, `winrm`, and `ssh+winrm`" + type = string + default = "ssh" +} + +variable "pause_before_connecting" { + description = "We recommend that you enable SSH or WinRM as the very last step in your guest's bootstrap script, but sometimes you may have a race condition where you need Packer to wait before attempting to connect to your guest" + type = string + default = null +} + +variable "ssh_agent_auth" { + description = "If true, the local SSH agent will be used to authenticate connections to the source instance. No temporary keypair will be created, and the values of `ssh_password` and `ssh_private_key_file` will be ignored. The environment variable `SSH_AUTH_SOCK` must be set for this option to work properly" + type = bool + default = null +} + +variable "ssh_bastion_agent_auth" { + description = "If `true`, the local SSH agent will be used to authenticate with the bastion host. Defaults to `false`" + type = bool + default = null +} + +variable "ssh_bastion_certificate_file" { + description = "Path to user certificate used to authenticate with bastion host. The ~ can be used in path and will be expanded to the home directory of current user" + type = string + default = null +} + +variable "ssh_bastion_host" { + description = "A bastion host to use for the actual SSH connection" + type = string + default = null +} + +variable "ssh_bastion_interactive" { + description = "If `true`, the keyboard-interactive used to authenticate with bastion host" + type = bool + default = null +} + +variable "ssh_bastion_password" { + description = "The password to use to authenticate with the bastion host" + type = string + default = null +} + +variable "ssh_bastion_port" { + description = "The port of the bastion host. Defaults to `22`" + type = number + default = null +} + +variable "ssh_bastion_private_key_file" { + description = "Path to a PEM encoded private key file to use to authenticate with the bastion host. The `~` can be used in path and will be expanded to the home directory of current user" + type = string + default = null +} + +variable "ssh_bastion_username" { + description = "The username to connect to the bastion host" + type = string + default = null +} + +variable "ssh_ciphers" { + description = "This overrides the value of ciphers supported by default by Golang. The default value is `[\"aes128-gcm@openssh.com\", \"chacha20-poly1305@openssh.com\", \"aes128-ctr\", \"aes192-ctr\", \"aes256-ctr\"]`" + type = list(string) + default = null +} + +variable "ssh_certificate_file" { + description = "Path to user certificate used to authenticate with SSH. The `~` can be used in path and will be expanded to the home directory of current user" + type = string + default = null +} + +variable "ssh_clear_authorized_keys" { + description = "If true, Packer will attempt to remove its temporary key from `~/.ssh/authorized_keys` and `/root/.ssh/authorized_keys`" + type = bool + default = null +} + +variable "ssh_disable_agent_forwarding" { + description = "If `true`, SSH agent forwarding will be disabled. Defaults to `false`" + type = bool + default = null +} + +variable "ssh_file_transfer_method" { + description = "How to transfer files, Secure copy (`scp` default) or SSH File Transfer Protocol (`sftp`)" + type = string + default = null +} + +variable "ssh_handshake_attempts" { + description = "The number of handshakes to attempt with SSH once it can connect. This defaults to `10`, unless a `ssh_timeout` is set" + type = number + default = null +} + +variable "ssh_host" { + description = "The address to SSH to. This usually is automatically configured by the builder" + type = string + default = null +} + +variable "ssh_interface" { + description = "One of `public_ip`, `private_ip`, `public_dns`, `private_dns` or `session_manager`. If set, either the public IP address, private IP address, public DNS name or private DNS name will be used as the host for SSH. The default behavior if inside a VPC is to use the public IP address if available, otherwise the private IP address will be used. If not in a VPC the public DNS name will be used" + type = string + default = "public_ip" +} + +variable "ssh_keep_alive_interval" { + description = "How often to send \"keep alive\" messages to the server. Set to a negative value (`-1s`) to disable. Defaults to `5s`" + type = string + default = null +} + +variable "ssh_key_exchange_algorithms" { + description = "If set, Packer will override the value of key exchange (kex) algorithms supported by default by Golang. Acceptable values include: `curve25519-sha256@libssh.org`, `ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `diffie-hellman-group14-sha1`, and `diffie-hellman-group1-sha1`" + type = list(string) + default = null +} + +variable "ssh_keypair_name" { + description = "If specified, this is the key that will be used for SSH with the machine. The key must match a key pair name loaded up into the remote" + type = string + default = null +} + +variable "ssh_local_tunnels" { + description = "A list of local tunnels to use when connecting to the host" + type = list(string) + default = null +} + +variable "ssh_password" { + description = "A plaintext password to use to authenticate with SSH" + type = string + default = null +} + +variable "ssh_port" { + description = "The port to connect to SSH. This defaults to `22`" + type = number + default = null +} + +variable "ssh_private_key_file" { + description = "Path to a PEM encoded private key file to use to authenticate with SSH. The ~ can be used in path and will be expanded to the home directory of current user" + type = string + default = null +} + +variable "ssh_proxy_host" { + description = "A SOCKS proxy host to use for SSH connection" + type = string + default = null +} + +variable "ssh_proxy_password" { + description = "The optional password to use to authenticate with the proxy server" + type = string + default = null +} + +variable "ssh_proxy_port" { + description = "A port of the SOCKS proxy. Defaults to `1080`" + type = number + default = null +} + +variable "ssh_proxy_username" { + description = "The optional username to authenticate with the proxy server" + type = string + default = null +} + +variable "ssh_pty" { + description = "If `true`, a PTY will be requested for the SSH connection. This defaults to `false`" + type = bool + default = null +} + +variable "ssh_read_write_timeout" { + description = "The amount of time to wait for a remote command to end. This might be useful if, for example, packer hangs on a connection after a reboot. Example: `5m`. Disabled by default" + type = string + default = null +} + +variable "ssh_remote_tunnels" { + description = "A list of remote tunnels to use when connecting to the host" + type = list(string) + default = null +} + +variable "ssh_timeout" { + description = "The time to wait for SSH to become available. Packer uses this to determine when the machine has booted so this is usually quite long. This defaults to `5m`, unless `ssh_handshake_attempts` is set" + type = string + default = null +} + +variable "ssh_username" { + description = "The username to connect to SSH with. Required if using SSH" + type = string + default = "ubuntu" +} + +variable "temporary_key_pair_type" { + description = "Specifies the type of key to create. The possible values are 'dsa', 'ecdsa', 'ed25519', or 'rsa'. Default is `ed25519`" + type = string + default = "ed25519" +} + +variable "temporary_key_pair_bits" { + description = "Specifies the number of bits in the key to create. For RSA keys, the minimum size is 1024 bits and the default is 4096 bits. Generally, 3072 bits is considered sufficient" + type = number + default = null +} + +# Run Configuration + +variable "associate_public_ip_address" { + description = "If using a non-default VPC, public IP addresses are not provided by default. If this is true, your new instance will get a Public IP" + type = bool + default = true +} + +variable "capacity_reservation_preference" { + description = "Set the preference for using a capacity reservation if one exists. Either will be `open` or `none`. Defaults to `none`" + type = string + default = null +} + +variable "capacity_reservation_group_arn" { + description = "Provide the EC2 Capacity Reservation Group ARN that will be used by Packer" + type = string + default = null +} + +variable "capacity_reservation_id" { + description = "Provide the specific EC2 Capacity Reservation ID that will be used by Packer" + type = string + default = null +} + +variable "disable_stop_instance" { + description = "If this is set to true, Packer will not stop the instance but will assume that you will send the stop signal yourself through your final provisioner" + type = bool + default = null +} + +variable "ebs_optimized" { + description = "Mark instance as EBS Optimized. Default `false`" + type = bool + default = null +} + +variable "enable_nitro_enclave" { + description = "Enable support for Nitro Enclaves on the instance" + type = bool + default = null +} + +variable "enable_unlimited_credits" { + description = "Enabling Unlimited credits allows the source instance to burst additional CPU beyond its available CPU Credits for as long as the demand exists" + type = bool + default = null +} + +variable "iam_instance_profile" { + description = "The name of an IAM instance profile to launch the EC2 instance with" + type = string + default = null +} + +variable "instance_type" { + description = "The EC2 instance type to use while building the AMI, such as `m5.large`" + type = string + default = "c5.xlarge" +} + +variable "fleet_tags" { + description = "Key/value pair tags to apply tags to the fleet that is issued" + type = map(string) + default = null +} + +variable "pause_before_ssm" { + description = "The time to wait before establishing the Session Manager session" + type = string + default = null +} + +variable "placement" { + description = "Describes the placement of an instance" + type = map(string) + default = {} +} + +variable "run_tags" { + description = "Key/value pair tags to apply to the generated key-pair, security group, iam profile and role, snapshot, network interfaces and instance that is launched to create the EBS volumes. The resulting AMI will also inherit these tags" + type = map(string) + default = null +} + +variable "security_group_ids" { + description = "A list of security group IDs to assign to the instance. By default this is not set and Packer will automatically create a new temporary security group to allow SSH access" + type = list(string) + default = null +} + +variable "security_group_filter" { + description = "Filters used to populate the `security_group_ids` field. `security_group_ids` take precedence over this" + type = list(map(string)) + default = [] +} + +variable "session_manager_port" { + description = "Which port to connect the local end of the session tunnel to. If left blank, Packer will choose a port for you from available ports. This option is only used when `ssh_interface` is set `session_manager`" + type = number + default = null +} + +variable "shutdown_behavior" { + description = "Automatically terminate instances on shutdown in case Packer exits ungracefully. Possible values are `stop` and `terminate`. Defaults to `stop`" + type = string + default = null +} + +variable "skip_profile_validation" { + description = "Whether or not to check if the IAM instance profile exists. Defaults to `false`" + type = bool + default = null +} + +variable "subnet_filter" { + description = "Filters used to populate the subnet_id field. `subnet_id` take precedence over this" + default = { + filters = { + "tag:eks-hybrid-packer" = "true" + } + random = true + } +} + +variable "subnet_id" { + description = "f using VPC, the ID of the subnet, such as subnet-12345def, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC" + type = string + default = null +} + +variable "temporary_iam_instance_profile_policy_document" { + description = "Creates a temporary instance profile policy document to grant permissions to the EC2 instance. This is an alternative to using an existing `iam_instance_profile`" + default = [ + { + Effect = "Allow" + Action = [ + "ec2:Describe*", + ] + Resource = ["*"] + }, + ] +} + +variable "temporary_security_group_source_cidrs" { + description = "A list of IPv4 CIDR blocks to be authorized access to the instance, when packer is creating a temporary security group. The default is `[0.0.0.0/0]`" + type = list(string) + default = null +} + +variable "temporary_security_group_source_public_ip" { + description = "When enabled, use public IP of the host (obtained from https://checkip.amazonaws.com) as CIDR block to be authorized access to the instance, when packer is creating a temporary security group. Defaults to `false`" + type = bool + default = null +} + +variable "user_data" { + description = "User data to apply when launching the instance" + type = string + default = null +} + +variable "user_data_file" { + description = "Path to a file that will be used for the user data when launching the instance" + type = string + default = null +} + +variable "vpc_filter" { + description = "Filters used to populate the `vpc_id` field. `vpc_id` take precedence over this" + type = list(map(string)) + default = [] +} + +variable "vpc_id" { + description = "If launching into a VPC subnet, Packer needs the VPC ID in order to create a temporary security group within the VPC. Requires `subnet_id` to be set. If this field is left blank, Packer will try to get the VPC ID from the `subnet_id`" + type = string + default = null +} + +variable "metadata_options" { + description = "Configures the metadata options for the instance launched" + type = map(string) + default = { + http_endpoint = "enabled" + http_tokens = "required" + http_put_response_hop_limit = 1 + } +} + +################################################################################ +# Build +################################################################################ + +variable "shell_provisioner1" { + description = "Values passed to the first shell provisioner" + default = {} +} + +variable "shell_provisioner2" { + description = "Values passed to the second shell provisioner" + default = {} +} + +variable "shell_provisioner3" { + description = "Values passed to the third/last shell provisioner" + default = {} +} diff --git a/examples/eks-hybrid-nodes/main.tf b/examples/eks-hybrid-nodes/main.tf new file mode 100644 index 0000000000..8791e6cb53 --- /dev/null +++ b/examples/eks-hybrid-nodes/main.tf @@ -0,0 +1,148 @@ +provider "aws" { + region = local.region +} + +provider "helm" { + kubernetes { + host = module.eks.cluster_endpoint + cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) + + exec { + api_version = "client.authentication.k8s.io/v1beta1" + command = "aws" + # This requires the awscli to be installed locally where Terraform is executed + args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] + } + } +} + +locals { + name = "ex-${basename(path.cwd)}" + region = "us-west-2" + + cluster_version = "1.31" + + tags = { + Test = local.name + GithubRepo = "terraform-aws-eks" + GithubOrg = "terraform-aws-modules" + } +} + +################################################################################ +# EKS Cluster +################################################################################ + +module "eks" { + source = "../.." + + cluster_name = local.name + cluster_version = local.cluster_version + + cluster_endpoint_public_access = true + enable_cluster_creator_admin_permissions = true + + cluster_addons = { + coredns = {} + eks-pod-identity-agent = {} + kube-proxy = {} + } + + create_node_security_group = false + cluster_security_group_additional_rules = { + hybrid-all = { + cidr_blocks = [local.remote_network_cidr] + description = "Allow all traffic from remote node/pod network" + from_port = 0 + to_port = 0 + protocol = "all" + type = "ingress" + } + } + + cluster_compute_config = { + enabled = true + node_pools = ["system"] + } + + access_entries = { + hybrid-node-role = { + principal_arn = module.eks_hybrid_node_role.arn + type = "HYBRID_LINUX" + } + } + + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.private_subnets + + cluster_remote_network_config = { + remote_node_networks = { + cidrs = [local.remote_node_cidr] + } + remote_pod_networks = { + cidrs = [local.remote_pod_cidr] + } + } + + tags = local.tags +} + +################################################################################ +# VPC +################################################################################ + +locals { + vpc_cidr = "10.0.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 3) +} + +data "aws_availability_zones" "available" { + # Exclude local zones + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +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)] + intra_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 52)] + + enable_nat_gateway = true + single_nat_gateway = true + + public_subnet_tags = { + "kubernetes.io/role/elb" = 1 + } + + private_subnet_tags = { + "kubernetes.io/role/internal-elb" = 1 + } + + tags = local.tags +} + +################################################################################ +# VPC Peering Connection +################################################################################ + +resource "aws_vpc_peering_connection_accepter" "peer" { + vpc_peering_connection_id = aws_vpc_peering_connection.remote_node.id + auto_accept = true + + tags = local.tags +} + +resource "aws_route" "peer" { + route_table_id = one(module.vpc.private_route_table_ids) + destination_cidr_block = local.remote_network_cidr + vpc_peering_connection_id = aws_vpc_peering_connection.remote_node.id +} diff --git a/examples/eks-hybrid-nodes/outputs.tf b/examples/eks-hybrid-nodes/outputs.tf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/eks-hybrid-nodes/remote.tf b/examples/eks-hybrid-nodes/remote.tf new file mode 100644 index 0000000000..3a44aba523 --- /dev/null +++ b/examples/eks-hybrid-nodes/remote.tf @@ -0,0 +1,314 @@ +provider "aws" { + alias = "remote" + region = "us-east-1" +} + +################################################################################ +# Hybrid Node IAM Module +################################################################################ + +module "eks_hybrid_node_role" { + source = "../../modules/hybrid-node-role" + + tags = local.tags +} + +################################################################################ +# Psuedo Hybrid Node +# Demonstration only - AWS EC2 instances are not supported for EKS Hybrid nodes +################################################################################ + +# Activation should be done is same region as cluster +resource "aws_ssm_activation" "this" { + name = "hybrid-node" + iam_role = module.eks_hybrid_node_role.name + registration_limit = 10 + + tags = local.tags +} + +module "key_pair" { + source = "terraform-aws-modules/key-pair/aws" + version = "~> 2.0" + + providers = { + aws = aws.remote + } + + key_name = "hybrid-node" + create_private_key = true + + tags = local.tags +} + +resource "local_file" "key_pem" { + content = module.key_pair.private_key_pem + filename = "key.pem" + file_permission = "0600" +} + +resource "local_file" "key_pub_pem" { + content = module.key_pair.public_key_pem + filename = "key_pub.pem" + file_permission = "0600" +} + +resource "local_file" "join" { + content = <<-EOT + #!/usr/bin/env bash + + cat < nodeConfig.yaml + apiVersion: node.eks.aws/v1alpha1 + kind: NodeConfig + spec: + cluster: + name: ${module.eks.cluster_name} + region: ${local.region} + hybrid: + ssm: + activationCode: ${aws_ssm_activation.this.activation_code} + activationId: ${aws_ssm_activation.this.id} + EOF + + # Use SCP/SSH to execute commands on the remote host + scp -i ${local_file.key_pem.filename} nodeConfig.yaml ubuntu@${aws_instance.hybrid_node["one"].public_ip}:/home/ubuntu/nodeConfig.yaml + ssh -n -i ${local_file.key_pem.filename} ubuntu@${aws_instance.hybrid_node["one"].public_ip} sudo nodeadm init -c file://nodeConfig.yaml + ssh -n -i ${local_file.key_pem.filename} ubuntu@${aws_instance.hybrid_node["one"].public_ip} sudo systemctl daemon-reload + + scp -i ${local_file.key_pem.filename} nodeConfig.yaml ubuntu@${aws_instance.hybrid_node["two"].public_ip}:/home/ubuntu/nodeConfig.yaml + ssh -n -i ${local_file.key_pem.filename} ubuntu@${aws_instance.hybrid_node["two"].public_ip} sudo nodeadm init -c file://nodeConfig.yaml + ssh -n -i ${local_file.key_pem.filename} ubuntu@${aws_instance.hybrid_node["two"].public_ip} sudo systemctl daemon-reload + + # Clean up + rm nodeConfig.yaml + EOT + filename = "join.sh" +} + +data "aws_ami" "hybrid_node" { + provider = aws.remote + + most_recent = true + name_regex = "eks-hybrid-ubuntu-${local.cluster_version}-amd64-*" + owners = ["self"] +} + +# Demonstration only - AWS EC2 instances are not supported for EKS Hybrid nodes +resource "aws_instance" "hybrid_node" { + provider = aws.remote + + for_each = { one = 0, two = 1 } + + ami = data.aws_ami.hybrid_node.id + associate_public_ip_address = true + instance_type = "m5.large" + + # Block IMDS to make instance look less like EC2 and more like vanilla VM + metadata_options { + http_endpoint = "disabled" + } + + vpc_security_group_ids = [aws_security_group.remote_node.id] + subnet_id = element(module.remote_node_vpc.public_subnets, each.value) + + tags = merge( + local.tags, + { Name = "hybrid-node-${each.key}" } + ) +} + +################################################################################ +# Psuedo Hybrid Node - Security Group +# Demonstration only - AWS EC2 instances are not supported for EKS Hybrid nodes +################################################################################ + +# Retrieve the IP of where the Terraform is running to restrict SSH access to that IP +data "http" "icanhazip" { + url = "http://icanhazip.com" +} + +resource "aws_security_group" "remote_node" { + provider = aws.remote + + name = "hybrid-node" + vpc_id = module.remote_node_vpc.vpc_id + revoke_rules_on_delete = true + + tags = merge( + local.tags, + { Name = "hybrid-node" } + ) +} + +resource "aws_vpc_security_group_ingress_rule" "remote_node" { + provider = aws.remote + + for_each = { + cluster-all = { + description = "Allow all traffic from cluster network" + cidr_ipv4 = module.vpc.vpc_cidr_block + ip_protocol = "all" + } + remote-all = { + description = "Allow all traffic from within the remote network itself" + ip_protocol = "all" + referenced_security_group_id = aws_security_group.remote_node.id + } + # Restrict SSH access to only the IP where Terraform is running + ssh = { + description = "Local SSH access to join node to cluster" + cidr_ipv4 = "${chomp(data.http.icanhazip.response_body)}/32" + from_port = "22" + ip_protocol = "tcp" + } + } + + cidr_ipv4 = try(each.value.cidr_ipv4, null) + from_port = try(each.value.from_port, null) + ip_protocol = try(each.value.ip_protocol, null) + to_port = try(each.value.to_port, each.value.from_port, null) + referenced_security_group_id = try(each.value.referenced_security_group_id, null) + security_group_id = aws_security_group.remote_node.id + + tags = merge( + local.tags, + { Name = "hybrid-node-${each.key}" } + ) +} + +resource "aws_vpc_security_group_egress_rule" "remote_node" { + provider = aws.remote + + for_each = { + all = { + description = "Allow all egress" + cidr_ipv4 = "0.0.0.0/0" + ip_protocol = "all" + } + } + + cidr_ipv4 = try(each.value.cidr_ipv4, null) + from_port = try(each.value.from_port, null) + ip_protocol = try(each.value.ip_protocol, null) + to_port = try(each.value.to_port, each.value.from_port, null) + referenced_security_group_id = try(each.value.referenced_security_group_id, null) + security_group_id = aws_security_group.remote_node.id + + tags = merge( + local.tags, + { Name = "hybrid-node-${each.key}" } + ) +} + +################################################################################ +# Cilium CNI +################################################################################ + +resource "helm_release" "cilium" { + name = "cilium" + repository = "https://helm.cilium.io/" + chart = "cilium" + version = "1.16.4" + namespace = "kube-system" + wait = false + + values = [ + <<-EOT + nodeSelector: + eks.amazonaws.com/compute-type: hybrid + ipam: + mode: cluster-pool + operator: + clusterPoolIPv4MaskSize: 26 + clusterPoolIPv4PodCIDRList: + - ${local.remote_pod_cidr} + operator: + unmanagedPodWatcher: + restart: false + EOT + ] +} + +################################################################################ +# VPC +################################################################################ + +locals { + remote_network_cidr = "172.16.0.0/16" + remote_node_cidr = cidrsubnet(local.remote_network_cidr, 2, 0) + remote_pod_cidr = cidrsubnet(local.remote_network_cidr, 2, 1) + + remote_node_azs = slice(data.aws_availability_zones.remote.names, 0, 3) +} + +data "aws_availability_zones" "remote" { + provider = aws.remote + + # Exclude local zones + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +module "remote_node_vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 5.0" + + providers = { + aws = aws.remote + } + + name = local.name + cidr = local.remote_network_cidr + + azs = local.remote_node_azs + private_subnets = [for k, v in local.remote_node_azs : cidrsubnet(local.remote_network_cidr, 4, k)] + public_subnets = [for k, v in local.remote_node_azs : cidrsubnet(local.remote_network_cidr, 8, k + 48)] + + public_subnet_tags = { + # For building the AMI + "eks-hybrid-packer" : "true" + } + + enable_nat_gateway = true + single_nat_gateway = true + + tags = local.tags +} + +################################################################################ +# VPC Peering Connection +################################################################################ + +resource "aws_vpc_peering_connection" "remote_node" { + provider = aws.remote + + auto_accept = false + + peer_vpc_id = module.vpc.vpc_id + peer_region = local.region + + vpc_id = module.remote_node_vpc.vpc_id + + tags = merge( + local.tags, + { Name = "remote-node" } + ) +} + +resource "aws_route" "remote_node_private" { + provider = aws.remote + + route_table_id = one(module.remote_node_vpc.private_route_table_ids) + destination_cidr_block = module.vpc.vpc_cidr_block + vpc_peering_connection_id = aws_vpc_peering_connection.remote_node.id +} + +resource "aws_route" "remote_node_public" { + provider = aws.remote + + route_table_id = one(module.remote_node_vpc.public_route_table_ids) + destination_cidr_block = module.vpc.vpc_cidr_block + vpc_peering_connection_id = aws_vpc_peering_connection.remote_node.id +} diff --git a/examples/eks-hybrid-nodes/variables.tf b/examples/eks-hybrid-nodes/variables.tf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/eks-hybrid-nodes/versions.tf b/examples/eks-hybrid-nodes/versions.tf new file mode 100644 index 0000000000..6cd8533c17 --- /dev/null +++ b/examples/eks-hybrid-nodes/versions.tf @@ -0,0 +1,26 @@ +terraform { + required_version = ">= 1.3.2" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.79" + } + helm = { + source = "hashicorp/helm" + version = ">= 2.16" + } + http = { + source = "hashicorp/http" + version = ">= 3.4" + } + local = { + source = "hashicorp/local" + version = ">= 2.5" + } + tls = { + source = "hashicorp/tls" + version = ">= 4.0" + } + } +} diff --git a/examples/eks-managed-node-group/main.tf b/examples/eks-managed-node-group/main.tf index 4409a2e388..451bbdb385 100644 --- a/examples/eks-managed-node-group/main.tf +++ b/examples/eks-managed-node-group/main.tf @@ -2,7 +2,13 @@ provider "aws" { region = local.region } -data "aws_availability_zones" "available" {} +data "aws_availability_zones" "available" { + # Exclude local zones + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} locals { name = "ex-eks-mng" diff --git a/examples/eks-managed-node-group/versions.tf b/examples/eks-managed-node-group/versions.tf index 0099e6baaf..5b36c201aa 100644 --- a/examples/eks-managed-node-group/versions.tf +++ b/examples/eks-managed-node-group/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } } } diff --git a/examples/karpenter/README.md b/examples/karpenter/README.md index 15d51bcdb9..87a7d8ae78 100644 --- a/examples/karpenter/README.md +++ b/examples/karpenter/README.md @@ -89,7 +89,7 @@ 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.75 | +| [aws](#requirement\_aws) | >= 5.79 | | [helm](#requirement\_helm) | >= 2.7 | | [kubectl](#requirement\_kubectl) | >= 2.0 | @@ -97,8 +97,8 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.75 | -| [aws.virginia](#provider\_aws.virginia) | >= 5.75 | +| [aws](#provider\_aws) | >= 5.79 | +| [aws.virginia](#provider\_aws.virginia) | >= 5.79 | | [helm](#provider\_helm) | >= 2.7 | | [kubectl](#provider\_kubectl) | >= 2.0 | @@ -128,52 +128,5 @@ 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 | -| [karpenter\_event\_rules](#output\_karpenter\_event\_rules) | Map of the event rules created and their attributes | -| [karpenter\_iam\_role\_arn](#output\_karpenter\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the controller IAM role | -| [karpenter\_iam\_role\_name](#output\_karpenter\_iam\_role\_name) | The name of the controller IAM role | -| [karpenter\_iam\_role\_unique\_id](#output\_karpenter\_iam\_role\_unique\_id) | Stable and unique string identifying the controller IAM role | -| [karpenter\_instance\_profile\_arn](#output\_karpenter\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | -| [karpenter\_instance\_profile\_id](#output\_karpenter\_instance\_profile\_id) | Instance profile's ID | -| [karpenter\_instance\_profile\_name](#output\_karpenter\_instance\_profile\_name) | Name of the instance profile | -| [karpenter\_instance\_profile\_unique](#output\_karpenter\_instance\_profile\_unique) | Stable and unique string identifying the IAM instance profile | -| [karpenter\_node\_iam\_role\_arn](#output\_karpenter\_node\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | -| [karpenter\_node\_iam\_role\_name](#output\_karpenter\_node\_iam\_role\_name) | The name of the IAM role | -| [karpenter\_node\_iam\_role\_unique\_id](#output\_karpenter\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | -| [karpenter\_queue\_arn](#output\_karpenter\_queue\_arn) | The ARN of the SQS queue | -| [karpenter\_queue\_name](#output\_karpenter\_queue\_name) | The name of the created Amazon SQS queue | -| [karpenter\_queue\_url](#output\_karpenter\_queue\_url) | The URL for the created Amazon SQS queue | -| [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 | +No outputs. diff --git a/examples/karpenter/main.tf b/examples/karpenter/main.tf index 49321c977e..9a6223e253 100644 --- a/examples/karpenter/main.tf +++ b/examples/karpenter/main.tf @@ -35,7 +35,14 @@ provider "kubectl" { } } -data "aws_availability_zones" "available" {} +data "aws_availability_zones" "available" { + # Exclude local zones + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + data "aws_ecrpublic_authorization_token" "token" { provider = aws.virginia } diff --git a/examples/karpenter/outputs.tf b/examples/karpenter/outputs.tf index d85a8b034e..e69de29bb2 100644 --- a/examples/karpenter/outputs.tf +++ b/examples/karpenter/outputs.tf @@ -1,297 +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 -} - -################################################################################ -# 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 -} - -################################################################################ -# Karpenter controller IAM Role -################################################################################ - -output "karpenter_iam_role_name" { - description = "The name of the controller IAM role" - value = module.karpenter.iam_role_name -} - -output "karpenter_iam_role_arn" { - description = "The Amazon Resource Name (ARN) specifying the controller IAM role" - value = module.karpenter.iam_role_arn -} - -output "karpenter_iam_role_unique_id" { - description = "Stable and unique string identifying the controller IAM role" - value = module.karpenter.iam_role_unique_id -} - -################################################################################ -# Node Termination Queue -################################################################################ - -output "karpenter_queue_arn" { - description = "The ARN of the SQS queue" - value = module.karpenter.queue_arn -} - -output "karpenter_queue_name" { - description = "The name of the created Amazon SQS queue" - value = module.karpenter.queue_name -} - -output "karpenter_queue_url" { - description = "The URL for the created Amazon SQS queue" - value = module.karpenter.queue_url -} - -################################################################################ -# Node Termination Event Rules -################################################################################ - -output "karpenter_event_rules" { - description = "Map of the event rules created and their attributes" - value = module.karpenter.event_rules -} - -################################################################################ -# Node IAM Role -################################################################################ - -output "karpenter_node_iam_role_name" { - description = "The name of the IAM role" - value = module.karpenter.node_iam_role_name -} - -output "karpenter_node_iam_role_arn" { - description = "The Amazon Resource Name (ARN) specifying the IAM role" - value = module.karpenter.node_iam_role_arn -} - -output "karpenter_node_iam_role_unique_id" { - description = "Stable and unique string identifying the IAM role" - value = module.karpenter.node_iam_role_unique_id -} - -################################################################################ -# Node IAM Instance Profile -################################################################################ - -output "karpenter_instance_profile_arn" { - description = "ARN assigned by AWS to the instance profile" - value = module.karpenter.instance_profile_arn -} - -output "karpenter_instance_profile_id" { - description = "Instance profile's ID" - value = module.karpenter.instance_profile_id -} - -output "karpenter_instance_profile_name" { - description = "Name of the instance profile" - value = module.karpenter.instance_profile_name -} - -output "karpenter_instance_profile_unique" { - description = "Stable and unique string identifying the IAM instance profile" - value = module.karpenter.instance_profile_unique -} diff --git a/examples/karpenter/versions.tf b/examples/karpenter/versions.tf index 5caab8394a..5d3e660ea4 100644 --- a/examples/karpenter/versions.tf +++ b/examples/karpenter/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } helm = { source = "hashicorp/helm" diff --git a/examples/self-managed-node-group/main.tf b/examples/self-managed-node-group/main.tf index ed6982aa58..f8efaeb389 100644 --- a/examples/self-managed-node-group/main.tf +++ b/examples/self-managed-node-group/main.tf @@ -2,7 +2,13 @@ provider "aws" { region = local.region } -data "aws_availability_zones" "available" {} +data "aws_availability_zones" "available" { + # Exclude local zones + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} locals { name = "ex-self-mng" diff --git a/examples/self-managed-node-group/versions.tf b/examples/self-managed-node-group/versions.tf index 0099e6baaf..5b36c201aa 100644 --- a/examples/self-managed-node-group/versions.tf +++ b/examples/self-managed-node-group/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } } } diff --git a/main.tf b/main.tf index 50f4323c24..c2889b0dd4 100644 --- a/main.tf +++ b/main.tf @@ -18,6 +18,9 @@ locals { create_outposts_local_cluster = length(var.outpost_config) > 0 enable_cluster_encryption_config = length(var.cluster_encryption_config) > 0 && !local.create_outposts_local_cluster + + auto_mode_enabled = try(var.cluster_compute_config.enabled, false) + auto_mode_nodepools_enabled = length(try(var.cluster_compute_config.node_pools, [])) > 0 } ################################################################################ @@ -31,7 +34,7 @@ resource "aws_eks_cluster" "this" { role_arn = local.cluster_role version = var.cluster_version enabled_cluster_log_types = var.cluster_enabled_log_types - bootstrap_self_managed_addons = var.bootstrap_self_managed_addons + bootstrap_self_managed_addons = local.auto_mode_enabled ? coalesce(var.bootstrap_self_managed_addons, false) : var.bootstrap_self_managed_addons access_config { authentication_mode = var.authentication_mode @@ -44,6 +47,16 @@ resource "aws_eks_cluster" "this" { bootstrap_cluster_creator_admin_permissions = false } + dynamic "compute_config" { + for_each = length(var.cluster_compute_config) > 0 ? [var.cluster_compute_config] : [] + + content { + enabled = try(compute_config.value.enabled, null) + node_pools = local.auto_mode_enabled ? try(compute_config.value.node_pools, []) : null + node_role_arn = local.auto_mode_enabled ? try(compute_config.value.node_role_arn, aws_iam_role.eks_auto[0].arn, null) : null + } + } + vpc_config { security_group_ids = compact(distinct(concat(var.cluster_additional_security_group_ids, [local.cluster_security_group_id]))) subnet_ids = coalescelist(var.control_plane_subnet_ids, var.subnet_ids) @@ -57,6 +70,14 @@ resource "aws_eks_cluster" "this" { for_each = local.create_outposts_local_cluster ? [] : [1] content { + dynamic "elastic_load_balancing" { + for_each = local.auto_mode_enabled ? [1] : [] + + content { + enabled = local.auto_mode_enabled + } + } + ip_family = var.cluster_ip_family service_ipv4_cidr = var.cluster_service_ipv4_cidr service_ipv6_cidr = var.cluster_service_ipv6_cidr @@ -84,6 +105,39 @@ resource "aws_eks_cluster" "this" { } } + dynamic "remote_network_config" { + # Not valid on Outposts + for_each = length(var.cluster_remote_network_config) > 0 && !local.create_outposts_local_cluster ? [var.cluster_remote_network_config] : [] + + content { + dynamic "remote_node_networks" { + for_each = [remote_network_config.value.remote_node_networks] + + content { + cidrs = remote_node_networks.value.cidrs + } + } + + dynamic "remote_pod_networks" { + for_each = try([remote_network_config.value.remote_pod_networks], []) + + content { + cidrs = remote_pod_networks.value.cidrs + } + } + } + } + + dynamic "storage_config" { + for_each = local.auto_mode_enabled ? [1] : [] + + content { + block_storage { + enabled = local.auto_mode_enabled + } + } + } + dynamic "upgrade_policy" { for_each = length(var.cluster_upgrade_policy) > 0 ? [var.cluster_upgrade_policy] : [] @@ -199,7 +253,7 @@ locals { association_policy_arn = pol_val.policy_arn association_access_scope_type = pol_val.access_scope.type association_access_scope_namespaces = lookup(pol_val.access_scope, "namespaces", []) - } : k => v if !contains(["EC2_LINUX", "EC2_WINDOWS", "FARGATE_LINUX"], lookup(entry_val, "type", "STANDARD")) }, + } : k => v if !contains(["EC2", "EC2_LINUX", "EC2_WINDOWS", "FARGATE_LINUX", "HYBRID_LINUX"], lookup(entry_val, "type", "STANDARD")) }, ) ] ]) @@ -380,14 +434,41 @@ locals { iam_role_policy_prefix = "arn:${local.partition}:iam::aws:policy" cluster_encryption_policy_name = coalesce(var.cluster_encryption_policy_name, "${local.iam_role_name}-ClusterEncryption") + + # Standard EKS cluster + eks_standard_iam_role_policies = { for k, v in { + AmazonEKSClusterPolicy = "${local.iam_role_policy_prefix}/AmazonEKSClusterPolicy", + } : k => v if !local.create_outposts_local_cluster && !local.auto_mode_enabled } + + # EKS cluster with EKS auto mode enabled + eks_auto_mode_iam_role_policies = { for k, v in { + AmazonEKSClusterPolicy = "${local.iam_role_policy_prefix}/AmazonEKSClusterPolicy" + AmazonEKSComputePolicy = "${local.iam_role_policy_prefix}/AmazonEKSComputePolicy" + AmazonEKSBlockStoragePolicy = "${local.iam_role_policy_prefix}/AmazonEKSBlockStoragePolicy" + AmazonEKSLoadBalancingPolicy = "${local.iam_role_policy_prefix}/AmazonEKSLoadBalancingPolicy" + AmazonEKSNetworkingPolicy = "${local.iam_role_policy_prefix}/AmazonEKSNetworkingPolicy" + } : k => v if !local.create_outposts_local_cluster && local.auto_mode_enabled } + + # EKS local cluster on Outposts + eks_outpost_iam_role_policies = { for k, v in { + AmazonEKSClusterPolicy = "${local.iam_role_policy_prefix}/AmazonEKSLocalOutpostClusterPolicy" + } : k => v if local.create_outposts_local_cluster && !local.auto_mode_enabled } + + # Security groups for pods + eks_sgpp_iam_role_policies = { for k, v in { + AmazonEKSVPCResourceController = "${local.iam_role_policy_prefix}/AmazonEKSVPCResourceController" + } : k => v if var.enable_security_groups_for_pods && !local.create_outposts_local_cluster && !local.auto_mode_enabled } } data "aws_iam_policy_document" "assume_role_policy" { count = local.create && var.create_iam_role ? 1 : 0 statement { - sid = "EKSClusterAssumeRole" - actions = ["sts:AssumeRole"] + sid = "EKSClusterAssumeRole" + actions = [ + "sts:AssumeRole", + "sts:TagSession", + ] principals { type = "Service" @@ -398,10 +479,8 @@ data "aws_iam_policy_document" "assume_role_policy" { for_each = local.create_outposts_local_cluster ? [1] : [] content { - type = "Service" - identifiers = [ - "ec2.amazonaws.com", - ] + type = "Service" + identifiers = ["ec2.amazonaws.com"] } } } @@ -424,10 +503,12 @@ resource "aws_iam_role" "this" { # Policies attached ref https://docs.aws.amazon.com/eks/latest/userguide/service_IAM_role.html resource "aws_iam_role_policy_attachment" "this" { - for_each = { for k, v in { - AmazonEKSClusterPolicy = local.create_outposts_local_cluster ? "${local.iam_role_policy_prefix}/AmazonEKSLocalOutpostClusterPolicy" : "${local.iam_role_policy_prefix}/AmazonEKSClusterPolicy", - AmazonEKSVPCResourceController = "${local.iam_role_policy_prefix}/AmazonEKSVPCResourceController", - } : k => v if local.create_iam_role } + for_each = { for k, v in merge( + local.eks_standard_iam_role_policies, + local.eks_auto_mode_iam_role_policies, + local.eks_outpost_iam_role_policies, + local.eks_sgpp_iam_role_policies, + ) : k => v if local.create_iam_role } policy_arn = each.value role = aws_iam_role.this[0].name @@ -602,3 +683,218 @@ resource "aws_eks_identity_provider_config" "this" { tags = merge(var.tags, try(each.value.tags, {})) } + +################################################################################ +# EKS Auto Node IAM Role +################################################################################ + +locals { + create_node_iam_role = local.create && var.create_node_iam_role && local.auto_mode_nodepools_enabled + node_iam_role_name = coalesce(var.node_iam_role_name, "${var.cluster_name}-eks-auto") + + create_node_iam_role_custom_policy = local.create_node_iam_role && (var.enable_node_custom_tags_permissions || length(var.node_iam_role_policy_statements) > 0) +} + +data "aws_iam_policy_document" "node_assume_role_policy" { + count = local.create_node_iam_role ? 1 : 0 + + statement { + sid = "EKSAutoNodeAssumeRole" + actions = [ + "sts:AssumeRole", + "sts:TagSession", + ] + + principals { + type = "Service" + identifiers = ["ec2.amazonaws.com"] + } + } +} + +resource "aws_iam_role" "eks_auto" { + count = local.create_node_iam_role ? 1 : 0 + + name = var.node_iam_role_use_name_prefix ? null : local.node_iam_role_name + name_prefix = var.node_iam_role_use_name_prefix ? "${local.node_iam_role_name}-" : null + path = var.node_iam_role_path + description = var.node_iam_role_description + + assume_role_policy = data.aws_iam_policy_document.node_assume_role_policy[0].json + permissions_boundary = var.node_iam_role_permissions_boundary + force_detach_policies = true + + tags = merge(var.tags, var.node_iam_role_tags) +} + +# Policies attached ref https://docs.aws.amazon.com/eks/latest/userguide/service_IAM_role.html +resource "aws_iam_role_policy_attachment" "eks_auto" { + for_each = { for k, v in { + AmazonEKSWorkerNodeMinimalPolicy = "${local.iam_role_policy_prefix}/AmazonEKSWorkerNodeMinimalPolicy", + AmazonEC2ContainerRegistryPullOnly = "${local.iam_role_policy_prefix}/AmazonEC2ContainerRegistryPullOnly", + } : k => v if local.create_node_iam_role } + + policy_arn = each.value + role = aws_iam_role.eks_auto[0].name +} + +resource "aws_iam_role_policy_attachment" "eks_auto_additional" { + for_each = { for k, v in var.node_iam_role_additional_policies : k => v if local.create_node_iam_role } + + policy_arn = each.value + role = aws_iam_role.eks_auto[0].name +} + +resource "aws_iam_role_policy_attachment" "eks_auto_custom" { + count = local.create_node_iam_role_custom_policy ? 1 : 0 + + policy_arn = aws_iam_policy.eks_auto_custom[0].arn + role = aws_iam_role.eks_auto[0].name +} + +data "aws_iam_policy_document" "eks_auto_custom" { + count = local.create_node_iam_role_custom_policy ? 1 : 0 + + dynamic "statement" { + for_each = var.enable_node_custom_tags_permissions ? [1] : [] + + content { + sid = "Compute" + actions = [ + "ec2:CreateFleet", + "ec2:RunInstances", + "ec2:CreateLaunchTemplate", + ] + resources = ["*"] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = ["$${aws:PrincipalTag/eks:eks-cluster-name}"] + } + + condition { + test = "StringLike" + variable = "aws:RequestTag/eks:kubernetes-node-class-name" + values = ["*"] + } + + condition { + test = "StringLike" + variable = "aws:RequestTag/eks:kubernetes-node-pool-name" + values = ["*"] + } + } + } + + dynamic "statement" { + for_each = var.enable_node_custom_tags_permissions ? [1] : [] + + content { + sid = "Storage" + actions = [ + "ec2:CreateVolume", + "ec2:CreateSnapshot", + ] + resources = [ + "arn:${local.partition}:ec2:*:*:volume/*", + "arn:${local.partition}:ec2:*:*:snapshot/*", + ] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = ["$${aws:PrincipalTag/eks:eks-cluster-name}"] + } + } + } + + dynamic "statement" { + for_each = var.enable_node_custom_tags_permissions ? [1] : [] + + content { + sid = "Networking" + actions = ["ec2:CreateNetworkInterface"] + resources = ["*"] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = ["$${aws:PrincipalTag/eks:eks-cluster-name}"] + } + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:kubernetes-cni-node-name" + values = ["*"] + } + } + } + + dynamic "statement" { + for_each = var.enable_node_custom_tags_permissions ? [1] : [] + + content { + sid = "LoadBalancer" + actions = [ + "elasticloadbalancing:CreateLoadBalancer", + "elasticloadbalancing:CreateTargetGroup", + "elasticloadbalancing:CreateListener", + "elasticloadbalancing:CreateRule", + "ec2:CreateSecurityGroup", + ] + resources = ["*"] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = ["$${aws:PrincipalTag/eks:eks-cluster-name}"] + } + } + } + + dynamic "statement" { + for_each = var.enable_node_custom_tags_permissions ? [1] : [] + + content { + sid = "ShieldProtection" + actions = ["shield:CreateProtection"] + resources = ["*"] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = ["$${aws:PrincipalTag/eks:eks-cluster-name}"] + } + } + } + + dynamic "statement" { + for_each = var.enable_node_custom_tags_permissions ? [1] : [] + + content { + sid = "ShieldTagResource" + actions = ["shield:TagResource"] + resources = ["arn:${local.partition}:shield::*:protection/*"] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = ["$${aws:PrincipalTag/eks:eks-cluster-name}"] + } + } + } +} + +resource "aws_iam_policy" "eks_auto_custom" { + count = local.create_node_iam_role_custom_policy ? 1 : 0 + + name = var.node_iam_role_use_name_prefix ? null : local.node_iam_role_name + name_prefix = var.node_iam_role_use_name_prefix ? "${local.node_iam_role_name}-" : null + path = var.node_iam_role_path + description = var.node_iam_role_description + + policy = data.aws_iam_policy_document.eks_auto_custom[0].json + + tags = merge(var.tags, var.node_iam_role_tags) +} diff --git a/modules/eks-managed-node-group/README.md b/modules/eks-managed-node-group/README.md index 23df973444..0936b8f790 100644 --- a/modules/eks-managed-node-group/README.md +++ b/modules/eks-managed-node-group/README.md @@ -64,13 +64,13 @@ module "eks_managed_node_group" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.75 | +| [aws](#requirement\_aws) | >= 5.79 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.75 | +| [aws](#provider\_aws) | >= 5.79 | ## Modules diff --git a/modules/eks-managed-node-group/versions.tf b/modules/eks-managed-node-group/versions.tf index 0099e6baaf..5b36c201aa 100644 --- a/modules/eks-managed-node-group/versions.tf +++ b/modules/eks-managed-node-group/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } } } diff --git a/modules/fargate-profile/README.md b/modules/fargate-profile/README.md index a7b12553ff..0d47f4697a 100644 --- a/modules/fargate-profile/README.md +++ b/modules/fargate-profile/README.md @@ -29,13 +29,13 @@ module "fargate_profile" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.75 | +| [aws](#requirement\_aws) | >= 5.79 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.75 | +| [aws](#provider\_aws) | >= 5.79 | ## Modules diff --git a/modules/fargate-profile/versions.tf b/modules/fargate-profile/versions.tf index 0099e6baaf..5b36c201aa 100644 --- a/modules/fargate-profile/versions.tf +++ b/modules/fargate-profile/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } } } diff --git a/modules/hybrid-node-role/README.md b/modules/hybrid-node-role/README.md new file mode 100644 index 0000000000..bd93c4ba1d --- /dev/null +++ b/modules/hybrid-node-role/README.md @@ -0,0 +1,159 @@ +# EKS Hybrid Node Role Module + +Terraform module which creates IAM role and policy resources for Amazon EKS Hybrid Node(s). + +## Usage + +EKS Hybrid nodes use the AWS IAM Authenticator and temporary IAM credentials provisioned by AWS SSM or AWS IAM Roles Anywhere to authenticate with the EKS cluster. This module supports both SSM and IAM Roles Anywhere based IAM permissions. + +### SSM + +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + + ... + access_entries = { + hybrid-node-role = { + principal_arn = module.eks_hybrid_node_role.arn + type = "HYBRID_LINUX" + } + } +} + +module "eks_hybrid_node_role" { + source = "terraform-aws-modules/eks/aws//modules/hybrid-node-role" + + name = "hybrid" + + tags = { + Environment = "dev" + Terraform = "true" + } +} +``` + +### IAM Roles Anywhere + +```hcl +module "eks" { + source = "terraform-aws-modules/eks/aws" + + ... + access_entries = { + hybrid-node-role = { + principal_arn = module.eks_hybrid_node_role.arn + type = "HYBRID_LINUX" + } + } +} + +module "eks_hybrid_node_role" { + source = "terraform-aws-modules/eks/aws//modules/hybrid-node-role" + + name = "hybrid-ira" + + enable_ira = true + + ira_trust_anchor_source_type = "CERTIFICATE_BUNDLE" + ira_trust_anchor_x509_certificate_data = <<-EOT + MIIFMzCCAxugAwIBAgIRAMnVXU7ncv/+Cl16eJbZ9hswDQYJKoZIhvcNAQELBQAw + ... + MGx/BMRkrNUVcg3xA0lhECo/olodCkmZo5/mjybbjFQwJzDSKFoW + EOT + + tags = { + Environment = "dev" + Terraform = "true" + } +} +``` + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.2 | +| [aws](#requirement\_aws) | >= 5.79 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 5.79 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.intermediate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.intermediate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.intermediate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_rolesanywhere_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rolesanywhere_profile) | resource | +| [aws_rolesanywhere_trust_anchor.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rolesanywhere_trust_anchor) | resource | +| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.intermediate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.intermediate_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cluster\_arns](#input\_cluster\_arns) | List of EKS cluster ARNs to allow the node to describe | `list(string)` |
[
"*"
]
| no | +| [create](#input\_create) | Controls if resources should be created (affects nearly all resources) | `bool` | `true` | no | +| [description](#input\_description) | IAM role description | `string` | `"EKS Hybrid Node IAM role"` | no | +| [enable\_ira](#input\_enable\_ira) | Enables IAM Roles Anywhere based IAM permissions on the node | `bool` | `false` | no | +| [enable\_pod\_identity](#input\_enable\_pod\_identity) | Enables EKS Pod Identity based IAM permissions on the node | `bool` | `true` | no | +| [intermediate\_policy\_name](#input\_intermediate\_policy\_name) | Name of the IAM policy | `string` | `null` | no | +| [intermediate\_policy\_statements](#input\_intermediate\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed | `any` | `[]` | no | +| [intermediate\_policy\_use\_name\_prefix](#input\_intermediate\_policy\_use\_name\_prefix) | Determines whether the name of the IAM policy (`intermediate_policy_name`) is used as a prefix | `bool` | `true` | no | +| [intermediate\_role\_description](#input\_intermediate\_role\_description) | IAM role description | `string` | `"EKS Hybrid Node IAM Roles Anywhere intermediate IAM role"` | no | +| [intermediate\_role\_name](#input\_intermediate\_role\_name) | Name of the IAM role | `string` | `null` | no | +| [intermediate\_role\_path](#input\_intermediate\_role\_path) | Path of the IAM role | `string` | `"/"` | no | +| [intermediate\_role\_policies](#input\_intermediate\_role\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | +| [intermediate\_role\_use\_name\_prefix](#input\_intermediate\_role\_use\_name\_prefix) | Determines whether the name of the IAM role (`intermediate_role_name`) is used as a prefix | `bool` | `true` | no | +| [ira\_profile\_duration\_seconds](#input\_ira\_profile\_duration\_seconds) | The number of seconds the vended session credentials are valid for. Defaults to `3600` | `number` | `null` | no | +| [ira\_profile\_managed\_policy\_arns](#input\_ira\_profile\_managed\_policy\_arns) | A list of managed policy ARNs that apply to the vended session credentials | `list(string)` | `[]` | no | +| [ira\_profile\_name](#input\_ira\_profile\_name) | Name of the Roles Anywhere profile | `string` | `null` | no | +| [ira\_profile\_require\_instance\_properties](#input\_ira\_profile\_require\_instance\_properties) | Specifies whether instance properties are required in [CreateSession](https://docs.aws.amazon.com/rolesanywhere/latest/APIReference/API_CreateSession.html) requests with this profile | `bool` | `null` | no | +| [ira\_profile\_session\_policy](#input\_ira\_profile\_session\_policy) | A session policy that applies to the trust boundary of the vended session credentials | `string` | `null` | no | +| [ira\_trust\_anchor\_acm\_pca\_arn](#input\_ira\_trust\_anchor\_acm\_pca\_arn) | The ARN of the ACM PCA that issued the trust anchor certificate | `string` | `null` | no | +| [ira\_trust\_anchor\_name](#input\_ira\_trust\_anchor\_name) | Name of the Roles Anywhere trust anchor | `string` | `null` | no | +| [ira\_trust\_anchor\_notification\_settings](#input\_ira\_trust\_anchor\_notification\_settings) | Notification settings for the trust anchor | `any` | `[]` | no | +| [ira\_trust\_anchor\_source\_type](#input\_ira\_trust\_anchor\_source\_type) | The source type of the trust anchor | `string` | `null` | no | +| [ira\_trust\_anchor\_x509\_certificate\_data](#input\_ira\_trust\_anchor\_x509\_certificate\_data) | The X.509 certificate data of the trust anchor | `string` | `null` | no | +| [max\_session\_duration](#input\_max\_session\_duration) | Maximum API session duration in seconds between 3600 and 43200 | `number` | `null` | no | +| [name](#input\_name) | Name of the IAM role | `string` | `"EKSHybridNode"` | no | +| [path](#input\_path) | Path of the IAM role | `string` | `"/"` | no | +| [permissions\_boundary\_arn](#input\_permissions\_boundary\_arn) | Permissions boundary ARN to use for the IAM role | `string` | `null` | no | +| [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | +| [policy\_description](#input\_policy\_description) | IAM policy description | `string` | `"EKS Hybrid Node IAM role policy"` | no | +| [policy\_name](#input\_policy\_name) | Name of the IAM policy | `string` | `"EKSHybridNode"` | no | +| [policy\_path](#input\_policy\_path) | Path of the IAM policy | `string` | `"/"` | no | +| [policy\_statements](#input\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed | `any` | `[]` | 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 | +| [trust\_anchor\_arns](#input\_trust\_anchor\_arns) | List of IAM Roles Anywhere trust anchor ARNs. Required if `enable_ira` is set to `true` | `list(string)` | `[]` | no | +| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the name of the IAM role (`name`) is used as a prefix | `bool` | `true` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The Amazon Resource Name (ARN) specifying the node IAM role | +| [intermediate\_role\_arn](#output\_intermediate\_role\_arn) | The Amazon Resource Name (ARN) specifying the node IAM role | +| [intermediate\_role\_name](#output\_intermediate\_role\_name) | The name of the node IAM role | +| [intermediate\_role\_unique\_id](#output\_intermediate\_role\_unique\_id) | Stable and unique string identifying the node IAM role | +| [name](#output\_name) | The name of the node IAM role | +| [unique\_id](#output\_unique\_id) | Stable and unique string identifying the node IAM role | + diff --git a/modules/hybrid-node-role/main.tf b/modules/hybrid-node-role/main.tf new file mode 100644 index 0000000000..5a2473f29b --- /dev/null +++ b/modules/hybrid-node-role/main.tf @@ -0,0 +1,369 @@ +data "aws_partition" "current" { + count = var.create ? 1 : 0 +} + +locals { + partition = try(data.aws_partition.current[0].partition, "aws") +} + +################################################################################ +# Node IAM Role +################################################################################ + +data "aws_iam_policy_document" "assume_role" { + count = var.create ? 1 : 0 + + # SSM + dynamic "statement" { + for_each = var.enable_ira ? [] : [1] + + content { + actions = [ + "sts:AssumeRole", + "sts:TagSession", + ] + + principals { + type = "Service" + identifiers = ["ssm.amazonaws.com"] + } + } + } + + # IAM Roles Anywhere + dynamic "statement" { + for_each = var.enable_ira ? [1] : [] + + content { + actions = [ + "sts:TagSession", + "sts:SetSourceIdentity", + ] + + principals { + type = "AWS" + identifiers = [aws_iam_role.intermediate[0].arn] + } + } + } + + dynamic "statement" { + for_each = var.enable_ira ? [1] : [] + + content { + actions = [ + "sts:AssumeRole", + "sts:TagSession", + ] + + principals { + type = "AWS" + identifiers = [aws_iam_role.intermediate[0].arn] + } + + condition { + test = "StringEquals" + variable = "sts:RoleSessionName" + values = ["$${aws:PrincipalTag/x509Subject/CN}"] + } + } + } +} + +resource "aws_iam_role" "this" { + count = var.create ? 1 : 0 + + name = var.use_name_prefix ? null : var.name + name_prefix = var.use_name_prefix ? "${var.name}-" : null + path = var.path + description = var.description + + assume_role_policy = data.aws_iam_policy_document.assume_role[0].json + max_session_duration = var.max_session_duration + permissions_boundary = var.permissions_boundary_arn + force_detach_policies = true + + tags = var.tags +} + +################################################################################ +# Node IAM Role Policy +################################################################################ + +data "aws_iam_policy_document" "this" { + count = var.create ? 1 : 0 + + statement { + actions = [ + "ssm:DeregisterManagedInstance", + "ssm:DescribeInstanceInformation", + ] + + resources = ["*"] + } + + statement { + actions = ["eks:DescribeCluster"] + resources = var.cluster_arns + } + + dynamic "statement" { + for_each = var.enable_pod_identity ? [1] : [] + + content { + actions = ["eks-auth:AssumeRoleForPodIdentity"] + resources = ["*"] + } + } + + dynamic "statement" { + for_each = var.policy_statements + + 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 + } + } + } + } +} + +resource "aws_iam_policy" "this" { + count = var.create ? 1 : 0 + + name = var.policy_use_name_prefix ? null : var.policy_name + name_prefix = var.policy_use_name_prefix ? "${var.policy_name}-" : null + path = var.policy_path + description = var.policy_description + policy = data.aws_iam_policy_document.this[0].json + + tags = var.tags +} + +resource "aws_iam_role_policy_attachment" "this" { + for_each = { for k, v in merge( + { + node = try(aws_iam_policy.this[0].arn, null) + AmazonSSMManagedInstanceCore = "arn:${local.partition}:iam::aws:policy/AmazonSSMManagedInstanceCore" + AmazonEC2ContainerRegistryPullOnly = "arn:${local.partition}:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly" + }, + var.policies + ) : k => v if var.create } + + policy_arn = each.value + role = aws_iam_role.this[0].name +} + +################################################################################ +# Roles Anywhere Profile +################################################################################ + +locals { + enable_ira = var.create && var.enable_ira +} + +resource "aws_rolesanywhere_profile" "this" { + count = local.enable_ira ? 1 : 0 + + duration_seconds = var.ira_profile_duration_seconds + managed_policy_arns = var.ira_profile_managed_policy_arns + name = try(coalesce(var.ira_profile_name, var.name), null) + require_instance_properties = var.ira_profile_require_instance_properties + role_arns = [aws_iam_role.intermediate[0].arn] + session_policy = var.ira_profile_session_policy + + tags = var.tags +} + +################################################################################ +# Roles Anywhere Trust Anchor +################################################################################ + +resource "aws_rolesanywhere_trust_anchor" "this" { + count = local.enable_ira ? 1 : 0 + + name = try(coalesce(var.ira_trust_anchor_name, var.name), null) + + dynamic "notification_settings" { + for_each = var.ira_trust_anchor_notification_settings + + content { + channel = try(notification_settings.value.channel, null) + enabled = try(notification_settings.value.enabled, null) + event = try(notification_settings.value.event, null) + threshold = try(notification_settings.value.threshold, null) + } + } + + source { + source_data { + acm_pca_arn = var.ira_trust_anchor_acm_pca_arn + x509_certificate_data = var.ira_trust_anchor_x509_certificate_data + } + source_type = var.ira_trust_anchor_source_type + } + + tags = var.tags +} + +################################################################################ +# Intermediate IAM Role +################################################################################ + +data "aws_iam_policy_document" "intermediate_assume_role" { + count = local.enable_ira ? 1 : 0 + + statement { + actions = [ + "sts:AssumeRole", + "sts:TagSession", + "sts:SetSourceIdentity", + ] + + principals { + type = "Service" + identifiers = ["rolesanywhere.amazonaws.com"] + } + + condition { + test = "ArnEquals" + variable = "aws:SourceArn" + values = concat(var.trust_anchor_arns, aws_rolesanywhere_trust_anchor.this[*].arn) + } + } +} + +locals { + intermediate_role_use_name_prefix = coalesce(var.intermediate_role_use_name_prefix, var.use_name_prefix) + intermediate_role_name = coalesce(var.intermediate_role_name, "${var.name}-inter") +} + +resource "aws_iam_role" "intermediate" { + count = local.enable_ira ? 1 : 0 + + name = local.intermediate_role_use_name_prefix ? null : local.intermediate_role_name + name_prefix = local.intermediate_role_use_name_prefix ? "${local.intermediate_role_name}-" : null + path = coalesce(var.intermediate_role_path, var.path) + description = var.intermediate_role_description + + assume_role_policy = data.aws_iam_policy_document.intermediate_assume_role[0].json + max_session_duration = var.max_session_duration + permissions_boundary = var.permissions_boundary_arn + force_detach_policies = true + + tags = var.tags +} + +################################################################################ +# Intermediate IAM Role Policy +################################################################################ + +data "aws_iam_policy_document" "intermediate" { + count = local.enable_ira ? 1 : 0 + + statement { + actions = ["eks:DescribeCluster"] + resources = var.cluster_arns + } + + dynamic "statement" { + for_each = var.intermediate_policy_statements + + 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 + } + } + } + } +} + +locals { + intermediate_policy_use_name_prefix = coalesce(var.intermediate_policy_use_name_prefix, var.policy_use_name_prefix) + intermediate_policy_name = coalesce(var.intermediate_policy_name, var.policy_name) +} + +resource "aws_iam_policy" "intermediate" { + count = local.enable_ira ? 1 : 0 + + name = local.intermediate_policy_use_name_prefix ? null : local.intermediate_policy_name + name_prefix = local.intermediate_policy_use_name_prefix ? "${local.intermediate_policy_name}-" : null + path = var.policy_path + description = var.policy_description + policy = data.aws_iam_policy_document.intermediate[0].json + + tags = var.tags +} + +resource "aws_iam_role_policy_attachment" "intermediate" { + for_each = { for k, v in merge( + { + intermediate = try(aws_iam_policy.intermediate[0].arn, null) + AmazonEC2ContainerRegistryPullOnly = "arn:${local.partition}:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly" + }, + var.intermediate_role_policies + ) : k => v if local.enable_ira } + + policy_arn = each.value + role = aws_iam_role.this[0].name +} diff --git a/modules/hybrid-node-role/outputs.tf b/modules/hybrid-node-role/outputs.tf new file mode 100644 index 0000000000..dc4e26e519 --- /dev/null +++ b/modules/hybrid-node-role/outputs.tf @@ -0,0 +1,37 @@ +################################################################################ +# Node IAM Role +################################################################################ + +output "name" { + description = "The name of the node IAM role" + value = try(aws_iam_role.this[0].name, null) +} + +output "arn" { + description = "The Amazon Resource Name (ARN) specifying the node IAM role" + value = try(aws_iam_role.this[0].arn, null) +} + +output "unique_id" { + description = "Stable and unique string identifying the node IAM role" + value = try(aws_iam_role.this[0].unique_id, null) +} + +################################################################################ +# Intermedaite IAM Role +################################################################################ + +output "intermediate_role_name" { + description = "The name of the node IAM role" + value = try(aws_iam_role.intermediate[0].name, null) +} + +output "intermediate_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the node IAM role" + value = try(aws_iam_role.intermediate[0].arn, null) +} + +output "intermediate_role_unique_id" { + description = "Stable and unique string identifying the node IAM role" + value = try(aws_iam_role.intermediate[0].unique_id, null) +} diff --git a/modules/hybrid-node-role/variables.tf b/modules/hybrid-node-role/variables.tf new file mode 100644 index 0000000000..6e8d80465e --- /dev/null +++ b/modules/hybrid-node-role/variables.tf @@ -0,0 +1,239 @@ +variable "create" { + description = "Controls if resources should be created (affects nearly all resources)" + type = bool + default = true +} + +################################################################################ +# Node IAM Role +################################################################################ + +variable "name" { + description = "Name of the IAM role" + type = string + default = "EKSHybridNode" +} + +variable "use_name_prefix" { + description = "Determines whether the name of the IAM role (`name`) is used as a prefix" + type = bool + default = true +} + +variable "path" { + description = "Path of the IAM role" + type = string + default = "/" +} + +variable "description" { + description = "IAM role description" + type = string + default = "EKS Hybrid Node IAM role" +} + +variable "max_session_duration" { + description = "Maximum API session duration in seconds between 3600 and 43200" + type = number + default = null +} + +variable "permissions_boundary_arn" { + description = "Permissions boundary ARN to use for the IAM role" + type = string + default = null +} + +variable "tags" { + description = "A map of additional tags to add the the IAM role" + type = map(any) + default = {} +} + +variable "enable_ira" { + description = "Enables IAM Roles Anywhere based IAM permissions on the node" + type = bool + default = false +} + +variable "trust_anchor_arns" { + description = "List of IAM Roles Anywhere trust anchor ARNs. Required if `enable_ira` is set to `true`" + type = list(string) + default = [] +} + +################################################################################ +# Node IAM Role Policy +################################################################################ + +variable "policy_name" { + description = "Name of the IAM policy" + type = string + default = "EKSHybridNode" +} + +variable "policy_use_name_prefix" { + description = "Determines whether the name of the IAM policy (`policy_name`) is used as a prefix" + type = bool + default = true +} + +variable "policy_path" { + description = "Path of the IAM policy" + type = string + default = "/" +} + +variable "policy_description" { + description = "IAM policy description" + type = string + default = "EKS Hybrid Node IAM role policy" +} + +variable "policy_statements" { + description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed" + type = any + default = [] +} + +variable "policies" { + description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" + type = map(string) + default = {} +} + +variable "cluster_arns" { + description = "List of EKS cluster ARNs to allow the node to describe" + type = list(string) + default = ["*"] +} + +variable "enable_pod_identity" { + description = "Enables EKS Pod Identity based IAM permissions on the node" + type = bool + default = true +} + +################################################################################ +# IAM Roles Anywhere Profile +################################################################################ + +variable "ira_profile_name" { + description = "Name of the Roles Anywhere profile" + type = string + default = null +} + +variable "ira_profile_duration_seconds" { + description = "The number of seconds the vended session credentials are valid for. Defaults to `3600`" + type = number + default = null +} + +variable "ira_profile_managed_policy_arns" { + description = "A list of managed policy ARNs that apply to the vended session credentials" + type = list(string) + default = [] +} + +variable "ira_profile_require_instance_properties" { + description = "Specifies whether instance properties are required in [CreateSession](https://docs.aws.amazon.com/rolesanywhere/latest/APIReference/API_CreateSession.html) requests with this profile" + type = bool + default = null +} + +variable "ira_profile_session_policy" { + description = "A session policy that applies to the trust boundary of the vended session credentials" + type = string + default = null +} + +################################################################################ +# Roles Anywhere Trust Anchor +################################################################################ + +variable "ira_trust_anchor_name" { + description = "Name of the Roles Anywhere trust anchor" + type = string + default = null +} + +variable "ira_trust_anchor_notification_settings" { + description = "Notification settings for the trust anchor" + type = any + default = [] +} + +variable "ira_trust_anchor_acm_pca_arn" { + description = "The ARN of the ACM PCA that issued the trust anchor certificate" + type = string + default = null +} + +variable "ira_trust_anchor_x509_certificate_data" { + description = "The X.509 certificate data of the trust anchor" + type = string + default = null +} + +variable "ira_trust_anchor_source_type" { + description = "The source type of the trust anchor" + type = string + default = null +} + +################################################################################ +# Intermediate IAM Role +################################################################################ + +variable "intermediate_role_name" { + description = "Name of the IAM role" + type = string + default = null +} + +variable "intermediate_role_use_name_prefix" { + description = "Determines whether the name of the IAM role (`intermediate_role_name`) is used as a prefix" + type = bool + default = true +} + +variable "intermediate_role_path" { + description = "Path of the IAM role" + type = string + default = "/" +} + +variable "intermediate_role_description" { + description = "IAM role description" + type = string + default = "EKS Hybrid Node IAM Roles Anywhere intermediate IAM role" +} + +################################################################################ +# Intermediate IAM Role Policy +################################################################################ + +variable "intermediate_policy_name" { + description = "Name of the IAM policy" + type = string + default = null +} + +variable "intermediate_policy_use_name_prefix" { + description = "Determines whether the name of the IAM policy (`intermediate_policy_name`) is used as a prefix" + type = bool + default = true +} + +variable "intermediate_policy_statements" { + description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed" + type = any + default = [] +} + +variable "intermediate_role_policies" { + description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" + type = map(string) + default = {} +} diff --git a/modules/hybrid-node-role/versions.tf b/modules/hybrid-node-role/versions.tf new file mode 100644 index 0000000000..5b36c201aa --- /dev/null +++ b/modules/hybrid-node-role/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.3.2" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.79" + } + } +} diff --git a/modules/karpenter/README.md b/modules/karpenter/README.md index ef2be2099c..891d1635df 100644 --- a/modules/karpenter/README.md +++ b/modules/karpenter/README.md @@ -86,13 +86,13 @@ module "karpenter" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.75 | +| [aws](#requirement\_aws) | >= 5.79 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.75 | +| [aws](#provider\_aws) | >= 5.79 | ## Modules diff --git a/modules/karpenter/versions.tf b/modules/karpenter/versions.tf index 0099e6baaf..5b36c201aa 100644 --- a/modules/karpenter/versions.tf +++ b/modules/karpenter/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } } } diff --git a/modules/self-managed-node-group/README.md b/modules/self-managed-node-group/README.md index 7c76477049..654e223b1e 100644 --- a/modules/self-managed-node-group/README.md +++ b/modules/self-managed-node-group/README.md @@ -43,13 +43,13 @@ module "self_managed_node_group" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.2 | -| [aws](#requirement\_aws) | >= 5.75 | +| [aws](#requirement\_aws) | >= 5.79 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.75 | +| [aws](#provider\_aws) | >= 5.79 | ## Modules diff --git a/modules/self-managed-node-group/versions.tf b/modules/self-managed-node-group/versions.tf index 0099e6baaf..5b36c201aa 100644 --- a/modules/self-managed-node-group/versions.tf +++ b/modules/self-managed-node-group/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } } } diff --git a/outputs.tf b/outputs.tf index 0bd2d10f4e..688076f604 100644 --- a/outputs.tf +++ b/outputs.tf @@ -176,12 +176,12 @@ output "cluster_tls_certificate_sha1_fingerprint" { ################################################################################ output "cluster_iam_role_name" { - description = "IAM role name of the EKS cluster" + description = "Cluster IAM role name" value = try(aws_iam_role.this[0].name, null) } output "cluster_iam_role_arn" { - description = "IAM role ARN of the EKS cluster" + description = "Cluster IAM role ARN" value = try(aws_iam_role.this[0].arn, null) } @@ -190,6 +190,25 @@ output "cluster_iam_role_unique_id" { value = try(aws_iam_role.this[0].unique_id, null) } +################################################################################ +# EKS Auto Node IAM Role +################################################################################ + +output "node_iam_role_name" { + description = "EKS Auto node IAM role name" + value = try(aws_iam_role.eks_auto[0].name, null) +} + +output "node_iam_role_arn" { + description = "EKS Auto node IAM role ARN" + value = try(aws_iam_role.eks_auto[0].arn, null) +} + +output "node_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = try(aws_iam_role.eks_auto[0].unique_id, null) +} + ################################################################################ # EKS Addons ################################################################################ diff --git a/tests/fargate-profile/README.md b/tests/eks-fargate-profile/README.md similarity index 91% rename from tests/fargate-profile/README.md rename to tests/eks-fargate-profile/README.md index a50029c722..9035c4efbf 100644 --- a/tests/fargate-profile/README.md +++ b/tests/eks-fargate-profile/README.md @@ -1,4 +1,4 @@ -# Fargate Profile +# EKS Fargate Profile ## Usage @@ -18,13 +18,13 @@ 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.75 | +| [aws](#requirement\_aws) | >= 5.79 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.75 | +| [aws](#provider\_aws) | >= 5.79 | ## Modules @@ -58,8 +58,8 @@ No inputs. | [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\_arn](#output\_cluster\_iam\_role\_arn) | Cluster IAM role ARN | +| [cluster\_iam\_role\_name](#output\_cluster\_iam\_role\_name) | Cluster IAM role name | | [cluster\_iam\_role\_unique\_id](#output\_cluster\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [cluster\_id](#output\_cluster\_id) | The ID of the EKS cluster. Note: currently a value is returned only for local EKS clusters created on Outposts | | [cluster\_identity\_providers](#output\_cluster\_identity\_providers) | Map of attribute maps for all EKS identity providers enabled | @@ -79,6 +79,9 @@ No inputs. | [kms\_key\_arn](#output\_kms\_key\_arn) | The Amazon Resource Name (ARN) of the key | | [kms\_key\_id](#output\_kms\_key\_id) | The globally unique identifier for the key | | [kms\_key\_policy](#output\_kms\_key\_policy) | The IAM resource policy set on the key | +| [node\_iam\_role\_arn](#output\_node\_iam\_role\_arn) | EKS Auto node IAM role ARN | +| [node\_iam\_role\_name](#output\_node\_iam\_role\_name) | EKS Auto node IAM role name | +| [node\_iam\_role\_unique\_id](#output\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [node\_security\_group\_arn](#output\_node\_security\_group\_arn) | Amazon Resource Name (ARN) of the node shared security group | | [node\_security\_group\_id](#output\_node\_security\_group\_id) | ID of the node shared security group | | [oidc\_provider](#output\_oidc\_provider) | The OpenID Connect identity provider (issuer URL without leading `https://`) | diff --git a/tests/fargate-profile/main.tf b/tests/eks-fargate-profile/main.tf similarity index 96% rename from tests/fargate-profile/main.tf rename to tests/eks-fargate-profile/main.tf index fc9afc76ed..37fc44b3f9 100644 --- a/tests/fargate-profile/main.tf +++ b/tests/eks-fargate-profile/main.tf @@ -2,7 +2,13 @@ provider "aws" { region = local.region } -data "aws_availability_zones" "available" {} +data "aws_availability_zones" "available" { + # Exclude local zones + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} locals { name = "ex-${basename(path.cwd)}" diff --git a/tests/eks-fargate-profile/outputs.tf b/tests/eks-fargate-profile/outputs.tf new file mode 100644 index 0000000000..9ed8c27220 --- /dev/null +++ b/tests/eks-fargate-profile/outputs.tf @@ -0,0 +1,245 @@ +################################################################################ +# 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 = "Cluster IAM role name" + value = module.eks.cluster_iam_role_name +} + +output "cluster_iam_role_arn" { + description = "Cluster IAM role ARN" + 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 Auto Node IAM Role +################################################################################ + +output "node_iam_role_name" { + description = "EKS Auto node IAM role name" + value = module.eks.node_iam_role_name +} + +output "node_iam_role_arn" { + description = "EKS Auto node IAM role ARN" + value = module.eks.node_iam_role_arn +} + +output "node_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.eks.node_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/eks-fargate-profile/variables.tf b/tests/eks-fargate-profile/variables.tf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/eks-fargate-profile/versions.tf b/tests/eks-fargate-profile/versions.tf new file mode 100644 index 0000000000..5b36c201aa --- /dev/null +++ b/tests/eks-fargate-profile/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.3.2" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.79" + } + } +} diff --git a/tests/eks-hybrid-nodes/README.md b/tests/eks-hybrid-nodes/README.md new file mode 100644 index 0000000000..341b73150e --- /dev/null +++ b/tests/eks-hybrid-nodes/README.md @@ -0,0 +1,65 @@ +# EKS Hybrid Node IAM Role + +## 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.79 | +| [tls](#requirement\_tls) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [tls](#provider\_tls) | >= 4.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [disabled\_eks\_hybrid\_node\_role](#module\_disabled\_eks\_hybrid\_node\_role) | ../../modules/hybrid-node-role | n/a | +| [eks\_hybrid\_node\_role](#module\_eks\_hybrid\_node\_role) | ../../modules/hybrid-node-role | n/a | +| [ira\_eks\_hybrid\_node\_role](#module\_ira\_eks\_hybrid\_node\_role) | ../../modules/hybrid-node-role | n/a | + +## Resources + +| Name | Type | +|------|------| +| [tls_private_key.example](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource | +| [tls_self_signed_cert.example](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/self_signed_cert) | resource | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The Amazon Resource Name (ARN) specifying the node IAM role | +| [intermediate\_role\_arn](#output\_intermediate\_role\_arn) | The Amazon Resource Name (ARN) specifying the node IAM role | +| [intermediate\_role\_name](#output\_intermediate\_role\_name) | The name of the node IAM role | +| [intermediate\_role\_unique\_id](#output\_intermediate\_role\_unique\_id) | Stable and unique string identifying the node IAM role | +| [ira\_arn](#output\_ira\_arn) | The Amazon Resource Name (ARN) specifying the node IAM role | +| [ira\_intermediate\_role\_arn](#output\_ira\_intermediate\_role\_arn) | The Amazon Resource Name (ARN) specifying the node IAM role | +| [ira\_intermediate\_role\_name](#output\_ira\_intermediate\_role\_name) | The name of the node IAM role | +| [ira\_intermediate\_role\_unique\_id](#output\_ira\_intermediate\_role\_unique\_id) | Stable and unique string identifying the node IAM role | +| [ira\_name](#output\_ira\_name) | The name of the node IAM role | +| [ira\_unique\_id](#output\_ira\_unique\_id) | Stable and unique string identifying the node IAM role | +| [name](#output\_name) | The name of the node IAM role | +| [unique\_id](#output\_unique\_id) | Stable and unique string identifying the node IAM role | + diff --git a/tests/eks-hybrid-nodes/main.tf b/tests/eks-hybrid-nodes/main.tf new file mode 100644 index 0000000000..ec49725102 --- /dev/null +++ b/tests/eks-hybrid-nodes/main.tf @@ -0,0 +1,84 @@ +provider "aws" { + region = local.region +} + +locals { + name = "ex-${basename(path.cwd)}" + region = "us-west-2" + + tags = { + Test = local.name + GithubRepo = "terraform-aws-eks" + GithubOrg = "terraform-aws-modules" + } +} + +################################################################################ +# Hybrid Node IAM Module +################################################################################ + +# Default (SSM) +module "eks_hybrid_node_role" { + source = "../../modules/hybrid-node-role" + + policy_statements = [ + { + actions = [ + "s3:Get*", + "s3:List*", + ] + resources = ["*"] + } + ] + + tags = local.tags +} + +# IAM Roles Anywhere +module "ira_eks_hybrid_node_role" { + source = "../../modules/hybrid-node-role" + + name = "${local.name}-ira" + + enable_ira = true + + ira_trust_anchor_source_type = "CERTIFICATE_BUNDLE" + ira_trust_anchor_x509_certificate_data = local.cert_data + + tags = local.tags +} + +module "disabled_eks_hybrid_node_role" { + source = "../../modules/hybrid-node-role" + + create = false +} + +################################################################################ +# Supporting Resources +################################################################################ + +resource "tls_private_key" "example" { + algorithm = "RSA" + rsa_bits = 4096 +} + +resource "tls_self_signed_cert" "example" { + private_key_pem = tls_private_key.example.private_key_pem + + subject { + common_name = "Custom root" + organization = "ACME Examples, Inc" + } + + validity_period_hours = 17544 + is_ca_certificate = true + + allowed_uses = [ + "cert_signing", + ] +} + +locals { + cert_data = trimspace(replace(trimprefix(tls_self_signed_cert.example.cert_pem, "-----BEGIN CERTIFICATE-----"), "-----END CERTIFICATE-----", "")) +} diff --git a/tests/eks-hybrid-nodes/outputs.tf b/tests/eks-hybrid-nodes/outputs.tf new file mode 100644 index 0000000000..ffea7ccbed --- /dev/null +++ b/tests/eks-hybrid-nodes/outputs.tf @@ -0,0 +1,71 @@ +################################################################################ +# Default (SSM) - Node IAM Role +################################################################################ + +# Node IAM Role +output "name" { + description = "The name of the node IAM role" + value = module.eks_hybrid_node_role.name +} + +output "arn" { + description = "The Amazon Resource Name (ARN) specifying the node IAM role" + value = module.eks_hybrid_node_role.arn +} + +output "unique_id" { + description = "Stable and unique string identifying the node IAM role" + value = module.eks_hybrid_node_role.unique_id +} + +# Intermedaite IAM Role +output "intermediate_role_name" { + description = "The name of the node IAM role" + value = module.eks_hybrid_node_role.intermediate_role_name +} + +output "intermediate_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the node IAM role" + value = module.eks_hybrid_node_role.intermediate_role_arn +} + +output "intermediate_role_unique_id" { + description = "Stable and unique string identifying the node IAM role" + value = module.eks_hybrid_node_role.intermediate_role_unique_id +} + +################################################################################ +# IAM Roles Anywhere - Node IAM Role +################################################################################ + +# Node IAM Role +output "ira_name" { + description = "The name of the node IAM role" + value = module.ira_eks_hybrid_node_role.name +} + +output "ira_arn" { + description = "The Amazon Resource Name (ARN) specifying the node IAM role" + value = module.ira_eks_hybrid_node_role.arn +} + +output "ira_unique_id" { + description = "Stable and unique string identifying the node IAM role" + value = module.ira_eks_hybrid_node_role.unique_id +} + +# Intermedaite IAM Role +output "ira_intermediate_role_name" { + description = "The name of the node IAM role" + value = module.ira_eks_hybrid_node_role.intermediate_role_name +} + +output "ira_intermediate_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the node IAM role" + value = module.ira_eks_hybrid_node_role.intermediate_role_arn +} + +output "ira_intermediate_role_unique_id" { + description = "Stable and unique string identifying the node IAM role" + value = module.ira_eks_hybrid_node_role.intermediate_role_unique_id +} diff --git a/tests/eks-hybrid-nodes/variables.tf b/tests/eks-hybrid-nodes/variables.tf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/eks-hybrid-nodes/versions.tf b/tests/eks-hybrid-nodes/versions.tf new file mode 100644 index 0000000000..42232f11ed --- /dev/null +++ b/tests/eks-hybrid-nodes/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.3.2" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.79" + } + tls = { + source = "hashicorp/tls" + version = ">= 4.0" + } + } +} diff --git a/tests/eks-managed-node-group/README.md b/tests/eks-managed-node-group/README.md index b1f4ab1398..6b5b02ab26 100644 --- a/tests/eks-managed-node-group/README.md +++ b/tests/eks-managed-node-group/README.md @@ -18,13 +18,13 @@ 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.75 | +| [aws](#requirement\_aws) | >= 5.79 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.75 | +| [aws](#provider\_aws) | >= 5.79 | ## Modules @@ -68,8 +68,8 @@ No inputs. | [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\_arn](#output\_cluster\_iam\_role\_arn) | Cluster IAM role ARN | +| [cluster\_iam\_role\_name](#output\_cluster\_iam\_role\_name) | Cluster IAM role name | | [cluster\_iam\_role\_unique\_id](#output\_cluster\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [cluster\_id](#output\_cluster\_id) | The ID of the EKS cluster. Note: currently a value is returned only for local EKS clusters created on Outposts | | [cluster\_identity\_providers](#output\_cluster\_identity\_providers) | Map of attribute maps for all EKS identity providers enabled | @@ -89,6 +89,9 @@ No inputs. | [kms\_key\_arn](#output\_kms\_key\_arn) | The Amazon Resource Name (ARN) of the key | | [kms\_key\_id](#output\_kms\_key\_id) | The globally unique identifier for the key | | [kms\_key\_policy](#output\_kms\_key\_policy) | The IAM resource policy set on the key | +| [node\_iam\_role\_arn](#output\_node\_iam\_role\_arn) | EKS Auto node IAM role ARN | +| [node\_iam\_role\_name](#output\_node\_iam\_role\_name) | EKS Auto node IAM role name | +| [node\_iam\_role\_unique\_id](#output\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [node\_security\_group\_arn](#output\_node\_security\_group\_arn) | Amazon Resource Name (ARN) of the node shared security group | | [node\_security\_group\_id](#output\_node\_security\_group\_id) | ID of the node shared security group | | [oidc\_provider](#output\_oidc\_provider) | The OpenID Connect identity provider (issuer URL without leading `https://`) | diff --git a/tests/eks-managed-node-group/main.tf b/tests/eks-managed-node-group/main.tf index 8a4d48c7f0..5d26a53fca 100644 --- a/tests/eks-managed-node-group/main.tf +++ b/tests/eks-managed-node-group/main.tf @@ -3,7 +3,14 @@ provider "aws" { } data "aws_caller_identity" "current" {} -data "aws_availability_zones" "available" {} + +data "aws_availability_zones" "available" { + # Exclude local zones + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} locals { name = "ex-${replace(basename(path.cwd), "_", "-")}" diff --git a/tests/eks-managed-node-group/outputs.tf b/tests/eks-managed-node-group/outputs.tf index 9357464c29..9ed8c27220 100644 --- a/tests/eks-managed-node-group/outputs.tf +++ b/tests/eks-managed-node-group/outputs.tf @@ -142,12 +142,12 @@ output "cluster_tls_certificate_sha1_fingerprint" { ################################################################################ output "cluster_iam_role_name" { - description = "IAM role name of the EKS cluster" + description = "Cluster IAM role name" value = module.eks.cluster_iam_role_name } output "cluster_iam_role_arn" { - description = "IAM role ARN of the EKS cluster" + description = "Cluster IAM role ARN" value = module.eks.cluster_iam_role_arn } @@ -156,6 +156,25 @@ output "cluster_iam_role_unique_id" { value = module.eks.cluster_iam_role_unique_id } +################################################################################ +# EKS Auto Node IAM Role +################################################################################ + +output "node_iam_role_name" { + description = "EKS Auto node IAM role name" + value = module.eks.node_iam_role_name +} + +output "node_iam_role_arn" { + description = "EKS Auto node IAM role ARN" + value = module.eks.node_iam_role_arn +} + +output "node_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.eks.node_iam_role_unique_id +} + ################################################################################ # EKS Addons ################################################################################ diff --git a/tests/eks-managed-node-group/versions.tf b/tests/eks-managed-node-group/versions.tf index 0099e6baaf..5b36c201aa 100644 --- a/tests/eks-managed-node-group/versions.tf +++ b/tests/eks-managed-node-group/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } } } diff --git a/tests/fast-addons/README.md b/tests/fast-addons/README.md index 693784e79d..59a5cea926 100644 --- a/tests/fast-addons/README.md +++ b/tests/fast-addons/README.md @@ -22,13 +22,13 @@ 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.75 | +| [aws](#requirement\_aws) | >= 5.79 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.75 | +| [aws](#provider\_aws) | >= 5.79 | ## Modules diff --git a/tests/fast-addons/versions.tf b/tests/fast-addons/versions.tf index 0099e6baaf..5b36c201aa 100644 --- a/tests/fast-addons/versions.tf +++ b/tests/fast-addons/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } } } diff --git a/tests/self-managed-node-group/README.md b/tests/self-managed-node-group/README.md index 1587f7c177..437dfa851d 100644 --- a/tests/self-managed-node-group/README.md +++ b/tests/self-managed-node-group/README.md @@ -18,13 +18,13 @@ 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.75 | +| [aws](#requirement\_aws) | >= 5.79 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.75 | +| [aws](#provider\_aws) | >= 5.79 | ## Modules @@ -64,8 +64,8 @@ No inputs. | [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\_arn](#output\_cluster\_iam\_role\_arn) | Cluster IAM role ARN | +| [cluster\_iam\_role\_name](#output\_cluster\_iam\_role\_name) | Cluster IAM role name | | [cluster\_iam\_role\_unique\_id](#output\_cluster\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [cluster\_id](#output\_cluster\_id) | The ID of the EKS cluster. Note: currently a value is returned only for local EKS clusters created on Outposts | | [cluster\_identity\_providers](#output\_cluster\_identity\_providers) | Map of attribute maps for all EKS identity providers enabled | @@ -85,6 +85,9 @@ No inputs. | [kms\_key\_arn](#output\_kms\_key\_arn) | The Amazon Resource Name (ARN) of the key | | [kms\_key\_id](#output\_kms\_key\_id) | The globally unique identifier for the key | | [kms\_key\_policy](#output\_kms\_key\_policy) | The IAM resource policy set on the key | +| [node\_iam\_role\_arn](#output\_node\_iam\_role\_arn) | EKS Auto node IAM role ARN | +| [node\_iam\_role\_name](#output\_node\_iam\_role\_name) | EKS Auto node IAM role name | +| [node\_iam\_role\_unique\_id](#output\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [node\_security\_group\_arn](#output\_node\_security\_group\_arn) | Amazon Resource Name (ARN) of the node shared security group | | [node\_security\_group\_id](#output\_node\_security\_group\_id) | ID of the node shared security group | | [oidc\_provider](#output\_oidc\_provider) | The OpenID Connect identity provider (issuer URL without leading `https://`) | diff --git a/tests/self-managed-node-group/main.tf b/tests/self-managed-node-group/main.tf index afe7aac9a1..f2181fdc75 100644 --- a/tests/self-managed-node-group/main.tf +++ b/tests/self-managed-node-group/main.tf @@ -3,7 +3,14 @@ provider "aws" { } data "aws_caller_identity" "current" {} -data "aws_availability_zones" "available" {} + +data "aws_availability_zones" "available" { + # Exclude local zones + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} locals { name = "ex-${replace(basename(path.cwd), "_", "-")}" diff --git a/tests/self-managed-node-group/outputs.tf b/tests/self-managed-node-group/outputs.tf index 9357464c29..9ed8c27220 100644 --- a/tests/self-managed-node-group/outputs.tf +++ b/tests/self-managed-node-group/outputs.tf @@ -142,12 +142,12 @@ output "cluster_tls_certificate_sha1_fingerprint" { ################################################################################ output "cluster_iam_role_name" { - description = "IAM role name of the EKS cluster" + description = "Cluster IAM role name" value = module.eks.cluster_iam_role_name } output "cluster_iam_role_arn" { - description = "IAM role ARN of the EKS cluster" + description = "Cluster IAM role ARN" value = module.eks.cluster_iam_role_arn } @@ -156,6 +156,25 @@ output "cluster_iam_role_unique_id" { value = module.eks.cluster_iam_role_unique_id } +################################################################################ +# EKS Auto Node IAM Role +################################################################################ + +output "node_iam_role_name" { + description = "EKS Auto node IAM role name" + value = module.eks.node_iam_role_name +} + +output "node_iam_role_arn" { + description = "EKS Auto node IAM role ARN" + value = module.eks.node_iam_role_arn +} + +output "node_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.eks.node_iam_role_unique_id +} + ################################################################################ # EKS Addons ################################################################################ diff --git a/tests/self-managed-node-group/versions.tf b/tests/self-managed-node-group/versions.tf index 0099e6baaf..5b36c201aa 100644 --- a/tests/self-managed-node-group/versions.tf +++ b/tests/self-managed-node-group/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } } } diff --git a/variables.tf b/variables.tf index edd0d938a6..e59ee3dc6e 100644 --- a/variables.tf +++ b/variables.tf @@ -44,12 +44,24 @@ variable "authentication_mode" { default = "API_AND_CONFIG_MAP" } +variable "cluster_compute_config" { + description = "Configuration block for the cluster compute configuration" + type = any + default = {} +} + variable "cluster_upgrade_policy" { description = "Configuration block for the cluster upgrade policy" type = any default = {} } +variable "cluster_remote_network_config" { + description = "Configuration block for the cluster remote network configuration" + type = any + default = {} +} + variable "cluster_zonal_shift_config" { description = "Configuration block for the cluster zonal shift" type = any @@ -434,7 +446,7 @@ variable "custom_oidc_thumbprints" { ################################################################################ variable "create_iam_role" { - description = "Determines whether a an IAM role is created or to use an existing IAM role" + description = "Determines whether an IAM role is created for the cluster" type = bool default = true } @@ -458,7 +470,7 @@ variable "iam_role_use_name_prefix" { } variable "iam_role_path" { - description = "Cluster IAM role path" + description = "The IAM role path" type = string default = null } @@ -481,6 +493,13 @@ variable "iam_role_additional_policies" { default = {} } +# TODO - will be removed in next breaking change; user can add the policy on their own when needed +variable "enable_security_groups_for_pods" { + description = "Determines whether to add the necessary IAM permission policy for security groups for pods" + type = bool + default = true +} + variable "iam_role_tags" { description = "A map of additional tags to add to the IAM role created" type = map(string) @@ -549,6 +568,70 @@ variable "cluster_identity_providers" { default = {} } +################################################################################ +# EKS Auto Node IAM Role +################################################################################ + +variable "create_node_iam_role" { + description = "Determines whether an EKS Auto node IAM role is created" + type = bool + default = true +} + +variable "node_iam_role_name" { + description = "Name to use on the EKS Auto node IAM role created" + type = string + default = null +} + +variable "node_iam_role_use_name_prefix" { + description = "Determines whether the EKS Auto node IAM role name (`node_iam_role_name`) is used as a prefix" + type = bool + default = true +} + +variable "node_iam_role_path" { + description = "The EKS Auto node IAM role path" + type = string + default = null +} + +variable "node_iam_role_description" { + description = "Description of the EKS Auto node IAM role" + type = string + default = null +} + +variable "node_iam_role_permissions_boundary" { + description = "ARN of the policy that is used to set the permissions boundary for the EKS Auto node IAM role" + type = string + default = null +} + +variable "node_iam_role_additional_policies" { + description = "Additional policies to be added to the EKS Auto node IAM role" + type = map(string) + default = {} +} + +variable "node_iam_role_tags" { + description = "A map of additional tags to add to the EKS Auto node IAM role created" + type = map(string) + default = {} +} + +variable "enable_node_custom_tags_permissions" { + description = "Determines whether to enable permissions for custom tags for the EKS Auto node IAM role" + type = bool + default = true +} + +variable "node_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 = [] +} + ################################################################################ # Fargate ################################################################################ diff --git a/versions.tf b/versions.tf index fc9dadd253..aaa00e9b19 100644 --- a/versions.tf +++ b/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.75" + version = ">= 5.79" } tls = { source = "hashicorp/tls"