From c5737ada7dd528885cb6839288b601a3e92615f9 Mon Sep 17 00:00:00 2001 From: Steven Nemetz Date: Sat, 30 Dec 2017 15:32:36 -0800 Subject: [PATCH] Initial commit --- .gitignore | 3 ++ README.md | 66 +++++++++++++++++++++++++ main.tf | 127 ++++++++++++++++++++++++++++++++++++++++++++++++ outputs.tf | 60 +++++++++++++++++++++++ test/README.md | 2 + test/main.tf | 57 ++++++++++++++++++++++ test/outputs.tf | 62 +++++++++++++++++++++++ variables.tf | 41 ++++++++++++++++ 8 files changed, 418 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 main.tf create mode 100644 outputs.tf create mode 100644 test/README.md create mode 100644 test/main.tf create mode 100644 test/outputs.tf create mode 100644 variables.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d7db64a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.tfstate +*.tfstate.backup +.terraform diff --git a/README.md b/README.md new file mode 100644 index 0000000..b941238 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# terraform-local-label + +Terraform module to provide consistent label names and tags for resources. + +A single name format will not solve every use case, so multiple variants are returned and there is a few options to affect how they get build. The general name convention is `{organization}-{environment}-{name}-{attributes}`. `Name` is required, the other 3 can be turned on/off individually. The delimiter (`-`) can be changed + +All [devops-workflow](https://registry.terraform.io/search?q=devops-workflow&verified=false) modules will eventually use this. + +**NOTE:** `local` refers to this using [locals](https://www.terraform.io/docs/configuration/locals.html) and does not create any resources. It just builds new variables. + +Terraform registry: https://registry.terraform.io/modules/devops-workflow/label/local + +## Usage: + +#### Basic Example + +```hcl +module "name" { + source = "devops-workflow/label/local" + version = "0.1.2" + name = "name" + environment = "qa" +} +``` +This will create an `id` with the value of `qa-name` + +#### S3 Example + +```hcl +module "s3-name" { + source = "devops-workflow/label/local" + version = "0.1.2" + name = "data" + environment = "qa" + organization = "corp" + namespace-org = "true" +} +``` +This will create an `id` with the value of `corp-qa-data` + +Now reference `label` outputs to create the S3 bucket + +```hcl +resource "aws_s3_bucket" "data" { + bucket = "${module.s3-name.id}" + tags = "${module.s3-name.tags}" +} +``` + +#### All Variables Example +Using in a module and exposing all settings to upstream caller. + +```hcl +module "label" { + source = "devops-workflow/label/local" + version = "0.1.2" + organization = "${var.organization}" + name = "${var.name}" + namespace-env = "${var.namespace-env}" + namespace-org = "${var.namespace-org}" + environment = "${var.environment}" + delimiter = "${var.delimiter}" + attributes = "${var.attributes}" + tags = "${var.tags}" +} +``` diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..57df82a --- /dev/null +++ b/main.tf @@ -0,0 +1,127 @@ +# +# Terraform module to provide consistent naming +# +# TODO: +# Change where replace is done. Move to earlier in process. On initial `name`? +# Create tags_asg list from tags map. If possible +# New input tags_asg -> tags_asg with standard tags added + +module "autoscaling_group" { + source = "devops-workflow/boolean/local" + version = "0.1.1" + value = "${var.autoscaling_group}" +} +module "enabled" { + source = "devops-workflow/boolean/local" + version = "0.1.1" + value = "${var.enabled}" +} +module "namespace-env" { + source = "devops-workflow/boolean/local" + version = "0.1.1" + value = "${var.namespace-env}" +} +module "namespace-org" { + source = "devops-workflow/boolean/local" + version = "0.1.1" + value = "${var.namespace-org}" +} + +locals = { + attr = "${lower(format("%s", join(var.delimiter, compact(var.attributes))))}" + env = "${lower(format("%s", var.environment))}" + org = "${lower(format("%s", var.organization))}" +} +resource "null_resource" "names" { + count = "${module.enabled.value ? length(var.names) : 0}" + triggers = { + id = "${replace(lower(format("%s", element(var.names, count.index))),"_","-")}" + } +} +resource "null_resource" "env" { + count = "${module.enabled.value ? length(var.names) : 0}" + triggers = { + id = "${module.namespace-env.value ? + join(var.delimiter, list(local.env, element(null_resource.names.*.triggers.id, count.index))) : + element(null_resource.names.*.triggers.id, count.index)}" + } +} +resource "null_resource" "org" { + count = "${module.enabled.value ? length(var.names) : 0}" + triggers = { + id = "${module.namespace-org.value ? + join(var.delimiter, list(local.org, element(null_resource.env.*.triggers.id, count.index))) : + element(null_resource.env.*.triggers.id, count.index)}" + } +} +resource "null_resource" "ids" { + count = "${module.enabled.value ? length(var.names) : 0}" + triggers = { + id = "${length(local.attr) > 0 ? + join(var.delimiter, list(element(null_resource.org.*.triggers.id, count.index), local.attr)) : + element(null_resource.org.*.triggers.id, count.index)}" + } +} +resource "null_resource" "ids-trunc" { + count = "${module.enabled.value ? length(var.names) : 0}" + triggers = { + id_20 = "${substr(element(null_resource.ids.*.triggers.id, count.index),0,19 <= length(element(null_resource.ids.*.triggers.id, count.index)) ? 19 : length(element(null_resource.ids.*.triggers.id, count.index)))}" + id_32 = "${substr(element(null_resource.ids.*.triggers.id, count.index),0,31 <= length(element(null_resource.ids.*.triggers.id, count.index)) ? 31 : length(element(null_resource.ids.*.triggers.id, count.index)))}" + org_attr_20 = "${min(18 - length(local.attr), length(element(null_resource.org.*.triggers.id, count.index)))}" + org_attr_32 = "${min(30 - length(local.attr), length(element(null_resource.org.*.triggers.id, count.index)))}" + } +} +resource "null_resource" "ids-trunc-attr" { + count = "${module.enabled.value ? length(var.names) : 0}" + triggers = { + id_attr_20 = "${19 <= length(element(null_resource.ids.*.triggers.id, count.index)) ? + join(var.delimiter, + list( + substr(element(null_resource.org.*.triggers.id, count.index),0, + element(null_resource.ids-trunc.*.triggers.org_attr_20, count.index) >= 0 ? + element(null_resource.ids-trunc.*.triggers.org_attr_20, count.index) : 0) + ), + list(local.attr) + ) + : element(null_resource.ids.*.triggers.id, count.index)}" + id_attr_32 = "${31 <= length(element(null_resource.ids.*.triggers.id, count.index)) ? + join(var.delimiter, + list( + substr(element(null_resource.org.*.triggers.id, count.index),0, + element(null_resource.ids-trunc.*.triggers.org_attr_32, count.index) >= 0 ? + element(null_resource.ids-trunc.*.triggers.org_attr_32, count.index) : 0) + ), + list(local.attr) + ) + : element(null_resource.ids.*.triggers.id, count.index)}" + } +} +/* +resource "null_resource" "tags" { + count = "${module.enabled.value ? length(var.names) : 0}" + triggers = { + #TODO: only add Organization if not "" + tags = "${ merge( + var.tags, + map( + "Name", "${element(null_resource.ids.*.triggers.id, count.index)}", + "Environment", "${local.env}", + "Organization", "${local.org}", + "Terraform", "true" + ) + )}" + } +} +*/ +/* +resource "null_resource" "this" { + count = "${module.enabled.value ? length(var.names) : 0}" + # TODO: change all name related refs to array refs + # split into multi resources to be able to reference created labels + triggers = { + } + lifecycle { + create_before_destroy = true + } +} +*/ diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..eb31424 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,60 @@ + +output "attributes" { + description = "Attribute string lowercase" + value = "${local.attr}" +} +output "environment" { + description = "Environment name lowercase" + value = "${local.env}" +} +output "id" { + description = "Fully formatted name ID" + value = "${compact(concat(null_resource.ids.*.triggers.id, list("")))}" +} +output "id_20" { + description = "ID truncated to 20 characters" + value = "${compact(concat(null_resource.ids-trunc.*.triggers.id_20, list("")))}" +} +output "id_32" { + description = "ID truncated to 32 characters" + value = "${compact(concat(null_resource.ids-trunc.*.triggers.id_32, list("")))}" +} +output "id_attr_20" { + description = "ID max size 20 characters by truncating `id_org` then appending `attributes`" + value = "${compact(concat(null_resource.ids-trunc-attr.*.triggers.id_attr_20, list("")))}" +} +output "id_attr_32" { + description = "ID max size 32 characters by truncating `id_org` then appending `attributes`" + value = "${compact(concat(null_resource.ids-trunc-attr.*.triggers.id_attr_32, list("")))}" +} +output "id_env" { + description = "If env namespace enabled - else " + value = "${compact(concat(null_resource.env.*.triggers.id, list("")))}" +} +output "id_org" { + description = "If org namespace enabled - else " + value = "${compact(concat(null_resource.org.*.triggers.id, list("")))}" +} +output "name" { + description = "Name lowercase" + value = "${compact(concat(null_resource.names.*.triggers.id, list("")))}" +} +output "organization" { + description = "Organization name lowercase" + value = "${local.org}" +} +/* +output "tags" { + description = "Tags map merged with standard tags" + value = "${compact(concat(null_resource.tags.*.triggers.tag, list("")))}" +} +*/ +//debugging +output "org_attr_20" { + description = "Internal debugging. DO NOT USE" + value = "${compact(concat(null_resource.ids-trunc.*.triggers.org_attr_20, list("")))}" +} +output "org_attr_32" { + description = "Internal debugging. DO NOT USE" + value = "${compact(concat(null_resource.ids-trunc.*.triggers.org_attr_32, list("")))}" +} diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..f8bc9aa --- /dev/null +++ b/test/README.md @@ -0,0 +1,2 @@ +Test cases +=== diff --git a/test/main.tf b/test/main.tf new file mode 100644 index 0000000..90a4786 --- /dev/null +++ b/test/main.tf @@ -0,0 +1,57 @@ + +module "labels" { + source = "../" + names = ["CapMe", "Test2"] + environment = "Dev" + organization = "CorpXyZ" + namespace-org = true + #attributes = ["role", "policy", "use", ""] + attributes = ["8080"] + tags = "${map("Key", "Value")}" + autoscaling_group = true +} + +/* +module "labels-tags" { + source = "../" + name = "CapMe" + environment = "Dev" + organization = "CorpXyZ" + attributes = ["role", "policy", "use", ""] + tags = "${map("Key", "Value")}" +} +*/ +module "labels-disabled" { + source = "../" + names = ["CapMe"] + environment = "Dev" + organization = "CorpXyZ" + enabled = false +} +/* + +module "labels-env" { + source = "../" + name = "CapMe" + environment = "Dev" + organization = "CorpXyZ" + namespace-env = true + namespace-org = false +} +module "labels-org" { + source = "../" + name = "CapMe" + environment = "Dev" + organization = "CorpXyZ" + namespace-env = false + namespace-org = true +} +module "labels-org-env" { + source = "../" + name = "Application" + environment = "Environment" + organization = "Organization" + namespace-env = true + namespace-org = true +} +*/ diff --git a/test/outputs.tf b/test/outputs.tf new file mode 100644 index 0000000..90fb761 --- /dev/null +++ b/test/outputs.tf @@ -0,0 +1,62 @@ + +output "attributes" { + description = "Attribute string lowercase" + value = "${module.labels.attributes}" +} +output "environment" { + description = "Environment name lowercase" + value = "${module.labels.environment}" +} +output "id" { + description = "Full combined ID" + value = "${module.labels.id}" +} +output "name" { + description = "Name lowercase" + value = "${module.labels.name}" +} +output "id_20" { + description = "ID truncated to 20 characters" + value = "${module.labels.id_20}" +} +output "id_32" { + description = "ID truncated to 32 characters" + value = "${module.labels.id_32}" +} +output "id_attr_20" { + description = "ID max size 20 characters by truncating `id_org` then appending `attributes`" + value = "${module.labels.id_attr_20}" +} +output "id_attr_32" { + description = "ID max size 32 characters by truncating `id_org` then appending `attributes`" + value = "${module.labels.id_attr_32}" +} +output "id_env" { + description = "If env namespace enabled - else " + value = "${module.labels.id_env}" +} +output "id_org" { + description = "If org namespace enabled - else " + value = "${module.labels.id_org}" +} +output "organization" { + description = "Organization name lowercase" + value = "${module.labels.organization}" +} +/* +output "tags" { + description = "Tags with standard tags added" + value = "${module.labels.tags}" +} +*/ +output "org_attr_20" { + value = "${module.labels.org_attr_20}" +} +output "org_attr_32" { + value = "${module.labels.org_attr_32}" +} + +output "disabled-name" { + description = "Name lowercase" + value = "${module.labels-disabled.name}" +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..09e9e78 --- /dev/null +++ b/variables.tf @@ -0,0 +1,41 @@ +variable "attributes" { + description = "Suffix name with additional attributes (policy, role, etc.)" + type = "list" + default = [] +} +variable "autoscaling_group" { + description = "DOES NOTHING YET. If true, generate ASG tags map resource" + default = false +} +variable "delimiter" { + description = "Delimiter to be used between `name`, `namespaces`, `attributes`, etc." + default = "-" +} +variable "enabled" { + description = "" + default = true +} +variable "environment" { + description = "Environment (ex: `dev`, `qa`, `stage`, `prod`). (Second or top level namespace. Depending on namespacing options)" +} +variable "names" { + description = "Base names for resources" + type = "list" +} +variable "namespace-env" { + description = "Prefix name with the environment. If true, format is: -" + default = true +} +variable "namespace-org" { + description = "Prefix name with the organization. If true, format is: -. If both env and org namespaces are used, format will be --" + default = false +} +variable "organization" { + description = "Organization name (Top level namespace)." + default = "" +} +variable "tags" { + description = "A map of additional tags" + type = "map" + default = {} +}