This post will continue a previous post on using Hashicorp's Terraform with OCI (Oracle Cloud Infrastructure). The general principles of this series apply to any cloud, but most other clouds offer a Key Management Service, so there's not really a need to build your own Vault instance, since you can manage it natively. That's actually a part of OCI as well, but at the time I started this series it was not. It's also worth noting that at this time, I can't actually use KMS, because when I try to launch a vault, I get a service limit exception, even though I have no vaults provisioned.

Building the Network Resources

Let's walk through a single region where we will build out the network resources where our Vault installation will reside. Here's the variables where we define the storage backend and Terraform provider. It's the same basic setup as defined in my previous post.

csuttles@cs-mbp15:[~/src/oci-vault/iad/network]:(master)
[Exit: 0] 11:19: cat variables.tf
variable "tenancy" {}
variable "tenancy_ocid" {}
variable "user_ocid" {}
variable "fingerprint" {}
variable "private_key_path" {}
variable "region" {
default = "us-ashburn-1"
}
variable "compartment_ocid" {}

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}"
version = "~> 3.1.0"
}

...

# use s3 compatible API
terraform {
backend "s3" {
bucket   = "oci-vault"
key      = "common/terraform.tfstate"
region   = "us-ashburn-1"
endpoint = "https://YOUR_TENANCY_NAME.compat.objectstorage.REGION.oraclecloud.com"
skip_region_validation      = true
skip_credentials_validation = true
skip_requesting_account_id  = true
skip_get_ec2_platforms      = true
skip_metadata_api_check     = true
force_path_style            = true
shared_credentials_file     = "/Users/csuttles/.aws/oci-creds"
}
}
/*
# use a PAR for backend storage
terraform {
backend "http" {
update_method = "PUT"
address       = "https://objectstorage.us-ashburn-1.oraclecloud.com/p/big-par-baseurl-goes-here/n/NAMESPACE/b/BUCKET/o/OBJECTPATH/terraform.tfstate"
}
}
*/

Setting up a VCN

In OCI a VCN is very similar to a VPC in AWS. It's easy to think of as a bag in which all your other related network resources reside. Here's the terraform config for creating a VCN.

csuttles@cs-mbp15:[~/src/oci-vault/iad/network]:(master)
[Exit: 0] 12:19: cat vcn.tf
resource "oci_core_virtual_network" "oci-vault-vcn1" {
  cidr_block     = "10.0.0.0/16"
  dns_label      = "ocivault"
  compartment_id = "${data.terraform_remote_state.common.network_compartment}"
  display_name   = "oci-vault-vcn1"
}

resource "oci_core_internet_gateway" "oci-vault-ig1" {
  compartment_id = "${var.compartment_ocid}"
  display_name   = "oci-vault-ig1"
  vcn_id         = "${oci_core_virtual_network.oci-vault-vcn1.id}"
}

resource "oci_core_default_route_table" "oci-vault-default-route-table" {
  manage_default_resource_id = "${oci_core_virtual_network.oci-vault-vcn1.default_route_table_id}"
  display_name               = "oci-vault-default-route-table"

  route_rules {
    destination       = "0.0.0.0/0"
    destination_type  = "CIDR_BLOCK"
    network_entity_id = "${oci_core_internet_gateway.oci-vault-ig1.id}"
  }
}

resource "oci_core_route_table" "oci-vault-route-table1" {
  compartment_id = "${var.compartment_ocid}"
  vcn_id         = "${oci_core_virtual_network.oci-vault-vcn1.id}"
  display_name   = "oci-vault-route-table1"

  route_rules {
    destination       = "0.0.0.0/0"
    destination_type  = "CIDR_BLOCK"
    network_entity_id = "${oci_core_internet_gateway.oci-vault-ig1.id}"
  }
}

resource "oci_core_default_dhcp_options" "oci-vault-default-dhcp-options" {
  manage_default_resource_id = "${oci_core_virtual_network.oci-vault-vcn1.default_dhcp_options_id}"
  display_name               = "oci-vault-default-dhcp-options"

  // required
  options {
    type        = "DomainNameServer"
    server_type = "VcnLocalPlusInternet"
  }

  // optional
  options {
    type                = "SearchDomain"
    search_domain_names = ["csuttles.io"]
  }
}

resource "oci_core_dhcp_options" "oci-vault-dhcp-options1" {
  compartment_id = "${var.compartment_ocid}"
  vcn_id         = "${oci_core_virtual_network.oci-vault-vcn1.id}"
  display_name   = "oci-vault-dhcp-options1"

  // required
  options {
    type        = "DomainNameServer"
    server_type = "VcnLocalPlusInternet"
  }

  // optional
  options {
    type                = "SearchDomain"
    search_domain_names = ["csuttles.io"]
  }
}

Create Subnets and Seclists in the VCN

In OCI, Virtual NICs (VNICs) attach to subnets, which live inside a VCN. The seclist construct is also very similar to AWS.

Here's how that looks in Terraform:

csuttles@cs-mbp15:[~/src/oci-vault/iad/network]:(master)
[Exit: 0] 13:27: cat subnets.tf
variable "availability_domain" {
  default = 3
}
/*
Because you can specify multiple security lists/subnet the security_list_ids value must be specified as a list in []'s.
 See https://www.terraform.io/docs/configuration/syntax.html

Generally you wouldn't specify a subnet without first specifying a VCN. Once the VCN has been created you would get the vcn_id, route_table_id, and security_list_id(s) from that resource and use Terraform attributes below to populate those values.
 See https://www.terraform.io/docs/configuration/interpolation.html*/
resource "oci_core_subnet" "oci_vault" {
  count = 3
  //availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
  availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[count.index],"name")}"
  cidr_block          = "${format("10.0.%d.0/24", count.index + 1)}"
  display_name        = "${format("oci_vault_subnet_%d", count.index + 1)}"
  compartment_id = "${data.terraform_remote_state.common.network_compartment}"
  vcn_id              = "${oci_core_virtual_network.oci-vault-vcn1.id}"
  security_list_ids   = ["${oci_core_virtual_network.oci-vault-vcn1.default_security_list_id}"]
  route_table_id      = "${oci_core_virtual_network.oci-vault-vcn1.default_route_table_id}"
  dhcp_options_id     = "${oci_core_virtual_network.oci-vault-vcn1.default_dhcp_options_id}"
}

data "oci_identity_availability_domains" "ADs" {
  compartment_id = "${var.tenancy_ocid}"
}

data "oci_core_subnets" "oci_vault_subnets" {
    #Required
    compartment_id = "${data.terraform_remote_state.common.network_compartment}"
    vcn_id              = "${oci_core_virtual_network.oci-vault-vcn1.id}"

    #Optional
/*
    display_name = "${var.subnet_display_name}"
    state = "${var.subnet_state}"
*/
}

output "subnets" {
    value = "${data.oci_core_subnets.oci_vault_subnets.subnets}"
}
csuttles@cs-mbp15:[~/src/oci-vault/iad/network]:(master)
[Exit: 0] 14:30: cat seclists.tf
resource "oci_core_default_security_list" "oci-vault-default-security-list" {
  manage_default_resource_id = "${oci_core_virtual_network.oci-vault-vcn1.default_security_list_id}"
  display_name               = "oci-vault-default-security-list"

  // allow outbound tcp traffic on all ports
  egress_security_rules {
    destination = "0.0.0.0/0"
    protocol    = "6"
  }

  // allow outbound udp traffic on a port range
  egress_security_rules {
    destination = "0.0.0.0/0"
    protocol    = "17"        // udp
    stateless   = true

    udp_options {
      "min" = 319
      "max" = 320
    }
  }

  // allow inbound ssh traffic
  ingress_security_rules {
    protocol  = "6"         // tcp
    source    = "0.0.0.0/0"
    stateless = false

    tcp_options {
      "min" = 22
      "max" = 22
    }
  }

  // allow inbound icmp traffic of a specific type
  ingress_security_rules {
    protocol  = 1
    source    = "0.0.0.0/0"
    stateless = true

    icmp_options {
      "type" = 3
      "code" = 4
    }
  }
}

Apply Infrastructure Changes via Terraform

Finally, from the same directory where these resources are defined, we run terraform apply and create the resources in the IAD region. The pipe to Perl is just to obfuscate my OCIDs because the internet is a scary place to just leave your data lying around, and is totally unnecessary in the 'real world'.

csuttles@cs-mbp15:[~/src/oci-vault/iad/network]:(master)
[Exit: 0] 15:00: tf apply 2>&1 | perl -pe 's/ocid.([\w\.]+)\.(?:[\w]+)([\W\s]*?)$/ocid.$1.07734$2/'
data.terraform_remote_state.common: Refreshing state...
data.oci_identity_availability_domains.ADs: Refreshing state...
data.oci_identity_availability_domains.ads: Refreshing state...

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
 <= read (data resources)

Terraform will perform the following actions:

 <= data.oci_core_subnets.oci_vault_subnets
      id:                                                                 <computed>
      compartment_id:                                                     "ocid..compartment.oc1..07734"
      subnets.#:                                                          <computed>
      vcn_id:                                                             "${oci_core_virtual_network.oci-vault-vcn1.id}"

  + oci_core_default_dhcp_options.oci-vault-default-dhcp-options
      id:                                                                 <computed>
      display_name:                                                       "oci-vault-default-dhcp-options"
      freeform_tags.%:                                                    <computed>
      manage_default_resource_id:                                         "${oci_core_virtual_network.oci-vault-vcn1.default_dhcp_options_id}"
      options.#:                                                          "2"
      options.0.server_type:                                              "VcnLocalPlusInternet"
      options.0.type:                                                     "DomainNameServer"
      options.1.search_domain_names.#:                                    "1"
      options.1.search_domain_names.0:                                    "csuttles.io"
      options.1.type:                                                     "SearchDomain"
      state:                                                              <computed>
      time_created:                                                       <computed>

  + oci_core_default_route_table.oci-vault-default-route-table
      id:                                                                 <computed>
      display_name:                                                       "oci-vault-default-route-table"
      freeform_tags.%:                                                    <computed>
      manage_default_resource_id:                                         "${oci_core_virtual_network.oci-vault-vcn1.default_route_table_id}"
      route_rules.#:                                                      "1"
      route_rules.~1768267378.cidr_block:                                 <computed>
      route_rules.~1768267378.destination:                                "0.0.0.0/0"
      route_rules.~1768267378.destination_type:                           "CIDR_BLOCK"
      route_rules.~1768267378.network_entity_id:                          "${oci_core_internet_gateway.oci-vault-ig1.id}"
      state:                                                              <computed>
      time_created:                                                       <computed>
      time_modified:                                                      <computed>

  + oci_core_default_security_list.oci-vault-default-security-list
      id:                                                                 <computed>
      display_name:                                                       "oci-vault-default-security-list"
      egress_security_rules.#:                                            "2"
      egress_security_rules.1420396200.destination:                       "0.0.0.0/0"
      egress_security_rules.1420396200.destination_type:                  <computed>
      egress_security_rules.1420396200.icmp_options.#:                    "0"
      egress_security_rules.1420396200.protocol:                          "6"
      egress_security_rules.1420396200.stateless:                         <computed>
      egress_security_rules.1420396200.tcp_options.#:                     "0"
      egress_security_rules.1420396200.udp_options.#:                     "0"
      egress_security_rules.1565661692.destination:                       "0.0.0.0/0"
      egress_security_rules.1565661692.destination_type:                  <computed>
      egress_security_rules.1565661692.icmp_options.#:                    "0"
      egress_security_rules.1565661692.protocol:                          "17"
      egress_security_rules.1565661692.stateless:                         "true"
      egress_security_rules.1565661692.tcp_options.#:                     "0"
      egress_security_rules.1565661692.udp_options.#:                     "1"
      egress_security_rules.1565661692.udp_options.0.max:                 "320"
      egress_security_rules.1565661692.udp_options.0.min:                 "319"
      egress_security_rules.1565661692.udp_options.0.source_port_range.#: "0"
      freeform_tags.%:                                                    <computed>
      ingress_security_rules.#:                                           "2"
      ingress_security_rules.1709695539.icmp_options.#:                   "1"
      ingress_security_rules.1709695539.icmp_options.0.code:              "4"
      ingress_security_rules.1709695539.icmp_options.0.type:              "3"
      ingress_security_rules.1709695539.protocol:                         "1"
      ingress_security_rules.1709695539.source:                           "0.0.0.0/0"
      ingress_security_rules.1709695539.source_type:                      <computed>
      ingress_security_rules.1709695539.stateless:                        "true"
      ingress_security_rules.1709695539.tcp_options.#:                    "0"
      ingress_security_rules.1709695539.udp_options.#:                    "0"
      ingress_security_rules.47193274.icmp_options.#:                     "0"
      ingress_security_rules.47193274.protocol:                           "6"
      ingress_security_rules.47193274.source:                             "0.0.0.0/0"
      ingress_security_rules.47193274.source_type:                        <computed>
      ingress_security_rules.47193274.stateless:                          "false"
      ingress_security_rules.47193274.tcp_options.#:                      "1"
      ingress_security_rules.47193274.tcp_options.0.max:                  "22"
      ingress_security_rules.47193274.tcp_options.0.min:                  "22"
      ingress_security_rules.47193274.tcp_options.0.source_port_range.#:  "0"
      ingress_security_rules.47193274.udp_options.#:                      "0"
      manage_default_resource_id:                                         "${oci_core_virtual_network.oci-vault-vcn1.default_security_list_id}"
      state:                                                              <computed>
      time_created:                                                       <computed>

  + oci_core_dhcp_options.oci-vault-dhcp-options1
      id:                                                                 <computed>
      compartment_id:                                                     "ocid..compartment.oc1..07734"
      display_name:                                                       "oci-vault-dhcp-options1"
      freeform_tags.%:                                                    <computed>
      options.#:                                                          "2"
      options.0.server_type:                                              "VcnLocalPlusInternet"
      options.0.type:                                                     "DomainNameServer"
      options.1.search_domain_names.#:                                    "1"
      options.1.search_domain_names.0:                                    "csuttles.io"
      options.1.type:                                                     "SearchDomain"
      state:                                                              <computed>
      time_created:                                                       <computed>
      vcn_id:                                                             "${oci_core_virtual_network.oci-vault-vcn1.id}"

  + oci_core_internet_gateway.oci-vault-ig1
      id:                                                                 <computed>
      compartment_id:                                                     "ocid..compartment.oc1..07734"
      display_name:                                                       "oci-vault-ig1"
      enabled:                                                            "true"
      freeform_tags.%:                                                    <computed>
      state:                                                              <computed>
      time_created:                                                       <computed>
      time_modified:                                                      <computed>
      vcn_id:                                                             "${oci_core_virtual_network.oci-vault-vcn1.id}"

  + oci_core_route_table.oci-vault-route-table1
      id:                                                                 <computed>
      compartment_id:                                                     "ocid..compartment.oc1..07734"
      display_name:                                                       "oci-vault-route-table1"
      freeform_tags.%:                                                    <computed>
      route_rules.#:                                                      "1"
      route_rules.~1768267378.cidr_block:                                 <computed>
      route_rules.~1768267378.destination:                                "0.0.0.0/0"
      route_rules.~1768267378.destination_type:                           "CIDR_BLOCK"
      route_rules.~1768267378.network_entity_id:                          "${oci_core_internet_gateway.oci-vault-ig1.id}"
      state:                                                              <computed>
      time_created:                                                       <computed>
      time_modified:                                                      <computed>
      vcn_id:                                                             "${oci_core_virtual_network.oci-vault-vcn1.id}"

  + oci_core_subnet.oci_vault[0]
      id:                                                                 <computed>
      availability_domain:                                                "bwOa:US-ASHBURN-AD-1"
      cidr_block:                                                         "10.0.1.0/24"
      compartment_id:                                                     "ocid..compartment.oc1..07734"
      dhcp_options_id:                                                    "${oci_core_virtual_network.oci-vault-vcn1.default_dhcp_options_id}"
      display_name:                                                       "oci_vault_subnet_1"
      freeform_tags.%:                                                    <computed>
      prohibit_public_ip_on_vnic:                                         <computed>
      route_table_id:                                                     "${oci_core_virtual_network.oci-vault-vcn1.default_route_table_id}"
      security_list_ids.#:                                                <computed>
      state:                                                              <computed>
      subnet_domain_name:                                                 <computed>
      time_created:                                                       <computed>
      vcn_id:                                                             "${oci_core_virtual_network.oci-vault-vcn1.id}"
      virtual_router_ip:                                                  <computed>
      virtual_router_mac:                                                 <computed>

  + oci_core_subnet.oci_vault[1]
      id:                                                                 <computed>
      availability_domain:                                                "bwOa:US-ASHBURN-AD-2"
      cidr_block:                                                         "10.0.2.0/24"
      compartment_id:                                                     "ocid..compartment.oc1..07734"
      dhcp_options_id:                                                    "${oci_core_virtual_network.oci-vault-vcn1.default_dhcp_options_id}"
      display_name:                                                       "oci_vault_subnet_2"
      freeform_tags.%:                                                    <computed>
      prohibit_public_ip_on_vnic:                                         <computed>
      route_table_id:                                                     "${oci_core_virtual_network.oci-vault-vcn1.default_route_table_id}"
      security_list_ids.#:                                                <computed>
      state:                                                              <computed>
      subnet_domain_name:                                                 <computed>
      time_created:                                                       <computed>
      vcn_id:                                                             "${oci_core_virtual_network.oci-vault-vcn1.id}"
      virtual_router_ip:                                                  <computed>
      virtual_router_mac:                                                 <computed>

  + oci_core_subnet.oci_vault[2]
      id:                                                                 <computed>
      availability_domain:                                                "bwOa:US-ASHBURN-AD-3"
      cidr_block:                                                         "10.0.3.0/24"
      compartment_id:                                                     "ocid..compartment.oc1..07734"
      dhcp_options_id:                                                    "${oci_core_virtual_network.oci-vault-vcn1.default_dhcp_options_id}"
      display_name:                                                       "oci_vault_subnet_3"
      freeform_tags.%:                                                    <computed>
      prohibit_public_ip_on_vnic:                                         <computed>
      route_table_id:                                                     "${oci_core_virtual_network.oci-vault-vcn1.default_route_table_id}"
      security_list_ids.#:                                                <computed>
      state:                                                              <computed>
      subnet_domain_name:                                                 <computed>
      time_created:                                                       <computed>
      vcn_id:                                                             "${oci_core_virtual_network.oci-vault-vcn1.id}"
      virtual_router_ip:                                                  <computed>
      virtual_router_mac:                                                 <computed>

  + oci_core_virtual_network.oci-vault-vcn1
      id:                                                                 <computed>
      cidr_block:                                                         "10.0.0.0/16"
      compartment_id:                                                     "ocid..compartment.oc1..07734"
      default_dhcp_options_id:                                            <computed>
      default_route_table_id:                                             <computed>
      default_security_list_id:                                           <computed>
      display_name:                                                       "oci-vault-vcn1"
      dns_label:                                                          "ocivault"
      freeform_tags.%:                                                    <computed>
      state:                                                              <computed>
      time_created:                                                       <computed>
      vcn_domain_name:                                                    <computed>


Plan: 10 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

yes

...

Apply complete! Resources: 10 added, 0 changed, 0 destroyed.

Outputs:

show-ads = [
    {
        compartment_id = ocid..tenancy.oc1..07734,
        name = bwOa:US-ASHBURN-AD-1
    },
    {
        compartment_id = ocid..tenancy.oc1..07734,
        name = bwOa:US-ASHBURN-AD-2
    },
    {
        compartment_id = ocid..tenancy.oc1..07734,
        name = bwOa:US-ASHBURN-AD-3
    }
]
subnets = [
    {
        availability_domain = bwOa:US-ASHBURN-AD-1,
        cidr_block = 10.0.1.0/24,
        compartment_id = ocid..compartment.oc1..07734,
        defined_tags = map[],
        dhcp_options_id = ocid..dhcpoptions.oc1.iad.07734,
        display_name = oci_vault_subnet_1,
        dns_label = ,
        freeform_tags = map[],
        id = ocid..subnet.oc1.iad.07734,
        prohibit_public_ip_on_vnic = 0,
        route_table_id = ocid..routetable.oc1.iad.07734,
        security_list_ids = [ocid..securitylist.oc1.iad.07734],
        state = AVAILABLE,
        subnet_domain_name = ,
        time_created = 2018-10-27 16:28:17.417 +0000 UTC,
        vcn_id = ocid..vcn.oc1.iad.07734,
        virtual_router_ip = 10.0.1.1,
        virtual_router_mac = 00:00:17:CF:08:33
    },
    {
        availability_domain = bwOa:US-ASHBURN-AD-2,
        cidr_block = 10.0.2.0/24,
        compartment_id = ocid..compartment.oc1..07734,
        defined_tags = map[],
        dhcp_options_id = ocid..dhcpoptions.oc1.iad.07734,
        display_name = oci_vault_subnet_2,
        dns_label = ,
        freeform_tags = map[],
        id = ocid..subnet.oc1.iad.07734,
        prohibit_public_ip_on_vnic = 0,
        route_table_id = ocid..routetable.oc1.iad.07734,
        security_list_ids = [ocid..securitylist.oc1.iad.07734],
        state = AVAILABLE,
        subnet_domain_name = ,
        time_created = 2018-10-27 16:28:16.55 +0000 UTC,
        vcn_id = ocid..vcn.oc1.iad.07734,
        virtual_router_ip = 10.0.2.1,
        virtual_router_mac = 00:00:17:CF:08:33
    },
    {
        availability_domain = bwOa:US-ASHBURN-AD-3,
        cidr_block = 10.0.3.0/24,
        compartment_id = ocid..compartment.oc1..07734,
        defined_tags = map[],
        dhcp_options_id = ocid..dhcpoptions.oc1.iad.07734,
        display_name = oci_vault_subnet_3,
        dns_label = ,
        freeform_tags = map[],
        id = ocid..subnet.oc1.iad.07734,
        prohibit_public_ip_on_vnic = 0,
        route_table_id = ocid..routetable.oc1.iad.07734,
        security_list_ids = [ocid..securitylist.oc1.iad.07734],
        state = AVAILABLE,
        subnet_domain_name = ,
        time_created = 2018-10-27 16:28:15.494 +0000 UTC,
        vcn_id = ocid..vcn.oc1.iad.07734,
        virtual_router_ip = 10.0.3.1,
        virtual_router_mac = 00:00:17:CF:08:33
    }
]

Summary

This is one more milestone toward a working, automated, and easily repeatable deployment of Hashicorp Vault in OCI. In the next installment, we will get to provisioning the instances and the application, using the resources we created earlier in this series.