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!
Table of Contents
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 ScriptKey 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.)
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:
- Authentication: Connects to OCI using your configured profile credentials
- Compartment Discovery: Identifies all active compartments in your tenancy, including the root compartment
- Resource Scanning: For each compartment, scans for various resource types (block volumes, public IPs, NSGs, load balancers, compute instances)
- 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
- Cost Estimation: Calculates approximate monthly cost impact for identified resources
- 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:
# 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
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
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:
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
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.
- Download the script - Save the
oci_forgotten_resources.py
file to your preferred location - Make it executable (Linux/macOS) - Run
chmod +x oci_forgotten_resources.py
- 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:
- Install the OCI CLI following the official documentation
- Run
oci setup config
and follow the prompts - Verify your configuration with
oci iam region list
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
Advanced Usage
The script supports several parameters for more customized analysis:
Parameter | Description | Default | Example |
---|---|---|---|
--profile | OCI CLI profile name to use | DEFAULT | --profile production |
--old-shape-pattern | Regex to flag old-gen compute shapes | VM\\.Standard1.* | --old-shape-pattern "VM\\.(Standard1|Standard2).*" |
--suspicious-name-regex | Regex for suspicious resource names | \b(test|temp|demo|old|backup|poc)\b | --suspicious-name-regex "\b(dev|qa|test|temp)\b" |
--output-csv | Path to write CSV report | forgotten_resources_report.csv | --output-csv "/reports/oci_waste_2025_06.csv" |
--output-html | Path to write HTML report | forgotten_resources_report.html | --output-html "/reports/oci_waste_2025_06.html" |
Example Commands
Example 1: Use a specific OCI profile
Example 2: Customize the old shape pattern to include more shapes
Example 3: Comprehensive custom analysis
--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"
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
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 {}, })
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:
- Open Task Scheduler and create a new Basic Task
- Set it to run weekly
- Action: Start a program
- Program/script: python.exe
- 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:
- Create an OCI Function application
- Modify the script to use instance principal authentication instead of config file
- Deploy the function to OCI
- Set up an OCI Events rule to trigger the function on a schedule
- Configure the function to save reports to Object Storage
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:
Issue | Possible Cause | Solution |
---|---|---|
Authentication Error | Invalid or expired OCI config | Verify your OCI config file is correctly set up and your key is valid |
Permission Denied | Insufficient permissions for the user | Ensure the user has read access to all resource types being scanned |
Missing Module Error | OCI SDK not installed | Run pip install oci to install the OCI Python SDK |
Script Runs Slowly | Large tenancy with many resources | Be patient; the script needs to scan all resources. Consider running during off-hours |
No Issues Found | Limited access or clean tenancy | Verify the user has access to all compartments; adjust detection patterns if needed |
Debugging Tips
If you're experiencing issues, try these debugging approaches:
- Verify OCI CLI Access: Run a simple OCI CLI command to confirm your authentication works:oci iam region list
- Check Python Version: Ensure you're using Python 3.6 or higher:python --version
- Verify OCI SDK Installation: Confirm the OCI SDK is installed:pip list | grep oci
- Add Debug Prints: Add print statements to the script to track progress and identify where issues occur
- Run with Limited Scope: Modify the script to scan only a specific compartment to isolate issues
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
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.