OCI Forgotten Resource Detective: Sniff Out Orphaned OCI Resources Like a FinOps Bloodhound

Hunt down forgotten, wasteful OCI resources in your tenancy with this Python-powered detective—no apron needed.

Spread the FinOps flavor!

Overview

The OCI Forgotten Resource Detective is a Python script that sniffs out those dusty, unused resources lurking in your OCI kitchen and quietly padding your bill. It’s your FinOps bloodhound—pointing out the wasteful ingredients you can toss to slim down costs and keep your cloud pantry lean.

Forget just scratching the surface—this script dives deep into your OCI pantry to sniff out the real leftovers: orphaned block volumes, floating public IPs, empty NSGs, and more. It then whips up both a detailed CSV “shopping list” and a colorful HTML report (complete with cost estimates) so you know exactly which waste to toss first.

Download the Script

Grab the OCI Forgotten Resource Detective script and start trimming your cloud pantry of waste today.

Download Script

Key Features

Comprehensive Scanning

Dives into every nook and cranny—root compartment and all—so no orphaned resource escapes your FinOps inspection.

Cost Estimation

Serves up monthly cost estimates for each resource so you know which leftovers to toss first.

Risk Assessment

Sorts findings into High, Medium, and Low risk tiers—so you can tackle the spiciest budget burners first.

Dual Reporting

Whips up a CSV “shopping list” and a stylish HTML menu for easy review and sharing with your FinOps crew.

Resource Types Detected

  • Orphaned Block Volumes: Volumes with no attachments that continue to incur storage costs
  • Unattached Public IPs: Reserved public IP addresses not attached to any resource
  • Empty Network Security Groups: NSGs with no VNICs attached, creating unnecessary complexity
  • Load Balancers with No Backends: Load balancers with backend sets but no actual backends
  • Old-Generation Compute Instances: Instances using outdated shapes that could be migrated to more efficient options
  • Untagged Resources: Resources with no tags, making cost allocation and tracking difficult
  • Suspiciously Named Resources: Resources with names suggesting temporary or test usage (test, temp, demo, etc.)
Cost Optimization Potential

By tossing out those forgotten leftovers in your OCI pantry, you’ll carve off juicy savings from your monthly cloud bill—like trimming the fat for a leaner, tastier outcome.

How It Works

This FinOps bloodhound uses the OCI Python SDK to patrol your tenancy, inspecting resources and their settings to sniff out potential waste. Here’s how the recipe unfolds:

  1. Authentication: Connects to OCI using your configured profile credentials
  2. Compartment Discovery: Identifies all active compartments in your tenancy, including the root compartment
  3. Resource Scanning: For each compartment, scans for various resource types (block volumes, public IPs, NSGs, load balancers, compute instances)
  4. Waste Analysis: Applies specific criteria to identify potentially wasteful resources:
    • Block volumes with no attachments
    • Reserved public IPs not attached to any VNIC
    • Network security groups with no VNICs
    • Load balancers with no backends
    • Compute instances with shapes matching the old-generation pattern
    • Resources with no tags
    • Resources with names matching suspicious patterns
  5. Cost Estimation: Calculates approximate monthly cost impact for identified resources
  6. Reporting: Generates both CSV and HTML reports with the findings

Key Detection Logic

Here’s how we taste-test resources to spot the stale, wasteful ones in your OCI pantry:

Orphaned Block Volumes:
# Find any attachments for this volume
attachments = compute_client.list_volume_attachments(
    compartment_id=comp_id,
    volume_id=vol.id
).data

# If no attachments found → it's orphaned
if len(attachments) == 0:
    # Add to findings
Untagged Resources:
def has_no_tags(resource):
    ff = getattr(resource, "freeform_tags", None)
    df = getattr(resource, "defined_tags", None)
    if (not ff or len(ff) == 0) and (not df or len(df) == 0):
        return True
    return False
Read-Only Operation

This detective only sniffs around—never stirs the pot—performing read-only checks so it’s totally safe to run in your production kitchen.

Prerequisites

Before firing up the OCI Forgotten Resource Detective, make sure your kitchen is stocked with:

  • Python 3.6 or higher - The script is compatible with Python 3.6+
  • OCI Python SDK - The script requires the OCI Python SDK to interact with Oracle Cloud
  • OCI CLI Configuration - A properly configured OCI CLI profile with the necessary permissions
  • Required Permissions - The user or service principal used must have read access to the resources being scanned

Installing Required Packages

Install the OCI Python SDK using pip:

pip install oci

Required OCI Permissions

The user or service principal running the script needs the following permissions:

  • Identity: inspect on compartments
  • Compute: inspect on instances and volume-attachments
  • Block Storage: inspect on volumes
  • Virtual Network: inspect on public-ips and network-security-groups
  • Load Balancer: inspect on load-balancers
Permission Requirements

If the user or service principal doesn't have access to certain compartments or resource types, the script will still run but may not detect all wasteful resources. For best results, use a user with read access across your entire tenancy.

Installation

Installing this detective is as easy as preheating the oven—just grab the script, install dependencies, and you’re ready to cook.

  1. Download the script - Save the oci_forgotten_resources.py file to your preferred location
  2. Make it executable (Linux/macOS) - Run chmod +x oci_forgotten_resources.py
  3. Install dependencies - Ensure the OCI Python SDK is installed

OCI CLI Configuration

The script uses the OCI CLI configuration file located at ~/.oci/config. If you haven't configured the OCI CLI yet, follow these steps:

  1. Install the OCI CLI following the official documentation
  2. Run oci setup config and follow the prompts
  3. Verify your configuration with oci iam region list
Multiple OCI Profiles

If you have multiple OCI profiles configured, you can specify which one to use with the --profile parameter when running the script. This is useful if you need to scan multiple tenancies or use different credentials.

Usage

Basic Usage

Want the no-frills option? Just run the script as-is (no flags needed), and it will:

  • Use your DEFAULT OCI CLI profile
  • Flag VM.Standard1.* shapes as old-generation
  • Flag resources with names containing test, temp, demo, old, backup, or poc
  • Save the CSV report as "forgotten_resources_report.csv" in the current directory
  • Save the HTML report as "forgotten_resources_report.html" in the current directory
python oci_forgotten_resources.py

Advanced Usage

The script supports several parameters for more customized analysis:

ParameterDescriptionDefaultExample
--profileOCI CLI profile name to useDEFAULT--profile production
--old-shape-patternRegex to flag old-gen compute shapesVM\\.Standard1.*--old-shape-pattern "VM\\.(Standard1|Standard2).*"
--suspicious-name-regexRegex for suspicious resource names\b(test|temp|demo|old|backup|poc)\b--suspicious-name-regex "\b(dev|qa|test|temp)\b"
--output-csvPath to write CSV reportforgotten_resources_report.csv--output-csv "/reports/oci_waste_2025_06.csv"
--output-htmlPath to write HTML reportforgotten_resources_report.html--output-html "/reports/oci_waste_2025_06.html"

Example Commands

Example 1: Use a specific OCI profile

python oci_forgotten_resources.py --profile production

Example 2: Customize the old shape pattern to include more shapes

python oci_forgotten_resources.py --old-shape-pattern "VM\\.(Standard1|Standard.E2).*"

Example 3: Comprehensive custom analysis

python oci_forgotten_resources.py \
  --profile production \
  --old-shape-pattern "VM\\.(Standard1|Standard2|Standard.E2).*" \
  --suspicious-name-regex "\b(dev|test|temp|demo|old|backup|poc)\b" \
  --output-csv "/reports/oci_waste_$(date +%Y-%m-%d).csv" \
  --output-html "/reports/oci_waste_$(date +%Y-%m-%d).html"
Runtime Considerations

Just like baking a multi-layer cake takes time, this script needs a few minutes to sift through every OCI compartment and resource. For big tenancies, let it simmer—waiting a bit ensures you get the full flavor of comprehensive results.

Understanding the Report

This detective serves up results three ways—console output, a CloudCostChefs-styled HTML report, and a handy CSV “shopping list.

Console Output

The console output provides immediate insights and includes:

  • Compartment Scanning Progress - Shows which compartments are being scanned
  • Summary Statistics - Total number of issues found
  • Report Locations - Paths to the generated CSV and HTML reports
🔍 Fetching all active compartments under tenancy ocid1.tenancy.oc1..example …
   Found 12 compartments (including root).

⏳ Scanning compartment ocid1.compartment.oc1..example1 …
⏳ Scanning compartment ocid1.compartment.oc1..example2 …
⏳ Scanning compartment ocid1.compartment.oc1..example3 …
...

⚠️  Found 24 forgotten/suspicious resources in total.
🗒️  CSV report saved to: forgotten_resources_report.csv
🖼️  HTML report saved to: forgotten_resources_report.html

HTML Report

The HTML report dishes out a beautiful, easy-to-share feast of your findings—perfect for serving to the whole FinOps team.

Header Section

  • Report title with CloudCostChefs branding
  • Generation timestamp
  • Total issues found

Results Table

  • Risk level (color-coded)
  • Resource name and type
  • Compartment ID
  • Issue description
  • Cost estimate
  • Additional information
  • Tags (if any)

Color Coding

The HTML report uses color coding to highlight risk levels:

  • High Risk (Light Red): Resources with significant cost impact or security concerns
  • Medium Risk (Light Orange): Resources with moderate cost impact or potential issues
  • Low Risk (Light Green): Resources with minimal cost impact but still worth reviewing

CSV Report

The CSV report offers a full, spreadsheet-ready spread of your findings—perfect for spreadsheets or data analysis tools hungry for details:

CSV Columns

  • ResourceName: Name of the identified resource
  • ResourceType: Type of resource (BlockVolume, PublicIP, etc.)
  • CompartmentId: OCID of the compartment containing the resource
  • Issue: Description of the identified issue
  • RiskLevel: High, Medium, or Low
  • CostEstimate: Estimated monthly cost impact
  • AdditionalInfo: Extra details about the resource
  • FreeformTags: Any freeform tags on the resource
  • DefinedTags: Any defined tags on the resource
Report Analysis

The CSV report is particularly useful for further analysis and filtering. You can import it into Excel or Google Sheets to:

  • Sort by cost estimate to prioritize high-impact cleanup
  • Filter by resource type to focus on specific areas
  • Group by compartment to assign cleanup tasks to teams
  • Create pivot tables to analyze patterns in your cloud waste

Customization Options

Feel free to tweak the detective’s recipe—adjust the shape regex or suspicious-name regex so it fits your OCI kitchen’s unique flavor:

Customizing Old-Generation Shape Detection

You can adjust the regex pattern used to identify old-generation compute shapes:

# Default pattern (flags VM.Standard1.* shapes)
--old-shape-pattern "VM\.Standard1.*"

# More aggressive pattern (flags VM.Standard1.*, VM.Standard2.*, and VM.Standard.E2.*)
--old-shape-pattern "VM\.(Standard1|Standard2|Standard\.E2).*"

# Include AMD shapes
--old-shape-pattern "VM\.(Standard1|Standard\.E2|Standard\.A1).*"

Modifying the HTML Template

You can modify the HTML template in the script to change the report's appearance or add additional information:

# Find the HTML template section (starts with html_content = f"""<!DOCTYPE html>)
# Modify the CSS styles or HTML structure as needed
# For example, to change the background color:

body { 
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 
    background: #f0f0f0; /* Changed color */
    margin: 20px; 
}

Adding New Detection Rules

Advanced users can modify the script to add new detection rules. For example, to detect compute instances that have been stopped for a long time:

# Add to the scan_compartment function
# Check for long-stopped instances
for inst in instances:
    if inst.lifecycle_state == "STOPPED":
        # Get the instance's lifecycle history
        history = compute_client.list_instance_actions(
            instance_id=inst.id
        ).data
        
        # Look for the most recent stop action
        stop_actions = [a for a in history if a.action == "STOP"]
        if stop_actions and len(stop_actions) > 0:
            latest_stop = max(stop_actions, key=lambda x: x.time_created)
            stop_time = latest_stop.time_created
            
            # Calculate days since stopped
            days_stopped = (datetime.now(timezone.utc) - stop_time).days
            
            if days_stopped > 30:  # Stopped for more than 30 days
                findings.append({
                    "ResourceName": inst.display_name,
                    "ResourceType": "ComputeInstance",
                    "CompartmentId": comp_id,
                    "Issue": f"Stopped for {days_stopped} days",
                    "RiskLevel": "Medium",
                    "CostEstimate": "Storage only",
                    "AdditionalInfo": f"Shape: {inst.shape}, Stopped since: {stop_time.strftime('%Y-%m-%d')}",
                    "FreeformTags": inst.freeform_tags or {},
                    "DefinedTags": inst.defined_tags or {},
                })
Maintaining Script Functionality

When customizing the script, be careful not to modify the core functionality that retrieves and processes the resource data. Focus your changes on the detection patterns, HTML template, or output formatting.

Automation

For regular monitoring of forgotten resources, you can automate the OCI Forgotten Resource Detective using various methods:

Cron Job (Linux/macOS)

Set up a cron job to run the script on a regular basis:

# Edit crontab
crontab -e

# Add a line to run weekly on Sunday at 1 AM
0 1 * * 0 /path/to/python /path/to/oci_forgotten_resources.py --output-csv "/reports/oci_waste_$(date +\%Y-\%m-\%d).csv" --output-html "/reports/oci_waste_$(date +\%Y-\%m-\%d).html"

Task Scheduler (Windows)

Set up a scheduled task to run the script on a regular basis:

  1. Open Task Scheduler and create a new Basic Task
  2. Set it to run weekly
  3. Action: Start a program
  4. Program/script: python.exe
  5. Arguments: "C:\Path\To\oci_forgotten_resources.py" --output-csv "C:\Reports\oci_waste_%date:~10,4%-%date:~4,2%-%date:~7,2%.csv" --output-html "C:\Reports\oci_waste_%date:~10,4%-%date:~4,2%-%date:~7,2%.html"

OCI Functions

For a cloud-native approach, deploy the script as an OCI Function:

  1. Create an OCI Function application
  2. Modify the script to use instance principal authentication instead of config file
  3. Deploy the function to OCI
  4. Set up an OCI Events rule to trigger the function on a schedule
  5. Configure the function to save reports to Object Storage
Email Integration

For automated runs, you can modify the script to email the results to your team:

# Add this at the end of the main function
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

def send_email_report(csv_path, html_path, recipients):
    msg = MIMEMultipart()
    msg['Subject'] = f'OCI Forgotten Resource Report - {datetime.now().strftime("%Y-%m-%d")}'
    msg['From'] = 'oci-detective@yourcompany.com'
    msg['To'] = ', '.join(recipients)
    
    # Add body
    body = f"The OCI Forgotten Resource Detective found {len(all_findings)} issues. See attached reports."
    msg.attach(MIMEText(body, 'plain'))
    
    # Attach CSV
    with open(csv_path, 'rb') as file:
        attachment = MIMEApplication(file.read(), Name=os.path.basename(csv_path))
        attachment['Content-Disposition'] = f'attachment; filename="{os.path.basename(csv_path)}"'
        msg.attach(attachment)
    
    # Attach HTML
    with open(html_path, 'rb') as file:
        attachment = MIMEApplication(file.read(), Name=os.path.basename(html_path))
        attachment['Content-Disposition'] = f'attachment; filename="{os.path.basename(html_path)}"'
        msg.attach(attachment)
    
    # Send email
    smtp = smtplib.SMTP('smtp.yourcompany.com')
    smtp.send_message(msg)
    smtp.close()
    
    print(f"📧 Email report sent to {', '.join(recipients)}")

# Call the function if findings exist
if all_findings:
    send_email_report(
        args.output_csv,
        args.output_html,
        ['cloud-team@yourcompany.com', 'finance@yourcompany.com']
    )

Troubleshooting

If you encounter issues with the OCI Forgotten Resource Detective, here are some common problems and solutions:

IssuePossible CauseSolution
Authentication ErrorInvalid or expired OCI configVerify your OCI config file is correctly set up and your key is valid
Permission DeniedInsufficient permissions for the userEnsure the user has read access to all resource types being scanned
Missing Module ErrorOCI SDK not installedRun pip install oci to install the OCI Python SDK
Script Runs SlowlyLarge tenancy with many resourcesBe patient; the script needs to scan all resources. Consider running during off-hours
No Issues FoundLimited access or clean tenancyVerify the user has access to all compartments; adjust detection patterns if needed

Debugging Tips

If you're experiencing issues, try these debugging approaches:

  1. Verify OCI CLI Access: Run a simple OCI CLI command to confirm your authentication works:
    oci iam region list
  2. Check Python Version: Ensure you're using Python 3.6 or higher:
    python --version
  3. Verify OCI SDK Installation: Confirm the OCI SDK is installed:
    pip list | grep oci
  4. Add Debug Prints: Add print statements to the script to track progress and identify where issues occur
  5. Run with Limited Scope: Modify the script to scan only a specific compartment to isolate issues
Service Limits

The OCI API has service limits that may affect the script's performance. If you're running into rate limiting issues, consider adding retry logic or spreading the scans across multiple runs.

Next Steps

After identifying forgotten resources with this tool, consider these next steps to optimize your OCI costs:

Implement Resource Cleanup

Systematically review and remove identified wasteful resources after verifying they're truly unused.

Learn about safe cleanup procedures →

Establish Tagging Policies

Implement comprehensive tagging policies to prevent untagged resources in the future.

Explore tagging best practices →

Set Up Regular Monitoring

Automate this script to run regularly and track resource waste over time.

Learn about continuous optimization →

Implement Governance Controls

Create policies and procedures to prevent wasteful resources from being created.

Discover governance frameworks →

Extending the Script

Consider these enhancements to the script for more advanced use cases:

  • Resource Cleanup: Add functionality to automatically terminate or delete identified resources (with appropriate safeguards)
  • Additional Resource Types: Extend the script to check for other resource types like Object Storage buckets, Autonomous Databases, or File Storage
  • Integration with Ticketing Systems: Automatically create tickets for resource cleanup in systems like ServiceNow or Jira
  • Historical Tracking: Store results in a database to track waste over time and measure improvement
  • Multi-Tenancy Support: Enhance the script to scan multiple tenancies in a single run
Building a FinOps Practice

The OCI Forgotten Resource Detective is just one tool in your FinOps toolkit. For a comprehensive approach to cloud financial management, explore our complete FinOps implementation guide.

Share This Guide

If this recipe hit the spot, pass it along to your cloud crew and the wider kitchen!

Was this documentation helpful?

Have suggestions for improving this document? Contact us.