Resource Tagging or "(Host-)Names is for tombstones, baby!"
One of the first things I discovered using cloud services is that certain types of cloud resources really come and go. Especially instances with local ephemeral data or instances that access or process shared data are compared to “classical” architectures very short living. You scale up (creating) or scale down (a.k.a. destroying) instances depending on your current workload.
This is of course nothing new and was mentioned a thousand times before. What I learned in this context is the importance of tagging or labeling resources. For (a simple) example, to get a fast overview of how many instances are running to process product “x” in environment “Prod” you can query for those tags. And of course this is most useful in automation, e.g. provisioning, monitoring etc. If you use Puppet, the deployment can or even should depend on your instance tags. You do not provision instance “6snf3xbds”, you provision instance(s) with tags “webserver” and “Production”. In most cases you do not need hostnames1 anyway, especially in scale-out architectures, as they are usually behind a loadbalancer. Direct interactive ssh access should be an anti-pattern (An exception may be ssh access for ansible provisioning). In the age of cloud, immutable infrastructure should be prefered.
“If you have to use ssh to check what went wrong your monitoring is broken”
The IaaS tool of choice for me is Terraform and tagging of resources is supported. Of course it is critical to have a clear naming convention of your tags. Is it “Prod”, “prod”, “Production” etc. ?
I found this cool Terraform module terraform-null-label that takes care of project consistent tagging. For example:
resource "aws_instance" "puppetmaster" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.medium"
iam_instance_profile = aws_iam_instance_profile.puppet-master-instance-profile.name
associate_public_ip_address = "true"
subnet_id = aws_subnet.public-a.id
vpc_security_group_ids = [
aws_security_group.puppet-public-ssh.id,
aws_security_group.puppet-public-puppet.id
]
user_data = base64encode(data.template_file.puppet_master_init.rendered)
key_name = var.key_name
tags = module.puppet-master-labels.tags
}
This creates an instance, defines the subnet, security groups and so on. The single line tags = module.puppet-master-labels.tags
takes care of all tags this resource needs are in a consistent manner.
The tags itself are defined as follows:
module "puppet-master-labels" {
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=master"
context = module.base-labels.context
name = "puppetmaster"
label_order = ["namespace", "environment", "name"]
}
In this example only the specific name “puppetmaster” is individually set for this resource. Common tags are inherited by referencing the context, here context = module.base-labels.context
. All base/common labels are defined in this module:
module "base-labels" {
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=master"
namespace = "difu"
environment = var.environment
delimiter = "-"
label_order = ["namespace", "environment"]
tags = {
"environment" = var.environment
}
}
In this simple project the labels environment and namespace are defined and the order set as base labels that should be used by all resources.
I highly recommend tagging all resources from the very start, even it seems to make no sense. At the latest if you deploy your infrastructure in different environments and try to find out which belongs to which or even have name collisions you will remember this 😄
-
“Names is for tombstones, baby!” - Mr. Big in ‘Live and let die’ (1973) ↩︎