How to use unique resource names with Terraform

Naming things is hard, but this time it's because of a different reason

4 mins
I have a lot of challenges when it comes to AWS, but I bet your pain points are entirely different than mine. I'd love to hear what keeps you up at night. It would be great to hear from you by filling out this form. Thanks in advance!

Background

When a name is optional for a resource then, just like CloudFormation, Terraform will generate a random one when you deploy the stack. This makes it easy to deploy these resources and makes sure that you can safely deploy the same module multiple times.

For example, you can safely omit the bucket name:

resource "aws_s3_bucket" "bucket" {
}

The name is different each time the resource is deployed:

aws_s3_bucket.bucket: Creation complete after 1s [id=terraform-20190809095436960500000002]

aws_s3_bucket.bucket: Creation complete after 1s [id=terraform-20190809095501819400000002]

But several resource types require a name to be specified. And, unfortunately, most examples hardcode a value there making the stack less reusable.

Lambda function

In the case of the aws_lambda_function resource type, the function_name is marked as (Required) in the documentation. In this case, you must specify a name, and it’s tempting to put a constant string there:

resource "aws_lambda_function" "lambda" {
	# Don't do this
	function_name = "function"
	#	...
}

But when you deploy the same module again, apply fails:

Error: Error creating Lambda function: ResourceConflictException: Function already exist: function
        status code: 409, request id: 1961e440-7df5-4f6b-93e9-0c41bc4176a4

  on main.tf line 32, in resource "aws_lambda_function" "lambda":
  32: resource "aws_lambda_function" "lambda" {

Random ID

There is a terraform resource called random_id that generates a random value when you first apply the module. Using that, you can generate a different name every time you create a new stack.

resource "random_id" "id" {
	  byte_length = 8
}

resource "aws_lambda_function" "lambda" {
	function_name = "${random_id.id.hex}-function"
	# ...
}

Using the ${random_id.id.hex} value whenever you need to hardcode a string helps avoid the clashing of names.

Deploying the above lambda multiple times is now safe to do:

aws_lambda_function.lambda: Creation complete after 16s [id=e2aac02d233afeab-function]

aws_lambda_function.lambda: Creation complete after 16s [id=94fc006c721bc912-function]

Other resources

The same applies to the AWS Config Rule. Instead of hardcoding a name, use randomness:

resource "aws_config_config_rule" "rule" {
  name = "${random_id.id.hex}-rule"
	# ...

In the case of an SQS Queue, the name is optional. However, if you create a FIFO queue then the name must end with .fifo. In this case, the auto-generated name will result in an error.

# standard queue
resource "aws_sqs_queue" "standard_queueu" {
	# no name attribute
}

# FIFO queue
resource "aws_sqs_queue" "fifo_queue" {
  name = "${random_id.id.hex}-queue.fifo"
  fifo_queue = true
}

Conclusion

Non-unique naming is a particularly insidious problem that usually manifests itself later in the product lifecycle. Seems like everything is working fine, but then you can’t launch a dev environment in the same AWS account because of this. But with a pinch of randomness, you can avoid running into these problems.

Download our ebook on AWS account security basics

Learn 5 simple steps to avoid the rookie mistakes.

  1. Why the root account is bad for security
  2. Use multiple users
  3. Secure accounts with multi-factor authentication
  4. Security logging with CloudTrail
  5. Billing alerts as an early warning system

Download the free guide here:

27 August 2019