2
votes

I have a python script that generates custom cloud-init scripts for each AWS EC2 instance that I'm creating with terraform. But right now I'm unable to make a single call to terraform apply since the files for cloud-init don't exist at that moment, and I get an error. So for now I'm doing terraform apply -target=null_resource.pynit_lite to generate the files, then terraform apply to create the infrastructure.

What I'd like is to have the null_resource execute first, then the rest so that the cloud-init files exist at the moment of the ec2 resource creation, so that I can do a single call to terraform apply.

I guess I'd need to implement some sort of "depends_on" so that the EC2 module waits for the null_resource to finish, but this is not a feature in terraform.

My code looks like this:

provider "aws" {
  region = var.region
  profile = var.profile
}

resource "null_resource" "pynit_lite" {
  provisioner "local-exec" {
    command = "python3 pynit_lite.py"
  }
}

data "aws_vpc" "devvpc" {
  id = var.vpc_id
}

data "aws_subnet_ids" "devsubs" {
  vpc_id = var.vpc_id
}

module "EC2" {
  source = "../../../Modules/EC2"

  name = var.instanceNames
  DSKsize = var.DSKsize
  root_volume_size = var.root_volume_size
  ami = var.ami
  instance_count = length(var.instanceNames)
  instance_type = var.instance_type
  key_name = var.key_name
  ec2_security_group_ids = var.ec2_security_group_ids
  script = var.scripts

  subnet = var.subnet
  vpc = var.vpc_id
}

output "EIP-public" {
  value = module.EC2.EIPAddress
}
output "EIP-internal" {
  value = module.EC2.InternalIps
}

I've looked this question but it does not provide a solution for this: Unable to make Terraform module dependent on resource

This is non-blocking but certainly annoying, so any help would be greatly appreciated.

Thanks.

1

1 Answers

2
votes

Since only recently (terraform 13), modules also support the depends_on feature that resources already could use. There is a really great article on medium, which explains how you could accomplish such a structure. In your case this would be:

resource "null_resource" "pynit_lite" {
  provisioner "local-exec" {
    command = "python3 pynit_lite.py"
  }
}

module "EC2" {
  # ...
   depends_on = [null_resource.pynit_lite]
}

Another, less feasible approach, is to make use of a before-hook with terragrunt, which is a thin wrapper around terraform. In this before hook you could also run your command. You can read more about that here. Example:

terraform {
  before_hook "before_hook" {
    commands     = ["apply", "plan"]
    execute      = ["python3", "pynit_lite.py"]
  }
}