Using Terraform to Deploy an OCI Compute Instance

farisdurrani

Faris Durrani

Posted on June 19, 2023

Using Terraform to Deploy an OCI Compute Instance

This is a continuation from my last article on deploying a simple compartment on the Oracle Cloud Infrastructure (OCI).

As part of our effort to employ Infrastructure as Code (IaC), Terraform affords us one way to deploy cloud resources in a predictable and declarative method.

This is a guide to show you how to deploy an OCI Compute Instance from scratch using Terraform.

Prerequisites

1. Install Terraform.

Follow the official installation page to install Terraform on your machine.

2. Configure the OCI Provider

To deploy OCI resources, you need access to manage those resources from your machine. This can be achieved using an API Key. To complete this step, see Setting up the OCI Configuration File using API Keys.

How to deploy a Compute Instance using Terraform

The architectural goal

Our goal is to deploy the following:

Architectural goal of TF deployment

It is impossible to deploy only a Compute Instance since it depends on a network (Virtual Cloud Network) to be usable. Preferably, we want to SSH into the new instance so we need an Internet Gateway as well. All of that will be made in a new compartment in the Ashburn region.

The file structure goal

Our final file structure goal. We work in the new directory tf-test-1/:

📦tf-test-1
 ┣ 📜id_rsa
 ┣ 📜id_rsa.pub
 ┣ 📜terraform.tf
 ┣ 📜provider.tf
 ┣ 📜main-vars.tf
 ┣ 📜terraform.tfvars
 ┣ 📜outputs.tf
 ┗ 📜main.tf
Enter fullscreen mode Exit fullscreen mode

1. Generate an SSH key

This is optional, but if we want to access the instance locally, we need a public and private SSH key. Run:

ssh keygen
Enter fullscreen mode Exit fullscreen mode

I generate my private and public keys inside the directory with the name id_rsa.

2. Declare cloud provider in terraform.tf

We declare oracle/oci as the provider for this Terraform deployment, connecting to OCI.

# terraform.tf
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = ">= 6.0.0"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Declare profile in provider.tf

This step is required if you have multiple OCI profiles in your ~/.oci/config file to declare which profile you want to use. By default, the profile is DEFAULT.

# provider.tf
provider "oci" {
  config_file_profile = "DEFAULT"
}
Enter fullscreen mode Exit fullscreen mode

4. Declare variables in main-vars.tf

For simplicity, we also initialize most of the variables here. It is aesthetically preferred for you to initialize them in another file (Step 5).

# main-vars.tf
############################################
# Compartments
############################################

variable "compartment_id" {
  description = "The OCID of the parent compartment where the resources will be created."
  type        = string
}

variable "compartment_name" {
  description = "Compartment Name"
  type        = string
  default     = "test-compartment"
}

variable "compartment_description" {
  description = "Compartment Description"
  type        = string
  default     = "test-compartment description"
}

############################################
# VCN
############################################

variable "vcn1" {
  description = "The details of VCN1."
  default = {
    cidr_blocks : ["10.23.0.0/20"]
    display_name : "vcn01"
  }
}

############################################
# Public Subnet, Route Table, and Internet Gateway
############################################

variable "subnetA_pub" {
  description = "The details of the subnet"
  default = {
    cidr_block : "10.23.11.0/24"
    display_name : "IC_pub_snet-A"
    is_public : true
    route_table : {
      display_name = "routeTable-Apub"
      description  = "routeTable-Apub"
    }
  }
}

variable "internet_gateway_A" {
  description = "The details of the internet gateway"
  default = {
    display_name : "IC_IG-A"
    ig_destination = "0.0.0.0/0"
  }
}

############################################
# Compute Instance
############################################

data "oci_identity_availability_domain" "ad_1" {
  compartment_id = "ocid1.tenancy.oc1..aaaaaaaaxxxxxxx"
  ad_number = 1
}

variable "ic_pub_vm_A" {
  description = "The details of the compute instance"
  default = {
    display_name : "IC_pub_vm-A"
    assign_public_ip : true
    availability_domain : data.oci_identity_availability_domain.ad_1.name
    image_ocid : "ocid1.image.oc1.iad.aaaaaaaacnjdag7ngxnzim3ogkgfywf4aoclwz4gkcaqdr773hukt5o2ahma"
    shape : {
      name          = "VM.Standard.E4.Flex"
      ocpus         = 1
      memory_in_gbs = 8
    }
    ssh_authorized_keys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCuzY9AR7LiJN8EhHeG3qP9gWuf7IxUl+xDaf1gD/zvZjid4Uxa8fRjWkAkeRQGa1ZNBLjw7EH+zWpjqOlCg14eZqTUnNtmOzIfK/LmcSFNKmD2rGNryY8DQBH5cY94bZVasOA+lhxnaNOzJ0sDGrqeCrpqTWqGWZ2NZ/nxXSXTdescHYcz/lmEijRLGnxtI/ByWKufowPUQm9gA0x+DRqJk9mvT7i1ZHi9djbeVPJPZthn14Ppi5cjLIXtCrxTQUcALaCPkzcgAKen9KHlmNRfoW+hx8fRxC0RPZgYCAigqz3hktsnjr+n4pxkF+5e55ZJJYdAKQnaYbS5SVzegC9jNzyzxef8JmZqtrgTBo4dsvNdbw7iIn0/KGgK3xZNTR55L60kS4y4NPbVNhRey8ESjIRc2zoBycssLmVd8cp0a0iLdjXDH3PGLgfC0Ly3Tv7lmGLd27c3U7ndN6ldXxFJ9k7B9k6EibyoaQJM0fLX4eug1tGa6BaB3dwEyZgSUuM= fdurrani@AJTV3VGQF2.local"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Replace ssh_authorized_keys with your public SSH key that you generated in Step 1.

A few places for more customization:

  • image_ocid is the OS image of the VM in the instance. I picked Oracle Linux 9.0 which I found amongst other images here
  • Change ocid1.tenancy.oc1..aaaaaaaaxxxxxxx to your tenancy ocid found at Profile Picture > Tenancy.

5. Initialize variables in terraform.tfvars

# terraform.tfvars
compartment_id = "ocid1.compartment.oc1..aaaaaaaakbr7t5yqjirrmwm3ycg5jj4hytparentcompartmenterlfndenwfskn"
Enter fullscreen mode Exit fullscreen mode

Replace compartmend_id with the parent compartment of this new compartment or use your tenancy OCID found at Profile Picture > Tenancy.

6. Put any print statements in outputs.tf

This step is completely optional but if you want to print any variables, use this file:

# outputs.tf
output "compartment_id" {
  value = oci_identity_compartment.example_compartment.id
}
Enter fullscreen mode Exit fullscreen mode

7. The main file: main.tf

We do our actual deployments using this file. You may split this into different files if you want.

# main.tf
locals {
  cmpt_name_prefix = "A506"
  time_f           = formatdate("HHmmss", timestamp())
}

############################################
# Compartments
############################################
resource "oci_identity_compartment" "example_compartment" {
  # Required
  compartment_id = var.compartment_id
  description    = var.compartment_description
  name           = "${local.cmpt_name_prefix}-${var.compartment_name}-${local.time_f}"
}

############################################
# VCN
############################################

resource "oci_core_vcn" "example_vcn" {
  #Required
  compartment_id = oci_identity_compartment.example_compartment.id
  cidr_blocks    = var.vcn1.cidr_blocks
  #Optional
  display_name = var.vcn1.display_name
}

############################################
# Public Subnet
############################################

resource "oci_core_subnet" "subnetA_pub" {
  #Required
  compartment_id = oci_identity_compartment.example_compartment.id
  vcn_id         = oci_core_vcn.example_vcn.id
  cidr_block     = var.subnetA_pub.cidr_block
  #Optional
  display_name               = var.subnetA_pub.display_name
  prohibit_public_ip_on_vnic = !var.subnetA_pub.is_public
  prohibit_internet_ingress  = !var.subnetA_pub.is_public
}

############################################
# Internet Gateways and NAT Gateways
############################################

resource "oci_core_internet_gateway" "the_internet_gateway" {
  compartment_id = oci_identity_compartment.example_compartment.id
  vcn_id         = oci_core_vcn.example_vcn.id
  display_name   = var.internet_gateway_A.display_name
}


############################################
# Route Tables
############################################

resource "oci_core_default_route_table" "the_route_table" {
  #Required
  compartment_id             = oci_identity_compartment.example_compartment.id
  manage_default_resource_id = oci_core_vcn.example_vcn.default_route_table_id
  # Optional
  display_name = var.subnetA_pub.route_table.display_name
  dynamic "route_rules" {
    for_each = [true]
    content {
      destination       = var.internet_gateway_A.ig_destination
      description       = var.subnetA_pub.route_table.description
      network_entity_id = oci_core_internet_gateway.the_internet_gateway.id
    }
  }
}

# ############################################
# # Compute Instance
# ############################################

resource "oci_core_instance" "ic_pub_vm-A" {
  compartment_id      = oci_identity_compartment.example_compartment.id
  shape               = var.ic_pub_vm_A.shape.name
  availability_domain = var.ic_pub_vm_A.availability_domain
  display_name        = var.ic_pub_vm_A.display_name

  source_details {
    source_id   = var.ic_pub_vm_A.image_ocid
    source_type = "image"
  }

  dynamic "shape_config" {
    for_each = [true]
    content {
      #Optional
      memory_in_gbs = var.ic_pub_vm_A.shape.memory_in_gbs
      ocpus         = var.ic_pub_vm_A.shape.ocpus
    }
  }

  create_vnic_details {
    subnet_id        = oci_core_subnet.subnetA_pub.id
    assign_public_ip = var.ic_pub_vm_A.assign_public_ip
  }

  metadata = {
    ssh_authorized_keys = join("\n", [for k in var.ic_pub_vm_A.ssh_authorized_keys : chomp(k)])
  }
}
Enter fullscreen mode Exit fullscreen mode

8. Initialize and Apply Terraform plan

terraform init
terraform fmt .
terraform validate .
terraform plan --out="plan.out"
terraform apply "plan.out"
Enter fullscreen mode Exit fullscreen mode

If successful, you should see a success message like the following:

Success message in Terminal after deploying Terraform plan

9. Verify

The most straightforward to ensure correct deployment is SSH-ing into your new VM instance.

Searching Instances on the Oracle Console search bar (in the new compartment we created), we find the new Instance and its public IP address:

New instance on OCI

Let us access the VM. In the tf-test-1/ directory,

ssh -i id_rsa opc@182.181.138.162
Enter fullscreen mode Exit fullscreen mode

where

  • 182.181.138.162 is the public IP of the instance

SSH-ing into the new instance

We have successfully accessed the new VM instance.

Searching Network visualizer on the OCI Console, we see the deployed VCN and Internet Gateway:

Network visualizer on OCI

Destroying a Terraform deployment

To destroy everything deployed in the current plan, simply

terraform destroy
Enter fullscreen mode Exit fullscreen mode

Safe harbor statement
The information provided on this channel/article/story is solely intended for informational purposes and cannot be used as a part of any contractual agreement. The content does not guarantee the delivery of any material, code, or functionality, and should not be the sole basis for making purchasing decisions. The postings on this site are my own and do not necessarily reflect the views or work of Oracle or Mythics, LLC.

This work is licensed under a Creative Commons Attribution 4.0 International License.

💖 💪 🙅 🚩
farisdurrani
Faris Durrani

Posted on June 19, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related