Setting up Oracle Cloud Infrastructure (OCI) using Terraform gives you an automated, reproducible "Always Free" cloud environment. However, managing backend state safely and dealing with air-gapped or restricted provider installations can introduce friction.
In Part 01 of this series, we walk through configuring local credentials, OCI API integration, an S3-compatible backend for state management, an air-gapped local Terraform provider mirror, and a modular folder structure.
Prerequisites
- A local Linux machine (Debian/Ubuntu preferred) or WSL environment.
- An active Oracle Cloud Infrastructure (OCI) Free Tier account.
- OpenSSL, SSH, and Terraform installed.
Phase 1: Local Machine Setup
Generate local cryptographic keys required for SSH access to your instances and secure API authentication with OCI.
1.1 Generate SSH Key Pair (ED25519)
Generate a high-security ED25519 key pair without a passphrase for non-interactive infrastructure management:
ssh-keygen -t ed25519 -N "" -C "oci_vm_key" -f ~/.ssh/oci_vm_key
This generates two files:
-
Private key:
~/.ssh/oci_vm_key(keep safe) -
Public key:
~/.ssh/oci_vm_key.pub
1.2 Create OCI API Key Directory
Restrict permissions on the configuration directory to preserve security standards:
mkdir -p ~/.oci
chmod 700 ~/.oci
1.3 Generate OCI API Key Pair (RSA 4096)
OCI requires an RSA 4096-bit key pair for programmatic API authentication:
# Generate private key
openssl genrsa -out ~/.oci/oci.pem 4096
# Generate public key
openssl rsa -pubout -in ~/.oci/oci.pem -out ~/.oci/oci_public.pem
# Secure permissions (OCI rejects keys with permissions broader than 600)
chmod 600 ~/.oci/oci.pem
1.4 Generate Key Fingerprint
Extract the MD5 fingerprint of your public key. You will need this to verify API connectivity in the OCI Console:
openssl rsa -pubout -in ~/.oci/oci.pem -outform DER | openssl md5 -c
π‘ Output Example:
MD5(stdin)= 57:0d:ea:8c:07:11:22:33:44:55:66:77:88:99:aa:bb
Phase 2: OCI Console Configuration
2.1 Upload API Public Key
- Log into your OCI Console.
- Navigate to Profile (top right icon) β My Profile.
- Under Resources (bottom left), click API Keys β Add API Key.
- Select Paste a Public Key.
- Output the contents of
~/.oci/oci_public.pemand paste them into the text box:
cat ~/.oci/oci_public.pem
- Click Add.
π Save Configuration Details:
A Configuration File Preview popup will appear. Copy these valuesβthey contain youruserOCID,tenancyOCID,fingerprint, and your homeregion(e.g.,ap-hyderabad-1).
2.2 Retrieve Object Storage Namespace
Your Object Storage namespace is a unique, system-generated string tied to your tenant. Retrieve it using the OCI CLI or Cloud Shell:
oci os ns get
Expected JSON output:
{
"data": "axjetxazylvl"
}
2.3 Create the Terraform Remote State Bucket
Store your .tfstate files remotely in OCI Object Storage:
- Open Object Storage β Buckets in the OCI Console.
- Click Create Bucket.
- Fill in the parameters:
-
Bucket Name:
terraform-states -
Default Storage Tier:
Standard -
Visibility:
Private - Region: Select your Home Region
-
Bucket Name:
- Click Create.
2.4 Generate S3-Compatible Credentials
OCI Object Storage provides an S3-compatible API endpoint, allowing you to use standard S3 backend configurations in Terraform.
- Go to Profile β My Profile β Customer Secret Keys.
- Click Generate Secret Key.
- Enter a display name:
terraform-backend. - Click Generate Secret Key.
β οΈ Important: Copy and save the generated Secret Key immediately. It will never be displayed again.
- Access Key: Visible in the Customer Secret Keys table (e.g.,
f4068126c6fd14c08a121d17396u317e6144e6a2)- Secret Key: Copied from the modal dialog.
Phase 3: Shell Environment Setup (~/.bashrc)
To prevent hardcoding sensitive credentials inside your Terraform code, export them as environment variables.
Add the following block to the bottom of your ~/.bashrc file (replace placeholders with your actual OCIDs, keys, and paths):
# =============================================================================
# OCI Provider Credentials
# =============================================================================
export TF_VAR_tenancy_ocid="ocid1.tenancy.oc1..aaaaaaaa..."
export TF_VAR_user_ocid="ocid1.user.oc1..aaaaaaaa..."
export TF_VAR_fingerprint="57:0d:ea:8c:07:..."
export TF_VAR_private_key_path="$HOME/.oci/oci.pem"
export TF_VAR_region="ap-hyderabad-1"
export TF_VAR_compartment_id="ocid1.tenancy.oc1..aaaaaaaa..."
# =============================================================================
# S3-Compatible Backend Credentials (OCI Object Storage S3 API)
# =============================================================================
export AWS_ACCESS_KEY_ID="f4068126c6..."
export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_KEY_HERE"
export AWS_REQUEST_CHECKSUM_CALCULATION=when_required
# =============================================================================
# Backend Configuration Variables
# =============================================================================
export TF_VAR_backend_bucket="terraform-states"
export TF_VAR_backend_namespace="axjetxazylvl"
export TF_VAR_backend_region="ap-hyderabad-1"
# =============================================================================
# SSH Key for VM Provisioning
# =============================================================================
export TF_VAR_ssh_public_key_path="$HOME/.ssh/oci_vm_key.pub"
# =============================================================================
# OCI CLI Path & Autocomplete (Adjust path to your user home)
# =============================================================================
export PATH="$HOME/bin:$PATH"
if [[ -f "$HOME/lib/oracle-cli/lib/python3.13/site-packages/oci_cli/bin/oci_autocomplete.sh" ]]; then
source "$HOME/lib/oracle-cli/lib/python3.13/site-packages/oci_cli/bin/oci_autocomplete.sh"
fi
Apply the environment changes:
source ~/.bashrc
Phase 4: Offline / Local Provider Mirror Configuration
If you want fast execution or operate in an environment with limited internet access, you can mirror the official OCI provider locally.
4.1 Create Directory Structure
Build the target plugin directory structure expected by Terraform for oracle/oci version 6.20.0:
mkdir -p ~/.terraform.d/plugins-local/registry.terraform.io/oracle/oci/6.20.0/linux_amd64
4.2 Download and Unpack Provider Binary
Download terraform-provider-oci_6.20.0_linux_amd64.zip directly from the HashiCorp or Oracle releases registry, place it in the created path, and unpack it:
cd ~/.terraform.d/plugins-local/registry.terraform.io/oracle/oci/6.20.0/linux_amd64
# Unzip binary package
unzip terraform-provider-oci_6.20.0_linux_amd64.zip
# Rename provider binary to append protocol version target
mv terraform-provider-oci_v6.20.0 terraform-provider-oci_v6.20.0_x5
# Clean up archive
rm terraform-provider-oci_6.20.0_linux_amd64.zip
Verify that your local mirror directory tree matches this structure:
~/.terraform.d/
βββ plugins-local
βββ registry.terraform.io
βββ oracle
βββ oci
βββ 6.20.0
βββ linux_amd64
βββ terraform-provider-oci_v6.20.0_x5
4.3 Configure Global Provider Installation (~/.terraformrc)
Create a global CLI configuration file at ~/.terraformrc to force Terraform to load the OCI provider directly from your filesystem mirror instead of downloading it during terraform init:
provider_installation {
filesystem_mirror {
path = "/home/YOUR_USER_NAME/.terraform.d/plugins-local"
include = ["registry.terraform.io/oracle/oci"]
}
direct {
exclude = ["registry.terraform.io/oracle/oci"]
}
}
β οΈ Note: Replace
/home/YOUR_USER_NAME/with the absolute path to your home directory (Terraform does not expand~inside.terraformrc).
Phase 5: Project Directory Architecture
To manage networking and compute instances independently without creating monolithic state files, isolate components into modular subdirectories:
Create the workspace structure:
mkdir -p ~/oci-infra/{shared,vm-amd,vm-arm}
~/oci-infra/
βββ shared/ # VCN, subnets, IGW, route tables, security lists, buckets
β βββ backend.tf
β βββ provider.tf
β βββ variables.tf
β βββ main.tf
β βββ outputs.tf
βββ vm-amd/ # Always Free x86 Instance (E2.1.Micro + Oracle Linux 9)
β βββ backend.tf
β βββ provider.tf
β βββ variables.tf
β βββ data.tf
β βββ main.tf
β βββ outputs.tf
βββ vm-arm/ # Always Free ARM Instance (A1.Flex + Oracle Linux 9)
βββ backend.tf
βββ provider.tf
βββ variables.tf
βββ data.tf
βββ main.tf
βββ outputs.tf
Next Steps
With your API keys generated, environment variables configured, local provider mirrored, and project folders structured, your system is ready.
In Part 02, we will write the HCL declarations for the shared/ moduleβbuilding a Virtual Cloud Network (VCN), public subnets, internet gateways, and locking down security lists within OCI Always Free limits.
Top comments (0)