Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor for clarity #33

Merged
merged 1 commit into from
Sep 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,15 @@ Available targets:
| attributes | Additional attributes (e.g. `1`) | `list(string)` | `[]` | no |
| before\_cluster\_joining\_userdata | Additional `bash` commands to execute on each worker node before joining the EKS cluster (before executing the `bootstrap.sh` script). For more info, see https://kubedex.com/90-days-of-aws-eks-in-production | `string` | `""` | no |
| bootstrap\_additional\_options | Additional options to bootstrap.sh. DO NOT include `--kubelet-additional-args`, use `kubelet_additional_args` var instead. | `string` | `""` | no |
| cluster\_autoscaler\_enabled | Set true to label the node group so that the [Kubernetes Cluster Autoscaler](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/aws/README.md#auto-discovery-setup) will discover and autoscale it | `bool` | `null` | no |
| cluster\_name | The name of the EKS cluster | `string` | n/a | yes |
| context | Single object for setting entire context at once.<br>See description of individual variables for details.<br>Leave string and numeric variables as `null` to use default value.<br>Individual variable settings (non-null) override settings in context object,<br>except for attributes, tags, and additional\_tag\_map, which are merged. | <pre>object({<br> enabled = bool<br> namespace = string<br> environment = string<br> stage = string<br> name = string<br> delimiter = string<br> attributes = list(string)<br> tags = map(string)<br> additional_tag_map = map(string)<br> regex_replace_chars = string<br> label_order = list(string)<br> id_length_limit = number<br> })</pre> | <pre>{<br> "additional_tag_map": {},<br> "attributes": [],<br> "delimiter": null,<br> "enabled": true,<br> "environment": null,<br> "id_length_limit": null,<br> "label_order": [],<br> "name": null,<br> "namespace": null,<br> "regex_replace_chars": null,<br> "stage": null,<br> "tags": {}<br>}</pre> | no |
| create\_before\_destroy | Set true in order to create the new node group before destroying the old one.<br>If false, the old node group will be destroyed first, causing downtime.<br>Changing this setting will always cause node group to be replaced. | `bool` | `false` | no |
| delimiter | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.<br>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
| desired\_size | Initial desired number of worker nodes (external changes ignored) | `number` | n/a | yes |
| disk\_size | Disk size in GiB for worker nodes. Defaults to 20. Ignored it `launch_template_id` is supplied.<br>Terraform will only perform drift detection if a configuration value is provided. | `number` | `20` | no |
| ec2\_ssh\_key | SSH key pair name to use to access the worker nodes | `string` | `null` | no |
| enable\_cluster\_autoscaler | Set true to allow Kubernetes Cluster Auto Scaler to scale the node group | `bool` | `false` | no |
| enable\_cluster\_autoscaler | (Deprecated, use `cluster_autoscaler_enabled`) Set true to allow Kubernetes Cluster Auto Scaler to scale the node group | `bool` | `null` | no |
| enabled | Set to false to prevent the module from creating any resources | `bool` | `null` | no |
| environment | Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
| existing\_workers\_role\_policy\_arns | List of existing policy ARNs that will be attached to the workers default role on creation | `list(string)` | `[]` | no |
Expand All @@ -235,6 +236,7 @@ Available targets:
| subnet\_ids | A list of subnet IDs to launch resources in | `list(string)` | n/a | yes |
| tags | Additional tags (e.g. `map('BusinessUnit','XYZ')` | `map(string)` | `{}` | no |
| userdata\_override\_base64 | Many features of this module rely on the `bootstrap.sh` provided with Amazon Linux, and this module<br>may generate "user data" that expects to find that script. If you want to use an AMI that is not<br>compatible with the Amazon Linux `bootstrap.sh` initialization, then use `userdata_override_base64` to provide<br>your own (Base64 encoded) user data. Use "" to prevent any user data from being set.<br><br>Setting `userdata_override_base64` disables `kubernetes_taints`, `kubelet_additional_options`,<br>`before_cluster_joining_userdata`, `after_cluster_joining_userdata`, and `bootstrap_additional_options`. | `string` | `null` | no |
| worker\_role\_autoscale\_iam\_enabled | If true, the worker IAM role will be authorized to perform autoscaling operations. Not recommended.<br>Use [EKS IAM role for cluster autoscaler service account](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) instead. | `bool` | `false` | no |

## Outputs

Expand Down
4 changes: 3 additions & 1 deletion docs/terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@
| attributes | Additional attributes (e.g. `1`) | `list(string)` | `[]` | no |
| before\_cluster\_joining\_userdata | Additional `bash` commands to execute on each worker node before joining the EKS cluster (before executing the `bootstrap.sh` script). For more info, see https://kubedex.com/90-days-of-aws-eks-in-production | `string` | `""` | no |
| bootstrap\_additional\_options | Additional options to bootstrap.sh. DO NOT include `--kubelet-additional-args`, use `kubelet_additional_args` var instead. | `string` | `""` | no |
| cluster\_autoscaler\_enabled | Set true to label the node group so that the [Kubernetes Cluster Autoscaler](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/aws/README.md#auto-discovery-setup) will discover and autoscale it | `bool` | `null` | no |
| cluster\_name | The name of the EKS cluster | `string` | n/a | yes |
| context | Single object for setting entire context at once.<br>See description of individual variables for details.<br>Leave string and numeric variables as `null` to use default value.<br>Individual variable settings (non-null) override settings in context object,<br>except for attributes, tags, and additional\_tag\_map, which are merged. | <pre>object({<br> enabled = bool<br> namespace = string<br> environment = string<br> stage = string<br> name = string<br> delimiter = string<br> attributes = list(string)<br> tags = map(string)<br> additional_tag_map = map(string)<br> regex_replace_chars = string<br> label_order = list(string)<br> id_length_limit = number<br> })</pre> | <pre>{<br> "additional_tag_map": {},<br> "attributes": [],<br> "delimiter": null,<br> "enabled": true,<br> "environment": null,<br> "id_length_limit": null,<br> "label_order": [],<br> "name": null,<br> "namespace": null,<br> "regex_replace_chars": null,<br> "stage": null,<br> "tags": {}<br>}</pre> | no |
| create\_before\_destroy | Set true in order to create the new node group before destroying the old one.<br>If false, the old node group will be destroyed first, causing downtime.<br>Changing this setting will always cause node group to be replaced. | `bool` | `false` | no |
| delimiter | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.<br>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
| desired\_size | Initial desired number of worker nodes (external changes ignored) | `number` | n/a | yes |
| disk\_size | Disk size in GiB for worker nodes. Defaults to 20. Ignored it `launch_template_id` is supplied.<br>Terraform will only perform drift detection if a configuration value is provided. | `number` | `20` | no |
| ec2\_ssh\_key | SSH key pair name to use to access the worker nodes | `string` | `null` | no |
| enable\_cluster\_autoscaler | Set true to allow Kubernetes Cluster Auto Scaler to scale the node group | `bool` | `false` | no |
| enable\_cluster\_autoscaler | (Deprecated, use `cluster_autoscaler_enabled`) Set true to allow Kubernetes Cluster Auto Scaler to scale the node group | `bool` | `null` | no |
| enabled | Set to false to prevent the module from creating any resources | `bool` | `null` | no |
| environment | Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
| existing\_workers\_role\_policy\_arns | List of existing policy ARNs that will be attached to the workers default role on creation | `list(string)` | `[]` | no |
Expand All @@ -61,6 +62,7 @@
| subnet\_ids | A list of subnet IDs to launch resources in | `list(string)` | n/a | yes |
| tags | Additional tags (e.g. `map('BusinessUnit','XYZ')` | `map(string)` | `{}` | no |
| userdata\_override\_base64 | Many features of this module rely on the `bootstrap.sh` provided with Amazon Linux, and this module<br>may generate "user data" that expects to find that script. If you want to use an AMI that is not<br>compatible with the Amazon Linux `bootstrap.sh` initialization, then use `userdata_override_base64` to provide<br>your own (Base64 encoded) user data. Use "" to prevent any user data from being set.<br><br>Setting `userdata_override_base64` disables `kubernetes_taints`, `kubelet_additional_options`,<br>`before_cluster_joining_userdata`, `after_cluster_joining_userdata`, and `bootstrap_additional_options`. | `string` | `null` | no |
| worker\_role\_autoscale\_iam\_enabled | If true, the worker IAM role will be authorized to perform autoscaling operations. Not recommended.<br>Use [EKS IAM role for cluster autoscaler service account](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) instead. | `bool` | `false` | no |

## Outputs

Expand Down
85 changes: 85 additions & 0 deletions iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
locals {
aws_policy_prefix = format("arn:%s:iam::aws:policy", join("", data.aws_partition.current.*.partition))
}

data "aws_partition" "current" {
count = local.enabled ? 1 : 0
}

data "aws_iam_policy_document" "assume_role" {
count = local.enabled ? 1 : 0

statement {
effect = "Allow"
actions = ["sts:AssumeRole"]

principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}

data "aws_iam_policy_document" "amazon_eks_worker_node_autoscale_policy" {
count = (local.enabled && var.worker_role_autoscale_iam_enabled) ? 1 : 0
statement {
sid = "AllowToScaleEKSNodeGroupAutoScalingGroup"

actions = [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeAutoScalingInstances",
"autoscaling:DescribeLaunchConfigurations",
"autoscaling:DescribeTags",
"autoscaling:SetDesiredCapacity",
"autoscaling:TerminateInstanceInAutoScalingGroup",
"ec2:DescribeLaunchTemplateVersions"
]

resources = [
"*"
]
}
}

resource "aws_iam_policy" "amazon_eks_worker_node_autoscale_policy" {
count = (local.enabled && var.worker_role_autoscale_iam_enabled) ? 1 : 0
name = "${module.label.id}-autoscale"
policy = join("", data.aws_iam_policy_document.amazon_eks_worker_node_autoscale_policy.*.json)
}

resource "aws_iam_role" "default" {
count = local.enabled ? 1 : 0
name = module.label.id
assume_role_policy = join("", data.aws_iam_policy_document.assume_role.*.json)
tags = module.label.tags
}

resource "aws_iam_role_policy_attachment" "amazon_eks_worker_node_policy" {
count = local.enabled ? 1 : 0
policy_arn = format("%s/%s", local.aws_policy_prefix, "AmazonEKSWorkerNodePolicy")
role = join("", aws_iam_role.default.*.name)
}

resource "aws_iam_role_policy_attachment" "amazon_eks_worker_node_autoscale_policy" {
count = (local.enabled && var.worker_role_autoscale_iam_enabled) ? 1 : 0
policy_arn = join("", aws_iam_policy.amazon_eks_worker_node_autoscale_policy.*.arn)
role = join("", aws_iam_role.default.*.name)
}

resource "aws_iam_role_policy_attachment" "amazon_eks_cni_policy" {
count = local.enabled ? 1 : 0
policy_arn = format("%s/%s", local.aws_policy_prefix, "AmazonEKS_CNI_Policy")
role = join("", aws_iam_role.default.*.name)
}

resource "aws_iam_role_policy_attachment" "amazon_ec2_container_registry_read_only" {
count = local.enabled ? 1 : 0
policy_arn = format("%s/%s", local.aws_policy_prefix, "AmazonEC2ContainerRegistryReadOnly")
role = join("", aws_iam_role.default.*.name)
}

resource "aws_iam_role_policy_attachment" "existing_policies_for_eks_workers_role" {
for_each = local.enabled ? toset(var.existing_workers_role_policy_arns) : []
policy_arn = each.value
role = join("", aws_iam_role.default.*.name)
}
104 changes: 104 additions & 0 deletions launch-template.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
locals {
# The heavy use of the ternary operator `? :` is because it is one of the few ways to avoid
# evaluating expressions. The unused expression is not evaluated and so it does not have to be valid.
# This allows us to refer to resources that are only conditionally created and avoid creating
# dependencies on them that would not be avoided by using expressions like `join("",expr)`.
#
# We use this pattern with enabled for every boolean that begins with `need_` even though
# it is sometimes redundant, to ensure that ever `need_` is false and every dependent
# expression is not evaluated when enabled is false. Avoiding expression evaluations
# is also why, even for boolean expressions, we use
# local.enabled ? expression : false
# rather than
# local.enabled && expression
#
# The expression
# length(compact([var.launch_template_version])) > 0
# is a shorter way of accomplishing the same test as
# var.launch_template_version != null && var.launch_template_version != ""
# and as an idiom has the added benefit of being extensible:
# length(compact([x, y])) > 0
# is the same as
# x != null && x != "" && y != null && y != ""

configured_launch_template_name = var.launch_template_name == null ? "" : var.launch_template_name
configured_launch_template_version = length(local.configured_launch_template_name) > 0 && length(compact([var.launch_template_version])) > 0 ? var.launch_template_version : ""

generate_launch_template = local.enabled ? local.features_require_launch_template && length(local.configured_launch_template_name) == 0 : false
use_launch_template = local.enabled ? local.features_require_launch_template || length(local.configured_launch_template_name) > 0 : false

launch_template_id = local.use_launch_template ? (length(local.configured_launch_template_name) > 0 ? data.aws_launch_template.this[0].id : aws_launch_template.default[0].id) : ""
launch_template_version = local.use_launch_template ? (
length(local.configured_launch_template_version) > 0 ? local.configured_launch_template_version :
(
length(local.configured_launch_template_name) > 0 ? data.aws_launch_template.this[0].latest_version : aws_launch_template.default[0].latest_version
)
) : ""

launch_template_ami = length(local.configured_ami_image_id) == 0 ? (local.features_require_ami ? data.aws_ami.selected[0].image_id : "") : local.configured_ami_image_id

launch_template_vpc_security_group_ids = (
local.need_remote_access_sg ?
concat(data.aws_eks_cluster.this[0].vpc_config[*].cluster_security_group_id, aws_security_group.remote_access.*.id) : []
)

# launch_template_key = join(":", coalescelist(local.launch_template_vpc_security_group_ids, ["closed"]))
}

resource "aws_launch_template" "default" {
# We'll use this default if we aren't provided with a launch template during invocation
# We need to generate a new launch template every time the security group list changes
# so that we can detach the network interfaces from the security groups that we no
# longer need, so that the security groups can then be deleted.

# As a workaround for https://github.com/hashicorp/terraform/issues/26166 we
# always create a launch template. Commented out code will be restored when the bug is fixed.
count = local.enabled ? 1 : 0
#count = (local.enabled && local.generate_launch_template) ? 1 : 0
#for_each = (local.enabled && local.generate_launch_template) ? toset([local.launch_template_key]) : toset([])

block_device_mappings {
device_name = "/dev/xvda"

ebs {
volume_size = var.disk_size
}
}

name_prefix = module.label.id
update_default_version = true

instance_type = var.instance_types[0]
image_id = local.launch_template_ami == "" ? null : local.launch_template_ami
key_name = local.have_ssh_key ? var.ec2_ssh_key : null

dynamic "tag_specifications" {
for_each = var.resources_to_tag
content {
resource_type = tag_specifications.value
tags = local.node_tags
}
}

# See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
# and https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html
# Note in particular:
# If any containers that you deploy to the node group use the Instance Metadata Service Version 2,
# then make sure to set the Metadata response hop limit to 2 in your launch template.
metadata_options {
http_put_response_hop_limit = 2
# Despite being documented as "Optional", `http_endpoint` is required when `http_put_response_hop_limit` is set.
# We set it to the default setting of "enabled".
http_endpoint = "enabled"
}

vpc_security_group_ids = local.launch_template_vpc_security_group_ids
user_data = local.userdata
tags = local.node_group_tags
}

data "aws_launch_template" "this" {
count = local.enabled && length(local.configured_launch_template_name) > 0 ? 1 : 0

name = local.configured_launch_template_name
}
Loading