Note:
- This tutorial requires access to Oracle Cloud. To sign up for a free account, see Get started with Oracle Cloud Infrastructure Free Tier.
- It uses example values for Oracle Cloud Infrastructure credentials, tenancy, and compartments. When completing your lab, substitute these values with ones specific to your cloud environment.
Get Started with Terraform in Oracle Cloud Infrastructure
Introduction
Terraform is an Infrastructure as Code (IaC) tool developed by Hashicorp that allows you to define, provision, and manage your infrastructure across multiple cloud providers using the Terraform language syntax, HCL. Terraform enables you to automate the entire lifecycle of your cloud infrastructure, making it easy to build, update, and scale resources in a consistent and reproducible manner. Terraform is widely used by DevOps teams to manage cloud resources, improve infrastructure efficiency, reduce manual errors and version control cloud resources.
This tutorial helps you to start your journey into leveraging IaC for deploying and managing your cloud infrastructure.
Terraform Architecture Workflow
Objectives
- Install, configure and use Terraform in Oracle Cloud Infrastructure (OCI) for MacOS and Windows. We will walk through an example of deploying OCI networking and compute resources.
Prerequisites
-
Access to an OCI tenancy.
-
A user account with the privileges to access OCI services and resources.
-
Install the following dependencies.
-
For MacOS: Install HomeBrew package manager and Terraform.
-
Go to the HomeBrew site.
-
Copy the following installation command in terminal.
Command: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-
Enter password for sudo permission and installation will follow.
-
Run the following command to complete brew installation.
Command 1: (echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> /Users/{username}/.zprofile Command 2: eval "$(/opt/homebrew/bin/brew shellenv)"
-
Run the
brew help
command to check for successful installation. -
Run the following commands to install terraform.
Command 1: brew tap hashicorp/tap Command 2: brew install hashicorp/tap/terraform
-
Run the
terraform -help
command to check for successful installation.
-
-
For Windows:
-
Download the terraform
.exe
file from here: Install Terraform, use x86 or 64 based on the user Windows OS architecture. Unzip the terraform file which has been downloaded and copy the path. Example path:C:\Users\useradmin\Downloads\terraform_1.7.5_windows_amd64
. -
Search for System Environment Variables in Windows 10 or 11 and click Environment Variables to edit.
-
Under System Variables, select the path and click Edit.
-
Under Edit environment variables, select New and enter the path copied for the terraform file.
-
Click OK for all 3 windows for new entry to get updated.
-
Open the Command Prompt and run the following command.
terraform version
Note: In Windows laptop or virtual machine (VM), if you get the terraform unrecognizable error, you need to restart or reboot the system, so the new set of System Environment Variables will reflect on the Windows machine.
-
Task 1: Declare the Provider
A Terraform provider refers to a plugin that is responsible for understanding and interacting with a specific type of infrastructure or service. Providers are the bridge between Terraform and the APIs of the services or platforms you want to manage. Providers enable Terraform to communicate and interact with cloud platforms effectively. In other words, Terraform provider is like a translator that helps Terraform talk to different cloud services or systems, allowing it to create, manage, and delete resources like virtual machines, databases, and networks.
Each provider typically corresponds to a specific cloud platform (for example, OCI, AWS, Azure, GCP) or infrastructure service (for example, Kubernetes, Docker).
-
Create a folder named
terraform-beginners-demo
and open it in VS Code. -
Install HashiCorp Terraform extension in VS Code.
-
Create the
provider.tf
andvariables.tf
files under theterraform-beginners-demo
folder. -
Copy the following code in the
provider.tf
file.terraform { required_providers { oci = { source = "oracle/oci" version = "5.30.0" } } } provider "oci" { tenancy_ocid = var.tenancy_id user_ocid = var.user_id fingerprint = var.api_fingerprint private_key_path = var.api_private_key_path region = var.region }
Note: We have used the code from the official Terraform Registry page. For more information, see Oracle Cloud Infrastructure Provider and Latest Version (5.30.0).
Task 2: Create OCI Configuration
We need OCI User Oracle Cloud Identifier (OCID), Tenancy OCID, Compartment OCID to configure the provider.
-
Log in to the OCI Console.
-
Click the user profile icon and select My profile.
-
To get API key pair, follow the steps:
-
Under Resources, select API keys and click Add API key.
-
You can either generate a new API key pair or use your existing public key to generate an API key pair. Select Generate API key pair. Make sure to download the private key and public key.
-
Click Add. A configuration file preview box will be displayed.
-
Copy details provided on configuration file preview and save it somewhere. This will be used in the
variables.tf
file.It should look like:
[DEFAULT] user=ocid1.user.oc1..xxxxxxxxxxx fingerprint=xx:xx:xx:xx:xx tenancy=ocid1.tenancy.oc1..xxxxxxxxx region=us-phoenix-1 # your region ID key_file=<path to your private keyfile> # TODO
-
Click Close.
OR
To get API key pair, see Generate an API key configuration in OCI console.
-
Task 3: Configure Terraform Environment with OCI Credentials
-
Go to the VS Code editor and create a new file named
variables.tf
under theterraform-beginners-demo
folder and paste the following code.variable "api_fingerprint" { description = "Fingerprint of OCI API private key for Tenancy" type = string } variable "api_private_key_path" { description = "Path to OCI API private key used for Tenancy" type = string } variable "tenancy_id" { description = "Tenancy ID where to create resources for Tenancy" type = string } variable "user_id" { description = "User ID that Terraform will use to create resources for Tenancy" type = string } variable "region" { description = "OCI region where resources will be created for Tenancy" type = string }
-
Create another new file named
terraform.tfvars
and paste the following code. Define each variable fromvariables.tf
using your configuration file values from Task 2.3.# Identity and access parameters api_fingerprint = "REPLACE_BY_YOUR_FINGERPRINT" # Fingerprint of OCI API private key for Tenancy api_private_key_path = "~/.oci/oci_api_key.pem" # Path to OCI API private key used for Tenancy region = "us-phoenix-1" # OCI region where resources will be created for Tenancy tenancy_id = "REPLACE_YOUR_TENACY_OCID" # Tenancy ID where to create resources user_id = "REPLACE_BY_YOUR_USER_OCID" # Path to OCI API private key used for Tenancy
Note: In production environment it will be best practice to use the
terraform.tfvars
file to define the terraform variables to promote security and scalability. For more information, see Variable Definitions (.tfvars
) Files.
Task 4: Test Terraform Connectivity with your OCI Tenancy
-
Create a new file named
data_source.tf
under theterraform-beginners-demo
folder and copy the following code.data "oci_identity_availability_domains" "ad" { #Required compartment_id = var.tenancy_id }
-
Create new file named
output.tf
under theterraform-beginners-demo
folder and copy the following code.output "list_ads" { value = data.oci_identity_availability_domains.ad.availability_domains }
-
Open a new terminal in VS Code and run the following commands to run the terraform script.
Command 1: terraform init Command 2: terraform plan #to view your deployments what is going to be created Command 3: terraform apply #then type "yes" once prompted alternatively run "apply -auto-approve
Now you should be able to see the list of availability domains and the compartment associated with them under outputs.
You have now successfully configured your system to deploy resources in your OCI tenancy within Terraform. You have passed the variables but have not made any changes to your tenancy. We will walk through a few samples to help you better understand the process of adding, chaining and destroying resources using Terraform.
Task 5: (Example) Deploy an OCI Compute Instance using Terraform
Now we will build on top of what you have created in previous tasks to deploy networking and compute resources.
-
Create a file named
networking.tf
and copy the following code.resource "oci_core_vcn" "test_vcn" { count = (var.create_new_vcn) ? 1 : 0 #Required compartment_id = var.compartment_id cidr_block = var.vcn_cidr_block display_name = var.vcn_display_name dns_label = var.vcn_dns_label }
Note: For more information on core-vcn variable with additional configurations, see oci_core_vcn.
-
Edit your
variables.tf
file and add the following variables.#VCN specific variables variable "create_new_vcn" { description = "Boolean variable to specify whether to create a new VCN or to reuse an existing one." type = bool } variable "compartment_id" { description = "OCI compartment where resources will be created" type = string } variable "vcn_cidr_block" { description = "The list of IPv4 CIDR blocks the VCN will use" type = string } variable "vcn_display_name" { description = "provide a descriptive name for the VCN - this is what you will see displayed in the OCI console" type = string } variable "vcn_dns_label" { description = "provide a descriptive alphanumeric name for the DNS - this is what you will see displayed in the OCI console" type = string } variable "vcn_id" { description = "provide your existing VCN OCID if create_new_vcn = false" type = string } variable "private_subnet_id" { description = "provide existing private subnet OCID" type = string } variable "public_subnet_id" { description = "provide existing public subnet OCID" type = string }
-
Add the new variables in
terraform.tfvars
and define each variable according to your tenancy configuration.# VCN specific variables create_new_vcn = true # Set this to true if you want terraform to crearte the network for you, otherwise set it to false. compartment_id = "REPLACE_BY_YOUR_COMPARTMENT_OCID" # add your own compartment id where the vcn will be created vcn_cidr_block = "10.0.0.0/16" # The list of IPv4 CIDR blocks the VCN will use vcn_display_name = "terraform_vcn_example" # provide a descriptive name for the VCN - this is what you will see displayed in the OCI console vcn_dns_label = "terraformvcn" # provide a descriptive alphanumeric name for the DNS - this is what you will see displayed in the OCI console # Configure CIDR Blocks, Subnet(Public, Private) OCIDS for an existing VCN. # vcn_id = "REPLACE_BY_YOUR_VCN_OCID" #ADD WITH YOUR VCN OCID private_subnet_id = "REPLACE_BY_YOUR__PRIVATE_SUBNET_OCID" #ADD WITH YOUR PRIVATE SUBNET public_subnet_id = "REPLACE_BY_YOUR_PUBLIC_SUBNET__OCID" #AA WITH YOUR PUBLIC SUBNET
Note: If you already have an existing VCN then set the
create_new_vcn
to equal false and modifyvcn_id
,private_subnet_id
, andpublic_subnet_id
variables. -
Create subnets for your new VCN.
-
In the
networking.tf
file, add the following code to create your public and private subnets.resource "oci_core_subnet" "private_subnet" { count = (var.create_new_vcn) ? 1 : 0 #Required cidr_block = var.private_subnet_cidr_block compartment_id = var.compartment_id vcn_id = oci_core_vcn.test_vcn.*.id[0] display_name = var.private_subnet_display_name prohibit_public_ip_on_vnic = var.private_subnet_prohibit_public_ip_on_vnic } resource "oci_core_subnet" "public_subnet" { count = (var.create_new_vcn) ? 1 : 0 #Required cidr_block = var.public_subnet_cidr_block compartment_id = var.compartment_id vcn_id = oci_core_vcn.test_vcn.*.id[0] display_name = var.public_subnet_display_name prohibit_public_ip_on_vnic = var.public_subnet_prohibit_public_ip_on_vnic route_table_id = oci_core_route_table.test_route_table.*.id[0] }
Note: For more information on core-subnet variable with additional configurations, see oci_core_subnet.
-
Add the new subnet variables into the
variables.tf
file by copying the following code.#Private subnet variables variable "private_subnet_cidr_block" { description = "OCI private subnet CIDR block range" type = string } variable "private_subnet_display_name" { description = "provide a descriptive name for the private subnet - this is what you will see displayed in the OCI console" type = string } variable "private_subnet_prohibit_public_ip_on_vnic" { description = "Allow public IP address to the VNIC" type = bool } #Public subnet variables variable "public_subnet_cidr_block" { description = "OCI public subnet CIDR block range" type = string } variable "public_subnet_display_name" { description = "provide a descriptive name for the public subnet - this is what you will see displayed in the OCI console" type = string } variable "public_subnet_prohibit_public_ip_on_vnic" { description = "Allow public IP address to the VNIC" type = bool }
-
Declare the new subnet variables in
terrform.tfvars
file.#Private subnet variables private_subnet_cidr_block = "10.0.1.0/24" # OCI private subnet CIDR block range private_subnet_display_name = "terraform_private_subnet_example" # provide a descriptive name for the private subnet - this is what you will see displayed in the OCI console private_subnet_prohibit_public_ip_on_vnic = false # Allow public IP address to the VNIC #Public subnet variables public_subnet_cidr_block = "10.0.2.0/24" # OCI public subnet CIDR block range public_subnet_display_name = "terraform_public_subnet_example" # provide a descriptive name for the public subnet - this is what you will see displayed in the OCI console public_subnet_prohibit_public_ip_on_vnic = false
-
-
Add the following code in the
networking.tf
file to create an internet gateway.resource "oci_core_internet_gateway" "test_internet_gateway" { count = (var.create_new_vcn) ? 1 : 0 #Required compartment_id = var.compartment_id display_name = "INTERNET_GTWFOR_${var.vcn_display_name}" vcn_id = oci_core_vcn.test_vcn.*.id[0] #route_table_id = oci_core_route_table.test_route_table.id }
-
Add the following code in
networking.tf
for the route table to manage traffic to the internet.resource "oci_core_route_table" "test_route_table" { count = (var.create_new_vcn) ? 1 : 0 #Required compartment_id = var.compartment_id vcn_id = oci_core_vcn.test_vcn.*.id[0] route_rules { #Required network_entity_id = oci_core_internet_gateway.test_internet_gateway.*.id[0] description = "route rule internet access for ${var.vcn_display_name}" destination = "0.0.0.0/0" destination_type = "CIDR_BLOCK" } }
-
Create the Network Security Group (NSG) associated with the VCN. Copy the following code into the
networking.tf
file.resource "oci_core_network_security_group" "test_nsg" { count = (var.create_new_vcn) ? 1 : 0 #Required compartment_id = var.compartment_id vcn_id = oci_core_vcn.test_vcn.*.id[0] display_name = "NETWORK_SECURITY_GROUP_${var.vcn_display_name}" freeform_tags = { "Lab" = "Terraofm 101 Guide" } }
-
We will now create 2 new files,
compute_linux.tf
andcompute_windows.tf
, for deploying a Linux VM and a Windows VM. -
In
compute_linux.tf
, copy the following code to create a compute instance with a Linux OS.resource "oci_core_instance" "test_linux_instance" { #Required count = var.create_linux_instance ? 1 : 0 availability_domain = data.oci_identity_availability_domains.ad.availability_domains[0].name compartment_id = var.compartment_id create_vnic_details { assign_public_ip = "true" display_name = var.instance_display_name nsg_ids = [] skip_source_dest_check = "false" subnet_id = var.create_new_vcn ? oci_core_subnet.public_subnet.*.id[0] : var.public_subnet_id } display_name = "${var.instance_display_name}_linux" metadata = { ssh_authorized_keys = "${file(var.public_ssh_key)}" } shape = var.instance_shape shape_config { memory_in_gbs = var.instance_flex_memory_in_gbs ocpus = var.instance_flex_ocpus } launch_options { boot_volume_type = "PARAVIRTUALIZED" firmware = "UEFI_64" is_consistent_volume_naming_enabled = "true" is_pv_encryption_in_transit_enabled = "true" network_type = "PARAVIRTUALIZED" remote_data_volume_type = "PARAVIRTUALIZED" } source_details { #Required source_id = var.linux_image_ocid source_type = "image" } preserve_boot_volume = false }
-
In
compute_windows.tf
, copy the following code.resource "oci_core_instance" "test_windows_instance" { #Required count = var.create_windows_instance ? 1 : 0 availability_domain = data.oci_identity_availability_domains.ad.availability_domains[0].name compartment_id = var.compartment_id create_vnic_details { assign_public_ip = "true" display_name = var.instance_display_name nsg_ids = [] skip_source_dest_check = "false" subnet_id = var.create_new_vcn ? oci_core_subnet.public_subnet.*.id[0] : var.public_subnet_id } display_name = "${var.instance_display_name}_windows" metadata = { } shape = var.instance_shape shape_config { memory_in_gbs = var.instance_flex_memory_in_gbs ocpus = var.instance_flex_ocpus } launch_options { boot_volume_type = "PARAVIRTUALIZED" firmware = "UEFI_64" is_pv_encryption_in_transit_enabled = "true" network_type = "PARAVIRTUALIZED" remote_data_volume_type = "PARAVIRTUALIZED" } source_details { #Required source_id = var.windows_image_ocid source_type = "image" } preserve_boot_volume = false }
-
Update
variables.tf
with the new variables for incompute_linux
andcompute_windows
.#Compute variables variable "instance_shape" { description = "value" type = string } variable "instance_flex_memory_in_gbs" { description = "(Updatable) The total amount of memory available to the instance, in gigabytes." type = number } variable "instance_flex_ocpus" { description = "(Updatable) The total number of OCPUs available to the instance." type = number } variable "instance_create_vnic_details_assign_public_ip" { description = "To allow compute connectivity from internet" type = bool } variable "instance_display_name" { description = "provide a descriptive name for the compute instance - this is what you will see displayed in the OCI console" type = string } variable "public_ssh_key" { description = "Add your public ssh key - for provisioning your compute instance" type = string } variable "private_ssh_key" { description = "Add your private ssh key - for accessing your compute instance after creation" type = string } variable "create_linux_instance" { description = "Boolean variable to specify whether to provision a Linux instances" type = bool } variable "create_windows_instance" { description = "Boolean variable to specify whether to provision a Windows instances" type = bool } variable "windows_image_ocid" { description = "OCID of the Windows image to use" type = string } variable "linux_image_ocid" { description = "OCID of the Linux image to use" type = string }
-
Add and define the new variables in
terraform.tfvars
.#Compute variables - Make sure to select a compatible shape (e.g.: VM.Standard.E4.Flex) instance_shape = "VM.Standard.E5.Flex" # Shape of the compute instance instance_flex_memory_in_gbs = 16 # (Updatable) The total amount of memory available to the instance, in gigabytes. instance_flex_ocpus = 1 # (Updatable) The total number of OCPUs available to the instance. instance_create_vnic_details_assign_public_ip = true # To allow compute connectivity from internet instance_display_name = "terraform_compute_example" # provide a descriptive name for the compute instance - this is what you will see displayed in the OCI console #SSH keys https://docs.oracle.com/en/learn/generate_ssh_keys/index.html#introduction public_ssh_key = "~/cloudshellkey.pub" # Add your public ssh key private_ssh_key = "~/cloudshellkey" # Add your private ssh key create_linux_instance = true # if set to true a test linux instance will be created and false no linux instance will be deployed. create_windows_instance = true # # If set to true a test windows instance will be created and false no windows instance will be deployed. linux_image_ocid = "REPLACE_BY_YOUR_REGION_LINUX_IMAGE_OCID" # OCID for chosen image (Oracle Linux 9 example) specific to the test region (us-phoenix-1) windows_image_ocid = "REPLACE_BY_YOUR_REGION_WINDOWS_IMAGE_OCID" # OCID for chosen image (Windows example) specific to each region (us-phoenix-1) # Here are other image OCIDs for popular region (Ashburn, San Jose, Toronto) # Ashburn # Oracle linux image_id = ocid1.image.oc1.iad.aaaaaaaau7uaok7n5qd4nivgiyfatfdddhltmxddtfbyqg3bsg3fxk6z6aqq # Windows image_id = ocid1.image.oc1.iad.aaaaaaaamaaiupezxbrw6fji5ndk3jdujwhjuexcafheqjqf45g6nzyblz6a #San Jose # Oracle linux image_id = ocid1.image.oc1.us-sanjose-1.aaaaaaaabjixxpfouczgpcnpvgny5pcqtgjgi3nincszbfdkd2xr4jvzahua # Windows image_id = ocid1.image.oc1.us-sanjose-1.aaaaaaaatmjlzoqw5gzohjvygzcm5rpugomxyfho5xi6subjchoxnxo4wcfa #Toronto # Oracle linux image_id = ocid1.image.oc1.ca-toronto-1.aaaaaaaai6uhjrtajuuitl5hara5brnvwqvq4aebenmnbehv2cila75xbvzq # Windows image_id = ocid1.image.oc1.ca-toronto-1.aaaaaaaaeged3obrrmmwvyruvknszy23btvb2fqu7vn3c5azeecbj2prm64q # for other image OCIDs: https://docs.oracle.com/en-us/iaas/images/
Note: Image OCIDs are specific to each region. Within the above code there are images OCIDs for popular regions and you can find Image OCIDs for all other regions here: All Image Families.
-
In
output.tf
, copy the following code. This will print out the resources created in terminal.# Regions output "linux_instance_region" { value = oci_core_instance.test_linux_instance.*.region } output "windows_instance_region" { value = oci_core_instance.test_windows_instance.*.region } # Networking output "network_vcn_name" { value = oci_core_vcn.test_vcn.*.display_name } # Compute: Linux Test Instance output "output_linux_instance_display_name" { value = oci_core_instance.test_linux_instance.*.display_name } output "output_linux_instance_public_ip" { value = oci_core_instance.test_linux_instance.*.public_ip } output "output_linux_instance_state" { value = oci_core_instance.test_linux_instance.*.state } # Compute: Windows Test Instance output "output_windows_instance_display_name" { value = oci_core_instance.test_windows_instance.*.display_name } output "output_windows_instance_public_ip" { value = oci_core_instance.test_windows_instance.*.public_ip } output "output_windows_instance_state" { value = oci_core_instance.test_windows_instance.*.state }
-
Open new terminal for
terraform-example
and run the following commands.Command 1: terraform init Command 2: terraform plan Commnad 3: terraform apply (and then respond "yes") OR terraform apply -auto-approve
You should see the following output and a VCN, private subnet, and public subnet, route table, internet gateway, nsg, Linux VM and Windows VM should be created.
-
Resources have now been deployed using Terraform. Once you verify resource creation in your OCI tenancy use the following command to destroy all resources.
Command: terraform destroy
Next Steps
You have now built out the connection to Terraform and learned to deploy, update and destroy resources with a simple example. To continue your Terraform journey in OCI and set yourself up for the OCI Architect Professional Certification check out Accelerate Oracle Cloud Infrastructure Architect Professional Certification with Terraform.
We welcome contributions from readers to improve and expand this tutorial. Your contributions are highly valued in improving this resource for future Terraform learners.
Related Links
Acknowledgments
- Authors - Gretchen Zhang (Cloud Engineer), Akarsha I (Staff Cloud Engineer), Mahamat Guiagoussou (Master Principal Cloud Architect)
More Learning Resources
Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.
For product documentation, visit Oracle Help Center.
Get Started with Terraform in Oracle Cloud Infrastructure
G18634-01
November 2024