
Landing Zones
- 11 installs
- 22 repo stars
- Updated May 28, 2026
- acedergren/agentic-tools
landing-zones is a Claude Code skill for designing multi-tenant OCI environments and standing up landing-zone Terraform stacks with Security Zones.
About
landing-zones is a Claude Code skill for designing multi-tenant OCI environments and standing up landing-zone Terraform stacks. A developer uses it when planning compartment hierarchies, enforcing Security Zones, or designing hub-spoke network topology. It covers OCI compartment structures, multi-tenant IAM decision trees, Security Zone automation, CIS Foundations compliance, and DRG routing.
- OCI multi-tenant landing-zone architecture with compartment hierarchies
- Security Zones, hub-spoke topology, and CIS Foundations compliance
- Multi-tenant IAM decision tree and DRG routing
Landing Zones by the numbers
- 11 all-time installs (skills.sh)
- Ranked #841 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
landing-zones capabilities & compatibility
Free; guidance plus OCI CLI commands against an existing tenancy.
- Capabilities
- landing zone · cloud architecture · security zone
- Works with
- oracle · terraform
- Use cases
- devops · security audit
- Pricing
- Free
What landing-zones says it does
NEVER create a flat compartment structure
Security Zones prevent violations BEFORE resource creation. Auditing finds them AFTER compromise.
npx skills add https://github.com/acedergren/agentic-tools --skill landing-zonesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 11 |
|---|---|
| repo stars | ★ 22 |
| Last updated | May 28, 2026 |
| Repository | acedergren/agentic-tools ↗ |
What it does
Design multi-tenant OCI landing zones with compartment hierarchies, Security Zones, and hub-spoke networking.
Who is it for?
Multi-tenant OCI architecture, compartment hierarchy design, and Security Zone enforcement.
Skip if: Narrow single-service tasks like VCN peering or IAM verb syntax, which belong to sibling skills.
When should I use this skill?
Use when designing multi-tenant OCI environments, standing up landing zone Terraform stacks, enforcing Security Zones, or planning hub-spoke topology.
By the numbers
- hierarchical vs flat compartment example
- non-overlapping /16 CIDR allocation per VCN
Files
OCI Landing Zones - Expert Architecture
NEVER Do This
NEVER create a flat compartment structure
BAD:
tenancy/ app1-dev, app1-test, app1-prod, app2-dev ...
Problems:
- Cannot apply a single policy to all dev environments
- Cannot delegate administration per team
- Cost reports are unstructured
- Policy duplication grows O(n) with team count
GOOD - hierarchical:
tenancy/
Network/ (Hub, Spokes)
Security/ (Vault, Logging)
Workloads/
App1/ (Dev, Test, Prod)
App2/ (Dev, Test, Prod)
Shared-Services/ (Identity, Monitoring)Policy inheritance flows DOWN the tree. One policy at Workloads/ applies to all workloads.
NEVER reuse 10.0.0.0/16 across VCNs
BAD - same CIDR everywhere:
Dev VCN: 10.0.0.0/16
Test VCN: 10.0.0.0/16 # Cannot peer with Dev
Prod VCN: 10.0.0.0/16 # Cannot peer with either
VCN CIDR is IMMUTABLE. Wrong CIDR = complete rebuild + downtime.
GOOD - non-overlapping allocation:
Hub VCN: 10.0.0.0/16
Dev VCN: 10.10.0.0/16
Test VCN: 10.20.0.0/16
Prod VCN: 10.30.0.0/16NEVER skip Security Zones for production compartments
# BAD: Compartment with no guardrails
oci iam compartment create --compartment-id $PARENT --name "Prod"
# Result: Anyone can create public IPs, unencrypted buckets, etc.
# GOOD: Security Zone enforces policies BEFORE resource creation
oci cloud-guard security-zone-recipe create \
--compartment-id $TENANCY_ID \
--display-name "CIS-Prod-Recipe" \
--security-policies '["deny-public-ip","deny-public-bucket"]'
oci cloud-guard security-zone create \
--compartment-id $PROD_COMPARTMENT_ID \
--display-name "Prod-Security-Zone" \
--security-zone-recipe-id $RECIPE_IDSecurity Zones prevent violations BEFORE resource creation. Auditing finds them AFTER compromise.
NEVER put workload resources in the root compartment
Root compartment is for tenancy-wide IAM only (users, groups, policies).
Resources in root bypass governance, cannot be delegated, violate CIS OCI Foundations Benchmark.
Root should contain ONLY:
- Top-level child compartments
- Tenancy-wide IAM policies
Nothing else.NEVER mix dev and prod resources in the same compartment
Developers with dev access can accidentally delete prod resources. Cannot set different backup policies, tagging strategies, or budget alerts per environment.
NEVER skip tagging strategy
# Without tags: "oci.compute.instance: $5,234/month" — which team? which project?
# Cannot chargeback, cannot identify waste.
# RIGHT: Create tag namespace with mandatory defaults
oci iam tag-namespace create --compartment-id $TENANCY_ID --name "Organization"
# Create: CostCenter, Environment, Owner tags
# Apply tag-defaults at compartment level (auto-applied to all resources)
oci iam tag-default create \
--compartment-id $WORKLOAD_COMPARTMENT_ID \
--tag-definition-id $COSTCENTER_TAG_ID \
--value '${iam.principal.name}'NEVER allow internet egress from spoke VCNs directly
BAD: Spoke subnet → Internet Gateway
- Data exfiltration undetectable
- Egress cost $3k-5k/month per spoke (unmetered)
- No DPI or egress filtering
GOOD - hub-spoke with centralized control:
Spoke → DRG → Hub VCN → Network Firewall → NAT Gateway → Internet
- Single egress point with firewall policies
- Complete visibility via VCN Flow LogsNEVER use single-region for production workloads requiring SLA
Region outage = complete downtime. No automatic failover without DR.
Multi-region pattern:
Primary: us-ashburn-1 + DR: us-phoenix-1
- Autonomous Data Guard for database (near-zero RPO)
- Traffic Manager for DNS failover (RTO ~15 minutes)
- Object Storage cross-region replication
- Mirror compartment structure in DR region---
Multi-Tenant IAM Decision Tree
Workloads single-tenant?
│
├─ YES → Environment-centric model
│ Compartments: Network, Shared-Services, Workloads/App/Dev-Test-Prod
│ IAM: Per-env groups (DevAdmins, ProdOps) scoped to env compartment
│
└─ NO (Multi-tenant SaaS)?
│
├─ Strict tenant isolation required?
│ ├─ YES → Tenant-per-compartment: Org/TenantA, Org/TenantB
│ │ Dynamic Group per tenant VCN for instance principal auth
│ │ Policy: `allow dynamic-group TenantA-VMs to manage all-resources
│ │ in compartment TenantA`
│ └─ NO → Shared compartment + per-tenant tagging
│ (faster setup, shared blast radius)
│
├─ Multiple environments per tenant?
│ └─ Nest: TenantA/Dev, TenantA/Test, TenantA/Prod
│ Policies inherit down the tree automatically
│
└─ Centralized shared services?
└─ Shared-Services compartment (Logging, Monitoring, Identity)
Grant tenancy-level Ops group least-privileged read accessIAM policy template for multi-tenant:
Allow group TenantA-Admins to manage all-resources in compartment TenantA
Allow dynamic-group TenantA-VCN to manage virtual-network-family in compartment TenantA
Allow group Shared-Network to use virtual-network-family in compartment Shared-ServicesGuardrails:
- Never grant tenant-specific policies at root; scope to tenant compartment hierarchy
- Require DRG route approval workflow before adding new tenants
- Refer to
iam-identity-managementskill for fine-grained policy verb syntax
---
Security Zone Rollout Checklist
1. Inventory compartments — export JSON filtered by tag Environment=Prod 2. Create/update recipe — clone Oracle CIS recipe, append custom policies (no public LB, require CMEK). See references/security-zone-automation.md 3. Apply via CLI/Terraform — loop compartments or use Terraform module 4. Detect drift nightly — oci cloud-guard security-zone list-problems; alert on PROBLEM state 5. Rollback procedure — only remove zones with CISO approval; delete recipe only after all zones removed
Full scripts in references/security-zone-automation.md. Treat as MANDATORY for bulk Security Zone changes.
---
Reference Files
Load [`references/landing-zone-patterns.md`](references/landing-zone-patterns.md) when:
- Choosing compartment hierarchy pattern (workload-centric vs environment-centric vs tenant-centric)
- Designing hub-spoke network topology with DRG
- Setting up tagging strategy and cost allocation across compartments
Load [`references/landing-zone-cli.md`](references/landing-zone-cli.md) when:
- Creating compartment hierarchies via CLI
- Configuring Security Zones and Cloud Guard in bulk
- Setting up tag defaults and tag namespaces
- Creating budgets with cost tracking
Load [`references/security-zone-automation.md`](references/security-zone-automation.md) when (MANDATORY for bulk changes):
- Rolling out or updating Security Zone recipes across multiple compartments
- Remediating drift (compartment lost Security Zone enforcement)
- Writing Terraform automation for Cloud Guard recipes
Load [`references/oci-well-architected-framework.md`](references/oci-well-architected-framework.md) when:
- Starting a new landing zone design from scratch
- Preparing architectural review or compliance audit
- Comparing Core Landing Zone vs Operating Entities Landing Zone
- Need official Oracle guidance on all five pillars (Security, Reliability, Performance, Cost, Operations)
OCI CLI for Landing Zone Operations
Complete OCI CLI commands for deploying and managing landing zones.
Prerequisites
# Verify OCI CLI and authentication
oci --version
oci iam region list --output table
# Get tenancy OCID (needed for root compartment operations)
export TENANCY_ID=$(oci iam compartment list --all \
--compartment-id-in-subtree true \
--access-level ACCESSIBLE \
--include-root \
--query "data[?name=='root'].id | [0]" \
--raw-output)
echo "Tenancy ID: $TENANCY_ID"Compartment Management
Create Compartment Hierarchy
# 1. Create top-level compartments
NETWORK_CMP=$(oci iam compartment create \
--compartment-id $TENANCY_ID \
--name "Network" \
--description "Network resources and topology" \
--query 'data.id' --raw-output)
SECURITY_CMP=$(oci iam compartment create \
--compartment-id $TENANCY_ID \
--name "Security" \
--description "Security services" \
--query 'data.id' --raw-output)
WORKLOADS_CMP=$(oci iam compartment create \
--compartment-id $TENANCY_ID \
--name "Workloads" \
--description "Application workloads" \
--query 'data.id' --raw-output)
SHARED_CMP=$(oci iam compartment create \
--compartment-id $TENANCY_ID \
--name "Shared-Services" \
--description "Shared platform services" \
--query 'data.id' --raw-output)
# 2. Create Network sub-compartments
HUB_CMP=$(oci iam compartment create \
--compartment-id $NETWORK_CMP \
--name "Hub" \
--description "Hub VCN for centralized services" \
--query 'data.id' --raw-output)
SPOKES_CMP=$(oci iam compartment create \
--compartment-id $NETWORK_CMP \
--name "Spokes" \
--description "Spoke VCNs for workloads" \
--query 'data.id' --raw-output)
# 3. Create Workload compartments
APP1_CMP=$(oci iam compartment create \
--compartment-id $WORKLOADS_CMP \
--name "App1" \
--description "Application 1" \
--query 'data.id' --raw-output)
# 4. Create environment compartments under App1
APP1_DEV_CMP=$(oci iam compartment create \
--compartment-id $APP1_CMP \
--name "Dev" \
--description "Development environment" \
--query 'data.id' --raw-output)
APP1_TEST_CMP=$(oci iam compartment create \
--compartment-id $APP1_CMP \
--name "Test" \
--description "Test environment" \
--query 'data.id' --raw-output)
APP1_PROD_CMP=$(oci iam compartment create \
--compartment-id $APP1_CMP \
--name "Prod" \
--description "Production environment" \
--query 'data.id' --raw-output)List Compartment Hierarchy
# List all compartments with hierarchy
oci iam compartment list \
--compartment-id $TENANCY_ID \
--compartment-id-in-subtree true \
--access-level ACCESSIBLE \
--all \
--output table
# Get compartment OCID by name
oci iam compartment list \
--compartment-id $TENANCY_ID \
--name "Prod" \
--compartment-id-in-subtree true \
--query 'data[0].id' \
--raw-outputMove Resources Between Compartments
# Move compute instance to different compartment
oci compute instance change-compartment \
--instance-id ocid1.instance.oc1..xxx \
--compartment-id $APP1_PROD_CMP
# Move VCN to different compartment
oci network vcn change-compartment \
--vcn-id ocid1.vcn.oc1..xxx \
--compartment-id $NETWORK_CMPTag Namespace and Defaults
Create Tag Namespace
# Create organization tag namespace
TAG_NAMESPACE=$(oci iam tag-namespace create \
--compartment-id $TENANCY_ID \
--name "Organization" \
--description "Organization-wide required tags" \
--query 'data.id' --raw-output)
echo "Tag Namespace ID: $TAG_NAMESPACE"Create Tag Definitions
# CostCenter tag (mandatory)
COSTCENTER_TAG=$(oci iam tag create \
--tag-namespace-id $TAG_NAMESPACE \
--name "CostCenter" \
--description "Cost center for chargeback" \
--is-retired false \
--query 'data.id' --raw-output)
# Environment tag (mandatory, enum)
ENVIRONMENT_TAG=$(oci iam tag create \
--tag-namespace-id $TAG_NAMESPACE \
--name "Environment" \
--description "Environment type" \
--is-retired false \
--validator '{
"validatorType": "ENUM",
"values": ["Dev", "Test", "Prod", "Sandbox"]
}' \
--query 'data.id' --raw-output)
# Owner tag (mandatory)
OWNER_TAG=$(oci iam tag create \
--tag-namespace-id $TAG_NAMESPACE \
--name "Owner" \
--description "Resource owner email or team" \
--is-retired false \
--query 'data.id' --raw-output)
# DataClassification tag
DATACLASS_TAG=$(oci iam tag create \
--tag-namespace-id $TAG_NAMESPACE \
--name "DataClassification" \
--description "Data sensitivity classification" \
--is-retired false \
--validator '{
"validatorType": "ENUM",
"values": ["Public", "Internal", "Confidential", "Restricted"]
}' \
--query 'data.id' --raw-output)
# BackupPolicy tag
BACKUP_TAG=$(oci iam tag create \
--tag-namespace-id $TAG_NAMESPACE \
--name "BackupPolicy" \
--description "Backup retention policy" \
--is-retired false \
--validator '{
"validatorType": "ENUM",
"values": ["None", "Bronze", "Silver", "Gold"]
}' \
--query 'data.id' --raw-output)Set Tag Defaults (Auto-apply Tags)
# Make Environment=Prod default in Prod compartment
oci iam tag-default create \
--compartment-id $APP1_PROD_CMP \
--tag-definition-id $ENVIRONMENT_TAG \
--value "Prod"
# Make Environment=Dev default in Dev compartment
oci iam tag-default create \
--compartment-id $APP1_DEV_CMP \
--tag-definition-id $ENVIRONMENT_TAG \
--value "Dev"
# Make Owner default to creator's username
oci iam tag-default create \
--compartment-id $WORKLOADS_CMP \
--tag-definition-id $OWNER_TAG \
--value "\${iam.principal.name}"
# Make DataClassification=Internal default
oci iam tag-default create \
--compartment-id $WORKLOADS_CMP \
--tag-definition-id $DATACLASS_TAG \
--value "Internal"List Tags
# List all tag namespaces
oci iam tag-namespace list \
--compartment-id $TENANCY_ID \
--all \
--output table
# List tags in namespace
oci iam tag list \
--tag-namespace-id $TAG_NAMESPACE \
--all \
--output tableSecurity Zones
Create Security Zone Recipe
# Create CIS Foundation recipe
CIS_RECIPE=$(oci cloud-guard security-zone-recipe create \
--compartment-id $TENANCY_ID \
--display-name "CIS-Foundation-Recipe" \
--description "CIS OCI Foundations Benchmark security policies" \
--security-policies '["deny-public-ip-on-compute", "deny-public-bucket", "require-boot-volume-backup", "require-block-volume-backup"]' \
--query 'data.id' --raw-output)
# Create production-specific recipe (stricter)
PROD_RECIPE=$(oci cloud-guard security-zone-recipe create \
--compartment-id $TENANCY_ID \
--display-name "Production-Recipe" \
--description "Production security requirements" \
--security-policies '["deny-public-ip-on-compute", "deny-public-bucket", "deny-public-lb", "require-encryption-at-rest", "require-encryption-in-transit", "require-boot-volume-backup", "require-block-volume-backup", "deny-internet-gateway-in-private-subnet"]' \
--query 'data.id' --raw-output)Apply Security Zone to Compartment
# Apply production recipe to prod compartment
oci cloud-guard security-zone create \
--compartment-id $APP1_PROD_CMP \
--display-name "App1-Prod-Security-Zone" \
--description "Security zone for App1 production" \
--security-zone-recipe-id $PROD_RECIPE
# Apply CIS recipe to test compartment
oci cloud-guard security-zone create \
--compartment-id $APP1_TEST_CMP \
--display-name "App1-Test-Security-Zone" \
--description "Security zone for App1 test" \
--security-zone-recipe-id $CIS_RECIPEList Security Zones
# List all security zones
oci cloud-guard security-zone list \
--compartment-id $TENANCY_ID \
--compartment-id-in-subtree true \
--all \
--output table
# Get security zone details
oci cloud-guard security-zone get \
--security-zone-id ocid1.securityzone.oc1..xxxCloud Guard Configuration
Enable Cloud Guard
# Enable Cloud Guard for tenancy
oci cloud-guard configuration update \
--reporting-region us-ashburn-1 \
--status ENABLED \
--self-manage-resources true
# Check Cloud Guard status
oci cloud-guard configuration getCreate Cloud Guard Target
# Create target for workloads compartment
CLOUDGUARD_TARGET=$(oci cloud-guard target create \
--compartment-id $TENANCY_ID \
--display-name "Workloads-Target" \
--description "Cloud Guard monitoring for all workloads" \
--target-resource-type COMPARTMENT \
--target-resource-id $WORKLOADS_CMP \
--target-detector-recipes '[
{
"detectorRecipeId": "ocid1.cloudguarddetectorrecipe.oc1..configuration",
"detector": "IAAS_CONFIGURATION_DETECTOR"
},
{
"detectorRecipeId": "ocid1.cloudguarddetectorrecipe.oc1..activity",
"detector": "IAAS_ACTIVITY_DETECTOR"
}
]' \
--query 'data.id' --raw-output)List Cloud Guard Problems
# List all open problems
oci cloud-guard problem list \
--compartment-id $TENANCY_ID \
--compartment-id-in-subtree true \
--lifecycle-state OPEN \
--output table
# List problems by risk level
oci cloud-guard problem list \
--compartment-id $WORKLOADS_CMP \
--risk-level CRITICAL \
--output tableBudget Management
Create Budget for Compartment
# Create monthly budget for production
PROD_BUDGET=$(oci budgets budget create \
--compartment-id $TENANCY_ID \
--amount 25000 \
--reset-period MONTHLY \
--target-type COMPARTMENT \
--targets "[$APP1_PROD_CMP]" \
--display-name "App1-Prod-Monthly-Budget" \
--description "Production environment monthly budget: \$25,000" \
--query 'data.id' --raw-output)
# Create budget for dev environment (lower threshold)
DEV_BUDGET=$(oci budgets budget create \
--compartment-id $TENANCY_ID \
--amount 5000 \
--reset-period MONTHLY \
--target-type COMPARTMENT \
--targets "[$APP1_DEV_CMP]" \
--display-name "App1-Dev-Monthly-Budget" \
--description "Dev environment monthly budget: \$5,000" \
--query 'data.id' --raw-output)
# Create budget for tags (cost center-based)
oci budgets budget create \
--compartment-id $TENANCY_ID \
--amount 50000 \
--reset-period MONTHLY \
--target-type TAG \
--targets '["Organization.CostCenter=Engineering"]' \
--display-name "Engineering-CostCenter-Budget" \
--description "Engineering cost center budget: \$50,000"Create Budget Alert Rules
# Alert at 50% threshold
oci budgets alert-rule create \
--budget-id $PROD_BUDGET \
--type ACTUAL \
--threshold 50 \
--threshold-type PERCENTAGE \
--display-name "Prod-50%-Warning" \
--message "Production budget at 50% (\$12,500)" \
--recipients "sre-team@example.com"
# Alert at 80% threshold
oci budgets alert-rule create \
--budget-id $PROD_BUDGET \
--type ACTUAL \
--threshold 80 \
--threshold-type PERCENTAGE \
--display-name "Prod-80%-Critical" \
--message "Production budget at 80% (\$20,000) - CRITICAL" \
--recipients "sre-team@example.com,cfo@example.com"
# Alert at 100% threshold
oci budgets alert-rule create \
--budget-id $PROD_BUDGET \
--type ACTUAL \
--threshold 100 \
--threshold-type PERCENTAGE \
--display-name "Prod-100%-Exceeded" \
--message "Production budget EXCEEDED (\$25,000)" \
--recipients "sre-team@example.com,cfo@example.com,ceo@example.com"
# Forecast alert (predict 100% in current month)
oci budgets alert-rule create \
--budget-id $PROD_BUDGET \
--type FORECAST \
--threshold 100 \
--threshold-type PERCENTAGE \
--display-name "Prod-Forecast-100%" \
--message "Production forecasted to exceed budget this month" \
--recipients "sre-team@example.com"List Budgets
# List all budgets
oci budgets budget list \
--compartment-id $TENANCY_ID \
--target-type COMPARTMENT \
--output table
# Get budget utilization
oci budgets budget get \
--budget-id $PROD_BUDGETHub-Spoke Network Topology
Create Hub VCN
# Create Hub VCN in Hub compartment
HUB_VCN=$(oci network vcn create \
--compartment-id $HUB_CMP \
--display-name "Hub-VCN" \
--cidr-blocks '["10.0.0.0/16"]' \
--dns-label "hub" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
# Create Hub subnets
HUB_PUBLIC_SUBNET=$(oci network subnet create \
--compartment-id $HUB_CMP \
--vcn-id $HUB_VCN \
--display-name "Hub-Public-Subnet" \
--cidr-block "10.0.1.0/24" \
--prohibit-public-ip-on-vnic false \
--dns-label "hubpub" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
HUB_PRIVATE_SUBNET=$(oci network subnet create \
--compartment-id $HUB_CMP \
--vcn-id $HUB_VCN \
--display-name "Hub-Private-Subnet" \
--cidr-block "10.0.2.0/24" \
--prohibit-public-ip-on-vnic true \
--dns-label "hubpriv" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)Create DRG (Dynamic Routing Gateway)
# Create DRG for hub-spoke connectivity
DRG=$(oci network drg create \
--compartment-id $NETWORK_CMP \
--display-name "Hub-Spoke-DRG" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
# Attach Hub VCN to DRG
HUB_DRG_ATTACHMENT=$(oci network drg-attachment create \
--drg-id $DRG \
--display-name "Hub-VCN-Attachment" \
--vcn-id $HUB_VCN \
--wait-for-state ATTACHED \
--query 'data.id' --raw-output)Create Spoke VCNs
# Create Spoke VCN for App1 Prod
SPOKE1_VCN=$(oci network vcn create \
--compartment-id $SPOKES_CMP \
--display-name "Spoke-App1-Prod-VCN" \
--cidr-blocks '["10.10.0.0/16"]' \
--dns-label "app1prod" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
# Attach Spoke1 to DRG
SPOKE1_DRG_ATTACHMENT=$(oci network drg-attachment create \
--drg-id $DRG \
--display-name "Spoke-App1-Prod-Attachment" \
--vcn-id $SPOKE1_VCN \
--wait-for-state ATTACHED \
--query 'data.id' --raw-output)
# Create Spoke VCN for App1 Dev
SPOKE2_VCN=$(oci network vcn create \
--compartment-id $SPOKES_CMP \
--display-name "Spoke-App1-Dev-VCN" \
--cidr-blocks '["10.11.0.0/16"]' \
--dns-label "app1dev" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
# Attach Spoke2 to DRG
SPOKE2_DRG_ATTACHMENT=$(oci network drg-attachment create \
--drg-id $DRG \
--display-name "Spoke-App1-Dev-Attachment" \
--vcn-id $SPOKE2_VCN \
--wait-for-state ATTACHED \
--query 'data.id' --raw-output)Configure Hub NAT Gateway (Shared Egress)
# Create NAT Gateway in Hub VCN
HUB_NAT=$(oci network nat-gateway create \
--compartment-id $HUB_CMP \
--vcn-id $HUB_VCN \
--display-name "Hub-NAT-Gateway" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
# Create Service Gateway in Hub VCN (free egress to OCI services)
HUB_SGW=$(oci network service-gateway create \
--compartment-id $HUB_CMP \
--vcn-id $HUB_VCN \
--services '[{"serviceId": "ocid1.service.oc1.iad.xxx"}]' \
--display-name "Hub-Service-Gateway" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
# Get default route table for Hub VCN
HUB_RT=$(oci network vcn get \
--vcn-id $HUB_VCN \
--query 'data["default-route-table-id"]' \
--raw-output)
# Add route to NAT Gateway for internet egress
oci network route-table update \
--rt-id $HUB_RT \
--route-rules '[
{
"destination": "0.0.0.0/0",
"destinationType": "CIDR_BLOCK",
"networkEntityId": "'$HUB_NAT'"
},
{
"destination": "all-iad-services-in-oracle-services-network",
"destinationType": "SERVICE_CIDR_BLOCK",
"networkEntityId": "'$HUB_SGW'"
}
]' \
--forceConfigure DRG Route Tables (Spoke-to-Hub Routing)
# Get DRG route table ID
DRG_RT=$(oci network drg list-drg-route-tables \
--drg-id $DRG \
--query 'data[0].id' \
--raw-output)
# Add route distribution to allow spokes to reach hub
oci network drg-route-distribution create \
--drg-id $DRG \
--distribution-type IMPORT \
--display-name "Import-All-VCN-Routes"Resource Manager Stacks
Upload Landing Zone Terraform Configuration
# Create ZIP file with Terraform configs
cd landing-zone-terraform/
zip -r ../landing-zone.zip ./*
cd ..
# Create Resource Manager stack
STACK=$(oci resource-manager stack create \
--compartment-id $TENANCY_ID \
--display-name "OCI-Landing-Zone-Stack" \
--description "Complete landing zone deployment" \
--config-source-type ZIP_UPLOAD \
--zip-file-base64 "$(base64 landing-zone.zip)" \
--variables '{
"tenancy_ocid": "'$TENANCY_ID'",
"region": "us-ashburn-1",
"compartment_hierarchy": true,
"security_zones_enabled": true,
"hub_spoke_topology": true
}' \
--wait-for-state SUCCEEDED \
--query 'data.id' --raw-output)
# Plan the stack
PLAN_JOB=$(oci resource-manager job create-plan-job \
--stack-id $STACK \
--wait-for-state SUCCEEDED \
--query 'data.id' --raw-output)
# Apply the stack
APPLY_JOB=$(oci resource-manager job create-apply-job \
--stack-id $STACK \
--execution-plan-strategy AUTO_APPROVED \
--wait-for-state SUCCEEDED \
--query 'data.id' --raw-output)
# Get outputs
oci resource-manager stack get-stack-tf-state \
--stack-id $STACK \
--file stack-outputs.tfstateMulti-Region Setup
Create DR Region Landing Zone
# Set DR region
export OCI_CLI_REGION=us-phoenix-1
# Create same compartment hierarchy in DR region
# (Compartments are global, but resources are regional)
# Create DR Hub VCN
DR_HUB_VCN=$(oci network vcn create \
--compartment-id $HUB_CMP \
--display-name "Hub-VCN-DR" \
--cidr-blocks '["10.100.0.0/16"]' \
--dns-label "hubdr" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
# Create DR DRG
DR_DRG=$(oci network drg create \
--compartment-id $NETWORK_CMP \
--display-name "Hub-Spoke-DRG-DR" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
# Create Remote Peering Connection (primary to DR)
export OCI_CLI_REGION=us-ashburn-1
PRIMARY_RPC=$(oci network remote-peering-connection create \
--compartment-id $NETWORK_CMP \
--drg-id $DRG \
--display-name "Primary-to-DR-RPC" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
export OCI_CLI_REGION=us-phoenix-1
DR_RPC=$(oci network remote-peering-connection create \
--compartment-id $NETWORK_CMP \
--drg-id $DR_DRG \
--display-name "DR-to-Primary-RPC" \
--wait-for-state AVAILABLE \
--query 'data.id' --raw-output)
# Connect the peering
oci network remote-peering-connection connect \
--remote-peering-connection-id $DR_RPC \
--peer-id $PRIMARY_RPC \
--peer-region-name us-ashburn-1Validation and Reporting
List All Landing Zone Resources
# List compartments
oci iam compartment list \
--compartment-id $TENANCY_ID \
--compartment-id-in-subtree true \
--all \
--output table
# List VCNs across all compartments
oci network vcn list \
--compartment-id $TENANCY_ID \
--all \
--output table
# List Security Zones
oci cloud-guard security-zone list \
--compartment-id $TENANCY_ID \
--compartment-id-in-subtree true \
--all \
--output table
# List Budgets
oci budgets budget list \
--compartment-id $TENANCY_ID \
--output tableGenerate Cost Report by Compartment
# Get usage data for compartment
oci usage-api usage summarized-usage get \
--tenant-id $TENANCY_ID \
--time-usage-started "2026-01-01T00:00:00Z" \
--time-usage-ended "2026-01-31T23:59:59Z" \
--granularity MONTHLY \
--query-type COST \
--group-by "[\"compartmentPath\"]" \
--output json | jq '.data.items[] | {
compartment: .tags["Oracle-Tags"]["CreatedBy"],
cost: .["computed-amount"]
}'Best Practices
Always Use --wait-for-state
# ✅ GOOD - waits for compartment to be active
oci iam compartment create \
--compartment-id $TENANCY_ID \
--name "Prod" \
--wait-for-state ACTIVE
# ❌ BAD - returns immediately, compartment may not be ready
oci iam compartment create \
--compartment-id $TENANCY_ID \
--name "Prod"Use Environment Variables for OCIDs
# ✅ GOOD - reusable, maintainable
PROD_CMP=$(oci iam compartment create ... --query 'data.id' --raw-output)
oci network vcn create --compartment-id $PROD_CMP
# ❌ BAD - error-prone
oci network vcn create --compartment-id ocid1.compartment.oc1..xxxDocument CIDR Allocations
# Maintain CIDR allocation table
cat > cidr-allocation.txt <<EOF
Hub VCN: 10.0.0.0/16
Spoke-App1-Prod: 10.10.0.0/16
Spoke-App1-Test: 10.20.0.0/16
Spoke-App1-Dev: 10.30.0.0/16
Spoke-App2-Prod: 10.40.0.0/16
On-premises: 172.16.0.0/12
Reserved-Future: 10.50.0.0/16 - 10.99.0.0/16
EOF
# Check for overlaps before creating VCN
grep "10.10.0.0" cidr-allocation.txtWhen to Use Landing Zone CLI
Use these commands when you need to:
- Set up initial OCI tenancy structure
- Create compartment hierarchies
- Implement Security Zones and Cloud Guard
- Configure tagging strategy
- Deploy hub-spoke network topology
- Create budgets and cost controls
- Implement multi-region DR
Don't use for:
- Individual resource creation (covered in service-specific skills)
- Day-to-day operations (use service-specific CLIs)
- Troubleshooting (covered in other skills)
OCI Landing Zone Patterns Reference
Landing Zone Topology Patterns
Pattern 1: Hub-Spoke Topology (Recommended for Multi-Tenancy)
┌─────────────────────────┐
│ Hub VCN (10.0.0.0/16) │
│ │
│ - Network Firewall │
│ - NAT Gateway │
│ - Service Gateway │
│ - DRG (on-prem) │
└────────────┬────────────┘
│
DRG
┌────────────┼────────────┐
│ │ │
┌───────────▼──┐ ┌──────▼─────┐ ┌──▼───────────┐
│ Spoke 1 VCN │ │ Spoke 2 VCN│ │ Spoke 3 VCN │
│ App1-Prod │ │ App2-Prod │ │ Shared-Svcs │
│ 10.10.0.0/16 │ │ 10.20.0.0/16│ │ 10.30.0.0/16 │
└──────────────┘ └────────────┘ └──────────────┘
Benefits:
- Centralized egress control (cost + security)
- Spoke isolation (network segmentation)
- Shared services (DNS, monitoring, bastion)
- Transitive routing via DRG
Cost savings: $3,000-5,000/month via single NAT Gateway vs per-VCNPattern 2: Multi-Compartment Hierarchy
Tenancy (Root)
│
├─ Network [Network admins only]
│ ├─ Hub
│ └─ Spokes
│
├─ Security [Security team only]
│ ├─ Vault (keys, secrets)
│ ├─ Bastion
│ └─ Logging (audit logs, flow logs)
│
├─ Workloads [Application teams]
│ ├─ App1
│ │ ├─ Dev [Developers full access]
│ │ ├─ Test [QA full access]
│ │ └─ Prod [SRE read, operators limited write]
│ │
│ └─ App2
│ ├─ Dev
│ ├─ Test
│ └─ Prod
│
├─ Shared-Services [Platform team]
│ ├─ Identity (IDCS, federation)
│ ├─ Monitoring (APM, Logging Analytics)
│ └─ DevOps (CI/CD, artifact registry)
│
└─ Sandbox [Developers experiment, auto-delete after 30 days]
├─ User1-Sandbox
└─ User2-Sandbox
Policy inheritance:
- Network policies apply to Hub + Spokes
- Workload policies apply to all App environments
- Sandbox policies enforce auto-cleanupPattern 3: Security Zones & Cloud Guard Integration
Compartment: Prod
│
├─ Security Zone Recipe: CIS-Level-1
│ ├─ deny-public-ip-on-compute
│ ├─ deny-public-bucket
│ ├─ require-encryption-at-rest
│ ├─ require-encryption-in-transit
│ └─ deny-internet-gateway-in-private-subnet
│
├─ Cloud Guard Target
│ ├─ Detector: Configuration issues
│ ├─ Detector: Activity anomalies
│ └─ Responder: Auto-remediate violations
│
└─ Resources
├─ Compute: Public IP blocked ✓
├─ Object Storage: Private only ✓
├─ ADB: TDE enabled ✓
└─ Load Balancer: SSL enforced ✓
Result: Security violations prevented at creation time, not detected afterCompartment Design Decision Tree
"How should I structure compartments?"
│
├─ Single application, simple lifecycle?
│ └─ Pattern: Workload-centric
│ Workloads/
│ └─ MyApp/
│ ├─ Dev
│ ├─ Test
│ └─ Prod
│
├─ Multiple applications, shared platform?
│ └─ Pattern: Environment-centric
│ Workloads/
│ ├─ Dev/
│ │ ├─ App1
│ │ └─ App2
│ ├─ Test/
│ │ ├─ App1
│ │ └─ App2
│ └─ Prod/
│ ├─ App1
│ └─ App2
│
├─ Multi-tenant SaaS (customers isolated)?
│ └─ Pattern: Tenant-centric
│ Tenants/
│ ├─ Customer-A/
│ │ ├─ Network
│ │ ├─ Compute
│ │ └─ Database
│ └─ Customer-B/
│ ├─ Network
│ ├─ Compute
│ └─ Database
│
└─ Large enterprise, multiple business units?
└─ Pattern: Business-unit-centric
BusinessUnits/
├─ BU-Engineering/
│ └─ [Workload-centric per BU]
├─ BU-Marketing/
│ └─ [Workload-centric per BU]
└─ BU-Sales/
└─ [Workload-centric per BU]
Key principle: Choose hierarchy that matches org structure + cost allocationNetwork Topology Decision Tree
"Which network pattern should I use?"
│
├─ Single application, no shared services?
│ └─ Single VCN
│ Cost: Lowest
│ Complexity: Simplest
│ Use when: Proof of concept, single app
│
├─ Multiple apps, need isolation, shared egress?
│ └─ Hub-Spoke via DRG
│ Cost: $100/month DRG + $45/month NAT (shared)
│ Complexity: Medium
│ Egress savings: $3,000-5,000/month
│ Use when: Multi-app production
│
├─ Multi-region disaster recovery?
│ └─ Hub-Spoke + DRG Remote Peering
│ Primary Region: Hub-Spoke
│ DR Region: Hub-Spoke
│ Cost: +$100/month DRG per region
│ Use when: RTO < 1 hour required
│
└─ On-premises integration?
└─ Hub-Spoke + FastConnect
Hub VCN: FastConnect → On-prem
Spokes: Route via hub
Cost: $500-2,000/month FastConnect
Use when: Hybrid cloud architectureTagging Strategy
Required Tags (Mandatory)
Tag Namespace: Organization
Tags:
- CostCenter: [Finance code for chargeback]
Type: String
Mandatory: Yes
Default: None
- Environment: [Dev | Test | Prod | Sandbox]
Type: Enum
Mandatory: Yes
Default: None
- Owner: [Email or team name]
Type: String
Mandatory: Yes
Default: ${iam.principal.name}
- DataClassification: [Public | Internal | Confidential | Restricted]
Type: Enum
Mandatory: Yes (for data resources)
Default: Internal
- BackupPolicy: [None | Bronze | Silver | Gold]
Type: Enum
Mandatory: Yes (for stateful resources)
Default: BronzeOptional Tags (Recommended)
- Project: [Project or product name]
- ExpiryDate: [Auto-cleanup date for sandbox]
- Compliance: [PCI | HIPAA | SOC2]
- ManagedBy: [Terraform | Manual | Ansible]Cost Allocation Patterns
Budget Hierarchy
Tenancy Budget: $100,000/month
├─ Network: $10,000/month (fixed)
├─ Security: $5,000/month (fixed)
├─ Workloads: $75,000/month
│ ├─ App1-Dev: $5,000/month
│ ├─ App1-Test: $8,000/month
│ ├─ App1-Prod: $25,000/month
│ ├─ App2-Dev: $3,000/month
│ ├─ App2-Test: $4,000/month
│ └─ App2-Prod: $30,000/month
└─ Shared-Services: $10,000/month
Alerts:
- 50% threshold: Warning
- 80% threshold: Critical (page on-call)
- 100% threshold: Auto-stop dev/test resourcesSecurity Zone Automation Runbook
Use this playbook when rolling out Security Zones, recipes, and monitoring at scale across compartments/environments. All commands assume OCI CLI.
1. Define Security Policies (Recipe)
RECIPE_NAME="CIS-Prod-Recipe"
OCI_REGION="us-ashburn-1"
oci cloud-guard security-policy list --all \
--query 'data[].{"name":"display-name","id":"id"}' --output table
# capture policy OCIDs you want to enforce
# Example filters for specific display names
POLICY_IDS_JSON=$(oci cloud-guard security-policy list --all \
--query 'data[?"display-name"==`deny-public-ip` || "display-name"==`deny-public-bucket` || "display-name"==`require-encryption`].id')
oci cloud-guard security-zone-recipe create \
--compartment-id "$TENANCY_OCID" \
--display-name "$RECIPE_NAME" \
--security-policies "$POLICY_IDS_JSON"
RECIPE_ID=$(oci cloud-guard security-zone-recipe list --compartment-id "$TENANCY_OCID" \
--display-name "$RECIPE_NAME" --query 'data[0].id' --raw-output)2. Apply Recipe to Compartments
for COMPARTMENT in $(jq -r '.compartments[].id' compartments.json); do
oci cloud-guard security-zone create \
--compartment-id "$COMPARTMENT" \
--display-name "$(oci iam compartment get --compartment-id "$COMPARTMENT" --query 'data."name"' --raw-output)-SZ" \
--security-zone-recipe-id "$RECIPE_ID" \
--wait-for-state ACTIVE
doneTip: Generate compartments.json via oci iam compartment list --all --compartment-id $TENANCY_OCID and filter by tag (e.g., Environment=Prod).
3. Automate with Terraform
resource "oci_cloud_guard_security_zone" "prod" {
compartment_id = oci_identity_compartment.prod.id
display_name = "${var.compartment_name}-security-zone"
security_zone_recipe_id = oci_cloud_guard_security_zone_recipe.prod.id
}
data "oci_cloud_guard_security_policies" "all" {
compartment_id = var.tenancy_ocid
}
locals {
required_policy_names = ["deny-public-ip", "deny-public-bucket", "require-cmk-encryption"]
}
resource "oci_cloud_guard_security_zone_recipe" "prod" {
compartment_id = var.tenancy_ocid
display_name = "CIS-Prod"
security_policies = [
for policy in data.oci_cloud_guard_security_policies.all.security_policies : policy.id
if contains(local.required_policy_names, policy.display_name)
]
}Apply after any manual change so state remains accurate.
4. Verification Commands
oci cloud-guard security-zone get --security-zone-id $ZONE_ID --query 'data."lifecycle-state"'
oci cloud-guard security-zone list --compartment-id $TENANCY_OCID --all --output table
oci cloud-guard security-zone list-problems --security-zone-id $ZONE_ID --all --output tableAlert SRE if any compartment re-enters PROBLEM state after remediation.
5. Rollback / Removal
oci cloud-guard security-zone delete --security-zone-id $ZONE_ID --force
oci cloud-guard security-zone-recipe delete --security-zone-recipe-id $RECIPE_ID --forceOnly remove zones with compliance approval. Document reason in incident ticket.