Loading...
Loading...
Use when writing Terraform for OCI, troubleshooting provider errors, managing state files, or implementing Resource Manager stacks. Covers terraform-provider-oci gotchas, resource lifecycle anti-patterns, state management mistakes, authentication issues, and OCI Landing Zones.
npx skill4agent add acedergren/oci-agent-skills infrastructure-as-code# Writing Terraform from scratch for every resource
resource "oci_identity_compartment" "prod" { ... }
resource "oci_core_vcn" "main" { ... }
resource "oci_identity_policy" "policies" { ... }
# Result: Unmaintainable, inconsistent, no governance# Use official OCI Landing Zone modules
module "landing_zone" {
source = "oracle-terraform-modules/landing-zone/oci"
version = "~> 2.0"
# Infrastructure configuration
compartments_configuration = { ... }
network_configuration = { ... }
security_configuration = { ... }
}# WRONG - breaks when moving between regions/compartments
resource "oci_core_instance" "web" {
compartment_id = "ocid1.compartment.oc1..aaaaaa..." # Hardcoded!
subnet_id = "ocid1.subnet.oc1.phx.bbbbbb..." # Hardcoded!
}
# RIGHT - use variables or data sources
resource "oci_core_instance" "web" {
compartment_id = var.compartment_ocid
subnet_id = data.oci_core_subnet.existing.id
}preserve_boot_volume = true# WRONG - orphans boot volumes when instance destroyed ($50+/month per instance)
resource "oci_core_instance" "dev" {
preserve_boot_volume = true # Default behavior!
}
# RIGHT - explicit cleanup in dev/test
resource "oci_core_instance" "dev" {
preserve_boot_volume = false
}lifecycle# WRONG - accidental destroy can delete production database
resource "oci_database_autonomous_database" "prod" {
# No protection!
}
# RIGHT - prevent accidental destruction
resource "oci_database_autonomous_database" "prod" {
lifecycle {
prevent_destroy = true
ignore_changes = [defined_tags] # Ignore tag changes from console
}
}# WRONG - hardcoded AD breaks multi-region deployment
resource "oci_core_instance" "web" {
availability_domain = "fMgC:US-ASHBURN-AD-1" # Tenant-specific!
}
# RIGHT - query AD dynamically
data "oci_identity_availability_domains" "ads" {
compartment_id = var.tenancy_ocid
}
resource "oci_core_instance" "web" {
availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
}# WRONG - no locking, no collaboration
terraform {
backend "local" {}
}
# RIGHT - use OCI Object Storage with locking
terraform {
backend "s3" {
bucket = "terraform-state"
key = "prod/terraform.tfstate"
region = "us-phoenix-1"
endpoint = "https://namespace.compat.objectstorage.us-phoenix-1.oraclecloud.com"
skip_region_validation = true
skip_credentials_validation = true
skip_metadata_api_check = true
use_path_style = true
}
}count# WRONG - reordering list recreates ALL resources
resource "oci_core_instance" "web" {
count = length(var.instance_names)
display_name = var.instance_names[count.index]
}
# If instance_names changes from ["web1", "web2", "web3"] to ["web0", "web1", "web2", "web3"]
# Terraform RECREATES all instances!
# RIGHT - use for_each with stable keys
resource "oci_core_instance" "web" {
for_each = toset(var.instance_names)
display_name = each.value
}TF_VAR_*~/.oci/configauth = "InstancePrincipal"# In provider.tf
provider "oci" {
auth = "InstancePrincipal"
region = var.region
}
# Dynamic group matching rule:
# "ALL {instance.compartment.id = '<compartment-ocid>'}"
# IAM policy:
# "Allow dynamic-group terraform-instances to manage all-resources in tenancy"Error: 409-Conflict, Resource already exists# Import existing resource into state
terraform import oci_core_vcn.main ocid1.vcn.oc1.phx.xxxxx
# Then run plan/apply as normal
terraform planterraform importterraform plan # Shows unexpected changes
terraform show # Compare state to actual infrastructureterraform refresh # Updates state to match realityterraform import <resource_type>.<name> <ocid>lifecycle {
ignore_changes = [defined_tags, freeform_tags] # Ignore console tag edits
}terraform plan# 1. Make backup
cp terraform.tfstate terraform.tfstate.backup
# 2. Try state repair
terraform state pull > recovered.tfstate
mv recovered.tfstate terraform.tfstate
# 3. If that fails, restore from Object Storage versioning
# Or reconstruct with imports (last resort)Error: Resource still in use# 1. Visualize dependencies
terraform graph | dot -Tpng > graph.png
# 2. Destroy in reverse order
terraform destroy -target=oci_core_instance.web
terraform destroy -target=oci_core_subnet.private
terraform destroy -target=oci_core_vcn.main
# Or use depends_on explicitly:
resource "oci_core_vcn" "main" {
# ...
}
resource "oci_core_subnet" "private" {
vcn_id = oci_core_vcn.main.id
# depends_on is implicit via vcn_id reference
}# Database provisioning takes 15-30 minutes
resource "oci_database_autonomous_database" "prod" {
# ... configuration ...
timeouts {
create = "60m" # Default 20m often not enough
update = "60m"
delete = "30m"
}
}
# Compute instance usually fast, but can timeout on capacity issues
resource "oci_core_instance" "web" {
# ... configuration ...
timeouts {
create = "30m" # Allow retries on "out of capacity"
}
}github.com/oracle-quickstart/oci-landing-zones# EXPENSIVE - fixed shape
resource "oci_core_instance" "web" {
shape = "VM.Standard2.4" # 4 OCPUs, 60GB RAM, $218/month
}
# CHEAPER - flexible shape
resource "oci_core_instance" "web" {
shape = "VM.Standard.E4.Flex"
shape_config {
ocpus = 4
memory_in_gbs = 60
}
# Cost: (4 × $0.03 + 60 × $0.0015) × 730 = $153/month (30% savings)
}# Define locals for consistent tagging
locals {
common_tags = {
"CostCenter" = "Engineering"
"Environment" = var.environment
"ManagedBy" = "Terraform"
"Project" = var.project_name
}
}
resource "oci_core_instance" "web" {
freeform_tags = merge(
local.common_tags,
{
"Component" = "WebServer"
}
)
}oci-terraform-patterns.md