Faris Durrani
Posted on June 19, 2023
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:
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
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
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"
}
}
}
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"
}
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"]
}
}
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"
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
}
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)])
}
}
8. Initialize and Apply Terraform plan
terraform init
terraform fmt .
terraform validate .
terraform plan --out="plan.out"
terraform apply "plan.out"
If successful, you should see a success message like the following:
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:
Let us access the VM. In the tf-test-1/
directory,
ssh -i id_rsa opc@182.181.138.162
where
-
182.181.138.162
is the public IP of the instance
We have successfully accessed the new VM instance.
Searching Network visualizer on the OCI Console, we see the deployed VCN and Internet Gateway:
Destroying a Terraform deployment
To destroy everything deployed in the current plan, simply
terraform destroy
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.
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
October 29, 2021