Friday, 3 November 2017

Oracle Cloud Infrastructure and Terraform first steps on Windows

Oracle has released the 'Terraform provider for Oracle Cloud Infrastructure'. This tutorial is written to help with the first steps with Terraform and OCI on Windows.
In contrast to procedural tools like Oracle's ocicli, Terraform implements a declarative approach. That means, the user declares how the resulting architecture should look like and not how to get there. To do all the footwork, Terraform needs to know how to work with the target environment. That is what the Terraform provider for OCI is for.
To start with Terraform on OCI, you need to install Terraform first from and put it in your Path.

Check it with terraform -version. Next get the Terraform Provider for OCI from According to Oracle's documentation on GitHub, the windows_amd64 folder found in the file must be placed in %APPDATA%/terraform.d/plugins/. A %APPDATA%/terraform.rc would only be needed for compatibility with Terraform 0.9, so that can be skipped if not needed.

Now for the first steps with Terraform and OCI, we need at least the Tenancy OCID and the User OCID from our OCI environment as well as an private/public API key pair, the hash of the public key and the region name. For creating a VCN, we also need the compartment OCID.

The name of the region is on the top and the Tenancy OCID can be copied from the bottom of the the OCI welcome page.

The User OCID is found on the users settings page.

The Compartment OCID is found under Identity/Compartments. Make sure to pick the right compartment.

The API key we need is best created with openssl. Under Windows, get Git Bash with Git for Windows from and install it. Open Git Bash and run

$ mkdir ~/.oraclebmc (or any directory that is fine for you)
$ openssl genrsa -out ~/.oraclebmc/bmcs_api_key.pem 2048
$ chmod 0700 ~/.oraclebmc
$ chmod 0600 ~/.oraclebmc/bmcs_api_key.pem
$ openssl rsa -pubout -in ~/.oraclebmc/bmcs_api_key.pem -out ~/.oraclebmc/bmcs_api_key_public.pem
$ cat ~/.oraclebmc/bmcs_api_key_public.pem
$ openssl rsa -pubout -outform DER -in ~/.oraclebmc/bmcs_api_key.pem | openssl md5
$ openssl rsa -pubout -outform DER -in ~/.oraclebmc/bmcs_api_key.pem | openssl md5 > ~/.oraclebmc/bmcs_api_key_fingerprint

That should result in the following files:

The public key needs to be copied to the OCI UI, so copy the output from the cat command above ...

... click on Add Public Key and paste your key.

After adding the key, the OCI displays your keys fingerprint information. That should be the same as in bmcs_api_key.pem. If not, retake the steps from the first openssl command.

We need a few environment variables to pass these parameters to terraform. Create a working directory (eg. D:\Terraform-OCI), there create a batch file such as terraform-env.cmd and set the following

setx TF_VAR_tenancy_ocid "<your tenancy ocid>"
setx TF_VAR_user_ocid "<your user ocid>"
setx TF_VAR_fingerprint "<your api key fingerprint from bmcs_api_key.pem>"
setx TF_VAR_private_key_path "d:\Arnes\oraclebmc\bmcs_api_key.pem"
setx TF_VAR_compartment_ocid "<your compartment ocid>"
setx TF_VAR_region "us-phoenix-1"

Setx requires to close the shell and open a new one. Now we need to pass these variables to Terraform, so create a with the following:

variable "tenancy_ocid" {}
variable "user_ocid" {}
variable "fingerprint" {}
variable "private_key_path" {}
variable "region" {}

variable "compartment_ocid" {}

variable "VPC-CIDR" {
  default = ""

The first six variables are for Terraform to take our according environment variables. Every environment variable beginning with TF_VAR_ will automatically be mapped. We don't need the  CIDR variable right now, but later.
Next we need to pass the mandatory variables to the OCI provider. Create a with the following:

provider "oci" {
  tenancy_ocid     = "${var.tenancy_ocid}"
  user_ocid        = "${var.user_ocid}"
  fingerprint      = "${var.fingerprint}"
  private_key_path = "${var.private_key_path}"
  region           = "${var.region}"

Now we are ready for a first run to see if everything is set up correctly. In the working directory run a terraform init.

The OCI provider has been correctly initialized. With terraform plan we can check what Terraform is planning to do: 

Nothing of course, as we don't gave it any instructions on what to build. So let's create something. First we need to create a Virtual Cloud Network (VCN), so create a with the following

resource "oci_core_virtual_network" "Arne-VCN" {
  cidr_block     = "${var.VPC-CIDR}"
  compartment_id = "${var.compartment_ocid}"
  display_name   = "Arne-VCN"
  dns_label      = "arnevcn"

That will define a simple VCN using the CIDR block defined in VPC-CIDR. To see what Terraform will do with that, re-run terraform plan.

This time, Terraform displays, that it wants to create the VCN we defined. Looks good, so let Terraform execute that plan with terraform apply.

That should be done in seconds. Double-check that with the OCI web UI.

If everything worked, the newly created VCN should be shown here. Re-running terraform plan should show no changes. 
For a little more complex example, add the following to to add an internet gateway and a routing roule to the VCN.

resource "oci_core_internet_gateway" "Arne-IGW" {
  compartment_id = "${var.compartment_ocid}"
  display_name   = "Arne-IGW"
  vcn_id         = "${}"

resource "oci_core_route_table" "Arne-RT" {
  compartment_id = "${var.compartment_ocid}"
  vcn_id         = "${}"
  display_name   = "Arne-RT"

  route_rules {
    cidr_block        = ""
    network_entity_id = "${}"

Terraform plan should show, that the VCN won't be touched, as it already exists. But the internet gateway and the routing table with the rule will be created.
This is a very simple example, but the basic setup for using Terraform with OCI is done.