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.FlexorVM.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, andnofailstops 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
- 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.
- 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.
- Write a policy that lets a group read all resources in
lab-learningbut manage only buckets. Test it with a user in that group. - 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. - Add a lifecycle rule to
lab-bucketthat deletes objects after 1 day (see the lifecycle policies post). Why does the rule need a policy for the Object Storage service?
Knowledge check
- Why should you avoid creating resources in the root compartment?
- Which three things did the instance need to be reachable from the internet?
- What do the verbs
inspect,read,useandmanagemean, in order? - Why mount a block volume by UUID with
nofail? - What is an Object Storage namespace?
- What is a pre-authenticated request, and what is the main risk?
Answers
- 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.
- 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.
inspectlists resources;readalso shows details;uselets you work with existing resources;manageincludes create and delete.- Device names like
/dev/sdbcan change, and withoutnofaila missing volume can stop the instance booting. - A unique, tenancy-wide name that is the top level of Object Storage; bucket names are unique within it.
- 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
- OCI documentation: Getting Started, Managing Compartments, How Policies Work, Networking Overview, Overview of Block Volume, Object Storage Overview.
- On this site: OCI Logging Search cheat sheet, IAM policy cheat sheet, VCN gateways and routing.