Terraform has a lot of great formatting and templating tools, but one in particular I make heavy use of is the template_file
data source, which in my case, makes it super simple to pass in variable data from my cloud provider resources (and other resources generated locally when I run Terraform, and then managed in my remote state store) to these templates and use them, in my example, with cloud-init
when asking my provider to create a compute resource.
So, let's start with a simple use case where this is useful. My use cases is in provisioning a Kubernetes cluster, so for my cluster nodes, the cloud-init script (in my example, just a bash script, but this can be used for any text resource) needs the cluster controller IP address that API server is running on, the Kubernetes version being used, and the cluster token.
My script is pretty simple in this regard, and the template is saved as node.tpl
:
#!/bin/bash
function install_docker() {
apt-get update; \
apt-get install -y docker.io
cat << EOF > /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=cgroupfs"]
}
EOF
}
function install_kube_tools() {
apt-get update && apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list
apt-get update
apt-get install -y kubelet=${kube_version} kubeadm=${kube_version} kubectl=${kube_version}
}
function join_cluster() {
kubeadm join "${primary_node_ip}:6443" --token "${kube_token}" --discovery-token-unsafe-skip-ca-verification
}
and you'll see I call variables like kube_version
, kube_token
, and primary_node_ip
to pass in variable values from the Terraform manifest, which we'll cover in a moment.
In the node.tf
manifest, we have our compute resource defined:
resource "digitalocean_droplet" "k8s_node" {
name = "${format("${var.cluster_name}-node-%02d", count.index)}"
image = "ubuntu-16-04-x64"
count = "${var.count}"
size = "${var.primary_size}"
region = "${var.region}"
private_networking = "true"
ssh_keys = "${var.ssh_key_fingerprints}"
user_data = "${data.template_file.node.rendered}"
}
and you'll see for user_data
(the DigitalOcean provider idiom for cloud-init), we're referencing data.template_file.node.rendered
, which will contain the rendered version of our template above, but in order to render this, we need to, first, create the resource, and then populate those values, local to that resource:
data "template_file" "node" {
template = "${file("${path.module}/node.tpl")}"
vars {
kube_token = "${random_string.kube_init_token_a.result}.${random_string.kube_init_token_b.result}"
primary_node_ip = "${digitalocean_droplet.k8s_primary.ipv4_address}"
kube_version = "${var.kubernetes_version}"
}
}
So, you'll see I set my vars
to a variety of different sources, some are referencing attributes from another resource (my k8s_primary
node's primary_ipv4_address
, for example, and a user variable kubernetes_version
, and then a random string generated elsewhere in my project).
Then, when you proceed to apply
Terraform, the user_data
field will be populated by the rendered template, with the data you provided substituted in for the relevant variables.
Top comments (0)