From 57f5f01d9d491e362d01cee1d4865834f128711e Mon Sep 17 00:00:00 2001 From: Benjamin Smith Date: Wed, 23 Aug 2023 13:32:45 -0700 Subject: [PATCH 1/4] upstream tailsclae --- modules/eks/tailscale/README.md | 145 ++++++++++ modules/eks/tailscale/context.tf | 279 +++++++++++++++++++ modules/eks/tailscale/main.tf | 265 ++++++++++++++++++ modules/eks/tailscale/outputs.tf | 3 + modules/eks/tailscale/provider-kubernetes.tf | 132 +++++++++ modules/eks/tailscale/providers.tf | 29 ++ modules/eks/tailscale/remote-state.tf | 20 ++ modules/eks/tailscale/variables.tf | 63 +++++ modules/eks/tailscale/versions.tf | 14 + 9 files changed, 950 insertions(+) create mode 100644 modules/eks/tailscale/README.md create mode 100644 modules/eks/tailscale/context.tf create mode 100644 modules/eks/tailscale/main.tf create mode 100644 modules/eks/tailscale/outputs.tf create mode 100644 modules/eks/tailscale/provider-kubernetes.tf create mode 100644 modules/eks/tailscale/providers.tf create mode 100644 modules/eks/tailscale/remote-state.tf create mode 100644 modules/eks/tailscale/variables.tf create mode 100644 modules/eks/tailscale/versions.tf diff --git a/modules/eks/tailscale/README.md b/modules/eks/tailscale/README.md new file mode 100644 index 000000000..15cc519e1 --- /dev/null +++ b/modules/eks/tailscale/README.md @@ -0,0 +1,145 @@ +# Component: eks/tailscale + +## Usage + +**Stack Level**: Regional + +Use this in the catalog or use these variables to overwrite the catalog values. + +```yaml +components: + terraform: + eks/tailscale: + vars: + enabled: true + name: tailscale + create_namespace: true + kubernetes_namespace: "tailscale" + image_repo: tailscale/k8s-operator + image_tag: unstable +``` + +## Requirements + +| Name | Version | +| ---------------------------------------- | -------- | +| [terraform](./#requirement\_terraform) | >= 1.0.0 | +| [aws](./#requirement\_aws) | >= 4.0 | +| [kubernetes](./#requirement\_kubernetes) | >= 2.7.1 | + +## Providers + +| Name | Version | +| ------------------------------------- | -------- | +| [aws](./#provider\_aws) | >= 4.0 | +| [kubernetes](./#provider\_kubernetes) | >= 2.7.1 | + +## Modules + +| Name | Source | Version | +| ------------------------------------- | -------------------------------------------------- | ------- | +| [eks](./#module\_eks) | cloudposse/stack-config/yaml//modules/remote-state | 1.4.1 | +| [iam\_roles](./#module\_iam\_roles) | ../../account-map/modules/iam-roles | n/a | +| [store\_read](./#module\_store\_read) | cloudposse/ssm-parameter-store/aws | 0.10.0 | +| [this](./#module\_this) | cloudposse/label/null | 0.25.0 | + +## Resources + +| Name | Type | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| [kubernetes\_cluster\_role.tailscale\_operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/cluster\_role) | resource | +| [kubernetes\_cluster\_role\_binding.tailscale\_operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/cluster\_role\_binding) | resource | +| [kubernetes\_deployment.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/deployment) | resource | +| [kubernetes\_namespace.default](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource | +| [kubernetes\_role.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role) | resource | +| [kubernetes\_role.proxies](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role) | resource | +| [kubernetes\_role\_binding.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role\_binding) | resource | +| [kubernetes\_role\_binding.proxies](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role\_binding) | resource | +| [kubernetes\_secret.operator\_oauth](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | +| [kubernetes\_service\_account.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service\_account) | resource | +| [kubernetes\_service\_account.proxies](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service\_account) | resource | +| [aws\_eks\_cluster.kubernetes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks\_cluster) | data source | +| [aws\_eks\_cluster\_auth.eks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks\_cluster\_auth) | data source | +| [aws\_subnet.vpc\_subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +| -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | +| [additional\_tag\_map](./#input\_additional\_tag\_map) |

Additional key-value pairs to add to each map in tags_as_list_of_maps. Not added to tags or id.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration.

| `map(string)` | `{}` | no | +| [attributes](./#input\_attributes) |

ID element. Additional attributes (e.g. workers or cluster) to add to id,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the delimiter
and treated as a single ID element.

| `list(string)` | `[]` | no | +| [chart\_values](./#input\_chart\_values) | Addition map values to yamlencode as `helm_release` values. | `any` | `{}` | no | +| [context](./#input\_context) |

Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as null to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional_tag_map, which are merged.

| `any` |
{
+  "additional_tag_map": {},
+  "attributes": [],
+  "delimiter": null,
+  "descriptor_formats": {},
+  "enabled": true,
+  "environment": null,
+  "id_length_limit": null,
+  "label_key_case": null,
+  "label_order": [],
+  "label_value_case": null,
+  "labels_as_tags": [
+    "unset"
+  ],
+  "name": null,
+  "namespace": null,
+  "regex_replace_chars": null,
+  "stage": null,
+  "tags": {},
+  "tenant": null
+}
+
| no | +| [create\_namespace](./#input\_create\_namespace) | Create the namespace if it does not yet exist. Defaults to `false`. | `bool` | `false` | no | +| [delimiter](./#input\_delimiter) |

Delimiter to be used between ID elements.
Defaults to - (hyphen). Set to "" to use no delimiter at all.

| `string` | `null` | no | +| [deployment\_name](./#input\_deployment\_name) | Name of the tailscale deployment, defaults to `tailscale` if this is null | `string` | `null` | no | +| [descriptor\_formats](./#input\_descriptor\_formats) |

Describe additional descriptors to be output in the descriptors output map.
Map of maps. Keys are names of descriptors. Values are maps of the form
{<br> format = string<br> labels = list(string)<br>}
(Type is any so the map values can later be enhanced to provide additional options.)
format is a Terraform format string to be passed to the format() function.
labels is a list of labels, in order, to pass to format() function.
Label values will be normalized before being passed to format() so they will be
identical to how they appear in id.
Default is {} (descriptors output will be empty).

| `any` | `{}` | no | +| [eks\_component\_name](./#input\_eks\_component\_name) | The name of the eks component | `string` | `"eks/cluster"` | no | +| [enabled](./#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | +| [env](./#input\_env) | Map of ENV vars in the format `key=value`. These ENV vars will be set in the `utils` provider before executing the data source | `map(string)` | `null` | no | +| [environment](./#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | +| [helm\_manifest\_experiment\_enabled](./#input\_helm\_manifest\_experiment\_enabled) | Enable storing of the rendered manifest for helm\_release so the full diff of what is changing can been seen in the plan | `bool` | `true` | no | +| [id\_length\_limit](./#input\_id\_length\_limit) |

Limit id to this many characters (minimum 6).
Set to 0 for unlimited length.
Set to null for keep the existing setting, which defaults to 0.
Does not affect id_full.

| `number` | `null` | no | +| [image\_repo](./#input\_image\_repo) | Image repository for the deployment | `string` | `"ghcr.io/tailscale/tailscale"` | no | +| [image\_tag](./#input\_image\_tag) | Image Tag for the deployment. | `string` | `"latest"` | no | +| [import\_profile\_name](./#input\_import\_profile\_name) | AWS Profile name to use when importing a resource | `string` | `null` | no | +| [import\_role\_arn](./#input\_import\_role\_arn) | IAM Role ARN to use when importing a resource | `string` | `null` | no | +| [kube\_data\_auth\_enabled](./#input\_kube\_data\_auth\_enabled) |

If true, use an aws_eks_cluster_auth data source to authenticate to the EKS cluster.
Disabled by kubeconfig_file_enabled or kube_exec_auth_enabled.

| `bool` | `false` | no | +| [kube\_exec\_auth\_aws\_profile](./#input\_kube\_exec\_auth\_aws\_profile) | The AWS config profile for `aws eks get-token` to use | `string` | `""` | no | +| [kube\_exec\_auth\_aws\_profile\_enabled](./#input\_kube\_exec\_auth\_aws\_profile\_enabled) | If `true`, pass `kube_exec_auth_aws_profile` as the `profile` to `aws eks get-token` | `bool` | `false` | no | +| [kube\_exec\_auth\_enabled](./#input\_kube\_exec\_auth\_enabled) |

If true, use the Kubernetes provider exec feature to execute aws eks get-token to authenticate to the EKS cluster.
Disabled by kubeconfig_file_enabled, overrides kube_data_auth_enabled.

| `bool` | `true` | no | +| [kube\_exec\_auth\_role\_arn](./#input\_kube\_exec\_auth\_role\_arn) | The role ARN for `aws eks get-token` to use | `string` | `""` | no | +| [kube\_exec\_auth\_role\_arn\_enabled](./#input\_kube\_exec\_auth\_role\_arn\_enabled) | If `true`, pass `kube_exec_auth_role_arn` as the role ARN to `aws eks get-token` | `bool` | `true` | no | +| [kube\_secret](./#input\_kube\_secret) | Kube Secret Name for tailscale | `string` | `"tailscale"` | no | +| [kubeconfig\_context](./#input\_kubeconfig\_context) | Context to choose from the Kubernetes kube config file | `string` | `""` | no | +| [kubeconfig\_exec\_auth\_api\_version](./#input\_kubeconfig\_exec\_auth\_api\_version) | The Kubernetes API version of the credentials returned by the `exec` auth plugin | `string` | `"client.authentication.k8s.io/v1beta1"` | no | +| [kubeconfig\_file](./#input\_kubeconfig\_file) | The Kubernetes provider `config_path` setting to use when `kubeconfig_file_enabled` is `true` | `string` | `""` | no | +| [kubeconfig\_file\_enabled](./#input\_kubeconfig\_file\_enabled) | If `true`, configure the Kubernetes provider with `kubeconfig_file` and use that kubeconfig file for authenticating to the EKS cluster | `bool` | `false` | no | +| [kubernetes\_namespace](./#input\_kubernetes\_namespace) | The namespace to install the release into. | `string` | n/a | yes | +| [label\_key\_case](./#input\_label\_key\_case) |

Controls the letter case of the tags keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the tags input.
Possible values: lower, title, upper.
Default value: title.

| `string` | `null` | no | +| [label\_order](./#input\_label\_order) |

The order in which the labels (ID elements) appear in the id.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present.

| `list(string)` | `null` | no | +| [label\_value\_case](./#input\_label\_value\_case) |

Controls the letter case of ID elements (labels) as included in id,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the tags input.
Possible values: lower, title, upper and none (no transformation).
Set this to title and set delimiter to "" to yield Pascal Case IDs.
Default value: lower.

| `string` | `null` | no | +| [labels\_as\_tags](./#input\_labels\_as\_tags) |

Set of labels (ID elements) to include as tags in the tags output.
Default is to include all labels.
Tags with empty values will not be included in the tags output.
Set to [] to suppress all generated tags.
Notes:
The value of the name tag, if included, will be the id, not the name.
Unlike other null-label inputs, the initial setting of labels_as_tags cannot be
changed in later chained modules. Attempts to change it will be silently ignored.

| `set(string)` |
[
+  "default"
+]
+
| no | +| [name](./#input\_name) |

ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as a tag.
The "name" tag is set to the full id string. There is no tag with the value of the name input.

| `string` | `null` | no | +| [namespace](./#input\_namespace) | ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique | `string` | `null` | no | +| [regex\_replace\_chars](./#input\_regex\_replace\_chars) |

Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, "/[^a-zA-Z0-9-]/" is used to remove all characters other than hyphens, letters and digits.

| `string` | `null` | no | +| [region](./#input\_region) | AWS Region | `string` | n/a | yes | +| [routes](./#input\_routes) | List of CIDR Ranges or IPs to allow Tailscale to connect to | `list(string)` | `[]` | no | +| [stage](./#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no | +| [tags](./#input\_tags) |

Additional tags (e.g. {'BusinessUnit': 'XYZ'}).
Neither the tag keys nor the tag values will be modified by this module.

| `map(string)` | `{}` | no | +| [tenant](./#input\_tenant) | ID element \_(Rarely used, not included by default)\_. A customer identifier, indicating who this instance of a resource is for | `string` | `null` | no | + +## Outputs + +| Name | Description | +| ----------------------------------- | ----------- | +| [deployment](./#output\_deployment) | n/a | +| | | + +## References + +* https://github.com/Ealenn/tailscale diff --git a/modules/eks/tailscale/context.tf b/modules/eks/tailscale/context.tf new file mode 100644 index 000000000..5e0ef8856 --- /dev/null +++ b/modules/eks/tailscale/context.tf @@ -0,0 +1,279 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.25.0" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + tenant = var.tenant + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + descriptor_formats = var.descriptor_formats + labels_as_tags = var.labels_as_tags + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + tenant = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + descriptor_formats = {} + # Note: we have to use [] instead of null for unset lists due to + # https://github.com/hashicorp/terraform/issues/28137 + # which was not fixed until Terraform 1.0.0, + # but we want the default to be all the labels in `label_order` + # and we want users to be able to prevent all tag generation + # by setting `labels_as_tags` to `[]`, so we need + # a different sentinel to indicate "default" + labels_as_tags = ["unset"] + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" +} + +variable "tenant" { + type = string + default = null + description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" +} + +variable "environment" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +variable "labels_as_tags" { + type = set(string) + default = ["default"] + description = <<-EOT + Set of labels (ID elements) to include as tags in the `tags` output. + Default is to include all labels. + Tags with empty values will not be included in the `tags` output. + Set to `[]` to suppress all generated tags. + **Notes:** + The value of the `name` tag, if included, will be the `id`, not the `name`. + Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be + changed in later chained modules. Attempts to change it will be silently ignored. + EOT +} + +variable "tags" { + type = map(string) + default = {} + description = <<-EOT + Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). + Neither the tag keys nor the tag values will be modified by this module. + EOT +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = <<-EOT + Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. + This is for some rare cases where resources want additional configuration of tags + and therefore take a list of maps with tag key, value, and additional configuration. + EOT +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "descriptor_formats" { + type = any + default = {} + description = <<-EOT + Describe additional descriptors to be output in the `descriptors` output map. + Map of maps. Keys are names of descriptors. Values are maps of the form + `{ + format = string + labels = list(string) + }` + (Type is `any` so the map values can later be enhanced to provide additional options.) + `format` is a Terraform format string to be passed to the `format()` function. + `labels` is a list of labels, in order, to pass to `format()` function. + Label values will be normalized before being passed to `format()` so they will be + identical to how they appear in `id`. + Default is `{}` (`descriptors` output will be empty). + EOT +} + +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/modules/eks/tailscale/main.tf b/modules/eks/tailscale/main.tf new file mode 100644 index 000000000..f14108d47 --- /dev/null +++ b/modules/eks/tailscale/main.tf @@ -0,0 +1,265 @@ +locals { + enabled = module.this.enabled + create_namespace = local.enabled + + routes = join(",", concat(var.routes, [for k, v in data.aws_subnet.vpc_subnets : v.cidr_block])) +} + +module "store_read" { + source = "cloudposse/ssm-parameter-store/aws" + version = "0.10.0" + + parameter_read = [ + "/tailscale/client_id", + "/tailscale/client_secret", + ] +} + +resource "kubernetes_secret" "operator_oauth" { + metadata { + name = "operator-oauth" + namespace = var.kubernetes_namespace + } + data = { + client_id = module.store_read.map["/tailscale/client_id"] + client_secret = module.store_read.map["/tailscale/client_secret"] + } +} + +resource "kubernetes_namespace" "default" { + count = local.create_namespace ? 1 : 0 + + metadata { + name = var.kubernetes_namespace + + labels = module.this.tags + } +} + + +resource "kubernetes_service_account" "proxies" { + metadata { + name = "proxies" + namespace = var.kubernetes_namespace + } +} + +resource "kubernetes_role" "proxies" { + metadata { + name = "proxies" + namespace = var.kubernetes_namespace + } + + rule { + verbs = ["*"] + api_groups = [""] + resources = ["secrets"] + } +} + +resource "kubernetes_role_binding" "proxies" { + metadata { + name = "proxies" + namespace = var.kubernetes_namespace + } + + subject { + kind = "ServiceAccount" + name = "proxies" + namespace = var.kubernetes_namespace + } + + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "Role" + name = "proxies" + } +} + +resource "kubernetes_service_account" "operator" { + metadata { + name = "operator" + namespace = var.kubernetes_namespace + } +} + +resource "kubernetes_cluster_role" "tailscale_operator" { + metadata { + name = "tailscale-operator" + } + + rule { + verbs = ["*"] + api_groups = [""] + resources = ["services", "services/status"] + } +} + +resource "kubernetes_cluster_role_binding" "tailscale_operator" { + metadata { + name = "tailscale-operator" + } + + subject { + kind = "ServiceAccount" + name = "operator" + namespace = var.kubernetes_namespace + } + + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = "tailscale-operator" + } +} + +resource "kubernetes_role" "operator" { + metadata { + name = "operator" + namespace = var.kubernetes_namespace + } + + rule { + verbs = ["*"] + api_groups = [""] + resources = ["secrets"] + } + + rule { + verbs = ["*"] + api_groups = ["apps"] + resources = ["statefulsets"] + } +} + +resource "kubernetes_role_binding" "operator" { + metadata { + name = "operator" + namespace = var.kubernetes_namespace + } + + subject { + kind = "ServiceAccount" + name = "operator" + namespace = var.kubernetes_namespace + } + + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "Role" + name = "operator" + } +} + +resource "kubernetes_deployment" "operator" { + metadata { + name = coalesce(var.deployment_name, "tailscale-operator") + namespace = var.kubernetes_namespace + labels = { + app = "tailscale" + } + } + + spec { + replicas = 1 + + selector { + match_labels = { + app = "operator" + } + } + + template { + metadata { + labels = { + app = "operator" + } + } + + spec { + volume { + name = "oauth" + + secret { + secret_name = "operator-oauth" + } + } + + container { + image = format("%s:%s", var.image_repo, var.image_tag) + name = "tailscale" + + env { + name = "OPERATOR_HOSTNAME" + value = format("%s-%s-%s-%s", "tailscale-operator", var.tenant, var.environment, var.stage) + } + + env { + name = "OPERATOR_SECRET" + value = "operator" + } + + env { + name = "OPERATOR_LOGGING" + value = "info" + } + + env { + name = "OPERATOR_NAMESPACE" + + value_from { + field_ref { + field_path = "metadata.namespace" + } + } + } + + env { + name = "CLIENT_ID_FILE" + value = "/oauth/client_id" + } + + env { + name = "CLIENT_SECRET_FILE" + value = "/oauth/client_secret" + } + + env { + name = "PROXY_IMAGE" + value = "tailscale/tailscale:unstable" + } + + env { + name = "PROXY_TAGS" + value = "tag:k8s" + } + + env { + name = "AUTH_PROXY" + value = "false" + } + + resources { + requests = { + cpu = "500m" + + memory = "100Mi" + } + } + + volume_mount { + name = "oauth" + read_only = true + mount_path = "/oauth" + } + } + + service_account_name = "operator" + } + } + + strategy { + type = "Recreate" + } + } +} diff --git a/modules/eks/tailscale/outputs.tf b/modules/eks/tailscale/outputs.tf new file mode 100644 index 000000000..2957f2de3 --- /dev/null +++ b/modules/eks/tailscale/outputs.tf @@ -0,0 +1,3 @@ +output "deployment" { + value = kubernetes_deployment.operator +} diff --git a/modules/eks/tailscale/provider-kubernetes.tf b/modules/eks/tailscale/provider-kubernetes.tf new file mode 100644 index 000000000..00cfd1542 --- /dev/null +++ b/modules/eks/tailscale/provider-kubernetes.tf @@ -0,0 +1,132 @@ +################## +# +# This file is a drop-in to provide a helm provider. +# +# All the following variables are just about configuring the Kubernetes provider +# to be able to modify EKS cluster. The reason there are so many options is +# because at various times, each one of them has had problems, so we give you a choice. +# +# The reason there are so many "enabled" inputs rather than automatically +# detecting whether or not they are enabled based on the value of the input +# is that any logic based on input values requires the values to be known during +# the "plan" phase of Terraform, and often they are not, which causes problems. +# +variable "kubeconfig_file_enabled" { + type = bool + default = false + description = "If `true`, configure the Kubernetes provider with `kubeconfig_file` and use that kubeconfig file for authenticating to the EKS cluster" +} + +variable "kubeconfig_file" { + type = string + default = "" + description = "The Kubernetes provider `config_path` setting to use when `kubeconfig_file_enabled` is `true`" +} + +variable "kubeconfig_context" { + type = string + default = "" + description = "Context to choose from the Kubernetes kube config file" +} + +variable "kube_data_auth_enabled" { + type = bool + default = false + description = <<-EOT + If `true`, use an `aws_eks_cluster_auth` data source to authenticate to the EKS cluster. + Disabled by `kubeconfig_file_enabled` or `kube_exec_auth_enabled`. + EOT +} + +variable "kube_exec_auth_enabled" { + type = bool + default = true + description = <<-EOT + If `true`, use the Kubernetes provider `exec` feature to execute `aws eks get-token` to authenticate to the EKS cluster. + Disabled by `kubeconfig_file_enabled`, overrides `kube_data_auth_enabled`. + EOT +} + +variable "kube_exec_auth_role_arn" { + type = string + default = "" + description = "The role ARN for `aws eks get-token` to use" +} + +variable "kube_exec_auth_role_arn_enabled" { + type = bool + default = true + description = "If `true`, pass `kube_exec_auth_role_arn` as the role ARN to `aws eks get-token`" +} + +variable "kube_exec_auth_aws_profile" { + type = string + default = "" + description = "The AWS config profile for `aws eks get-token` to use" +} + +variable "kube_exec_auth_aws_profile_enabled" { + type = bool + default = false + description = "If `true`, pass `kube_exec_auth_aws_profile` as the `profile` to `aws eks get-token`" +} + +variable "kubeconfig_exec_auth_api_version" { + type = string + default = "client.authentication.k8s.io/v1beta1" + description = "The Kubernetes API version of the credentials returned by the `exec` auth plugin" +} + +variable "helm_manifest_experiment_enabled" { + type = bool + default = true + description = "Enable storing of the rendered manifest for helm_release so the full diff of what is changing can been seen in the plan" +} + +locals { + kubeconfig_file_enabled = var.kubeconfig_file_enabled + kube_exec_auth_enabled = local.kubeconfig_file_enabled ? false : var.kube_exec_auth_enabled + kube_data_auth_enabled = local.kube_exec_auth_enabled ? false : var.kube_data_auth_enabled + + # Eventually we might try to get this from an environment variable + kubeconfig_exec_auth_api_version = var.kubeconfig_exec_auth_api_version + + exec_profile = local.kube_exec_auth_enabled && var.kube_exec_auth_aws_profile_enabled ? [ + "--profile", var.kube_exec_auth_aws_profile + ] : [] + + kube_exec_auth_role_arn = coalesce(var.kube_exec_auth_role_arn, var.import_role_arn, module.iam_roles.terraform_role_arn) + exec_role = local.kube_exec_auth_enabled && var.kube_exec_auth_role_arn_enabled ? [ + "--role-arn", local.kube_exec_auth_role_arn + ] : [] + + certificate_authority_data = module.eks.outputs.eks_cluster_certificate_authority_data + eks_cluster_id = module.eks.outputs.eks_cluster_id + eks_cluster_endpoint = module.eks.outputs.eks_cluster_endpoint +} + +data "aws_eks_cluster_auth" "eks" { + count = local.kube_data_auth_enabled ? 1 : 0 + name = local.eks_cluster_id +} + +provider "kubernetes" { + host = local.eks_cluster_endpoint + cluster_ca_certificate = base64decode(local.certificate_authority_data) + token = local.kube_data_auth_enabled ? data.aws_eks_cluster_auth.eks[0].token : null + # The Kubernetes provider will use information from KUBECONFIG if it exists, but if the default cluster + # in KUBECONFIG is some other cluster, this will cause problems, so we override it always. + config_path = local.kubeconfig_file_enabled ? var.kubeconfig_file : "" + config_context = var.kubeconfig_context + + dynamic "exec" { + for_each = local.kube_exec_auth_enabled ? ["exec"] : [] + content { + api_version = local.kubeconfig_exec_auth_api_version + command = "aws" + args = concat(local.exec_profile, [ + "eks", "get-token", "--cluster-name", local.eks_cluster_id + ], local.exec_role) + } + } +} diff --git a/modules/eks/tailscale/providers.tf b/modules/eks/tailscale/providers.tf new file mode 100644 index 000000000..c2419aabb --- /dev/null +++ b/modules/eks/tailscale/providers.tf @@ -0,0 +1,29 @@ +provider "aws" { + region = var.region + + profile = module.iam_roles.profiles_enabled ? coalesce(var.import_profile_name, module.iam_roles.terraform_profile_name) : null + + dynamic "assume_role" { + for_each = module.iam_roles.profiles_enabled ? [] : ["role"] + content { + role_arn = coalesce(var.import_role_arn, module.iam_roles.terraform_role_arn) + } + } +} + +module "iam_roles" { + source = "../../account-map/modules/iam-roles" + context = module.this.context +} + +variable "import_profile_name" { + type = string + default = null + description = "AWS Profile name to use when importing a resource" +} + +variable "import_role_arn" { + type = string + default = null + description = "IAM Role ARN to use when importing a resource" +} diff --git a/modules/eks/tailscale/remote-state.tf b/modules/eks/tailscale/remote-state.tf new file mode 100644 index 000000000..42155e076 --- /dev/null +++ b/modules/eks/tailscale/remote-state.tf @@ -0,0 +1,20 @@ +module "eks" { + source = "cloudposse/stack-config/yaml//modules/remote-state" + version = "1.4.1" + + component = var.eks_component_name + + context = module.this.context +} + +data "aws_eks_cluster" "kubernetes" { + count = local.enabled ? 1 : 0 + + name = module.eks.outputs.eks_cluster_id +} + +data "aws_subnet" "vpc_subnets" { + for_each = local.enabled ? data.aws_eks_cluster.kubernetes[0].vpc_config[0].subnet_ids : [] + + id = each.value +} diff --git a/modules/eks/tailscale/variables.tf b/modules/eks/tailscale/variables.tf new file mode 100644 index 000000000..90be9cc2a --- /dev/null +++ b/modules/eks/tailscale/variables.tf @@ -0,0 +1,63 @@ +variable "region" { + type = string + description = "AWS Region" +} + +variable "eks_component_name" { + type = string + description = "The name of the eks component" + default = "eks/cluster" +} + +variable "chart_values" { + type = any + description = "Addition map values to yamlencode as `helm_release` values." + default = {} +} + +variable "deployment_name" { + type = string + description = "Name of the tailscale deployment, defaults to `tailscale` if this is null" + default = null +} + +variable "image_repo" { + type = string + description = "Image repository for the deployment" + default = "ghcr.io/tailscale/tailscale" +} + +variable "image_tag" { + type = string + description = "Image Tag for the deployment." + default = "latest" +} + +variable "create_namespace" { + type = bool + description = "Create the namespace if it does not yet exist. Defaults to `false`." + default = false +} + +variable "kubernetes_namespace" { + type = string + description = "The namespace to install the release into." +} + +variable "kube_secret" { + type = string + description = "Kube Secret Name for tailscale" + default = "tailscale" +} + +variable "routes" { + type = list(string) + description = "List of CIDR Ranges or IPs to allow Tailscale to connect to" + default = [] +} + +variable "env" { + type = map(string) + description = "Map of ENV vars in the format `key=value`. These ENV vars will be set in the `utils` provider before executing the data source" + default = null +} diff --git a/modules/eks/tailscale/versions.tf b/modules/eks/tailscale/versions.tf new file mode 100644 index 000000000..8b70f9f52 --- /dev/null +++ b/modules/eks/tailscale/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.7.1" + } + } +} From d6d44f744040c776210280af5a8458164e638731 Mon Sep 17 00:00:00 2001 From: Igor Rodionov Date: Tue, 1 Oct 2024 16:05:24 +0300 Subject: [PATCH 2/4] Update outputs.tf --- modules/eks/tailscale/outputs.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/eks/tailscale/outputs.tf b/modules/eks/tailscale/outputs.tf index 2957f2de3..811fe1ff4 100644 --- a/modules/eks/tailscale/outputs.tf +++ b/modules/eks/tailscale/outputs.tf @@ -1,3 +1,4 @@ output "deployment" { - value = kubernetes_deployment.operator + value = kubernetes_deployment.operator + description = "Tail scale operator deployment K8S resource" } From 39957c6eb7d26b92a5ccd6b575b87394ed964064 Mon Sep 17 00:00:00 2001 From: Benjamin Smith Date: Tue, 1 Oct 2024 10:41:09 -0700 Subject: [PATCH 3/4] update readme --- modules/eks/tailscale/README.md | 187 ++++++++++++++------------------ 1 file changed, 83 insertions(+), 104 deletions(-) diff --git a/modules/eks/tailscale/README.md b/modules/eks/tailscale/README.md index 15cc519e1..24148533d 100644 --- a/modules/eks/tailscale/README.md +++ b/modules/eks/tailscale/README.md @@ -19,127 +19,106 @@ components: image_tag: unstable ``` + + ## Requirements -| Name | Version | -| ---------------------------------------- | -------- | -| [terraform](./#requirement\_terraform) | >= 1.0.0 | -| [aws](./#requirement\_aws) | >= 4.0 | -| [kubernetes](./#requirement\_kubernetes) | >= 2.7.1 | +| Name | Version |k +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0.0 | +| [aws](#requirement\_aws) | >= 4.0 | +| [kubernetes](#requirement\_kubernetes) | >= 2.7.1 | ## Providers -| Name | Version | -| ------------------------------------- | -------- | -| [aws](./#provider\_aws) | >= 4.0 | -| [kubernetes](./#provider\_kubernetes) | >= 2.7.1 | +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | +| [kubernetes](#provider\_kubernetes) | >= 2.7.1 | ## Modules -| Name | Source | Version | -| ------------------------------------- | -------------------------------------------------- | ------- | -| [eks](./#module\_eks) | cloudposse/stack-config/yaml//modules/remote-state | 1.4.1 | -| [iam\_roles](./#module\_iam\_roles) | ../../account-map/modules/iam-roles | n/a | -| [store\_read](./#module\_store\_read) | cloudposse/ssm-parameter-store/aws | 0.10.0 | -| [this](./#module\_this) | cloudposse/label/null | 0.25.0 | +| Name | Source | Version | +|------|--------|---------| +| [eks](#module\_eks) | cloudposse/stack-config/yaml//modules/remote-state | 1.4.1 | +| [iam\_roles](#module\_iam\_roles) | ../../account-map/modules/iam-roles | n/a | +| [store\_read](#module\_store\_read) | cloudposse/ssm-parameter-store/aws | 0.10.0 | +| [this](#module\_this) | cloudposse/label/null | 0.25.0 | ## Resources -| Name | Type | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| [kubernetes\_cluster\_role.tailscale\_operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/cluster\_role) | resource | -| [kubernetes\_cluster\_role\_binding.tailscale\_operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/cluster\_role\_binding) | resource | -| [kubernetes\_deployment.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/deployment) | resource | -| [kubernetes\_namespace.default](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource | -| [kubernetes\_role.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role) | resource | -| [kubernetes\_role.proxies](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role) | resource | -| [kubernetes\_role\_binding.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role\_binding) | resource | -| [kubernetes\_role\_binding.proxies](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role\_binding) | resource | -| [kubernetes\_secret.operator\_oauth](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | -| [kubernetes\_service\_account.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service\_account) | resource | -| [kubernetes\_service\_account.proxies](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service\_account) | resource | -| [aws\_eks\_cluster.kubernetes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks\_cluster) | data source | -| [aws\_eks\_cluster\_auth.eks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks\_cluster\_auth) | data source | -| [aws\_subnet.vpc\_subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | +| Name | Type | +|------|------| +| [kubernetes_cluster_role.tailscale_operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/cluster_role) | resource | +| [kubernetes_cluster_role_binding.tailscale_operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/cluster_role_binding) | resource | +| [kubernetes_deployment.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/deployment) | resource | +| [kubernetes_namespace.default](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource | +| [kubernetes_role.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role) | resource | +| [kubernetes_role.proxies](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role) | resource | +| [kubernetes_role_binding.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role_binding) | resource | +| [kubernetes_role_binding.proxies](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role_binding) | resource | +| [kubernetes_secret.operator_oauth](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | +| [kubernetes_service_account.operator](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service_account) | resource | +| [kubernetes_service_account.proxies](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service_account) | resource | +| [aws_eks_cluster.kubernetes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks_cluster) | data source | +| [aws_eks_cluster_auth.eks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks_cluster_auth) | data source | +| [aws_subnet.vpc_subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | ## Inputs -| Name | Description | Type | Default | Required | -| -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | -| [additional\_tag\_map](./#input\_additional\_tag\_map) |

Additional key-value pairs to add to each map in tags_as_list_of_maps. Not added to tags or id.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration.

| `map(string)` | `{}` | no | -| [attributes](./#input\_attributes) |

ID element. Additional attributes (e.g. workers or cluster) to add to id,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the delimiter
and treated as a single ID element.

| `list(string)` | `[]` | no | -| [chart\_values](./#input\_chart\_values) | Addition map values to yamlencode as `helm_release` values. | `any` | `{}` | no | -| [context](./#input\_context) |

Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as null to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional_tag_map, which are merged.

| `any` |
{
-  "additional_tag_map": {},
-  "attributes": [],
-  "delimiter": null,
-  "descriptor_formats": {},
-  "enabled": true,
-  "environment": null,
-  "id_length_limit": null,
-  "label_key_case": null,
-  "label_order": [],
-  "label_value_case": null,
-  "labels_as_tags": [
-    "unset"
-  ],
-  "name": null,
-  "namespace": null,
-  "regex_replace_chars": null,
-  "stage": null,
-  "tags": {},
-  "tenant": null
-}
-
| no | -| [create\_namespace](./#input\_create\_namespace) | Create the namespace if it does not yet exist. Defaults to `false`. | `bool` | `false` | no | -| [delimiter](./#input\_delimiter) |

Delimiter to be used between ID elements.
Defaults to - (hyphen). Set to "" to use no delimiter at all.

| `string` | `null` | no | -| [deployment\_name](./#input\_deployment\_name) | Name of the tailscale deployment, defaults to `tailscale` if this is null | `string` | `null` | no | -| [descriptor\_formats](./#input\_descriptor\_formats) |

Describe additional descriptors to be output in the descriptors output map.
Map of maps. Keys are names of descriptors. Values are maps of the form
{<br> format = string<br> labels = list(string)<br>}
(Type is any so the map values can later be enhanced to provide additional options.)
format is a Terraform format string to be passed to the format() function.
labels is a list of labels, in order, to pass to format() function.
Label values will be normalized before being passed to format() so they will be
identical to how they appear in id.
Default is {} (descriptors output will be empty).

| `any` | `{}` | no | -| [eks\_component\_name](./#input\_eks\_component\_name) | The name of the eks component | `string` | `"eks/cluster"` | no | -| [enabled](./#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | -| [env](./#input\_env) | Map of ENV vars in the format `key=value`. These ENV vars will be set in the `utils` provider before executing the data source | `map(string)` | `null` | no | -| [environment](./#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | -| [helm\_manifest\_experiment\_enabled](./#input\_helm\_manifest\_experiment\_enabled) | Enable storing of the rendered manifest for helm\_release so the full diff of what is changing can been seen in the plan | `bool` | `true` | no | -| [id\_length\_limit](./#input\_id\_length\_limit) |

Limit id to this many characters (minimum 6).
Set to 0 for unlimited length.
Set to null for keep the existing setting, which defaults to 0.
Does not affect id_full.

| `number` | `null` | no | -| [image\_repo](./#input\_image\_repo) | Image repository for the deployment | `string` | `"ghcr.io/tailscale/tailscale"` | no | -| [image\_tag](./#input\_image\_tag) | Image Tag for the deployment. | `string` | `"latest"` | no | -| [import\_profile\_name](./#input\_import\_profile\_name) | AWS Profile name to use when importing a resource | `string` | `null` | no | -| [import\_role\_arn](./#input\_import\_role\_arn) | IAM Role ARN to use when importing a resource | `string` | `null` | no | -| [kube\_data\_auth\_enabled](./#input\_kube\_data\_auth\_enabled) |

If true, use an aws_eks_cluster_auth data source to authenticate to the EKS cluster.
Disabled by kubeconfig_file_enabled or kube_exec_auth_enabled.

| `bool` | `false` | no | -| [kube\_exec\_auth\_aws\_profile](./#input\_kube\_exec\_auth\_aws\_profile) | The AWS config profile for `aws eks get-token` to use | `string` | `""` | no | -| [kube\_exec\_auth\_aws\_profile\_enabled](./#input\_kube\_exec\_auth\_aws\_profile\_enabled) | If `true`, pass `kube_exec_auth_aws_profile` as the `profile` to `aws eks get-token` | `bool` | `false` | no | -| [kube\_exec\_auth\_enabled](./#input\_kube\_exec\_auth\_enabled) |

If true, use the Kubernetes provider exec feature to execute aws eks get-token to authenticate to the EKS cluster.
Disabled by kubeconfig_file_enabled, overrides kube_data_auth_enabled.

| `bool` | `true` | no | -| [kube\_exec\_auth\_role\_arn](./#input\_kube\_exec\_auth\_role\_arn) | The role ARN for `aws eks get-token` to use | `string` | `""` | no | -| [kube\_exec\_auth\_role\_arn\_enabled](./#input\_kube\_exec\_auth\_role\_arn\_enabled) | If `true`, pass `kube_exec_auth_role_arn` as the role ARN to `aws eks get-token` | `bool` | `true` | no | -| [kube\_secret](./#input\_kube\_secret) | Kube Secret Name for tailscale | `string` | `"tailscale"` | no | -| [kubeconfig\_context](./#input\_kubeconfig\_context) | Context to choose from the Kubernetes kube config file | `string` | `""` | no | -| [kubeconfig\_exec\_auth\_api\_version](./#input\_kubeconfig\_exec\_auth\_api\_version) | The Kubernetes API version of the credentials returned by the `exec` auth plugin | `string` | `"client.authentication.k8s.io/v1beta1"` | no | -| [kubeconfig\_file](./#input\_kubeconfig\_file) | The Kubernetes provider `config_path` setting to use when `kubeconfig_file_enabled` is `true` | `string` | `""` | no | -| [kubeconfig\_file\_enabled](./#input\_kubeconfig\_file\_enabled) | If `true`, configure the Kubernetes provider with `kubeconfig_file` and use that kubeconfig file for authenticating to the EKS cluster | `bool` | `false` | no | -| [kubernetes\_namespace](./#input\_kubernetes\_namespace) | The namespace to install the release into. | `string` | n/a | yes | -| [label\_key\_case](./#input\_label\_key\_case) |

Controls the letter case of the tags keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the tags input.
Possible values: lower, title, upper.
Default value: title.

| `string` | `null` | no | -| [label\_order](./#input\_label\_order) |

The order in which the labels (ID elements) appear in the id.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present.

| `list(string)` | `null` | no | -| [label\_value\_case](./#input\_label\_value\_case) |

Controls the letter case of ID elements (labels) as included in id,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the tags input.
Possible values: lower, title, upper and none (no transformation).
Set this to title and set delimiter to "" to yield Pascal Case IDs.
Default value: lower.

| `string` | `null` | no | -| [labels\_as\_tags](./#input\_labels\_as\_tags) |

Set of labels (ID elements) to include as tags in the tags output.
Default is to include all labels.
Tags with empty values will not be included in the tags output.
Set to [] to suppress all generated tags.
Notes:
The value of the name tag, if included, will be the id, not the name.
Unlike other null-label inputs, the initial setting of labels_as_tags cannot be
changed in later chained modules. Attempts to change it will be silently ignored.

| `set(string)` |
[
-  "default"
-]
-
| no | -| [name](./#input\_name) |

ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as a tag.
The "name" tag is set to the full id string. There is no tag with the value of the name input.

| `string` | `null` | no | -| [namespace](./#input\_namespace) | ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique | `string` | `null` | no | -| [regex\_replace\_chars](./#input\_regex\_replace\_chars) |

Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, "/[^a-zA-Z0-9-]/" is used to remove all characters other than hyphens, letters and digits.

| `string` | `null` | no | -| [region](./#input\_region) | AWS Region | `string` | n/a | yes | -| [routes](./#input\_routes) | List of CIDR Ranges or IPs to allow Tailscale to connect to | `list(string)` | `[]` | no | -| [stage](./#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no | -| [tags](./#input\_tags) |

Additional tags (e.g. {'BusinessUnit': 'XYZ'}).
Neither the tag keys nor the tag values will be modified by this module.

| `map(string)` | `{}` | no | -| [tenant](./#input\_tenant) | ID element \_(Rarely used, not included by default)\_. A customer identifier, indicating who this instance of a resource is for | `string` | `null` | no | +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [additional\_tag\_map](#input\_additional\_tag\_map) | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration. | `map(string)` | `{}` | no | +| [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | +| [chart\_values](#input\_chart\_values) | Addition map values to yamlencode as `helm_release` values. | `any` | `{}` | no | +| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional\_tag\_map, which are merged. | `any` |
{
"additional_tag_map": {},
"attributes": [],
"delimiter": null,
"descriptor_formats": {},
"enabled": true,
"environment": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"labels_as_tags": [
"unset"
],
"name": null,
"namespace": null,
"regex_replace_chars": null,
"stage": null,
"tags": {},
"tenant": null
}
| no | +| [create\_namespace](#input\_create\_namespace) | Create the namespace if it does not yet exist. Defaults to `false`. | `bool` | `false` | no | +| [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no | +| [deployment\_name](#input\_deployment\_name) | Name of the tailscale deployment, defaults to `tailscale` if this is null | `string` | `null` | no | +| [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.
Map of maps. Keys are names of descriptors. Values are maps of the form
`{
format = string
labels = list(string)
}`
(Type is `any` so the map values can later be enhanced to provide additional options.)
`format` is a Terraform format string to be passed to the `format()` function.
`labels` is a list of labels, in order, to pass to `format()` function.
Label values will be normalized before being passed to `format()` so they will be
identical to how they appear in `id`.
Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no | +| [eks\_component\_name](#input\_eks\_component\_name) | The name of the eks component | `string` | `"eks/cluster"` | no | +| [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | +| [env](#input\_env) | Map of ENV vars in the format `key=value`. These ENV vars will be set in the `utils` provider before executing the data source | `map(string)` | `null` | no | +| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | +| [helm\_manifest\_experiment\_enabled](#input\_helm\_manifest\_experiment\_enabled) | Enable storing of the rendered manifest for helm\_release so the full diff of what is changing can been seen in the plan | `bool` | `true` | no | +| [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no | +| [image\_repo](#input\_image\_repo) | Image repository for the deployment | `string` | `"ghcr.io/tailscale/tailscale"` | no | +| [image\_tag](#input\_image\_tag) | Image Tag for the deployment. | `string` | `"latest"` | no | +| [import\_profile\_name](#input\_import\_profile\_name) | AWS Profile name to use when importing a resource | `string` | `null` | no | +| [import\_role\_arn](#input\_import\_role\_arn) | IAM Role ARN to use when importing a resource | `string` | `null` | no | +| [kube\_data\_auth\_enabled](#input\_kube\_data\_auth\_enabled) | If `true`, use an `aws_eks_cluster_auth` data source to authenticate to the EKS cluster.
Disabled by `kubeconfig_file_enabled` or `kube_exec_auth_enabled`. | `bool` | `false` | no | +| [kube\_exec\_auth\_aws\_profile](#input\_kube\_exec\_auth\_aws\_profile) | The AWS config profile for `aws eks get-token` to use | `string` | `""` | no | +| [kube\_exec\_auth\_aws\_profile\_enabled](#input\_kube\_exec\_auth\_aws\_profile\_enabled) | If `true`, pass `kube_exec_auth_aws_profile` as the `profile` to `aws eks get-token` | `bool` | `false` | no | +| [kube\_exec\_auth\_enabled](#input\_kube\_exec\_auth\_enabled) | If `true`, use the Kubernetes provider `exec` feature to execute `aws eks get-token` to authenticate to the EKS cluster.
Disabled by `kubeconfig_file_enabled`, overrides `kube_data_auth_enabled`. | `bool` | `true` | no | +| [kube\_exec\_auth\_role\_arn](#input\_kube\_exec\_auth\_role\_arn) | The role ARN for `aws eks get-token` to use | `string` | `""` | no | +| [kube\_exec\_auth\_role\_arn\_enabled](#input\_kube\_exec\_auth\_role\_arn\_enabled) | If `true`, pass `kube_exec_auth_role_arn` as the role ARN to `aws eks get-token` | `bool` | `true` | no | +| [kube\_secret](#input\_kube\_secret) | Kube Secret Name for tailscale | `string` | `"tailscale"` | no | +| [kubeconfig\_context](#input\_kubeconfig\_context) | Context to choose from the Kubernetes kube config file | `string` | `""` | no | +| [kubeconfig\_exec\_auth\_api\_version](#input\_kubeconfig\_exec\_auth\_api\_version) | The Kubernetes API version of the credentials returned by the `exec` auth plugin | `string` | `"client.authentication.k8s.io/v1beta1"` | no | +| [kubeconfig\_file](#input\_kubeconfig\_file) | The Kubernetes provider `config_path` setting to use when `kubeconfig_file_enabled` is `true` | `string` | `""` | no | +| [kubeconfig\_file\_enabled](#input\_kubeconfig\_file\_enabled) | If `true`, configure the Kubernetes provider with `kubeconfig_file` and use that kubeconfig file for authenticating to the EKS cluster | `bool` | `false` | no | +| [kubernetes\_namespace](#input\_kubernetes\_namespace) | The namespace to install the release into. | `string` | n/a | yes | +| [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no | +| [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no | +| [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no | +| [labels\_as\_tags](#input\_labels\_as\_tags) | Set of labels (ID elements) to include as tags in the `tags` output.
Default is to include all labels.
Tags with empty values will not be included in the `tags` output.
Set to `[]` to suppress all generated tags.
**Notes:**
The value of the `name` tag, if included, will be the `id`, not the `name`.
Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be
changed in later chained modules. Attempts to change it will be silently ignored. | `set(string)` |
[
"default"
]
| no | +| [name](#input\_name) | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no | +| [namespace](#input\_namespace) | ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique | `string` | `null` | no | +| [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no | +| [region](#input\_region) | AWS Region | `string` | n/a | yes | +| [routes](#input\_routes) | List of CIDR Ranges or IPs to allow Tailscale to connect to | `list(string)` | `[]` | no | +| [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no | +| [tags](#input\_tags) | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`).
Neither the tag keys nor the tag values will be modified by this module. | `map(string)` | `{}` | no | +| [tenant](#input\_tenant) | ID element \_(Rarely used, not included by default)\_. A customer identifier, indicating who this instance of a resource is for | `string` | `null` | no | ## Outputs -| Name | Description | -| ----------------------------------- | ----------- | -| [deployment](./#output\_deployment) | n/a | -| | | +| Name | Description | +|------|-------------| +| [deployment](#output\_deployment) | Tail scale operator deployment K8S resource | + + ## References -* https://github.com/Ealenn/tailscale +- https://github.com/Ealenn/tailscale From e96c854bae1d6056c6cabd6eac08051775e400d6 Mon Sep 17 00:00:00 2001 From: Benjamin Smith Date: Tue, 1 Oct 2024 10:42:07 -0700 Subject: [PATCH 4/4] update readme --- modules/eks/tailscale/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/eks/tailscale/README.md b/modules/eks/tailscale/README.md index 24148533d..74866021b 100644 --- a/modules/eks/tailscale/README.md +++ b/modules/eks/tailscale/README.md @@ -23,7 +23,7 @@ components: ## Requirements -| Name | Version |k +| Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0.0 | | [aws](#requirement\_aws) | >= 4.0 |