Once the underlying infrastructure is generated by Terraform, chances are the job isn't already finished. That's the moment a configuration management tool such as Chef, Ansible, or Puppet enters the game, to provision the virtual machine. Thankfully, Chef is a first class provisioning tool in Terraform. We'll see here how to fully bootstrap a CentOS 7.2 instance on AWS with Terraform, from nothing to a fully configured node, by gracefully handing over the configuration to Chef after having it automatically deployed and registered on Hosted Chef.
If it's the first time you've launched CentOS 7 servers on AWS, you have to agree their terms and conditions at https://aws.amazon.com/marketplace/pp/B00O7WM7QW.
To step through this recipe, you will need the following:
As there're a lot of sources involved, let's put all the required information in a table (the Chef information is taken from the Chef Starter Kit, or your own Chef server, fill in your own values):
Hostname |
|
---|---|
Instance type |
|
AMI in eu-west-1 |
ami-7abd0209 |
AMI in us-east-1 |
ami-6d1c2007 |
SSH username |
centos |
SSH key |
|
TCP ports needed |
22 |
Cookbook(s) to apply |
starter |
Chef server URL | |
Validation key |
|
Validation client name |
iacbook |
Chef client version |
12.13.37 |
variables.tf
file:variable "aws_centos_ami" { type = "map" default = { eu-west-1 = "ami-7abd0209" us-east-1 = "ami-6d1c2007" } }
variable "aws_instance_type" { default = "t2.micro" description = "Instance Type" }
variable "chef_version" { default = "12.13.37" }
knife.rb
file: it's simply https://api.chef.io/organizations/<your_organization_name>, otherwise, use your own Chef server):variable "chef_server_url" { default = "https://api.chef.io/organizations/iacbook" }
variable "chef_validation_client_name" { default = "iacbook" }
centos
, but as it can evolve or you may use your own images, it's better to fix it in a variable as well:variable "ssh_user" { default = "centos" }
We know from previous recipes that a basic instance running CentOS looks like this in Terraform's instances.tf
using a security group named base_security_group
:
resource "aws_instance" "centos" { ami = "${lookup(var.aws_centos_ami, var.aws_region)}" instance_type = "${var.aws_instance_type}" key_name = "${aws_key_pair.admin_key.key_name}" security_groups = ["${aws_security_group.base_security_group.name}"] associate_public_ip_address = true tags { Name = "CentOS-${count.index+1} by Terraform" } }
Now we need to provide two kinds of information to our Terraform file: what to do with Chef on the server and how to connect to it.
To tell Terraform how to connect itself to the new EC2 instance, we use a connection {}
block inside the aws_instance
resource to tell it which user and key to use through SSH:
connection { type = "ssh" user = "${var.ssh_user}" key_file = "${var.aws_ssh_admin_key_file}" }
We need to give some information to Terraform to pass it on to Chef. This will all happen inside a provisioner "chef" {}
block inside the aws_instance
resource.
Using all the variables we declared, here's how it looks:
resource "aws_instance" "centos" { [...] provisioner "chef" { node_name = "centos-${count.index+1}" run_list = ["starter"] server_url = "${var.chef_server_url}" validation_client_name = "${var.chef_validation_client_name}" validation_key = "${file("chef/validator.pem")}" version = "${var.chef_version}" } }
Now you can terraform apply
this and see everything happen, from instance creation to Chef Client deployment and cookbook installation.
First, Terraform creates the required AWS environment (keys, security groups, and instances), and once the instance is running, it connects to it with the right credentials by SSH, then deploys the specified Chef client version from the official source, and finally executes an initial chef-client run that registers the node on the Chef server and applies the requested cookbooks.
A lot more configuration options are possible for the Chef provisioner inside Terraform. For example, all available chef-client options can be passed as an array using client_options
, and the Chef environment (usually very important) is passed using environment
as a string. If you use a custom built image with the Chef client already baked in, you will be interested in setting skip_install
to true
so it doesn't get reinstalled.
18.189.188.238