Quickstart

Installation

Make sure your operating system has Ruby 3.0 or later installed.

Install the application using your operating system’s package manager, or using RubyGems:

$ gem install kozo
$ kozo version
kozo 0.3.0

Set up directory

Create a directory that will hold your infrastructure files. It is generally a good idea to keep your code under version control.

$ mkdir my_app
$ cd my_app
$ git init
Initialized empty Git repository in /home/user/my_app/.git/

Add some useful exceptions to .gitignore:

### Kozo ###
*.kzstate
*.kzbackup

Set up backend

Create a main.kz file with the following contents:

##
# Kozo configuration
#
kozo do
  backend "local" do |b|
    # Defaults to kozo.kzstate
    b.file = "kozo.kzstate"
  end
end

Kozo will look at any files with the extension .kz in the current directory and evaluate these in alphabetical order, except main.kz which is always evaluated first. You are free to structure your code however you see fit: multiple files, subdirectories, … If you wish Kozo to ignore certain files or patterns, create a .kzignore file and add them to it:

/tmp/
*.bak.kz

The kozo { ... } block sets up the basic configuration of Kozo, such as the storage backend where the infrastructure state will be kept. Please refer to Backends to see a list of all supported backends.

Next, initialize the local state backend:

$ kozo init
Kozo initialized in /home/user/my_app

This creates the data structures for the infrastructure state as configured in the kozo { ... } block. For the local state, this is merely an empty kozo.kzstate file.

Set up provider

Append to main.kz:

##
# Providers
#
provider "hcloud" do |p|
  # Kozo automatically loads `.env`
  p.key = ENV.fetch("HCLOUD_TOKEN")
end

The provider directive instructs Kozo to register a new provider with the name hcloud. Passed to the block is the provider instance, which can be configured with the right credentials.

Create an .env file with the Hetzner Cloud token:

HCLOUD_TOKEN = my_token

Kozo will automatically load this file before evaluating your code, and is available under the Ruby ENV variable.

Create a resource

First, generate a public/private keypair:

ssh-keygen -f id_rsa -b 4096 -C "SSH Key" -P ""

Append to main.kz:

###
# Resources
#
resource "hcloud_ssh_key", "default" do |s|
  s.name = "default"
  s.public_key = File.read("id_rsa.pub")

  s.labels = {
    primary: "true",
  }
end

The resource directive instructs Kozo to create a new resource of type hcloud_ssh_key with name default. The latter two can be combined to form the resource’s address: hcloud_ssh_key.default. Subsequently, the resource can be configured by setting properties on the yielded object. In this case, the name and contents of the public key can be set.

The first step of syncing configuration using Kozo is checking the execution plan:

$ kozo plan
Kozo analyzed the state and created the following execution plan. Actions are indicated by the following symbols:
 + create
 ~ update
 - destroy

Kozo will perform the following actions:
# hcloud_ssh_key.default:
+ resource "hcloud_ssh_key", "default" do |r|
    r.id         = (known after apply)
  + r.name       = "default"
  + r.public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC/Zqnnfg24uLaKybQXEkhSs4rqqbKYLvPg..."
  + r.labels     = {
      primary = "true"
    }
    r.created    = (known after apply)
end

You can see in the execution plan that Kozo will create one resource with the properties configured in the main.kz file.

Next, actually create the resource by applying the changes:

$ kozo apply
Kozo analyzed the state and created the following execution plan. Actions are indicated by the following symbols:
 + create
 ~ update
 - destroy

Kozo will perform the following actions:
# hcloud_ssh_key.default:
+ resource "hcloud_ssh_key", "default" do |r|
    r.id         = (known after apply)
  + r.name       = "default"
  + r.public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC/Zqnnfg24uLaKybQXEkhSs4rqqbKYLvPg..."
  + r.labels     = {
      primary = "true"
    }
    r.created    = (known after apply)
end

hcloud_ssh_key.default: creating resource
hcloud_ssh_key.default: created resource

And that’s that, the SSH key was created successfully.

Update a resource

Now change the name of the SSH key in the configuration file and run kozo plan again. You’ll see something similar to this:

$ kozo plan
...

# hcloud_ssh_key.default:
~ resource "hcloud_ssh_key", "default" do |r|
    r.id         = 5562231
  ~ r.name       = "default" -> "new_default"
    r.public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC/Zqnnfg24uLaKybQXEkhSs4rqqbKYLvPg..."
    r.labels     = {
      primary = "true"
    }
    r.created    = 2022-02-13T22:09:51Z
end

...

Try changing the name of the SSH key in the Hetzner Cloud panel as well. You’ll see that Kozo tries to revert the name change.

Dependent resources

Resource can depend on other resources for their values. Kozo will make sure that the dependent resources are created in the correct order.

Append the following to your main.kz file:

resource "hcloud_server", "default" do |s|
  s.name = "default"
  s.location = "fsn1"
  s.image = "debian-11"
  s.server_type = "cx11"
  s.ssh_keys = [hcloud_ssh_key.default]
  s.user_data = File.read("cloud-init.yml")

  s.labels = {
    primary: "true",
  }
end
$ kozo plan
...

# hcloud_server.default:
+ resource "hcloud_server", "default" do |r|
    r.id          = (known after apply)
  + r.name        = "default"
  + r.image       = "debian-11"
  + r.server_type = "cx11"
  + r.location    = "fsn1"
    r.datacenter  = nil
  + r.labels      = {
      primary = "true"
    }
    r.locked      = (known after apply)
    r.created     = (known after apply)
  + r.user_data   = "#cloud-config  timezone: Europe/Berlin locale: C.UTF-8  ntp:   enabled: ..."
  + r.ssh_keys    = [
      5562231
    ]
end

Note that the SSH key reference was resolved to the ID of the SSH key that was previously created.

Destroy a resource

Instruct Kozo that the resource should be deleted simply by removing the relevant lines in the main.kz file. Running kozo plan gives you the following:

$ kozo plan
...

# hcloud_ssh_key.default:
- resource "hcloud_ssh_key", "default" do |r|
  - r.id         = 5562231
  - r.name       = "default"
  - r.public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCrNY2BsGWGBSphJH/mbg2ms4IYxmrxjJxu..."
  - r.labels     = {
      primary = "true"
    }
  - r.created    = 2022-02-13T22:09:51Z
end

Indicating that the resource is to be deleted.

Conclusion

This quickstart guide barely scratches the surface of what Kozo can do. Check out the introduction to learn more about Infrastructure as Code, or browse the provider documentation to get an idea of what resources can be manipulated by Kozo.