Code Monkey home page Code Monkey logo

terraform-null-label's Introduction

Project Banner

Latest ReleaseLast UpdatedSlack Community

Terraform module designed to generate consistent names and tags for resources. Use terraform-null-label to implement a strict naming convention.

There are 6 inputs considered "labels" or "ID elements" (because the labels are used to construct the ID):

  1. namespace
  2. tenant
  3. environment
  4. stage
  5. name
  6. attributes

This module generates IDs using the following convention by default: {namespace}-{environment}-{stage}-{name}-{attributes}. However, it is highly configurable. The delimiter (e.g. -) is configurable. Each label item is optional (although you must provide at least one). So if you prefer the term stage to environment and do not need tenant, you can exclude them and the label id will look like {namespace}-{stage}-{name}-{attributes}.

  • The tenant label was introduced in v0.25.0. To preserve backward compatibility, it is not included by default.
  • The attributes input is actually a list of strings and {attributes} expands to the list elements joined by the delimiter.
  • If attributes is excluded but namespace, stage, and environment are included, id will look like {namespace}-{environment}-{stage}-{name}. Excluding attributes is discouraged, though, because attributes are the main way modules modify the ID to ensure uniqueness when provisioning the same resource types.
  • If you want the label items in a different order, you can specify that, too, with the label_order list.
  • You can set a maximum length for the id, and the module will create a (probably) unique name that fits within that length. (The module uses a portion of the MD5 hash of the full id to represent the missing part, so there remains a slight chance of name collision.)
  • You can control the letter case of the generated labels which make up the id using var.label_value_case.
  • By default, all of the non-empty labels are also exported as tags, whether they appear in the id or not. You can control which labels are exported as tags by setting labels_as_tags to the list of labels you want exported, or the empty list [] if you want no labels exported as tags at all. Tags passed in via the tags variable are always exported, and regardless of settings, empty labels are never exported as tags. You can control the case of the tag names (keys) for the labels using var.label_key_case. Unlike the tags generated from the label inputs, tags passed in via the tags input are not modified.

There is an unfortunate collision over the use of the key name. Cloud Posse uses name in this module to represent the component, such as eks or rds. AWS uses a tag with the key Name to store the full human-friendly identifier of the thing tagged, which this module outputs as id, not name. So when converting input labels to tags, the value of the Name key is set to the module id output, and there is no tag corresponding to the module name output. An empty name label will not prevent the Name tag from being exported.

It's recommended to use one terraform-null-label module for every unique resource of a given resource type. For example, if you have 10 instances, there should be 10 different labels. However, if you have multiple different kinds of resources (e.g. instances, security groups, file systems, and elastic ips), then they can all share the same label assuming they are logically related.

For most purposes, the id output is sufficient to create an ID or label for a resource, and if you want a different ID or a different format, you would instantiate another instance of null-label and configure it accordingly. However, to accomodate situations where you want all the same inputs to generate multiple descriptors, this module provides the descriptors output, which is a map of strings generated according to the format specified by the descriptor_formats input. This feature is intentionally simple and minimally configurable and will not be enhanced to add more features that are already in null-label. See examples/complete/descriptors.tf for examples.

All Cloud Posse Terraform modules use this module to ensure resources can be instantiated multiple times within an account and without conflict.

The Cloud Posse convention is to use labels as follows:

  • namespace: A short (3-4 letters) abbreviation of the company name, to ensure globally unique IDs for things like S3 buckets
  • tenant: (Rarely needed) When a company creates a dedicated resource per customer, tenant can be used to identify the customer the resource is dedicated to
  • environment: A short abbreviation for the AWS region hosting the resource, or gbl for resources like IAM roles that have no region
  • stage: The name or role of the account the resource is for, such as prod or dev
  • name: The name of the component that owns the resources, such as eks or rds

NOTE: The null originally referred to the primary Terraform provider used in this module. With Terraform 0.12, this module no longer needs any provider, but the name was kept for continuity.

  • Releases of this module from 0.23.0 onward only work with Terraform 0.13 or newer.
  • Releases of this module from 0.12.0 through 0.22.1 support HCL2 and are compatible with Terraform 0.12 or newer.
  • Releases of this module prior to 0.12.0 are compatible with earlier versions of terraform like Terraform 0.11.

Tip

๐Ÿ‘ฝ Use Atmos with Terraform

Cloud Posse uses atmos to easily orchestrate multiple environments using Terraform.
Works with Github Actions, Atlantis, or Spacelift.

Watch demo of using Atmos with Terraform
Example of running atmos to manage infrastructure from our Quick Start tutorial.

Usage

Defaults

Cloud Posse Terraform modules share a common context object that is meant to be passed from module to module. The context object is a single object that contains all the input values for terraform-null-label. However, each input value can also be specified individually by name as a standard Terraform variable, and the value of those variables, when set to something other than null, will override the value in the context object. In order to allow chaining of these objects, where the context object input to one module is transformed and passed on to the next module, all the variables default to null or empty collections. The actual default values used when nothing is explicitly set are described in the documentation below.

For example, the default value of delimiter is shown as null, but if you leave it set to null, terraform-null-label will actually use the default delimiter - (hyphen).

A non-obvious but intentional consequence of this design is that once a module sets a non-default value, future modules in the chain cannot reset the value back to the original default. Instead, the new setting becomes the new default for downstream modules. Also, collections are not overwritten, they are merged, so once a tag is added, it will remain in the tag set and cannot be removed, although its value can be overwritten.

Because the purpose of labels_as_tags is primarily to prevent tags from being generated that would conflict with the AWS provider's default_tags, it is an exception to the rule that variables override the setting in the context object. The value in the context object cannot be changed, so that later modules cannot re-enable a problematic tag.

Simple Example

module "eg_prod_bastion_label" {
  source   = "cloudposse/label/null"
  # Cloud Posse recommends pinning every module to a specific version
  # version = "x.x.x"

  namespace  = "eg"
  stage      = "prod"
  name       = "bastion"
  attributes = ["public"]
  delimiter  = "-"

  tags = {
    "BusinessUnit" = "XYZ",
    "Snapshot"     = "true"
  }
}

This will create an id with the value of eg-prod-bastion-public because when generating id, the default order is namespace, environment, stage, name, attributes (you can override it by using the label_order variable, see Advanced Example 3).

Now reference the label when creating an instance:

resource "aws_instance" "eg_prod_bastion_public" {
  instance_type = "t1.micro"
  tags          = module.eg_prod_bastion_label.tags
}

Or define a security group:

resource "aws_security_group" "eg_prod_bastion_public" {
  vpc_id = var.vpc_id
  name   = module.eg_prod_bastion_label.id
  tags   = module.eg_prod_bastion_label.tags
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Advanced Example

Here is a more complex example with two instances using two different labels. Note how efficiently the tags are defined for both the instance and the security group.

Click to show
module "eg_prod_bastion_label" {
  source   = "cloudposse/label/null"
  # Cloud Posse recommends pinning every module to a specific version
  # version = "x.x.x"

  namespace  = "eg"
  stage      = "prod"
  name       = "bastion"
  delimiter  = "-"

  tags = {
    "BusinessUnit" = "XYZ",
    "Snapshot"     = "true"
  }
}

module "eg_prod_bastion_abc_label" {
  source   = "cloudposse/label/null"
  # Cloud Posse recommends pinning every module to a specific version
  # version = "x.x.x"

  attributes = ["abc"]

  tags = {
    "BusinessUnit" = "ABC" # Override the Business Unit tag set in the base label
  }

  # Copy all other fields from the base label
  context = module.eg_prod_bastion_label.context
}

resource "aws_security_group" "eg_prod_bastion_abc" {
  name = module.eg_prod_bastion_abc_label.id
  tags = module.eg_prod_bastion_abc_label.tags
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "eg_prod_bastion_abc" {
   instance_type          = "t1.micro"
   tags                   = module.eg_prod_bastion_abc_label.tags
   vpc_security_group_ids = [aws_security_group.eg_prod_bastion_abc.id]
}

module "eg_prod_bastion_xyz_label" {
  source   = "cloudposse/label/null"
  # Cloud Posse recommends pinning every module to a specific version
  # version = "x.x.x"

  attributes = ["xyz"]

  context = module.eg_prod_bastion_label.context
}

resource "aws_security_group" "eg_prod_bastion_xyz" {
  name = module.eg_prod_bastion_xyz_label.id
  tags = module.eg_prod_bastion_xyz_label.tags
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "eg_prod_bastion_xyz" {
   instance_type          = "t1.micro"
   tags                   = module.eg_prod_bastion_xyz_label.tags
   vpc_security_group_ids = [aws_security_group.eg_prod_bastion_xyz.id]
}

Advanced Example 2

Here is a more complex example with an autoscaling group that has a different tagging schema than other resources and requires its tags to be in this format, which this module can generate via additional_tag_map and tags_as_list_of_maps:

Click to show
tags = [
    {
        key = "Name",
        propagate_at_launch = true,
        value = "namespace-stage-name"
    },
    {
        key = "Namespace",
        propagate_at_launch = true,
        value = "namespace"
    },
    {
        key = "Stage",
        propagate_at_launch = true,
        value = "stage"
    }
]

Autoscaling group using propagating tagging below (full example: autoscalinggroup)

################################
# terraform-null-label example #
################################
module "label" {
  source    = "../../"
  namespace = "cp"
  stage     = "prod"
  name      = "app"

  tags = {
    BusinessUnit = "Finance"
    ManagedBy    = "Terraform"
  }

  additional_tag_map = {
    propagate_at_launch = true
  }
}

#######################
# Launch template     #
#######################
resource "aws_launch_template" "default" {
  # terraform-null-label example used here: Set template name prefix
  name_prefix                           = "${module.label.id}-"
  image_id                              = data.aws_ami.amazon_linux.id
  instance_type                         = "t2.micro"
  instance_initiated_shutdown_behavior  = "terminate"

  vpc_security_group_ids                = [data.aws_security_group.default.id]

  monitoring {
    enabled                             = false
  }
  # terraform-null-label example used here: Set tags on volumes
  tag_specifications {
    resource_type                       = "volume"
    tags                                = module.label.tags
  }
}

######################
# Autoscaling group  #
######################
resource "aws_autoscaling_group" "default" {
  # terraform-null-label example used here: Set ASG name prefix
  name_prefix                           = "${module.label.id}-"
  vpc_zone_identifier                   = data.aws_subnet_ids.all.ids
  max_size                              = 1
  min_size                              = 1
  desired_capacity                      = 1

  launch_template = {
    id                                  = aws_launch_template.default.id
    version                             = "$$Latest"
  }

  # terraform-null-label example used here: Set tags on ASG and EC2 Servers
  tags                                  = module.label.tags_as_list_of_maps
}

Advanced Example 3

See complete example for even more examples.

This example shows how you can pass the context output of one label module to the next label_module, allowing you to create one label that has the base set of values, and then creating every extra label as a derivative of that.

Click to show
module "label1" {
  source   = "cloudposse/label/null"
  # Cloud Posse recommends pinning every module to a specific version
  # version     = "x.x.x"

  namespace   = "CloudPosse"
  tenant      = "H.R.H"
  environment = "UAT"
  stage       = "build"
  name        = "Winston Churchroom"
  attributes  = ["fire", "water", "earth", "air"]

  label_order = ["name", "tenant", "environment", "stage", "attributes"]

  tags = {
    "City"        = "Dublin"
    "Environment" = "Private"
  }
}

module "label2" {
  source   = "cloudposse/label/null"
  # Cloud Posse recommends pinning every module to a specific version
  # version     = "x.x.x"

  name      = "Charlie"
  tenant    = "" # setting to `null` would have no effect
  stage     = "test"
  delimiter = "+"
  regex_replace_chars = "/[^a-zA-Z0-9-+]/"

  additional_tag_map = {
    propagate_at_launch = true
    additional_tag      = "yes"
  }

  tags = {
    "City"        = "London"
    "Environment" = "Public"
  }

  context   = module.label1.context
}

module "label3" {
  source   = "cloudposse/label/null"
  # Cloud Posse recommends pinning every module to a specific version
  # version     = "x.x.x"

  name      = "Starfish"
  stage     = "release"
  delimiter = "."
  regex_replace_chars = "/[^-a-zA-Z0-9.]/"

  tags = {
    "Eat"    = "Carrot"
    "Animal" = "Rabbit"
  }

  context   = module.label1.context
}

This creates label outputs like this:

label1 = {
  "attributes" = tolist([
    "fire",
    "water",
    "earth",
    "air",
  ])
  "delimiter" = "-"
  "id" = "winstonchurchroom-hrh-uat-build-fire-water-earth-air"
  "name" = "winstonchurchroom"
  "namespace" = "cloudposse"
  "stage" = "build"
  "tenant" = "hrh"
}
label1_context = {
  "additional_tag_map" = {}
  "attributes" = tolist([
    "fire",
    "water",
    "earth",
    "air",
  ])
  "delimiter" = tostring(null)
  "enabled" = true
  "environment" = "UAT"
  "id_length_limit" = tonumber(null)
  "label_key_case" = tostring(null)
  "label_order" = tolist([
    "name",
    "tenant",
    "environment",
    "stage",
    "attributes",
  ])
  "label_value_case" = tostring(null)
  "name" = "Winston Churchroom"
  "namespace" = "CloudPosse"
  "regex_replace_chars" = tostring(null)
  "stage" = "build"
  "tags" = {
    "City" = "Dublin"
    "Environment" = "Private"
  }
  "tenant" = "H.R.H"
}
label1_normalized_context = {
  "additional_tag_map" = {}
  "attributes" = tolist([
    "fire",
    "water",
    "earth",
    "air",
  ])
  "delimiter" = "-"
  "enabled" = true
  "environment" = "uat"
  "id_length_limit" = 0
  "label_key_case" = "title"
  "label_order" = tolist([
    "name",
    "tenant",
    "environment",
    "stage",
    "attributes",
  ])
  "label_value_case" = "lower"
  "name" = "winstonchurchroom"
  "namespace" = "cloudposse"
  "regex_replace_chars" = "/[^-a-zA-Z0-9]/"
  "stage" = "build"
  "tags" = {
    "Attributes" = "fire-water-earth-air"
    "City" = "Dublin"
    "Environment" = "Private"
    "Name" = "winstonchurchroom-hrh-uat-build-fire-water-earth-air"
    "Namespace" = "cloudposse"
    "Stage" = "build"
    "Tenant" = "hrh"
  }
  "tenant" = "hrh"
}
label1_tags = tomap({
  "Attributes" = "fire-water-earth-air"
  "City" = "Dublin"
  "Environment" = "Private"
  "Name" = "winstonchurchroom-hrh-uat-build-fire-water-earth-air"
  "Namespace" = "cloudposse"
  "Stage" = "build"
  "Tenant" = "hrh"
})
label2 = {
  "attributes" = tolist([
    "fire",
    "water",
    "earth",
    "air",
  ])
  "delimiter" = "+"
  "id" = "charlie+uat+test+fire+water+earth+air"
  "name" = "charlie"
  "namespace" = "cloudposse"
  "stage" = "test"
  "tenant" = ""
}
label2_context = {
  "additional_tag_map" = {
    "additional_tag" = "yes"
    "propagate_at_launch" = "true"
  }
  "attributes" = tolist([
    "fire",
    "water",
    "earth",
    "air",
  ])
  "delimiter" = "+"
  "enabled" = true
  "environment" = "UAT"
  "id_length_limit" = tonumber(null)
  "label_key_case" = tostring(null)
  "label_order" = tolist([
    "name",
    "tenant",
    "environment",
    "stage",
    "attributes",
  ])
  "label_value_case" = tostring(null)
  "name" = "Charlie"
  "namespace" = "CloudPosse"
  "regex_replace_chars" = "/[^a-zA-Z0-9-+]/"
  "stage" = "test"
  "tags" = {
    "City" = "London"
    "Environment" = "Public"
  }
  "tenant" = ""
}
label2_tags = tomap({
  "Attributes" = "fire+water+earth+air"
  "City" = "London"
  "Environment" = "Public"
  "Name" = "charlie+uat+test+fire+water+earth+air"
  "Namespace" = "cloudposse"
  "Stage" = "test"
})
label2_tags_as_list_of_maps = [
  {
    "additional_tag" = "yes"
    "key" = "Attributes"
    "propagate_at_launch" = "true"
    "value" = "fire+water+earth+air"
  },
  {
    "additional_tag" = "yes"
    "key" = "City"
    "propagate_at_launch" = "true"
    "value" = "London"
  },
  {
    "additional_tag" = "yes"
    "key" = "Environment"
    "propagate_at_launch" = "true"
    "value" = "Public"
  },
  {
    "additional_tag" = "yes"
    "key" = "Name"
    "propagate_at_launch" = "true"
    "value" = "charlie+uat+test+fire+water+earth+air"
  },
  {
    "additional_tag" = "yes"
    "key" = "Namespace"
    "propagate_at_launch" = "true"
    "value" = "cloudposse"
  },
  {
    "additional_tag" = "yes"
    "key" = "Stage"
    "propagate_at_launch" = "true"
    "value" = "test"
  },
]
label3 = {
  "attributes" = tolist([
    "fire",
    "water",
    "earth",
    "air",
  ])
  "delimiter" = "."
  "id" = "starfish.h.r.h.uat.release.fire.water.earth.air"
  "name" = "starfish"
  "namespace" = "cloudposse"
  "stage" = "release"
  "tenant" = "h.r.h"
}
label3_context = {
  "additional_tag_map" = {}
  "attributes" = tolist([
    "fire",
    "water",
    "earth",
    "air",
  ])
  "delimiter" = "."
  "enabled" = true
  "environment" = "UAT"
  "id_length_limit" = tonumber(null)
  "label_key_case" = tostring(null)
  "label_order" = tolist([
    "name",
    "tenant",
    "environment",
    "stage",
    "attributes",
  ])
  "label_value_case" = tostring(null)
  "name" = "Starfish"
  "namespace" = "CloudPosse"
  "regex_replace_chars" = "/[^-a-zA-Z0-9.]/"
  "stage" = "release"
  "tags" = {
    "Animal" = "Rabbit"
    "City" = "Dublin"
    "Eat" = "Carrot"
    "Environment" = "Private"
  }
  "tenant" = "H.R.H"
}
label3_normalized_context = {
  "additional_tag_map" = {}
  "attributes" = tolist([
    "fire",
    "water",
    "earth",
    "air",
  ])
  "delimiter" = "."
  "enabled" = true
  "environment" = "uat"
  "id_length_limit" = 0
  "label_key_case" = "title"
  "label_order" = tolist([
    "name",
    "tenant",
    "environment",
    "stage",
    "attributes",
  ])
  "label_value_case" = "lower"
  "name" = "starfish"
  "namespace" = "cloudposse"
  "regex_replace_chars" = "/[^-a-zA-Z0-9.]/"
  "stage" = "release"
  "tags" = {
    "Animal" = "Rabbit"
    "Attributes" = "fire.water.earth.air"
    "City" = "Dublin"
    "Eat" = "Carrot"
    "Environment" = "Private"
    "Name" = "starfish.h.r.h.uat.release.fire.water.earth.air"
    "Namespace" = "cloudposse"
    "Stage" = "release"
    "Tenant" = "h.r.h"
  }
  "tenant" = "h.r.h"
}
label3_tags = tomap({
  "Animal" = "Rabbit"
  "Attributes" = "fire.water.earth.air"
  "City" = "Dublin"
  "Eat" = "Carrot"
  "Environment" = "Private"
  "Name" = "starfish.h.r.h.uat.release.fire.water.earth.air"
  "Namespace" = "cloudposse"
  "Stage" = "release"
  "Tenant" = "h.r.h"
})

Important

In Cloud Posse's examples, we avoid pinning modules to specific versions to prevent discrepancies between the documentation and the latest released versions. However, for your own projects, we strongly advise pinning each module to the exact version you're using. This practice ensures the stability of your infrastructure. Additionally, we recommend implementing a systematic approach for updating versions to avoid unexpected changes.

Makefile Targets

Available targets:

  help                                Help screen
  help/all                            Display help for all targets
  help/short                          This help short screen
  lint                                Lint terraform code

Requirements

Name Version
terraform >= 0.13.0

Providers

No providers.

Modules

No modules.

Resources

No resources.

Inputs

Name Description Type Default Required
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 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
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
delimiter Delimiter to be used between ID elements.
Defaults to - (hyphen). Set to "" to use no delimiter at all.
string null no
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
enabled Set to false to prevent the module from creating any resources bool null no
environment ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' string null no
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
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 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 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 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 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 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 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
stage ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' string null no
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 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
additional_tag_map The merged additional_tag_map
attributes List of attributes
context Merged but otherwise unmodified input to this module, to be used as context input to other modules.
Note: this version will have null values as defaults, not the values actually used as defaults.
delimiter Delimiter between namespace, tenant, environment, stage, name and attributes
descriptors Map of descriptors as configured by descriptor_formats
enabled True if module is enabled, false otherwise
environment Normalized environment
id Disambiguated ID string restricted to id_length_limit characters in total
id_full ID string not restricted in length
id_length_limit The id_length_limit actually used to create the ID, with 0 meaning unlimited
label_order The naming order actually used to create the ID
name Normalized name
namespace Normalized namespace
normalized_context Normalized context of this module
regex_replace_chars The regex_replace_chars actually used to create the ID
stage Normalized stage
tags Normalized Tag map
tags_as_list_of_maps This is a list with one map for each tag. Each map contains the tag key,
value, and contents of var.additional_tag_map. Used in the rare cases
where resources need additional configuration information for each tag.
tenant Normalized tenant

Tip

Use Terraform Reference Architectures for AWS

Use Cloud Posse's ready-to-go terraform architecture blueprints for AWS to get up and running quickly.

โœ… We build it together with your team.
โœ… Your team owns everything.
โœ… 100% Open Source and backed by fanatical support.

Request Quote

๐Ÿ“š Learn More

Cloud Posse is the leading DevOps Accelerator for funded startups and enterprises.

Your team can operate like a pro today.

Ensure that your team succeeds by using Cloud Posse's proven process and turnkey blueprints. Plus, we stick around until you succeed.

Day-0: Your Foundation for Success

  • Reference Architecture. You'll get everything you need from the ground up built using 100% infrastructure as code.
  • Deployment Strategy. Adopt a proven deployment strategy with GitHub Actions, enabling automated, repeatable, and reliable software releases.
  • Site Reliability Engineering. Gain total visibility into your applications and services with Datadog, ensuring high availability and performance.
  • Security Baseline. Establish a secure environment from the start, with built-in governance, accountability, and comprehensive audit logs, safeguarding your operations.
  • GitOps. Empower your team to manage infrastructure changes confidently and efficiently through Pull Requests, leveraging the full power of GitHub Actions.

Request Quote

Day-2: Your Operational Mastery

  • Training. Equip your team with the knowledge and skills to confidently manage the infrastructure, ensuring long-term success and self-sufficiency.
  • Support. Benefit from a seamless communication over Slack with our experts, ensuring you have the support you need, whenever you need it.
  • Troubleshooting. Access expert assistance to quickly resolve any operational challenges, minimizing downtime and maintaining business continuity.
  • Code Reviews. Enhance your teamโ€™s code quality with our expert feedback, fostering continuous improvement and collaboration.
  • Bug Fixes. Rely on our team to troubleshoot and resolve any issues, ensuring your systems run smoothly.
  • Migration Assistance. Accelerate your migration process with our dedicated support, minimizing disruption and speeding up time-to-value.
  • Customer Workshops. Engage with our team in weekly workshops, gaining insights and strategies to continuously improve and innovate.

Request Quote

โœจ Contributing

This project is under active development, and we encourage contributions from our community.

Many thanks to our outstanding contributors:

For ๐Ÿ› bug reports & feature requests, please use the issue tracker.

In general, PRs are welcome. We follow the typical "fork-and-pull" Git workflow.

  1. Review our Code of Conduct and Contributor Guidelines.
  2. Fork the repo on GitHub
  3. Clone the project to your own machine
  4. Commit changes to your own branch
  5. Push your work back up to your fork
  6. Submit a Pull Request so that we can review your changes

NOTE: Be sure to merge the latest changes from "upstream" before making a pull request!

๐ŸŒŽ Slack Community

Join our Open Source Community on Slack. It's FREE for everyone! Our "SweetOps" community is where you get to talk with others who share a similar vision for how to rollout and manage infrastructure. This is the best place to talk shop, ask questions, solicit feedback, and work together as a community to build totally sweet infrastructure.

๐Ÿ“ฐ Newsletter

Sign up for our newsletter and join 3,000+ DevOps engineers, CTOs, and founders who get insider access to the latest DevOps trends, so you can always stay in the know. Dropped straight into your Inbox every week โ€” and usually a 5-minute read.

๐Ÿ“† Office Hours

Join us every Wednesday via Zoom for your weekly dose of insider DevOps trends, AWS news and Terraform insights, all sourced from our SweetOps community, plus a live Q&A that you canโ€™t find anywhere else. It's FREE for everyone!

License

License

Preamble to the Apache License, Version 2.0

Complete license is available in the LICENSE file.

Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.

Trademarks

All other trademarks referenced herein are the property of their respective owners.


Copyright ยฉ 2017-2024 Cloud Posse, LLC

README footer

Beacon

terraform-null-label's People

Contributors

actions-user avatar aknysh avatar cloudpossebot avatar darend avatar dylanbannon avatar goruha avatar henrydobson avatar jamie-bitflight avatar kiddom-kq avatar korenyoni avatar maartenvanderhoef avatar max-lobur avatar maximmi avatar michaelpereira avatar nuru avatar osterman avatar pontostroy avatar r-divakaran-hrs avatar robusgauli avatar rverma-nikiai avatar s2504s avatar siran avatar solairerove avatar sweetops avatar tibbing avatar vadim-hleif avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

terraform-null-label's Issues

A reference to "each.value" has been used in a context in which it unavailable

tag_specifications {
for_each = ["instance", "volume", "elastic-gpu", "spot-instance-request"]
resource_type = each.value
tags = module.label.tags
}

tag_specifications {
for_each = ["instance", "volume", "elastic-gpu", "spot-instance-request"]

resource_type = each.value
tags          = module.label.tags

}

Error: each.value cannot be used in this context

on launchtemplate.tf line 68, in resource "aws_launch_template" "default":
68: resource_type = each.value

A reference to "each.value" has been used in a context in which it
unavailable, such as when the configuration no longer contains the value in
its "for_each" expression. Remove this reference to each.value in your
configuration to work around this error.

cfitch@8J98693:~/fitch-eks$ terraform plan

Error: each.value cannot be used in this context

on launchtemplate.tf line 68, in resource "aws_launch_template" "default":
68: resource_type = each.value

A reference to "each.value" has been used in a context in which it
unavailable, such as when the configuration no longer contains the value in
its "for_each" expression. Remove this reference to each.value in your
configuration to work around this error.

module.network_interface_label.provider.null is not present

Hey guys, I've seen similar issues but, is there any way around this, I am on v0.11.14, I even removed the label modules from the codebase but TF keeps complaining about this:

Error: Error asking for user input: 1 error occurred:

  • module.network_interface_label.data.null_data_source.tags_as_list_of_maps[2]: configuration for module.network_interface_label.provider.null is not present; a provider configuration block is required for all operations

This seems to happen either renaming the modules or taking them off completely.

Thanks

Create use-case driven examples in the README

Have a question? Please checkout our Slack Community or visit our Slack Archive.

Slack Community

Describe the Feature

The README has a Wall of Text (WoT) which is overwhelming even for people who are familiar with the null label.

This prevents people from deliberately adopting (instead of accidentally adopting through usage of the open source modules) this module.

Expected Behavior

Real simple

module "label" {
  source  = "cloudposse/label/null"
  version = "0.25.0"

  # usually short name of org/company
  namespace = "org"

  # optional company tenant
  # tenant = "acme"

  # usually short/fixed name of region
  environment = "ue1"

  # usually short name of account
  stage = "prod"

  # usually the name of the root terraform module/dir/component
  name = "eks"

  # optional attributes
  # attributes = ["blue"]
}

# without tenant, module.label.id = "org-ue1-prod-eks"
# with tenant, module.label.id = "org-acme-ue1-prod-eks"
# without tenant and w/ attributes, module.label.id = "org-ue1-prod-eks-blue"
# with tenant and w/ attributes, module.label.id = "org-acme-ue1-prod-eks-blue"
output "label" {
  value = module.label
}

Running the above terraform shows you the full output of the module

There are other examples already (label2.tf) in the repo too which can be useful to figure out how to use this. However you'd have to dig here.

Use Case

This module is difficult to explain to users. I find myself going to the readme and then creating my own explanation or showing the users via an example like the one above.

Describe Ideal Solution

  • Simplify the readme by making it less overwhelming
  • Explain the inconsistent naming problem that it solves
  • Explain how to use the null-label module
    • why is it called null-label instead of just label?
    • clearly what each common var means
    • what is generally used for each var and why?
    • how long should each of the vars be?
      • probably 5 or fewer characters
    • how to use it simply to get the identifier and tags?
      • see id and tags context
    • what is the context and what makes it so powerful?
      • anything that uses context.tf mixin can use the same identifier and override values
    • how to use it with other cloudposse modules?
      • can take advantage of context = module.label.contect to reuse names
  • other use cases
    • how to use it for resources that require shorter names like target groups and LBs?
      • use id_length_limit or set one of the above inputs to null or "" to omit it from the id
    • how does cloudposse's amazing terraform framework tool known as atmos make use of this module ?
    • what if the labels need to be reordered?
    • what is the export context.tf and why should I use it in my terraform module?
    • how would I have the original null label inherit from another null label and override only one var like name?
    • when is it common to change the delimiter?
    • when is tags as list of maps useful?
    • etc

Alternatives Considered

I write a blogpost on this or someone else does to help explain everything

Additional Context

N/A

enabled = 'false' causes "Resource 'null_resource.default' not found for variable 'null_resource.default.triggers.x''

When i use enabled = "false":

module "mylabel" {
  enabled = "false"
  source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=0.3.1"
  namespace = "${var.namespace}"
  stage = "${var.stage}"
  name = "${var.cname_label}"
  tags = "${module.infrastructure.default_tags}"
}

I get errors because the module outputs refer to a resource that is not present when enabled is false:

Error: Error running plan: 7 error(s) occurred:

* module.mylabel.output.id: Resource 'null_resource.default' not found for variable 'null_resource.default.triggers.id'
* module.mylabel.output.attributes: Resource 'null_resource.default' not found for variable 'null_resource.default.triggers.attributes'
* module.mylabel.output.tags: Resource 'null_resource.default' not found for variable 'null_resource.default.triggers.id'
* module.mylabel.output.stage: Resource 'null_resource.default' not found for variable 'null_resource.default.triggers.stage'
* module.mylabel.output.name: Resource 'null_resource.default' not found for variable 'null_resource.default.triggers.name'
* module.mylabel.output.namespace: Resource 'null_resource.default' not found for variable 'null_resource.default.triggers.namespace'

terragrunt -version

terragrunt version v0.13.23

terraform -version

Terraform v0.11.1

Meaning of Namespace

What is the intended meaning of the term 'namespace' in this context? Is it just an arbitrary name, like company name, or client name (anything you want?)

Using interpolated values in strings

Hi there,
I've traced down a problem from one of your other modules to the way that tags are consumed when you use interpolated values in tags. I'm fairly sure that it's a terraform problem, but you might have a way of working around it. Here is an example:

resource "random_id" "random" {
  byte_length = 1
}

module "eg_prod_bastion_label" {
  source     = "git::https://github.com/cloudposse/terraform-null-label.git?ref=master"
  namespace  = "eg"
  stage      = "prod"
  name       = "bastion"
  attributes = ["public"]
  delimiter  = "-"
  tags       = "${map("BusinessUnit", "${random_id.random.hex}", "Snapshot", "true")}"
}

Results in:

* module.eg_prod_bastion_label.data.null_data_source.tags_as_list_of_maps: data.null_data_source.tags_as_list_of_maps: value of 'count' cannot be computed

This is because the random_id has not been computed yet, so the module is struggling to see a value there.

You get similar problems (but different error messages) when you try and do this in any of your higher level modules. E.g. terraform-aws-multi-az-subnets suffers the same fate.

I really need those random prefixes for testing reasons. Do you have any idea how I can work around this?

Thanks, Phil

Fix Link in `README`

what

  • We link to wrong github search: https://github.com/cloudposse?utf8=%E2%9C%93&q=tf_&type=&language=

regex_replace_chars is not inherited by context

In your own code here: https://github.com/cloudposse/terraform-aws-tfstate-backend/blob/master/main.tf#L22

even when you pass regex_replace_chars to base, s3_bucket_label will have namespace scrubbed again with default regex_replace_chars: https://github.com/cloudposse/terraform-null-label/blob/master/main.tf#L34

It should be handled the same way as others - regex_replace_charsea->regex_replace_chars_context_or_default->regex_replace_chars_or_context->regex_replace_chars

terraform validaton fails

Using latest source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=0.25.0"

terraform --version                                                                                                                                                                     nikos
Terraform v1.3.6
on darwin_amd64
โ•ท
โ”‚ Error: Invalid function argument
โ”‚ 
โ”‚   on .terraform/modules/label/variables.tf line 38, in variable "context":
โ”‚   38:     condition     = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"])
โ”‚     โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
โ”‚     โ”‚ while calling lookup(inputMap, key, default...)
โ”‚     โ”‚ var.context is a string
โ”‚ 
โ”‚ Invalid value for "inputMap" parameter: lookup() requires a map as the
โ”‚ first argument.
โ•ต
โ•ท
โ”‚ Error: Invalid function argument
โ”‚ 
โ”‚   on .terraform/modules/label/variables.tf line 43, in variable "context":
โ”‚   43:     condition     = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"])
โ”‚     โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
โ”‚     โ”‚ while calling lookup(inputMap, key, default...)
โ”‚     โ”‚ var.context is a string
โ”‚ 
โ”‚ Invalid value for "inputMap" parameter: lookup() requires a map as the
โ”‚ first argument.
โ•ต


Validation failed: terraform/modules/label
โ•ท
โ”‚ Error: Invalid function argument
โ”‚ 
โ”‚   on .terraform/modules/label/variables.tf line 38, in variable "context":
โ”‚   38:     condition     = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"])
โ”‚     โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
โ”‚     โ”‚ while calling lookup(inputMap, key, default...)
โ”‚     โ”‚ var.context is a string
โ”‚ 
โ”‚ Invalid value for "inputMap" parameter: lookup() requires a map as the
โ”‚ first argument.
โ•ต
โ•ท
โ”‚ Error: Invalid function argument
โ”‚ 
โ”‚   on .terraform/modules/label/variables.tf line 43, in variable "context":
โ”‚   43:     condition     = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"])
โ”‚     โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
โ”‚     โ”‚ while calling lookup(inputMap, key, default...)
โ”‚     โ”‚ var.context is a string
โ”‚ 
โ”‚ Invalid value for "inputMap" parameter: lookup() requires a map as the
โ”‚ first argument.```

Empty delimiter does not work with TF 0.12

The delimiter is calculated with the coalesce function, which considers and empty string and a null string as the same thing. This prevents the delimiter from being set as "", which is useful for certain resources. For example, Azure storage account container names can only contain lowercase letters and numbers.

I'm not sure of the best way to fix this, other than add a flag that says to not use the delimiter. The regex is applied before the delimiter is added. I could submit a PR for a new flag if that would be acceptable.

Enforce required fields

Have a question? Please checkout our Slack Community or visit our Slack Archive.

Slack Community

Describe the Feature

It would be nice to enforce a required field. Sometimes we omit the name field because we're going through a transition where we are moving name from default.auto.tfvars to a yaml catalog and sometimes this field is omitted by mistake.

By enforcing required fields on the null label we can ensure that expected fields are always in place e.g. so we do not have a case where we accidentally create a resource such as an IAM role with a missing name.

Expected Behavior

Use something like required_fields = ["tenant", "namespace", "environment", "name"] for example and if any of those fields are missing, throw an error.

This would also remove the need to use required_tags from introspection.mixin.tf

Make it possible to have different naming sequences

Currently, this repo supports only namespace-stage-name sequence.
In some organizations, there is a different standard for example namespace-name-stage.

Please consider and explore whether it is possible to support different naming sequences.

Tags are Broken

Hi, I did a test of the module:

module "eg_prod_bastion_abc_label" {
  source     = "git::https://github.com/cloudposse/terraform-null-label.git?ref=master"
  namespace  = "eg"
  stage      = "prod"
  name       = "bastion"
  attributes = ["abc"]
  delimiter  = "-"
  tags       = "${map("BusinessUnit", "ABC")}"
}

resource "aws_security_group" "eg_prod_bastion_abc" {
  name = "${module.eg_prod_bastion_abc_label.id}"
  tags = "${module.eg_prod_bastion_abc_label.tags}"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

output:

โœ— terraform apply
provider.aws.region
  The region where AWS operations will take place. Examples
  are us-east-1, us-west-2, etc.

  Default: us-east-1
  Enter a value: us-east-1

aws_security_group.eg_prod_bastion_abc: Creating...
  description:                           "" => "Managed by Terraform"
  egress.#:                              "" => "<computed>"
  ingress.#:                             "" => "1"
  ingress.2541437006.cidr_blocks.#:      "" => "1"
  ingress.2541437006.cidr_blocks.0:      "" => "0.0.0.0/0"
  ingress.2541437006.description:        "" => ""
  ingress.2541437006.from_port:          "" => "22"
  ingress.2541437006.ipv6_cidr_blocks.#: "" => "0"
  ingress.2541437006.protocol:           "" => "tcp"
  ingress.2541437006.security_groups.#:  "" => "0"
  ingress.2541437006.self:               "" => "false"
  ingress.2541437006.to_port:            "" => "22"
  name:                                  "" => "<computed>"
  owner_id:                              "" => "<computed>"
  revoke_rules_on_delete:                "" => "false"
  tags.%:                                "" => "<computed>"
  vpc_id:                                "" => "<computed>"
aws_security_group.eg_prod_bastion_abc: Creation complete after 6s (ID: sg-59c3e52b)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

checking tags of the created resource

โœ— aws ec2 describe-tags --filters "Name=resource-id,Values=sg-59c3e52b"
{
    "Tags": []
}

Empty ID

Found a bug? Maybe our Slack Community can help. - I asked about 24h ago but didn't receive any answers yet.
Slack Community

Describe the Bug

  • After upgrading to the latest version(this used to work in 0.18.0) I'm always getting an empty label ID, regardless what I tried. I might be wrong in the way I use the module after the changes introduced in 0.19, but I tried to follow the docs to the best of my abilities/understanding.
  • The same happens when using module.label.id instead of module.label.id_full.
  • I also played with the enabled and id_length_limit parameters, but to no avail yet. Am I missing anything?

Here's a simplified code example that can be used to show this behavior.

module "label" {
  source          = "git::https://github.com/cloudposse/terraform-null-label.git?ref=0.21.0"
  context         = module.this.context
  enabled         = true
  id_length_limit = 10
}
output "ID" {
  value = "'${module.label.id_full}'"
}

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
ID = ''

Expected Behavior

I get a non-empty random'ish value in the ID output.

Steps to Reproduce

  1. Use the above code example, pasted into a main.tf file, stored in a new directory next to the context.tf file currently expected by terraform-null-label.
  2. Run 'terraform init && terraform apply'
  3. See the empty ID in the list of outputs.

Screenshots

See above code snippets

Environment (please complete the following information):

Anything that will help us triage the bug will help. Here are some ideas:

  • macOS Catalina 10.15.7

Additional Context

N/A

Proposal: `prefix` output

Describe the Feature

I find it common that folks will utilize what I think of as the label's prefix (e.g. ${namespace}-${stage}-${environment}) as a string interpolation prefix to some identifier that they want to create without building a full label.

Most of the time this is used as a crutch to avoid having to create a label module for each namable thing that you're working on. It could definitely be argued that this is a bad practice and that it shouldn't be done... but I've seen it enough now across multiple codebases that I know I'm not the only one who does it so figured this proposal was worth the discussion.

The proposal is to add a new output called prefix that is simply all label components up until the name component. e.g. ${namespace}-${stage}-${environment}, ${namespace}-${stage}, ${namespace}-${environment}, etc.

I would need to look into the code more to determine how we would handle that in regards to the label ordering... but figured I could bring this up for discussion before coming up with a proper solution.

Use Case

This would enable not having to create this label prefix for ad-hoc usages and therefore dry up some terraform string interpolation code.

Alternatives Considered

Alternatives:

  1. Don't do this... I'd be fine to hear the feedback here say something along the lines of: Hey that should be considered bad practice and we don't want to support it.
  2. Create a prefix label that is only made up of the components that you want instead of outputting the prefix from more specific labels.

Ability to set max length of ID output

I'm a heavy user of this module for naming resources, but often find myself writing workarounds for limiting the length of the output ID. Many resources have a max allowed length of the names (such as aws_lb and aws_alb_target_group (32 characters), aws_iam_role (64 characters) etc.

It would be really nice to have an input parameter max_id_length capping the output ID, while still keeping it unique. Suggested implementation:

....
variable "max_id_length" {
  type        = number
  default     = 64
  description = "Specify max length of output id result"
}

locals {
  ...
  full_id = lower(join(local.delimiter, local.labels))
  id_hash   = md5(local.full_id)
  id_capped = var.max_id_length <= 6 ? substr(local.id_hash, 0, var.max_id_length) : "${replace(substr(local.full_id, 0, var.max_id_length - 6), "/-$/", "")}-${substr(local.id_hash, 0, 5)}"
  id        = var.max_id_length != 0 && length(local.full_id) > var.max_id_length ? local.id_capped : local.full_id
}

I can give it a go and make a PR if you like the idea.

provider null_resource not found

Hello,

I get error:

Provider "null_resource" not available for installation.

A provider named "null_resource" could not be found in the official repository.

This may result from mistyping the provider name, or the given provider may
be a third-party provider that cannot be installed automatically.

In the latter case, the plugin must be installed manually by locating and
downloading a suitable distribution package and placing the plugin's executable
file in the following directory:
terraform.d/plugins/darwin_amd64

Terraform detects necessary plugins by inspecting the configuration and state.
To view the provider versions requested by each module, run
"terraform providers".

I check in official reposistory of terraform not proviver null_resource, only provider null
link ref: https://www.terraform.io/docs/providers/null/index.html

Add Example Usage

what

  • Add example invocation

why

  • We need this so we can soon enable automated continuous integration testing of module

Any plans to bump the supported terraform version to v0.12.19

Hi, I upgraded terraform to v0.12.19 to work around the forthcoming AWS RDS ssl certification expiration issue documented here.
However this upgrade is no longer compatible with traefic due to the version being pinned at required_version = "~> 0.12.0" in versions.tf
Are there any plans to support terraform versions >= 0.12.19 ?
Thanks,
Mark

`Initializing modules...

Error: Unsupported Terraform Core version

Module traefik.default_label (from
"git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.11.1")
does not support Terraform version 0.12.19. To proceed, either choose another
supported Terraform version or update the module's version constraint. Version
constraints are normally set for good reason, so updating the constraint may
lead to other errors or unexpected behavior.`

variables are not properly formatted

in main.tf and all terraform files the variables are not properly formatted. Terraform throws error if i use name = var.name it should be name = "${var.name}"

Document Usage of Stage Parameter

what

  • Document usage of stage parameter
  • Explain why the term stage is used rather than environment

why

  • Users may not be accustomed to the nomenclature due to commonly misused usage of the term stage

references

can you give me some idea why the term 'stage' was chosen over 'environment'? In the label module?

A "stage" is where software performs (e.g. runs). The term environment is also overloaded and often abbreviated as env, which from my subjective experience more confusing. Stage IMO is misused inside many organizations. We made it our personal mission to correct its usage, however, I could maybe consider adding environment after stage as another (optional) dimension of disambiguation.

image
image

that is to say, "staging is a stage", but a stage is not always staging.

what's the name for the env between dev and prod?

that might be staging. note, that staging is not the same word as stage. other options are "QA", "UAT" or "Preproduction".

https://en.wikipedia.org/wiki/Multistage_rocket

Unsupported Core Version 0.14.2

Found a bug? Maybe our Slack Community can help.

Slack Community

Describe the Bug

Initializing modules...

Error: Unsupported Terraform Core version

  on .terraform/modules/ec2-bastion-server.dns.this/versions.tf line 2, in terraform:
   2:   required_version = ">= 0.12.0, < 0.14.0"

Module module.ec2-bastion-server.module.dns.module.this (from
git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.19.2)
does not support Terraform version 0.14.2. To proceed, either choose another
supported Terraform version or update this version constraint. Version
constraints are normally set for good reason, so updating the constraint may
lead to other errors or unexpected behavior.

Expected Behavior

I expected the terraform init to setup the ec2-bastion-server module successfully.

Steps to Reproduce

Steps to reproduce the behavior:

  1. Use terraform 0.14.2
  2. Use the cloudposse/ec2-bastion-server module
  3. Perform terraform init

Screenshots

Initializing modules...

Error: Unsupported Terraform Core version

  on .terraform/modules/ec2-bastion-server.dns.this/versions.tf line 2, in terraform:
   2:   required_version = ">= 0.12.0, < 0.14.0"

Module module.ec2-bastion-server.module.dns.module.this (from
git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.19.2)
does not support Terraform version 0.14.2. To proceed, either choose another
supported Terraform version or update this version constraint. Version
constraints are normally set for good reason, so updating the constraint may
lead to other errors or unexpected behavior.

Environment (please complete the following information):

Anything that will help us triage the bug will help. Here are some ideas:

  • OS: MacOS
  • Version 10.15.7

Additional Context

Add any other context about the problem here.

Valid resource characters are being replaced by null label module.

Found a bug? Maybe our Slack Community can help.

Slack Community

Describe the Bug

Valid resource character / is being replaced in ECR's name variable.

Expected Behavior

Valid resource character / is not being replaced in ECR's name variable.

Steps to Reproduce

Steps to reproduce the behavior:

  1. Use tfr:///cloudposse/ecr/aws//.?version=0.38.0 terraform module
  2. Set name a/b
  3. Name becomes ab

Edit: I added / manually to regex_replace_chars for the time being but it would be nice to change the default to include slashes. :)

Short Id

How to handle short Id's for resources such as ElastiCache clusters which have the following constraints:

Cluster naming constraints

Must contain from 1 to 20 alphanumeric characters or hyphens.
Must begin with a letter.
Cannot contain two consecutive hyphens.
Cannot end with a hyphen.

Additional tags are not working

I am using the null-label module to achieve a naming convention for all aws resources. I should say its perfectly working, except for additional tags.

For example, in my sqs terraform module I am using null-label as below

module "sqs_name" {
  source             = "git::https://github.com/cloudposse/terraform-null-label.git?ref=master"
  namespace          = "${var.namespace}"
  environment        = "${var.environment}"
  name               = "${var.name}"
  attributes         = "${var.attributes}"
  delimiter          = "${var.delimiter}"
  tags               = "${var.tags}"
  additional_tag_map = "${var.additional_tags}"
  label_order        = ["namespace", "name", "environment", "attributes"]
}

resource "aws_sqs_queue" "this" {
  count = "${var.create && var.sqs_queue_with_kms == 0 ? 1 : 0}"

  name = "${module.sqs_name.id}"

  visibility_timeout_seconds  = "${var.visibility_timeout_seconds}"
  message_retention_seconds   = "${var.message_retention_seconds}"
  max_message_size            = "${var.max_message_size}"
  delay_seconds               = "${var.delay_seconds}"
  receive_wait_time_seconds   = "${var.receive_wait_time_seconds}"
  policy                      = "${var.policy}"
  redrive_policy              = "${var.redrive_policy}"
  fifo_queue                  = "${var.fifo_queue}"
  content_based_deduplication = "${var.content_based_deduplication}"

  tags = "${modue.sqs_name.tags}"
}

Later creating SQS like

module "app_queue" {
  source                    = "../sqs"
  name                      = "${var.sqs_name}"
  namespace                 = "${var.project}"
  environment               = "${var.environment}"
  message_retention_seconds = "3600"

  tags                      = "${var.tags}"
  additional_tags           = "${map("service", "app-service","purpose", "to_refresh_cache")}"
}

The sqs resource is getting created without additional tags. Could you please helm me ?

Support TF 0.14

Describe the Feature

Support TF version 0.14

Expected Behavior

I expect to not get "this module is not compatible, either change the source or contact..." messages when i terraform plan

Use Case

General labeling and naming

Alternatives Considered

Currently using a fork that has support for 0.14.0

terraform failure after v0.5.2 with empty tags

Up until v0.5.2, I was able to have empty values for namespace, stage, etc. Starting with v0.5.3, this is no longer true. I get this error with v0.5.3 and newer:

Error: Error asking for user input: 1 error(s) occurred:

* module.common_tags.local.generated_tags: local.generated_tags: zipmap: count of keys (1) does not match count of values (0) in:

${zipmap(
    compact(list("Name", local.namespace != "" ? "Namespace" : "", local.environment != "" ? "Environment" : "", local.stage != "" ? "Stage" : "")),
    compact(list(local.id, local.namespace, local.environment, local.stage))
    )}

Feature request: support renaming/dropping default tags

Describe the Feature

Allow changing the name of default tags, or dropping them entirely.

Expected Behavior

  1. Ability to rename default tags, eg rename Stage to environment
  2. Ability to remove default tags, eg prevent namespace tag from being created

Use Case

We use this module a lot, but some of the tag names don't match our internal standard. For example, we use "environment" rather than "stage". As a workaround, we set additional_tag_map = { environment = "dev" }, but this means we have a Stage tag which duplicates the value.

In AWS this is a problem since there is a limit of 10 tags per resource, and we add other tags globally for cost /security control.

Describe Ideal Solution

module "context" {
  ...
  stage_tag_name = "environment" # use 'environment' instead of 'stage'
  namespace_tag_name = "" # don't create a namespace tag at all
}

Additional Context

I suggested this idea in the CloudPosse slack, some feedback exists there: https://sweetops.slack.com/archives/CB6GHNLG0/p1621557927034000

Is the null provider still required?

I'm only starting to use this module, but I noticed the latest release and #80 and so thought to remove the null provider from versions.tf and it looks like it still works?

edit:
Specifically, the change at fressets#1. So far have only been using it for simple labels (as well as the context feature) for labeling DigitalOcean instances and volumes and not much else (but I think that covers most of this module?). And if it isn't needed, then there's the question about the module name not being apt anymore (for 0.12 at least), I guess?

regex_replace_chars does not behave as expected

regex_replace_chars conversion to a variable has been implemented as a string. See https://github.com/cloudposse/terraform-null-label/blob/master/main.tf#L30

This causes the desired regex replacements on the input strings to fail.

Incorrect

  name = "${lower(replace(local.name_or_context, "/var.regex_replace_chars/", ""))}"

Correct

  name = "${lower(replace(local.name_or_context, "/${var.regex_replace_chars}/", ""))}"

I expect the module.labels.id output of the following module configuration to be null-dev-activedirectory.

The actual output is null-dev-active-directory

module "labels" {
  source     = "git::https://github.com/cloudposse/terraform-null-label.git?ref=master"
  namespace  = "${var.namespace}" # null
  stage      = "${var.stage}" # dev
  name       = "${var.name}" # var.name = "active-directory"
  attributes = "${var.attributes}"
  delimiter  = "-"

  tags = "${merge(
    map("ShutDownPolicy", "uk-office-hours"),
    map("GitSha", "${var.revision_id}"),
    map("BranchName", "${var.branch_name}"),
    map("App", "${var.component_name}"),
    var.tags)}"
}

Make var.label_order "global"

This is not really an issue in this module but in all modules using this module.

I would like to set label_order in a top module ie ecs web app and then all submodules would inherit the label_order.

Our naming convention is not like yours and the only thing that keep me from going from your modules to my own is label_order.

Additional tag map isn't actually merged in with the tags

I'v recently updated to TF 0.12 and I noticed that changes to the additional_tag_map variable does nothing. I've also noticed that the additional tags no longer exist in any of our resources. I'm not exactly sure, but seems as though the following line in main.tf is missing a reference to var.additional_tag_map.
The line is where the tags local is being set, and it is:

tags = merge(var.context.tags, local.generated_tags, var.tags)

I believe the correction should be:

tags = merge(var.context.tags, local.generated_tags, var.tags, var.additional_tag_map)

Making this change locally seems to have done the trick for me, but I might be missing something.

Ability to Have Required Tags

Hi,

This is more of a question. Is there a way to enforce required tags while also providing the ability to add additional tags as needed.

For example, if I have BusinessUnit and CostCenter that every resource must have and "ManagedBy" is an ad-hoc tag that they want to add.

Could this be possible by creating a module (which has the required variables) that calls your module? Basically a pass-thru except it would create a joint tags map (required and ad-hoc) and pass that to your module.

Any ideas?

enabled input not working

I'm trying to implement tagging based on the environment, and one of the way I trying is by conditionally setting the 'enabled' string. e.g.

module "dev_label" {
enabled = "${var.env == "dev" ? true : false}"
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=0.11.1"
name = "test"

tags = {
"Dev" = "true",
"Snapshot" = "true"
}
}

But this doesn't seem to work, I even just set 'enable = false' to test the feature but that doesn't work either. What am I doing wrong or is this a bug?

I'm using terraform 0.11 hence using the 0.11.1 release.

Thanks,
Phu

A hyphen in environment name is ommited

Hello,

I am using this module for naming all AWS resources created via terraform. Recently I wanted to use it for creating a secret manager resource as well. The template looks as below

module "secret_identifier" {
  source             = "git::https://github.com/cloudposse/terraform-null-label.git?ref=master"
  namespace          = "${var.namespace}"
  environment        = "${var.environment}"
  name               = "${var.secret_name}"
  attributes         = "${var.attributes}"
  delimiter          = "${var.delimiter}"
  tags               = "${var.tags}"
  additional_tag_map = "${var.additional_tags}"
  label_order        = ["namespace", "environment", "name", "attributes"]
}

I've set the delimiter to be /

Then am using it like

module "service_secret" {
  source               = "../modules/secret_manager_norotation"
  namespace            = "innovate"
  environment          = "dev-100}"
  attributes           = ["internal_dbconnection"]

  secret_name          = "internal_service"
  secret_description   = "Credentials set for internal service to access  db"
  user_name            = "bob_internal"

  tags                 = "${var.tags}"
  additional_tags      = "${map("service", "internal-service","purpose", "access_nternal_db")}"
}

Finally when its creating the secret its name is coming up like
innovate/dev100/internalservice/internal_dbconnection

So as you have noticed, it omitted the hyphens in environment and also omitted the underscore in the name. Is that an expected behaviour ?
Any help will be greatly appreciated!

Thanks,
Raju.

Create multiple pre-truncated id outputs automatically

I am happy to implement this feature. I wanted to see if it would be supported first though ๐Ÿ˜Š

Describe the Feature

Add outputs id_16, id_32, id_64, id_128.

Expected Behavior

They would act like id output with differing id_length_limit values set.

Use Case

It's often the case I want to use the same label for different resources with different name restrictions. For example, using the label to name a RDS cluster (no limit) and to name_prefix an IAM Role (32char limit).

At the moment, I have to set the id_length_limit value to the smallest required by all resources, which can result in names that are needlessly terse for 99% of resources.

Describe Ideal Solution

Next minor release (add new outputs only):

  • Add id_16, id_32, id_64, id_128, etc as above

Next major release (require use of new outputs):

  • id, id_length_limit are removed
  • Users use id_N or id_full to get the correct length ID

Alternatives Considered

The major release idea can be dropped. I like the idea of making users be explicit around their length, since most resources do have some length restriction.

Make it TF 0.13 compatible

Using this module as part of the ecr module fails:

terraform13 init
Initializing modules...

Error: Unsupported Terraform Core version

  on .terraform/modules/ecr.ecr.label/versions.tf line 2, in terraform:
   2:   required_version = "~> 0.12.0"

Module module.ecr.module.ecr.module.label (from
git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.16.0)
does not support Terraform version 0.13.0-beta2. To proceed, either choose
another supported Terraform version or update this version constraint. Version
constraints are normally set for good reason, so updating the constraint may
lead to other errors or unexpected behavior.

Expected Behavior

Should pass

Use Case

Related to cloudposse/terraform-aws-ecr#51

Describe Ideal Solution

Fix version range

Alternatives Considered

N/A

Additional Context

cloudposse/terraform-aws-ecr#51 so that ecr module can be used with for loops

Change order of attribute concatination

When null-label was converted to the context.tf format, the context attributes were put after the var attributes.

attributes = compact(distinct(concat(var.attributes, var.context.attributes)))

This turns out to be not what we want in all the modules I have reviewed so far. Modules always want to put their attributes at the end of the list. Right now they do this with code like this:

module "label" {
  source  = "cloudposse/label/null"
  version = "0.22.0"

  attributes = compact(concat(var.attributes, ["workers"]))
  tags       = local.tags

  context = module.this.context
}

If null-label put the var.attributes last, like this:

 attributes  = compact(distinct(concat(var.context.attributes, var.attributes))) 

then modules would be simplified:

module "label" {
  source  = "cloudposse/label/null"
  version = "0.22.0"

  attributes = ["workers"]
  tags       = local.tags

  context = module.this.context
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.