Provisioning a CentOS 7 EC2 instance with Chef using Terraform

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.

Getting ready

To step through this recipe, you will need the following:

  • A working Terraform installation
  • An AWS account with an SSH key configured in Terraform and a security group allowing SSH connections from outside (refer to the Chapter 2, Provisioning IaaS with Terraform, recipes)
  • An account on a Chef server (we recommend using a free hosted Chef account. Please refer to the Creating a free hosted server Chef account and a Puppet server recipe of Chapter 6, Fundamentals of Managing Servers with Chef and Puppet), with the default cookbook uploaded
  • An Internet connection

How to do it…

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

centos-1

Instance type

t2.micro

AMI in eu-west-1

ami-7abd0209

AMI in us-east-1

ami-6d1c2007

SSH username

centos

SSH key

keys/aws_terraform

TCP ports needed

22

Cookbook(s) to apply

starter

Chef server URL

https://api.chef.io/organizations/iacbook

Validation key

iacbook.pem

Validation client name

iacbook

Chef client version

12.13.37

  1. Let's start by declaring our AMIs as a map in the variables.tf file:
    variable "aws_centos_ami" {
      type = "map"
    
      default = {
        eu-west-1 = "ami-7abd0209"
        us-east-1 = "ami-6d1c2007"
      }
    }
  2. Now add the instance type in the same file:
    variable "aws_instance_type" {
      default     = "t2.micro"
      description = "Instance Type"
    }
  3. Declare the Chef version we're currently using in production, so it's stable and stays the same:
    variable "chef_version" {
      default = "12.13.37"
    }
  4. Declare the Chef server URL. If you're using the book example with hosted Chef, you'll find the correct address in the 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"
    }
  5. Finally, add the validation client name for the Chef server:
    variable "chef_validation_client_name" {
      default = "iacbook"
    }
  6. To connect to the instance, we know the default username is 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"
    }

Creating the EC2 instance

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.

Passing connection information

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}"
  } 

Giving Chef information

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}"
  }
 }

Note

Don't forget to use a valid path for the validation key!

Now you can terraform apply this and see everything happen, from instance creation to Chef Client deployment and cookbook installation.

How it works…

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.

There's more…

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.188.238