/terrachef

Write Terraform configuration that looks just like Chef resources.

Primary LanguageRubyApache License 2.0Apache-2.0

terrachef

Write almost any Terraform configuration using code indistinguishable from Chef resources.

This should support anything Terraform supports; it doesn't rely on an explicit list of Terraform functionality, so when Terraform adds something, you can use it via Terrachef.

(This was a fun Hack Day project, so while it works, it has gaps due to lack of real-world use. The gaps are really about me learning to use Terraform and applying that knowledge here; the part where a Chef recipe gets turned into Terraform JSON is pretty solid. If you want it to be better, please let me know.

Future Work

Being tracked here.

Example

Terraform has its own DSL, but if you already know Chef, you should be able to use Chef! Or something that looks and feels like Chef.

WARNING: You can use the normal Terraform directives everywhere, EXCEPT for module: because module is a Ruby keyword, we use tf_module instead.

require "terrachef"

log "before Terraform"

terraform "my-terraform-block" do
  provider "docker" do
    host "tcp://192.168.59.103:2376"
    cert_path "/Users/cdoherty/.boot2docker/certs/boot2docker-vm"
  end

  docker_container "foo" do
    image "ubuntu:latest"
    name "running_container_name"
  end

  docker_image "ubuntu" do
    name "ubuntu:latest"
  end
end

log "after Terraform"

This gets compiled and run using Terraform's JSON format (which has feature parity with the DSL):

{
  "provider": {
    "docker": {
      "host": "tcp://192.168.59.103:2376",
      "cert_path": "/Users/cdoherty/.boot2docker/certs/boot2docker-vm"
    }
  },
  "resource": {
    "docker_container": {
      "foo": {
        "image": "ubuntu:latest",
        "name": "running_container_name"
      }
    },
    "docker_image": {
      "ubuntu": {
        "name": "ubuntu:latest"
      }
    }
  }
}

Running the above Chef recipe gives:

Converging 3 resources
Recipe: @recipe_files::/Users/cdoherty/repos/terrachef/recipe.rb
  * log[before Terraform] action write

  * terraform_execute[my-terraform-block] action execute
    * file[/tmp/my-terraform-block.tf.json] action create (up to date)
    * execute[Terraform block 'my-terraform-block'] action run
      - execute terraform

  * log[after Terraform] action write


Running handlers:
Running handlers complete
Chef Client finished, 4/5 resources updated in 04 seconds

You can use Terraform variables, and just like in Terraform, to ensure ordering, you must add depends_on yourself. Pretty much anything Terraform can do can be expressed with Terrachef: everything is passed straight through to Terraform. (If it's not, please file a bug.) This is one of Terraform's long examples, converted to Terrachef.

provider "aws" do
    access_key "foo"
    secret_key "bar"
  end

  provider "do" do
    api_key "${var.foo}"
  end

  variable "foo" do
    default "bar"
    description "bar"
  end

  aws_instance "db" do
    security_groups ["${aws_security_group.firewall.*.id}"]
    VPC "foo"
    depends_on ["aws_instance.web"]
    provisioner [{ file: {source: "foo", destination: "bar"} }]
  end

  aws_instance "web" do
    ami "${var.foo}"
    security_groups [
            "foo",
            "${aws_security_group.firewall.foo}"
        ]
    network_interface(device_index: 0,
                      description: "Main network interface")
    provisioner(
            file: {
                source: "foo",
                destination: "bar"
            }
        )
  end

  aws_security_group "firewall" do
    count 5
  end

  provisioner "file" do
      source "conf/myapp.conf"
      destination "C:/App/myapp.conf"
      connection(
          type: "winrm",
          user: "Administrator",
          password: "${var.admin_password}"
      )
  end

  # EXCEPTION: since "module" is a Ruby keyword, we have to use "tf_module" here.
  tf_module "consul" do
    source "github.com/hashicorp/consul/terraform/aws"
    servers 5
  end

  output "web_ip" do
    value "${aws_instance.web.private_ip}"
  end