Home / Learning

LearningLearningBeginner

Learning: OCI foundations for beginners - compartments, IAM policies, a VCN, a compute instance, block storage and Object Storage with the OCI CLI

A hands-on learning session. Set up the OCI CLI, create a compartment and an IAM policy, build a VCN with an internet gateway and a public subnet, launch a Linux instance and SSH to it, attach and mount a block volume, store a file in Object Storage and share it with a pre-authenticated request, then clean everything up. Exercises and a knowledge check included.

Deepak Bhardwaj11 min read

Applies toOracle Cloud Infrastructure (any region; Free Tier works with Always Free shapes), OCI CLI 3.x on Linux, macOS or Windows, Oracle Linux 9 images

About this session

Session Details
Duration 2–3 hours, including exercises
You will build A lab compartment with its own VCN, public subnet, one Linux VM, a 50 GB block volume mounted at /data, and an Object Storage bucket with a shared file
You will learn Tenancy and compartment structure, OCIDs, IAM policy syntax, VCN building blocks (gateways, route tables, security lists), compute shapes, paravirtualized block volumes, Object Storage namespaces and pre-authenticated requests, safe clean-up order
Prerequisites An OCI account where you are an administrator (a Free Tier account is fine), basic Linux shell skills, an SSH key pair

Note

Everything here fits in Always Free limits if you choose an Always Free shape (VM.Standard.A1.Flex or VM.Standard.E2.1.Micro) and keep the block volume within the free allowance. Check the current Always Free limits for your region and always run the clean-up module.

Module 1: the ideas you need first

Term What it means
Tenancy Your OCI account. The top-level (root) compartment
Compartment A folder for resources. Policies, quotas and cost reports work per compartment
OCID Every resource's unique ID, e.g. ocid1.instance.oc1.eu-frankfurt-1.aaaa...
Region / availability domain (AD) A region is a geographic area; ADs are isolated data centres inside some regions
VCN Virtual Cloud Network, your private network in a region
Policy Text rules that say which group may do what in which compartment

A golden rule: don't build things in the root compartment. Make a compartment per project or environment.

Module 2: install and configure the CLI

Install (Linux/macOS):

bash -c "$(curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh)"
oci --version

On Windows, use the PowerShell installer from the OCI CLI documentation, or pip install oci-cli in a Python virtual environment.

Configure it:

oci setup config

It asks for your user OCID and tenancy OCID (Console: profile menu > My profile and Tenancy), the region, and creates an API key pair. Upload the public key it prints under My profile > API keys.

Test:

oci iam region list --output table
export TENANCY=$(grep -m1 '^tenancy' ~/.oci/config | cut -d= -f2 | tr -d ' ')
oci iam tenancy get --tenancy-id $TENANCY --query 'data.name' --raw-output

Module 3: compartment and policy

Create a compartment:

oci iam compartment create --compartment-id $TENANCY \
  --name lab-learning --description "Learning session lab" --wait-for-state ACTIVE

export C=$(oci iam compartment list --compartment-id $TENANCY --name lab-learning \
  --query 'data[0].id' --raw-output)
echo $C

Now a group and a policy that lets that group manage everything only in this compartment. In tenancies with identity domains you can create the group in the Console (Identity & Security > Domains > Default > Groups); the CLI works for the Default domain:

oci iam group create --name lab-operators --description "Lab operators"

oci iam policy create --compartment-id $TENANCY --name lab-operators-policy \
  --description "Lab operators manage the lab compartment" \
  --statements '["Allow group lab-operators to manage all-resources in compartment lab-learning"]'

Read a policy statement left to right:

Allow group <group> to <verb> <resource-type> in compartment <name> [where <conditions>]
         verbs: inspect < read < use < manage

With identity domains other than Default, the group is written as '<domain>'/'<group>'. See the IAM policy cheat sheet for more patterns.

Module 4: build the network

# VCN
export VCN=$(oci network vcn create --compartment-id $C --display-name lab-vcn \
  --cidr-blocks '["10.0.0.0/16"]' --dns-label labvcn \
  --query 'data.id' --raw-output --wait-for-state AVAILABLE)

# Internet gateway
export IGW=$(oci network internet-gateway create --compartment-id $C --vcn-id $VCN \
  --is-enabled true --display-name lab-igw --query 'data.id' --raw-output --wait-for-state AVAILABLE)

# Send internet-bound traffic from the default route table to the gateway
export RT=$(oci network vcn get --vcn-id $VCN --query 'data."default-route-table-id"' --raw-output)
oci network route-table update --rt-id $RT --force \
  --route-rules "[{\"destination\":\"0.0.0.0/0\",\"destinationType\":\"CIDR_BLOCK\",\"networkEntityId\":\"$IGW\"}]"

# Public subnet (uses the default route table and default security list)
export SUBNET=$(oci network subnet create --compartment-id $C --vcn-id $VCN \
  --display-name public-subnet --cidr-block 10.0.1.0/24 --dns-label pub \
  --query 'data.id' --raw-output --wait-for-state AVAILABLE)

What you just built:

 Internet
    │
 [Internet gateway]
    │   route 0.0.0.0/0 → IGW  (default route table)
 ┌──┴───────────── VCN 10.0.0.0/16 ─────────────┐
 │  public-subnet 10.0.1.0/24                   │
 │  default security list: SSH 22 in, all out   │
 └──────────────────────────────────────────────┘

Look at the default security list. It allows SSH from anywhere, which is fine for a lab and not for production:

oci network security-list list --compartment-id $C --vcn-id $VCN \
  --query 'data[0]."ingress-security-rules"[].{src:source,proto:protocol,ports:"tcp-options"."destination-port-range"}'

The VCN gateways and routing guide explains NAT and service gateways for private subnets.

Module 5: launch a compute instance

export AD=$(oci iam availability-domain list --compartment-id $C --query 'data[0].name' --raw-output)

export SHAPE=VM.Standard.A1.Flex      # Always Free Arm; use VM.Standard.E4.Flex for x86
export IMG=$(oci compute image list --compartment-id $C \
  --operating-system "Oracle Linux" --operating-system-version "9" --shape $SHAPE \
  --sort-by TIMECREATED --sort-order DESC --limit 1 --query 'data[0].id' --raw-output)

export VM=$(oci compute instance launch --compartment-id $C --availability-domain $AD \
  --display-name lab-vm --shape $SHAPE --shape-config '{"ocpus":1,"memoryInGBs":6}' \
  --image-id $IMG --subnet-id $SUBNET --assign-public-ip true \
  --ssh-authorized-keys-file ~/.ssh/id_rsa.pub \
  --query 'data.id' --raw-output --wait-for-state RUNNING)

export IP=$(oci compute instance list-vnics --instance-id $VM --query 'data[0]."public-ip"' --raw-output)
ssh opc@$IP

If launch fails with "Out of host capacity" on A1, try another AD or try again later; this is common for free Arm capacity.

On the VM, look around:

cat /etc/os-release | head -2
curl -s -H "Authorization: Bearer Oracle" http://169.254.169.254/opc/v2/instance/ | head -20

The second command reads the instance metadata service (v2), which is how scripts on the VM learn about themselves.

Module 6: add a block volume

Back on your workstation:

export VOL=$(oci bv volume create --compartment-id $C --availability-domain $AD \
  --display-name lab-data --size-in-gbs 50 --query 'data.id' --raw-output --wait-for-state AVAILABLE)

oci compute volume-attachment attach --type paravirtualized \
  --instance-id $VM --volume-id $VOL --wait-for-state ATTACHED

Paravirtualized attachments appear as a new disk with no iSCSI commands needed. On the VM:

lsblk                                  # new 50G disk, usually /dev/sdb
sudo mkfs.xfs /dev/sdb
sudo mkdir /data
UUID=$(sudo blkid -s UUID -o value /dev/sdb)
echo "UUID=$UUID /data xfs defaults,_netdev,nofail 0 2" | sudo tee -a /etc/fstab
sudo mount -a && df -h /data

Tip

Always mount by UUID with _netdev,nofail. Device names can change between reboots, and nofail stops a missing volume from leaving the instance unbootable. The post on block volumes missing after reboot shows what happens otherwise.

Reboot (sudo reboot), reconnect and check /data is still mounted.

Module 7: Object Storage

export NS=$(oci os ns get --query data --raw-output)
oci os bucket create --compartment-id $C --name lab-bucket

echo "Hello from the OCI learning lab" > notes.txt
oci os object put --bucket-name lab-bucket --file notes.txt
oci os object list --bucket-name lab-bucket --output table

The bucket is private. To share one file for a limited time, create a pre-authenticated request (PAR):

EXPIRES=$(date -u -d '+1 day' +%Y-%m-%dT%H:%M:%SZ)   # macOS: date -u -v+1d +%Y-%m-%dT%H:%M:%SZ
PAR=$(oci os preauth-request create --bucket-name lab-bucket --name share-notes \
  --access-type ObjectRead --object-name notes.txt --time-expires $EXPIRES \
  --query 'data."access-uri"' --raw-output)

REGION=$(oci iam region-subscription list --query 'data[?"is-home-region"]."region-name" | [0]' --raw-output)
curl "https://objectstorage.$REGION.oraclecloud.com$PAR"

(If your lab region isn't your home region, use the lab region name instead.) Anyone with that URL can read the file until it expires, so treat PAR URLs like passwords.

Module 8: clean up (in the right order)

Resources that depend on others must go first.

# 1. Object Storage: PARs, objects, bucket
for p in $(oci os preauth-request list --bucket-name lab-bucket --query 'data[].id' --raw-output | tr -d '[]",'); do
  oci os preauth-request delete --bucket-name lab-bucket --par-id $p --force; done
oci os object bulk-delete --bucket-name lab-bucket --force
oci os bucket delete --bucket-name lab-bucket --force

# 2. Detach and delete the volume, terminate the instance (with its boot volume)
ATT=$(oci compute volume-attachment list --compartment-id $C --instance-id $VM --query 'data[0].id' --raw-output)
oci compute volume-attachment detach --volume-attachment-id $ATT --force --wait-for-state DETACHED
oci bv volume delete --volume-id $VOL --force
oci compute instance terminate --instance-id $VM --preserve-boot-volume false --force --wait-for-state TERMINATED

# 3. Network: subnet, route rule, gateway, VCN
oci network subnet delete --subnet-id $SUBNET --force --wait-for-state TERMINATED
oci network route-table update --rt-id $RT --route-rules '[]' --force
oci network internet-gateway delete --ig-id $IGW --force --wait-for-state TERMINATED
oci network vcn delete --vcn-id $VCN --force --wait-for-state TERMINATED

# 4. Policy, group and compartment
oci iam policy delete --policy-id $(oci iam policy list --compartment-id $TENANCY --name lab-operators-policy --query 'data[0].id' --raw-output) --force
oci iam group delete --group-id $(oci iam group list --name lab-operators --query 'data[0].id' --raw-output) --force
oci iam compartment delete --compartment-id $C --force

Compartment deletion runs in the background and fails if anything is left inside. Check for leftovers with Governance > Tenancy Explorer in the Console.

Exercises

  1. Change the default security list so SSH is allowed only from your own public IP. Prove that SSH from another network (e.g. a phone hotspot) now times out.
  2. Create a second, private subnet with no public IPs and a NAT gateway route. Launch a small instance there and reach it through the OCI Bastion service.
  3. Write a policy that lets a group read all resources in lab-learning but manage only buckets. Test it with a user in that group.
  4. Create a block volume backup with oci bv backup create, delete a file on /data, then restore the backup to a new volume and recover the file.
  5. Add a lifecycle rule to lab-bucket that deletes objects after 1 day (see the lifecycle policies post). Why does the rule need a policy for the Object Storage service?

Knowledge check

  1. Why should you avoid creating resources in the root compartment?
  2. Which three things did the instance need to be reachable from the internet?
  3. What do the verbs inspect, read, use and manage mean, in order?
  4. Why mount a block volume by UUID with nofail?
  5. What is an Object Storage namespace?
  6. What is a pre-authenticated request, and what is the main risk?
Answers
  1. Compartments give you isolation for policies, quotas, budgets and clean-up. Resources in root are visible to anyone with tenancy-wide rights and are hard to separate later.
  2. A public IP on the VNIC, a route to an internet gateway in the subnet's route table, and a security list or NSG rule allowing SSH in.
  3. inspect lists resources; read also shows details; use lets you work with existing resources; manage includes create and delete.
  4. Device names like /dev/sdb can change, and without nofail a missing volume can stop the instance booting.
  5. A unique, tenancy-wide name that is the top level of Object Storage; bucket names are unique within it.
  6. A URL that grants access to a bucket or object without signing in. Anyone holding the URL has that access until it expires or is deleted.

Summary

  • Organise everything in compartments and grant access with readable policy statements.
  • A VCN needs gateways, route rules and security rules before anything can talk to the internet.
  • Compute shapes, images and ADs are chosen at launch; metadata v2 tells the VM about itself.
  • Block volumes attach as disks; mount by UUID with _netdev,nofail.
  • Object Storage buckets are private by default; PARs share them for a limited time.
  • Clean up in dependency order: storage, compute, network, IAM, compartment.

Further reading