Skip to content

Instantly share code, notes, and snippets.

@addisonj
Created December 16, 2020 04:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save addisonj/ae9d733516e22f10e1334639dda9beb5 to your computer and use it in GitHub Desktop.
Save addisonj/ae9d733516e22f10e1334639dda9beb5 to your computer and use it in GitHub Desktop.
module "label" {
source = "cloudposse/label/null"
version = "0.22.0"
attributes = ["cluster"]
context = module.this.context
}
locals {
subnet_ids = concat(var.private_subnet_ids, var.public_subnet_ids)
cluster_label = "kubernetes.io/cluster/${module.label.id}"
}
resource "aws_ec2_tag" "vpc_tag" {
resource_id = var.vpc_id
key = local.cluster_label
value = "shared"
}
resource "aws_ec2_tag" "subnet_tag" {
count = length(local.subnet_ids)
resource_id = local.subnet_ids[count.index]
key = local.cluster_label
value = "shared"
}
resource "aws_ec2_tag" "private_subnet_tag" {
count = length(var.private_subnet_ids)
resource_id = var.private_subnet_ids[count.index]
key = "kubernetes.io/role/internal-elb"
value = "1"
}
resource "aws_ec2_tag" "public_subnet_tag" {
count = length(var.public_subnet_ids)
resource_id = var.public_subnet_ids[count.index]
key = "kubernetes.io/role/elb"
value = "1"
}
module "eks_cluster" {
source = "cloudposse/eks-cluster/aws"
version = "0.29.1"
region = var.region
vpc_id = var.vpc_id
subnet_ids = concat(var.private_subnet_ids, var.public_subnet_ids)
kubernetes_version = var.kubernetes_version
local_exec_interpreter = var.local_exec_interpreter
oidc_provider_enabled = var.oidc_provider_enabled
enabled_cluster_log_types = var.enabled_cluster_log_types
cluster_log_retention_period = var.cluster_log_retention_period
map_additional_iam_roles = var.map_additional_iam_roles
map_additional_aws_accounts = var.map_additional_aws_accounts
map_additional_iam_users = var.map_additional_iam_users
kubernetes_config_map_ignore_role_changes = false
context = module.this.context
}
# Ensure ordering of resource creation to eliminate the race conditions when applying the Kubernetes Auth ConfigMap.
# Do not create Node Group before the EKS cluster is created and the `aws-auth` Kubernetes ConfigMap is applied.
# Otherwise, EKS will create the ConfigMap first and add the managed node role ARNs to it,
# and the kubernetes provider will throw an error that the ConfigMap already exists (because it can't update the map, only create it).
# If we create the ConfigMap first (to add additional roles/users/accounts), EKS will just update it by adding the managed node role ARNs.
data "null_data_source" "wait_for_cluster_and_kubernetes_configmap" {
inputs = {
cluster_name = module.eks_cluster.eks_cluster_id
kubernetes_config_map_id = module.eks_cluster.kubernetes_config_map_id
}
}
module "eks_node_group" {
source = "cloudposse/eks-node-group/aws"
version = "0.15.0"
subnet_ids = var.private_subnet_ids
cluster_name = data.null_data_source.wait_for_cluster_and_kubernetes_configmap.outputs["cluster_name"]
instance_types = var.instance_types
desired_size = var.desired_size
min_size = var.min_size
max_size = var.max_size
kubernetes_labels = var.kubernetes_labels
disk_size = var.disk_size
context = module.this.context
}
#
# 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.
#
# 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.<var>`,
# 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.22.0"
enabled = var.enabled
namespace = var.namespace
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
context = var.context
}
# Copy contents of cloudposse/terraform-null-label/variables.tf here
variable "context" {
type = object({
enabled = bool
namespace = string
environment = string
stage = string
name = string
delimiter = string
attributes = list(string)
tags = map(string)
additional_tag_map = map(string)
regex_replace_chars = string
label_order = list(string)
id_length_limit = number
})
default = {
enabled = true
namespace = null
environment = null
stage = null
name = null
delimiter = null
attributes = []
tags = {}
additional_tag_map = {}
regex_replace_chars = null
label_order = []
id_length_limit = null
}
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
}
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 = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'"
}
variable "environment" {
type = string
default = null
description = "Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT'"
}
variable "stage" {
type = string
default = null
description = "Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release'"
}
variable "name" {
type = string
default = null
description = "Solution name, e.g. 'app' or 'jenkins'"
}
variable "delimiter" {
type = string
default = null
description = <<-EOT
Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all.
EOT
}
variable "attributes" {
type = list(string)
default = []
description = "Additional attributes (e.g. `1`)"
}
variable "tags" {
type = map(string)
default = {}
description = "Additional tags (e.g. `map('BusinessUnit','XYZ')`"
}
variable "additional_tag_map" {
type = map(string)
default = {}
description = "Additional tags for appending to tags_as_list_of_maps. Not added to `tags`."
}
variable "label_order" {
type = list(string)
default = null
description = <<-EOT
The naming order of the id output and Name tag.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 5 elements, but at least one must be present.
EOT
}
variable "regex_replace_chars" {
type = string
default = null
description = <<-EOT
Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.
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.
Set to `0` for unlimited length.
Set to `null` for default, which is `0`.
Does not affect `id_full`.
EOT
}
#### End of copy of cloudposse/terraform-null-label/variables.tf
module "s3_label" {
source = "cloudposse/label/null"
version = "0.22.0"
attributes = ["offload"]
context = module.this.context
}
module "tiered_resources" {
source = "streamnative/managed-cloud/aws//modules/tiered_storage"
version = "0.3.1"
bucket_name = module.s3_label.id
bucket_tags = module.s3_label.tags
}
// this role is used by vault and makes use of vault resources created above
module "tiered_storage_service_account" {
source = "cloudposse/eks-iam-role/aws"
version = "0.3.1"
context = module.this.context
aws_account_number = local.accountId
eks_cluster_oidc_issuer_url = local.issuerUrl
service_account_name = "pulsar"
service_account_namespace = var.pulsar_k8s_namespace
// attach a minimal policy here then attach a managed policy later
aws_iam_policy_document = data.aws_iam_policy_document.base_policy.json
}
resource "aws_iam_role_policy_attachment" "tiered_storage" {
role = module.tiered_storage_service_account.service_account_role_name
policy_arn = module.tiered_resources.policy_arn
}
variable "region" {
type = string
description = "AWS Region"
}
variable "kubernetes_version" {
type = string
default = "1.18"
description = "Desired Kubernetes master version. If you do not specify a value, the latest available version is used"
}
variable "enabled_cluster_log_types" {
type = list(string)
default = []
# TODO once we get cloudwatch access, enable this
#default = ["api", "audit", "authenticator", "controllerManager", "scheduler"]
description = "A list of the desired control plane logging to enable. For more information, see https://docs.aws.amazon.com/en_us/eks/latest/userguide/control-plane-logs.html. Possible values [`api`, `audit`, `authenticator`, `controllerManager`, `scheduler`]"
}
variable "cluster_log_retention_period" {
type = number
default = 7
description = "Number of days to retain cluster logs. Requires `enabled_cluster_log_types` to be set. See https://docs.aws.amazon.com/en_us/eks/latest/userguide/control-plane-logs.html."
}
variable "map_additional_aws_accounts" {
description = "Additional AWS account numbers to add to `config-map-aws-auth` ConfigMap"
type = list(string)
default = []
}
variable "map_additional_iam_roles" {
description = "Additional IAM roles to add to `config-map-aws-auth` ConfigMap"
type = list(object({
rolearn = string
username = string
groups = list(string)
}))
default = []
}
variable "map_additional_iam_users" {
description = "Additional IAM users to add to `config-map-aws-auth` ConfigMap"
type = list(object({
userarn = string
username = string
groups = list(string)
}))
default = []
}
variable "oidc_provider_enabled" {
type = bool
default = true
description = "Create an IAM OIDC identity provider for the cluster, then you can create IAM roles to associate with a service account in the cluster, instead of using `kiam` or `kube2iam`. For more information, see https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html"
}
variable "local_exec_interpreter" {
type = list(string)
default = ["/bin/sh", "-c"]
description = "shell to use for local_exec"
}
variable "disk_size" {
type = number
default = null
description = "Disk size in GiB for worker nodes. Defaults to 20. Terraform will only perform drift detection if a configuration value is provided"
}
variable "instance_types" {
type = list(string)
description = "Set of instance types associated with the EKS Node Group. Defaults to [\"t3.medium\"]. Terraform will only perform drift detection if a configuration value is provided"
}
variable "kubernetes_labels" {
type = map(string)
description = "Key-value mapping of Kubernetes labels. Only labels that are applied with the EKS API are managed by this argument. Other Kubernetes labels applied to the EKS Node Group will not be managed"
default = {}
}
variable "desired_size" {
type = number
description = "Desired number of worker nodes"
}
variable "max_size" {
type = number
description = "The maximum size of the AutoScaling Group"
}
variable "min_size" {
type = number
description = "The minimum size of the AutoScaling Group"
}
variable "vpc_id" {
type = string
description = "The id of existing VPC"
}
variable "private_subnet_ids" {
type = list(string)
description = "The ids of existing private subnets"
}
variable "public_subnet_ids" {
type = list(string)
description = "The ids of existing public subnets"
}
# vault vars
variable "vault_k8s_namespace" {
type = string
default = "pulsar"
description = "the namespace used for vault"
}
variable "pulsar_k8s_namespace" {
type = string
default = "pulsar"
description = "the namespace used for pulsar"
}
data "aws_caller_identity" "current" {}
locals {
accountId = data.aws_caller_identity.current.account_id
issuerUrl = module.eks_cluster.eks_cluster_identity_oidc_issuer
}
data aws_iam_policy_document "base_policy" {
statement {
actions = [
"sts:GetCallerIdentity"
]
resources = ["*"]
}
}
module "vault_resources" {
source = "streamnative/managed-cloud/aws//modules/vault_resources"
version = "0.3.1"
prefix = module.this.id
}
// this role is used by vault and makes use of vault resources created above
module "vault_service_account" {
source = "cloudposse/eks-iam-role/aws"
version = "0.3.1"
context = module.this.context
aws_account_number = local.accountId
eks_cluster_oidc_issuer_url = local.issuerUrl
service_account_name = "vault"
service_account_namespace = var.vault_k8s_namespace
// attach a minimal policy here then attach a managed policy later
aws_iam_policy_document = data.aws_iam_policy_document.base_policy.json
}
resource "aws_iam_role_policy_attachment" "vault" {
role = module.vault_service_account.service_account_role_name
policy_arn = module.vault_resources.policy_arn
}
@addisonj
Copy link
Author

For some context on these terraform modules:

They build on top of the "cloudposse" terraform modules (like https://github.com/cloudposse/terraform-aws-eks-cluster), which have a robust mechanism for naming and providing consistent tags.

This also includes reference to some of the streamnative modules (at https://github.com/streamnative/terraform-aws-managed-cloud)

These modules create a cluster suitable for use with the streamnative helm charts (https://github.com/streamnative/charts) or the mainline charts (https://github.com/apache/pulsar-helm-chart)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment