Why Oracle's OCI?
Oracle offers Oracle Cloud Infrastructure (OCI), a major cloud provider like AWS, GCP or Azure. However, they come not only with free-for-30-days options but we can use it to create an Always Free eligible VM. This VM comes with 1 OCPU, which stands for Oracle CPU and is a weird way to measure x86 compute capacity. Apparently 1 OCPU โ (at least) 2 vCPUs. (Threads x Cores) x Physical CPU = Number vCPU. On top of that 1 GB of memory and 0.48 Gbps of network bandwidth are supplied to us for free, aswell. Many reasons to be happy ๐!
Why Terraform
Creating our VM can be done manually through the OCI portal and for just one VM it might even be preferable. However, in this tutorial we want to explore how such a process can be automated by IaC tools too and have a look at Terraform. Terraform is used for infrastructure provisioning and can declaratively create ressources at many different providers like Alibaba Cloud, AWS, Azure, GCP, OCI or VMware vSphere. This means that with one file that describes the desired end state many developers could set up the same cloud environment without having to worry about the reproducibility of their system.
Basic setup - connect to OCI through API key
However, there obviously are some basic configurations that need to be done previously. An API signing key needs to be added to your OCI account (details at the official documentation).
Basic setup - Terraform
Once this is done, Terraform can unveil its magic. Terraform takes a main configuration file (we call it main.tf
). This file is supplemented by a variables.tf
file that defines variables being used in main.tf
. It is a way of cleaning up the structure of the files and bundling all specific user input in just one clear place. The actual values are then available in yet another file terraform.tfvars
. It is important to use the terraform.tfvars
file to separate variable declaration from variable definition.
[!WARNING]
Add terraform.tfvars
to your .gitignore
file to commit them to your git repository.
My main.tf
file is inspired by frennky and adapted to my specific needs. Frennky also created a security list that specifically allows traffic on port 22, which we later need to communicate with our VM via SSH. Before this explicit specification I have been running into security configuration problems and couldn't connect to my VM.
# Configure the OCI provider with authentication details
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
}
# new compartment within the tenancy
resource "oci_identity_compartment" "free_tier_compartment" {
compartment_id = var.tenancy_ocid
description = "Oracle Cloud Free Tier compartment"
name = "free-tier"
enable_delete = true
freeform_tags = {
"purpose" = "free-tier-resources"
}
}
# Virtual Cloud Network (VCN)
resource "oci_core_vcn" "free_tier_vcn" {
compartment_id = oci_identity_compartment.free_tier_compartment.id
cidr_block = var.vcn_cidr
display_name = "free-tier-vcn"
freeform_tags = {
"purpose" = "free-tier-network"
}
}
# subnet within the VCN
resource "oci_core_subnet" "free_tier_subnet" {
compartment_id = oci_identity_compartment.free_tier_compartment.id
vcn_id = oci_core_vcn.free_tier_vcn.id
cidr_block = var.subnet_cidr
display_name = "free-tier-subnet"
freeform_tags = {
"purpose" = "free-tier-network"
}
}
# Internet Gateway for the VCN
resource "oci_core_internet_gateway" "free_tier_internet_gateway" {
compartment_id = oci_identity_compartment.free_tier_compartment.id
display_name = "free-tier-internet-gateway"
vcn_id = oci_core_vcn.free_tier_vcn.id
freeform_tags = {
"purpose" = "free-tier-network"
}
}
# Route Table for the VCN
resource "oci_core_default_route_table" "free_tier_route_table" {
manage_default_resource_id = oci_core_vcn.free_tier_vcn.default_route_table_id
route_rules {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = oci_core_internet_gateway.free_tier_internet_gateway.id
}
freeform_tags = {
"purpose" = "free-tier-network"
}
}
# Security List for the VCN
resource "oci_core_default_security_list" "free_tier_security_list" {
manage_default_resource_id = oci_core_vcn.free_tier_vcn.default_security_list_id
# Allow all outbound traffic
egress_security_rules {
protocol = "all"
destination = "0.0.0.0/0"
}
# Allow inbound SSH traffic
ingress_security_rules {
protocol = "6" # TCP
source = "0.0.0.0/0"
tcp_options {
min = 22
max = 22
}
}
freeform_tags = {
"purpose" = "free-tier-network"
}
}
# Compute Instance
resource "oci_core_instance" "free_tier_instance" {
compartment_id = oci_identity_compartment.free_tier_compartment.id
availability_domain = var.availability_domain
shape = var.instance_shape
display_name = "free-tier-instance"
source_details {
source_type = "image"
source_id = data.oci_core_images.ubuntu.images[0].id
}
create_vnic_details {
subnet_id = oci_core_subnet.free_tier_subnet.id
assign_public_ip = true
}
metadata = {
ssh_authorized_keys = var.ssh_public_key
}
freeform_tags = {
"purpose" = "free-tier-compute"
}
}
# Data source to get the latest Ubuntu image
data "oci_core_images" "ubuntu" {
compartment_id = oci_identity_compartment.free_tier_compartment.id
operating_system = var.instance_os
operating_system_version = var.instance_os_version
shape = var.instance_shape
sort_by = "TIMECREATED"
sort_order = "DESC"
}
Next to your main.tf
file you also need variables.tf
:
# Authentication
variable "tenancy_ocid" {}
variable "user_ocid" {}
variable "fingerprint" {}
variable "private_key_path" {}
variable "region" {}
# Compartment
variable "compartment_ocid" {}
# Availability Domain
variable "availability_domain" {
default = "DAcm:EU-FRANKFURT-1-AD-1"
}
# SSH
variable "ssh_public_key" {}
# Network
variable "vcn_cidr" {
description = "CIDR block for the VCN"
default = "10.0.0.0/16"
}
variable "subnet_cidr" {
description = "CIDR block for the subnet"
default = "10.0.0.0/24"
}
# Compute
variable "instance_shape" {
description = "Shape for the free tier instance"
default = "VM.Standard.E2.1.Micro"
}
variable "instance_os" {
description = "Operating system for the instance"
default = "Canonical Ubuntu"
}
variable "instance_os_version" {
description = "Operating system version for the instance"
default = "24.04"
}
Last but not least, the structure for the terraform.tfvars
file I used looks like this:
tenancy_ocid = "ocid1.tenancy.oc1..aaaaYourTenancyOCID"
user_ocid = "ocid1.user.oc1..aaaaYourUserOCID"
fingerprint = "e8:6b:YourFingerprint"
private_key_path = "/your/path/to/.oci/oci_api_key.pem"
region = "your-region-1"
compartment_ocid = "ocid1.tenancy.oc1..aaaaYourCompartmentOCI"
ssh_public_key = "ssh-rsa AAAASSHKey ssh-public-key"
# Optionally, override defaults here:
# vcn_cidr = "10.1.0.0/16"
# subnet_cidr = "10.1.1.0/24"
# instance_shape = "VM.Standard.E2.1.Micro"
Running Terraform
- Run
terraform init
to initialize backend and specific provider plugins. - Run
terraform plan
to see a list of all changes that are planned to be executed by Terraform. This step is optional but makes sense for visibility and checking reasons. - Run
terraform apply
to set up your new Always Free VM ๐.
This step might take up to a couple of minutes. If everything goes fine, you'll see a message similar to
oci_core_instance.free_tier_instance: Creation complete after 35s [id=ocid1.instance.oc1.your-region-1.yourspecificid]
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
- Run
terraform destroy
to delete all the settings you have created before.
Done
Et voilรก! We can now check the OCI cloud portal to find out about the ip address of our new VM. With ssh -i path/to/your/shh_key ubuntu@your_ip_adress
we can connect to it.