<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tom Granot</title>
    <description>The latest articles on Forem by Tom Granot (@tomgranot).</description>
    <link>https://forem.com/tomgranot</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F179684%2Fe7c45226-dd01-4f18-86aa-e6e064e985cd.png</url>
      <title>Forem: Tom Granot</title>
      <link>https://forem.com/tomgranot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tomgranot"/>
    <language>en</language>
    <item>
      <title>Finding and Remediating Idle AWS Databases Made Easy</title>
      <dc:creator>Tom Granot</dc:creator>
      <pubDate>Tue, 08 Apr 2025 17:17:10 +0000</pubDate>
      <link>https://forem.com/tomgranot/finding-and-remediating-idle-aws-databases-made-easy-145a</link>
      <guid>https://forem.com/tomgranot/finding-and-remediating-idle-aws-databases-made-easy-145a</guid>
      <description>&lt;p&gt;It’s easy to overlook idle databases in AWS. Spun up in a hurry for testing, migration, or “just in case” scenarios, they often outlive their purpose and quietly accumulate charges in the background.&lt;/p&gt;

&lt;p&gt;This isn’t just about wasted money—though that’s a major factor. It’s also about infrastructure sprawl, increased attack surface, and the risk of unintentionally deleting something still critical because no one remembers what it was for.&lt;/p&gt;

&lt;p&gt;Over time, this clutter adds friction to your operations. Auditing becomes harder, deployment pipelines get noisier, and cost forecasting gets skewed by ghost workloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is an Idle AWS Database?
&lt;/h2&gt;

&lt;p&gt;An idle AWS database is any database instance—RDS, Aurora, Redshift, or others—that shows little to no meaningful engagement over time. That means no active connections, low or flatline read/write IOPS, and negligible CPU usage. It's not about a single moment of quiet—it's about a sustained pattern of inactivity confirmed by metrics and usage history.&lt;/p&gt;

&lt;p&gt;These instances often originate from valid use cases. A developer might create a new RDS instance for performance testing and forget to decommission it. A data team might launch a Redshift cluster for a one-time warehouse import. Or a migration project might leave behind a “just in case” snapshot that never gets used. What begins as a useful resource turns into a silent cost center.&lt;/p&gt;

&lt;p&gt;Idle doesn’t always mean harmless. These forgotten databases consume compute and storage; they also carry operational and security risks. When left unmonitored, they miss patches, drift from configuration baselines, and become more vulnerable to misconfigurations or open endpoints. Multiply that across regions, environments, or accounts, and the scale of the problem grows fast.&lt;/p&gt;

&lt;p&gt;The key characteristics of an idle instance usually follow a clear trend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero or near-zero active connections&lt;/strong&gt;: Measured through CloudWatch’s &lt;code&gt;DatabaseConnections&lt;/code&gt; metric across a 7–31 day span.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal IOPS activity&lt;/strong&gt;: &lt;code&gt;ReadIOPS&lt;/code&gt; and &lt;code&gt;WriteIOPS&lt;/code&gt; metrics show little to no disk activity, often under 20 operations per day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistently low CPU usage&lt;/strong&gt;: Less than 5% average CPU utilization over time is a strong indicator of inactivity—especially for provisioned instances, where you're paying for compute regardless.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What makes these databases tricky is that they don’t always show up when scanning for errors or failures. They’re not broken. They're just not doing anything. Unless you're specifically looking for them, they simply blend into the noise. That’s why a structured approach to AWS idle database management, like the one used at Tom Granot, helps teams identify and clean up these costly, hidden inefficiencies without risking disruption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do Idle Databases Matter?
&lt;/h2&gt;

&lt;p&gt;The biggest offender here is cost. Idle RDS instances, for example, continue charging for compute, provisioned IOPS, and storage—even if the database hasn't handled a single query in weeks. A multi-AZ SQL Server Enterprise instance left running for 90 days can quietly rack up over $10,000 in charges. These aren’t rare edge cases—they’re the result of default behaviors like forgetting to disable instances after a migration, or leaving replica clusters online “just in case.”&lt;/p&gt;

&lt;p&gt;Beyond cost, idle databases introduce unnecessary decision friction across engineering teams. During remediation or infrastructure reviews, teams get stuck triaging long lists of resources no one remembers provisioning. Old project databases hang around, cluttering dashboards and inflating usage graphs. CI/CD pipelines that spin up temporary databases often forget to tear them down, adding even more noise. This mess slows down operations, introduces confusion in ownership, and makes enforcing resource tagging standards harder.&lt;/p&gt;

&lt;p&gt;Attack surface is another issue. Idle doesn’t mean isolated—many of these databases still have active endpoints, permissive security groups, or unused IAM roles attached. Without owners watching them, they miss routine hardening: no patching, no version upgrades, and often no alarms. That’s how they become convenient entry points for attackers or compliance violations waiting to happen. In one real-world audit, a previously decommissioned staging database was discovered still accepting external connections—with default credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Types of Idle AWS Databases
&lt;/h2&gt;

&lt;p&gt;Idle databases tend to follow predictable origin stories. Most of them weren’t mistakes at the time—they were part of legitimate workflows that just never had a proper exit. Understanding how they get there is key to designing reliable cleanup processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Short-Lived Environments That Overstay
&lt;/h3&gt;

&lt;p&gt;Temporary environments are a known source of idle sprawl, but the root issue often lies in automation gaps. CI/CD pipelines may provision new RDS instances for integration tests or staging deployments, yet fail to include teardown logic on failure paths or timeouts. These instances don’t just persist—they accumulate silently across accounts and regions, especially when the automation lacks tagging or TTL enforcement.&lt;/p&gt;

&lt;p&gt;Another overlooked source comes from internal training or onboarding exercises. Teams spin up realistic environments to teach new hires or simulate customer setups, but unless someone owns the cleanup step, those environments become permanent residents. These databases often look like legitimate workloads—especially if they mimic production naming conventions—and are rarely flagged until a cost spike triggers a deeper review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Post-Migration Leftovers and Dormant Archives
&lt;/h3&gt;

&lt;p&gt;Infrastructure migrations tend to leave echoes: clusters, read replicas, or legacy endpoints that were supposed to be temporary but got lost in the shuffle. Often, the switch to a new instance happens cleanly, but the old one remains available out of habit or as a fallback that no one wants to touch. These drift further into obscurity when they’re excluded from IaC or drift detection tooling, making them invisible to standard audits.&lt;/p&gt;

&lt;p&gt;Then there are the archival workloads—datasets that serve compliance or audit use cases but are no longer part of day-to-day operations. These aren't abandoned, but they’re grossly overprovisioned. For example, an RDS instance storing quarterly financial logs might idle at under 1% CPU for months, yet still runs on a &lt;code&gt;db.m5.2xlarge&lt;/code&gt; tier. Without scheduled shutdowns or a shift to more storage-efficient models, they quietly burn through budget while doing almost nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Do Idle Databases Usually Live?
&lt;/h2&gt;

&lt;p&gt;Idle databases tend to hide in the edges of your infrastructure—the places no one checks unless something breaks. Non-production environments lead the pack: staging, QA, sandbox accounts. These environments exist to mirror production closely enough for testing, but they often evolve organically without consistent controls. Feature branches, parallel testing workflows, or hotfix validation setups leave behind full-stack environments that no longer map to active code. Over time, these “temporary” databases blend into the noise because no one knows if they’re still important—or who to ask.&lt;/p&gt;

&lt;p&gt;Automated systems, ironically, are another source of debris. Infrastructure-as-code tools, scheduled builds, or ephemeral environments generated on the fly often lack teardown hooks that execute reliably in every failure path. Over time, you’ll find RDS or Redshift instances created for one-off data loads, dry runs, or schema validation tests that never completed—but the instance stuck around. These workloads are rarely tagged with consistent metadata, and even when they are, they’re often excluded from cleanup policies due to lack of ownership mapping.&lt;/p&gt;

&lt;p&gt;Multi-region activity introduces another layer of complexity. Organizations experimenting with latency optimization, geo-redundancy, or cross-border compliance often deploy workloads in secondary or tertiary regions. These databases serve a purpose for a few days—but months later, they’re still online in regions like &lt;code&gt;me-south-1&lt;/code&gt; or &lt;code&gt;ap-northeast-3&lt;/code&gt;, quietly accruing storage and networking costs. Since most billing dashboards default to primary regions, and many teams work within isolated scopes, these idle workloads often escape detection until someone performs a CUR deep dive or a cross-region resource inventory.&lt;/p&gt;

&lt;p&gt;Then there’s the forgotten legacy tier—databases linked to deprecated services or abandoned proof-of-concepts. These are rarely part of modern CI/CD or infrastructure automation. Nobody’s watching the metrics, no alerts are configured, and no one has logged in for months. They’re still online because they’ve become “too old to touch”—deleting them feels risky without documentation, and the original owner probably left the organization or moved on to a different team. So they sit, draining budget and widening your security perimeter.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Find and Safely Remediate Idle Databases on AWS
&lt;/h2&gt;

&lt;p&gt;Identifying idle databases starts with surfacing underutilized infrastructure across your AWS footprint. Use the Cost and Usage Report (CUR) to isolate long-running database instances with consistent spend. From there, pull runtime metrics like &lt;code&gt;CPUUtilization&lt;/code&gt;, &lt;code&gt;FreeableMemory&lt;/code&gt;, and &lt;code&gt;NetworkThroughput&lt;/code&gt; via CloudWatch to detect usage anomalies or consistent inactivity patterns over time. These aren't just raw stats—they form the behavioral fingerprint of whether an instance is doing real work or just existing.&lt;/p&gt;

&lt;p&gt;To accelerate this process, use CLI queries to capture a snapshot of current state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds describe-db-instances &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"DBInstances[*].{DB:DBInstanceIdentifier,Class:DBInstanceClass,State:DBInstanceStatus,Created:InstanceCreateTime}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, compare that against CloudWatch data collected over a 14–31 day window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudwatch get-metric-statistics &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--metric-name&lt;/span&gt; CPUUtilization &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--start-time&lt;/span&gt; 2024-05-01T00:00:00Z &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--end-time&lt;/span&gt; 2024-05-31T23:59:59Z &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--period&lt;/span&gt; 86400 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; AWS/RDS &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--statistics&lt;/span&gt; Average &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dimensions&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;DBInstanceIdentifier,Value&lt;span class="o"&gt;=&lt;/span&gt;my-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instances with low CPU and negligible network activity—especially those with stable or declining memory usage—are strong idle candidates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validate Business Context Before Acting
&lt;/h3&gt;

&lt;p&gt;Before selecting a remediation path, establish the current relevance of the instance. Use existing tags (&lt;code&gt;Environment&lt;/code&gt;, &lt;code&gt;Project&lt;/code&gt;, &lt;code&gt;Owner&lt;/code&gt;, etc.) to identify responsible teams. If tags are missing or stale, use IAM access patterns, CloudTrail events, or recent login history to trace back recent activity. You can also leverage AWS Resource Groups to list assets tied to specific teams or projects.&lt;/p&gt;

&lt;p&gt;When ownership is unclear, append a review tag to the instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds add-tags-to-resource &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-name&lt;/span&gt; arn:aws:rds:us-east-1:123456789012:db:my-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ReviewStatus,Value&lt;span class="o"&gt;=&lt;/span&gt;NeedsVerification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates an audit trail and signals to others that the instance is under evaluation. Once ownership is confirmed or dismissed, update the tag accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choose the Right Remediation Strategy
&lt;/h3&gt;

&lt;p&gt;Each idle instance should follow a remediation path based on cost, risk, and future accessibility.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Delete with Backup&lt;/strong&gt;: When the instance is confirmed unused and not tied to compliance retention, create a snapshot and remove it entirely:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws rds create-db-snapshot &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; my-db &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-snapshot-identifier&lt;/span&gt; my-db-final-snapshot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws rds delete-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; my-db &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--final-db-snapshot-identifier&lt;/span&gt; my-db-final-snapshot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retain with Adjustments&lt;/strong&gt;: For rarely accessed but necessary databases, reduce instance size or switch to burstable classes like &lt;code&gt;db.t4g.micro&lt;/code&gt; to minimize costs:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws rds modify-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; my-db &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-instance-class&lt;/span&gt; db.t4g.micro &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--apply-immediately&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Suspend or Pause&lt;/strong&gt;: For Aurora Serverless clusters, configure auto-pause to suspend compute when idle:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws rds modify-db-cluster &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-cluster-identifier&lt;/span&gt; audit-logs-cluster &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--scaling-configuration&lt;/span&gt; &lt;span class="nv"&gt;MinCapacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0,AutoPause&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,SecondsUntilAutoPause&lt;span class="o"&gt;=&lt;/span&gt;3600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These options give you flexibility—whether you need to decommission, minimize, or preserve the database with reduced cost exposure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lock Down What You Keep
&lt;/h3&gt;

&lt;p&gt;Idle databases left online must be isolated from unnecessary access. Start by limiting network exposure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds modify-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; my-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vpc-security-group-ids&lt;/span&gt; sg-00000000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--apply-immediately&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach a security group that denies all inbound connections except from trusted internal ranges or jump boxes. Then remove any IAM roles, Lambda triggers, or SNS topics previously tied to the database. If the database must remain online for archival or reference, add a &lt;code&gt;Quarantine&lt;/code&gt; tag and restrict its visibility in dashboards and monitoring tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate Detection and Review
&lt;/h3&gt;

&lt;p&gt;For ongoing idle detection, build a Lambda function that runs daily and queries metrics across all RDS instances. Use thresholds like &lt;code&gt;&amp;lt; 1% CPU&lt;/code&gt; and &lt;code&gt;&amp;lt; 5 connections&lt;/code&gt; over 14 days to flag candidates. When flagged, tag them with &lt;code&gt;IdleCandidate=true&lt;/code&gt; and send alerts via SNS or Slack for manual review.&lt;/p&gt;

&lt;p&gt;You can also integrate with EventBridge to trigger evaluations on instance creation, ensuring new databases follow lifecycle policies from day one. Pair this with a runbook that outlines remediation protocols, backup policies, and escalation paths. That way, cleanup isn’t a one-time effort—it becomes part of the infrastructure lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Inventory Your Databases
&lt;/h2&gt;

&lt;p&gt;Start with coverage. Not partial, not “just the prod account”—full-region, full-account visibility. The point isn’t just to surface what’s online, it’s to expose what’s quietly burning budget with no clear owner. Begin with your AWS Organizations account structure. Use &lt;code&gt;list-accounts&lt;/code&gt; with &lt;code&gt;sts assume-role&lt;/code&gt; to pull RDS inventory from every child account. This way, you’re not blind to idle resources running in sandbox or legacy projects someone forgot to shut down three quarters ago.&lt;/p&gt;

&lt;p&gt;Here’s how to list RDS instances across multiple accounts and regions using a loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;account &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;aws organizations list-accounts &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Accounts[*].Id'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;creds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts assume-role &lt;span class="nt"&gt;--role-arn&lt;/span&gt; arn:aws:iam::&lt;span class="nv"&gt;$account&lt;/span&gt;:role/OrgAuditAccess &lt;span class="nt"&gt;--role-session-name&lt;/span&gt; audit-session&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$creds&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .Credentials.AccessKeyId&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$creds&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .Credentials.SecretAccessKey&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SESSION_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$creds&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .Credentials.SessionToken&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;region &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-regions &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Regions[*].RegionName'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Account: &lt;/span&gt;&lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="s2"&gt; | Region: &lt;/span&gt;&lt;span class="nv"&gt;$region&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    aws rds describe-db-instances &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$region&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"DBInstances[*].{DB:DBInstanceIdentifier,Type:Engine,Class:DBInstanceClass,Status:DBInstanceStatus}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--output&lt;/span&gt; table
  &lt;span class="k"&gt;done
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a multi-account, multi-region view of your database landscape. Once you’ve got the raw list, push it into a central repository—DynamoDB, Athena, or even a tagged S3 CSV—so you can filter by engine type, region, or creation date later. Tracking it in a living system makes it easier to attach review states over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cross-Reference Metrics Over Time
&lt;/h3&gt;

&lt;p&gt;Once inventory is established, focus on behavior. You’re not just looking for what exists—you’re looking for databases that consistently do nothing. Use AWS Cost Explorer to flag RDS instances with flat spend trends over 30+ days. Then layer in CloudWatch metrics like &lt;code&gt;DatabaseConnections&lt;/code&gt;, &lt;code&gt;ReadIOPS&lt;/code&gt;, and &lt;code&gt;WriteIOPS&lt;/code&gt; to validate inactivity.&lt;/p&gt;

&lt;p&gt;Instead of reviewing one instance at a time, automate the metric pull using &lt;code&gt;get-metric-data&lt;/code&gt;. This lets you batch-query multiple instances in a single request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudwatch get-metric-data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--metric-data-queries&lt;/span&gt; file://metric-queries.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--start-time&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'30 days ago'&lt;/span&gt; +%Y-%m-%dT00:00:00Z&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--end-time&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%Y-%m-%dT00:00:00Z&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;metric-queries.json&lt;/code&gt; contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"readiops1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"MetricStat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Metric"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS/RDS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"MetricName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ReadIOPS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Dimensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DBInstanceIdentifier"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-db-instance"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Period"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Stat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sum"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ReturnData"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Batching metrics like this helps you identify low-activity patterns at scale, without drowning in one-off CLI calls per instance. You’ll start to see which ones never spike, never connect, and never move data—your top idle suspects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capture Fallbacks Before You Touch Anything
&lt;/h3&gt;

&lt;p&gt;Before tagging or terminating anything, document the state. Store metadata like engine version, allocated storage, parameter groups, and security group associations. This makes rollback easier if someone flags the instance post-cleanup. For retention-sensitive environments, record your findings in a separate audit log and attach a flag like &lt;code&gt;IdleCandidate=True&lt;/code&gt; or &lt;code&gt;RemediationStatus=PendingReview&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If deletion is on the table, use a snapshot strategy that includes naming conventions and timestamping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds create-db-snapshot &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; my-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-snapshot-identifier&lt;/span&gt; idle-snap-my-db-instance-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t just rely on snapshots for backup—use them to prove due diligence. When you’re working across teams or regulated environments, a standardized naming scheme for snapshots and tags helps signal intent and avoids accidental overlaps with active workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Evaluate Each Database’s Role
&lt;/h2&gt;

&lt;p&gt;After surfacing idle candidates, the next step is confirming whether they still matter. Metrics show you what’s happening under the hood—but they don’t tell you whether the database still holds business value. That context lives in ownership, integrations, and historical decisions made outside the console.&lt;/p&gt;

&lt;p&gt;When metadata is missing or unreliable, trace the origin using CloudTrail’s creation logs. This gives you a timestamp and the IAM identity that created the instance—often enough to connect the dots. Run this to surface the creator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudtrail lookup-events &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lookup-attributes&lt;/span&gt; &lt;span class="nv"&gt;AttributeKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;EventName,AttributeValue&lt;span class="o"&gt;=&lt;/span&gt;CreateDBInstance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Events[*].{Time:EventTime,User:Username,Resource:Resources[0].ResourceName}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-results&lt;/span&gt; 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user is active, a short message asking whether the database supports anything critical can resolve ambiguity fast. If not, look for context in provisioning pipelines, resource policies, or existing role bindings. You’ll often find clues in IAM permissions, ECS task definitions, or Lambda environment variables that reference the database identifier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Determine Usage Through Connected Systems
&lt;/h3&gt;

&lt;p&gt;Rather than guessing at purpose, check whether the database appears in active configurations. You can scan Secrets Manager and Parameter Store for stored credentials that are still in use by apps or services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws secretsmanager list-secrets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"SecretList[?contains(Name, 'my-db')].Name"&lt;/span&gt;

aws ssm describe-parameters &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Parameters[?contains(Name, 'my-db')].Name"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also look at task queues, data pipelines, or scheduled jobs tied to the database. For example, a Redshift cluster may not see daily queries but could be processing monthly reports via a Step Functions state machine or AWS Glue job. In those cases, you’re dealing with low-frequency usage—not true idleness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apply Clear, Future-Friendly Tagging
&lt;/h3&gt;

&lt;p&gt;Once you’ve confirmed the role—or lack thereof—tag the instance to document its status and avoid reevaluating it later. Use clear labels like &lt;code&gt;Role=HistoricalReporting&lt;/code&gt;, &lt;code&gt;RemediationStatus=IdleCandidate&lt;/code&gt;, or &lt;code&gt;ReviewDate=2024-06-01&lt;/code&gt; to give future teams context at a glance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds add-tags-to-resource &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-name&lt;/span&gt; arn:aws:rds:us-west-2:123456789012:db:legacy-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Role,Value&lt;span class="o"&gt;=&lt;/span&gt;LegacyData &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;RemediationStatus,Value&lt;span class="o"&gt;=&lt;/span&gt;NeedsApproval &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ReviewDate,Value&lt;span class="o"&gt;=&lt;/span&gt;2024-06-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the database is still under review or lacks a clear owner, tag it accordingly and schedule a follow-up. This creates a paper trail—and avoids false positives the next time someone runs a cleanup script.&lt;/p&gt;

&lt;p&gt;The goal here isn’t just to clean up one instance. It’s to embed context into your infrastructure so that future reviews move quickly, decisions are traceable, and idle database management becomes a lightweight, continuous process.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Confirm Data Retention Requirements
&lt;/h2&gt;

&lt;p&gt;Before any remediation action—termination, downsizing, or archiving—you need to understand the data’s contractual or regulatory weight. Usage metrics don’t reveal whether a database contains GDPR-affected records, financial logs tied to tax filings, or archive content required by HIPAA. Removing an idle database without validating retention obligations exposes you to compliance violations, not just operational risk.&lt;/p&gt;

&lt;p&gt;Start by reviewing your internal data classification policy, if one exists. Many organizations mandate different retention periods for PII, logs, analytical models, or system telemetry. If the database falls under a formal data governance category—like “Regulatory Financials” or “Customer Interaction Logs”—you’ll want to escalate the review to a compliance lead before taking any action. When no classification is available, fall back to metadata like schema names (&lt;code&gt;audit_logs&lt;/code&gt;, &lt;code&gt;transactions&lt;/code&gt;, etc.) or table row counts to infer its historical value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export for Long-Term Retention Without Keeping Infrastructure
&lt;/h3&gt;

&lt;p&gt;If you confirm the data must remain accessible beyond the life of the instance, exporting it is a more durable and cost-efficient approach than keeping the instance running or relying solely on RDS snapshots. For example, you can export a snapshot to S3 in Parquet format, which supports downstream querying via Athena without spinning up compute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds export-db-snapshot &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-snapshot-identifier&lt;/span&gt; audit-db-snap-20240601 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-engine&lt;/span&gt; mysql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--s3-bucket-name&lt;/span&gt; org-compliance-archive &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--iam-role-arn&lt;/span&gt; arn:aws:iam::123456789012:role/RDSExportToS3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--kms-key-id&lt;/span&gt; arn:aws:kms:us-east-1:123456789012:key/abc12345-6789-0123-4567-abcdef123456
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This export strategy allows you to retain data in a queryable format, minimize storage costs, and avoid compute resources entirely. It’s especially useful for compliance teams that require access to historical data but don’t need a live database to retrieve it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Put Lifecycle Controls Around Retained Data
&lt;/h3&gt;

&lt;p&gt;Once exported, the data must follow structured lifecycle controls to avoid becoming the next generation of idle sprawl. Use S3 object tags and bucket lifecycle configurations to enforce expiration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ExpireComplianceDataAfter7Years"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RetentionPolicy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Finance"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Expiration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2555&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Couple this with versioning and access logging to create an auditable trail. If your organization uses centralized security tooling, integrate these exported records into your compliance vault or archival domain, and restrict access via resource-level IAM policies that reference specific tags like &lt;code&gt;DataSensitivity=High&lt;/code&gt; or &lt;code&gt;LegalHold=True&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of just preserving data reactively, this approach embeds retention logic into the export process—ensuring archived databases follow the same rigor as live systems but without the operational overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Apply the Best Remediation Strategy
&lt;/h2&gt;

&lt;p&gt;Once you've finished auditing, tagging, and confirming the purpose of each idle database, the next step is choosing the right remediation path. Not every instance needs to be deleted—your decision should reflect the level of risk, the need for historical access, and the frequency of future use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Termination with Lifecycle Controls
&lt;/h3&gt;

&lt;p&gt;For databases that serve zero ongoing purpose and carry no retention requirements, complete removal is appropriate—but it still requires a structured approach. Before deletion, register the instance in your internal cleanup log and check for any downstream dependencies (e.g., secrets, parameter references, or IAM bindings that might still reference the database). Rather than relying on a single snapshot, consider exporting the database contents to S3 using the &lt;code&gt;ExportDBSnapshot&lt;/code&gt; feature, especially if the data may need to be queried later via Athena or Redshift Spectrum.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds export-db-snapshot &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-snapshot-identifier&lt;/span&gt; legacy-db-snap-20240601 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-engine&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--s3-bucket-name&lt;/span&gt; longterm-archive-bucket &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--iam-role-arn&lt;/span&gt; arn:aws:iam::123456789012:role/RDSExportRole &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--kms-key-id&lt;/span&gt; arn:aws:kms:us-east-1:123456789012:key/abc123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After confirming the export, follow up with deletion and attach a record of the action to your central resource inventory or security audit trail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Downsizing to Align with Intermittent Use
&lt;/h3&gt;

&lt;p&gt;Some databases are technically idle but still serve low-frequency workloads—quarterly audits, monthly reports, or ad-hoc queries by internal teams. In those cases, full termination may be excessive. Instead, reduce the footprint to match actual usage. Choose instance classes with burstable capacity (&lt;code&gt;t3&lt;/code&gt;, &lt;code&gt;t4g&lt;/code&gt;) and evaluate whether storage throughput or IOPS provisioning can be scaled down. This tactic preserves availability while significantly reducing runtime costs.&lt;/p&gt;

&lt;p&gt;For example, to update a database to a smaller, ARM-based instance class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds modify-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; archive-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-instance-class&lt;/span&gt; db.t4g.micro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--storage-type&lt;/span&gt; gp3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--apply-immediately&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method is particularly effective when paired with scheduled backups and a defined retention period, allowing the database to remain functional but cost-aligned.&lt;/p&gt;

&lt;h3&gt;
  
  
  Suspend or Quarantine for Low-Risk Retention
&lt;/h3&gt;

&lt;p&gt;When deletion isn't an option and downsizing doesn’t go far enough—such as in compliance-heavy environments or when ownership is unclear—quarantine is a viable middle ground. This involves isolating the instance from the network, locking down IAM policies, and removing any triggers or downstream integrations. Instead of relying on auto-scaling or pause features alone, you enforce a state of intentional disconnection, reducing exposure without losing the data.&lt;/p&gt;

&lt;p&gt;For Aurora Serverless v1 or v2 clusters, where pausing is supported, configure inactivity thresholds explicitly. This allows the cluster to suspend compute resources automatically while data remains intact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds modify-db-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-cluster-identifier&lt;/span&gt; dormant-reporting-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--scaling-configuration&lt;/span&gt; &lt;span class="nv"&gt;MinCapacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1,MaxCapacity&lt;span class="o"&gt;=&lt;/span&gt;2,AutoPause&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,SecondsUntilAutoPause&lt;span class="o"&gt;=&lt;/span&gt;7200 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--apply-immediately&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For engines that don’t support pause, simulate the effect by disabling inbound access and scheduling instance stops. Use Systems Manager Automation documents or EventBridge rules to shut down the database outside known access windows, reducing runtime costs without removing the instance entirely. This gives you a predictable, low-risk fallback plan while buying time to confirm long-term decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Update Network and Access Configurations
&lt;/h2&gt;

&lt;p&gt;Idle databases that remain online often carry legacy access paths—open ports, broad CIDR blocks, or deprecated roles—that were never revisited once the database stopped serving live traffic. These misconfigurations don’t raise alarms on their own but present quiet, persistent risks. If a database is inactive, its exposure should reflect that status with strict, intentional controls.&lt;/p&gt;

&lt;p&gt;Start by identifying which security groups are still attached to databases flagged as idle. Use &lt;code&gt;describe-db-instances&lt;/code&gt; to retrieve the associated security group IDs, then inspect the ingress rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 describe-security-groups &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-ids&lt;/span&gt; sg-0123456789abcdef0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"SecurityGroups[*].IpPermissions"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of just removing security groups, consolidate idle instances into a dedicated isolation group with no inbound rules defined—effectively dead-ending all network access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 create-security-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-name&lt;/span&gt; rds-isolation-zone &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Blocked access for inactive RDS"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vpc-id&lt;/span&gt; vpc-0abc123def456ghij
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply it to the instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds modify-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; dormant-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vpc-security-group-ids&lt;/span&gt; sg-0abc123def456ghij &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--apply-immediately&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This replaces any prior rules—public or private—with a locked-down profile. From a network perspective, the database becomes inert without requiring full deletion or snapshot restoration.&lt;/p&gt;

&lt;p&gt;IAM roles and policies tied to idle databases often go unchecked. These roles may have been granted permissions like &lt;code&gt;rds:StartDBInstance&lt;/code&gt; or &lt;code&gt;rds:RestoreDBInstanceFromSnapshot&lt;/code&gt; months—or years—ago, and they persist long after their operational use ends. Use CloudTrail to pinpoint which IAM entities have interacted with the instance in the last 90 days:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudtrail lookup-events &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lookup-attributes&lt;/span&gt; &lt;span class="nv"&gt;AttributeKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ResourceName,AttributeValue&lt;span class="o"&gt;=&lt;/span&gt;dormant-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-results&lt;/span&gt; 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If no recent activity is found, simulate the role’s permissions to see whether it still has access to sensitive RDS actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam simulate-principal-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-source-arn&lt;/span&gt; arn:aws:iam::123456789012:role/ObsoleteRDSRole &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--action-names&lt;/span&gt; rds:StartDBInstance rds:RestoreDBInstanceFromSnapshot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For roles still permitted to act on idle databases, either remove the policy or modify the trust relationship to prevent future assumptions. For example, to explicitly deny assumptions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DenyObsoleteAccess"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ArnEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"aws:PrincipalArn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::123456789012:role/ObsoleteRDSRole"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Outside of network and IAM, configuration bloat can also linger in parameter and option groups. These often accumulate enabled features—like audit logs, minor version preferences, or legacy extensions—that no longer serve their original purpose. Instead of defaulting to a full reset, duplicate the current group, strip out unused settings, and reassign it to the instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds modify-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; dormant-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-parameter-group-name&lt;/span&gt; minimal-config &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--apply-immediately&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach reduces unnecessary complexity and aligns the database’s configuration with its current (or lack of) workload. It also makes future remediation simpler—leaner configurations mean fewer unknown variables when it’s time to archive, downsize, or delete.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Automate Ongoing Idle Detection
&lt;/h2&gt;

&lt;p&gt;Manual reviews don’t scale—not across multiple teams, regions, or accounts. Automation ensures idle database detection becomes a continuous process, not a quarterly fire drill. By embedding scheduled metric evaluation into lightweight Lambda functions, you can catch drift before it becomes budget bloat, and remove guesswork from the equation entirely.&lt;/p&gt;

&lt;p&gt;Start with a Lambda function that consumes instance IDs from a centralized inventory—stored in DynamoDB or fetched dynamically—and runs targeted metric queries via CloudWatch APIs. Instead of relying on fixed thresholds, implement configurable logic using environment variables or a lookup table to account for different environments and usage profiles (e.g., production vs. test). This flexibility allows you to tune for noise reduction without missing meaningful signals.&lt;/p&gt;

&lt;p&gt;Here’s a basic implementation that uses a DynamoDB table to load instance configurations and supports adjustable thresholds per instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;cloudwatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cloudwatch&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;rds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;INSTANCE_CONFIG_TABLE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DBInstanceIdentifier&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;threshold_cpu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CPUThreshold&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;threshold_conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ConnThreshold&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="n"&gt;conn_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_metric_statistics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS/RDS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;MetricName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DatabaseConnections&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Dimensions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DBInstanceIdentifier&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;db_id&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
            &lt;span class="n"&gt;StartTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;EndTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Statistics&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Average&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;cpu_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_metric_statistics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS/RDS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;MetricName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CPUUtilization&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Dimensions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DBInstanceIdentifier&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;db_id&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
            &lt;span class="n"&gt;StartTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;EndTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Statistics&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Average&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;conn_avg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Average&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;conn_stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Datapoints&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn_stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Datapoints&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;conn_stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Datapoints&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;cpu_avg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Average&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cpu_stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Datapoints&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cpu_stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Datapoints&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cpu_stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Datapoints&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;conn_avg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;threshold_conn&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;cpu_avg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;threshold_cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_tags_to_resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;ResourceName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DBInstanceArn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;IdleCandidate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Marked &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;db_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; as idle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version doesn’t assume one-size-fits-all logic. It adjusts based on the intended behavior of each instance and stores evidence in tagging, enabling downstream systems to take action—or escalate—without human intervention.&lt;/p&gt;

&lt;p&gt;To extend this pipeline, integrate the function with an SNS topic or webhook that feeds into your internal ops tooling. Instead of plain alerts, push structured payloads that include instance metadata, thresholds breached, and time-series summaries. This gives reviewers the context they need to make decisions quickly without digging through metrics dashboards.&lt;/p&gt;

&lt;p&gt;For long-term traceability, log all detection results and tagging actions to a dedicated S3 bucket or a time-partitioned Athena table. Include not just the instance ID and metric values but also the evaluation config that triggered the action. When teams review past decisions or investigate unexpected deletions, this audit log becomes the source of truth.&lt;/p&gt;

&lt;p&gt;Capture your automation stack and detection logic in a shared runbook accessible to all teams involved in infrastructure lifecycle management. Include a decision tree for each remediation path, escalation contacts, and examples of valid idle patterns versus anomalies. When detection becomes embedded into your review cadence, idle cleanup turns from a one-off effort into a predictable, low-friction routine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reasons to Keep an Eye on AWS Idle Databases
&lt;/h2&gt;

&lt;p&gt;Idle databases are easy to ignore—until they interfere with your ability to move fast. When your AWS inventory is bloated with unused resources, even simple tasks like running compliance scans or provisioning new environments take longer than they should. Visibility suffers, and so does the confidence that what you're deploying into is actually being used, secured, and maintained.&lt;/p&gt;

&lt;p&gt;Unmonitored databases tend to operate outside of your standard guardrails. They might still be accessible from legacy IP ranges, tied to IAM roles no one remembers creating, or logging to buckets that are no longer monitored. One idle PostgreSQL instance left with unrestricted ingress for weeks can quietly sidestep your perimeter defenses—not because of negligence, but because no one knew it was still there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure That Doesn’t Fight Back
&lt;/h3&gt;

&lt;p&gt;Clean infrastructure doesn’t just save money—it reduces resistance. When teams spin up new workloads or migrate existing ones, they aren’t forced to decipher what’s safe to reuse or what might break something. There’s no guessing if a database labeled "test-db-v2" is critical or disposable. By keeping idle databases out of the way, you prevent them from becoming blockers in future planning or sources of accidental regression.&lt;/p&gt;

&lt;p&gt;As environments scale up, the risk of misclassifying resources grows. A database that supported a proof-of-concept last quarter might still sit on a large instance class, untouched but protected by deletion safeguards. Without a consistent way to surface and review these kinds of leftovers, capacity planning and cost forecasting get skewed—and so do your infrastructure decisions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds describe-db-instances &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"DBInstances[?contains(DBInstanceIdentifier, 'poc') &amp;amp;&amp;amp; @.DBInstanceStatus=='available']"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this to isolate candidate instances by naming convention and status—then evaluate whether they’ve had any active connections or IOPS in the last 30 days. It’s not just about finding what’s idle. It’s about knowing what’s safe to ignore—and what isn’t—before it affects your next deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips on AWS Database Remediation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Tag Rigorously
&lt;/h3&gt;

&lt;p&gt;Tagging is only useful if it’s opinionated and enforced. It's not enough to label something “dev” or “test”—your tags should declare intent. Tags like &lt;code&gt;Owner&lt;/code&gt; or &lt;code&gt;Purpose&lt;/code&gt; are just the start. Introduce tags that reflect lifecycle (&lt;code&gt;TTL&lt;/code&gt;), cost center (&lt;code&gt;BillingCode&lt;/code&gt;), and compliance scope (&lt;code&gt;DataSensitivity&lt;/code&gt;). These tags help automation scripts decide what to ignore, what to archive, and what to shut down without involving a human.&lt;/p&gt;

&lt;p&gt;For example, when provisioning a database, inject a tag set that answers three questions: who owns it, when does it expire, and what kind of data lives inside?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds create-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; campaign-metrics &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-instance-class&lt;/span&gt; db.t3.small &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--engine&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Owner,Value&lt;span class="o"&gt;=&lt;/span&gt;team.analytics &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;TTL,Value&lt;span class="o"&gt;=&lt;/span&gt;2024-09-30 &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;DataSensitivity,Value&lt;span class="o"&gt;=&lt;/span&gt;PII &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;BillingCode,Value&lt;span class="o"&gt;=&lt;/span&gt;MKT-Q3-2024
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enforce tagging at org level, use AWS Organizations tag policies to define allowed tag keys and value patterns. Combine that with Config rules that flag instances not matching policy. For high-sensitivity environments, pair tag-based access control with IAM condition keys to prevent untagged resources from being created altogether.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Embrace Snapshots
&lt;/h3&gt;

&lt;p&gt;Snapshots are more than just a safety net—they’re a critical part of your idle database strategy. Instead of keeping dormant databases online “just in case,” convert them into snapshots that can be retained in low-cost storage tiers or exported to S3 in a queryable format. This gives teams access to historical data without incurring compute or high IOPS charges.&lt;/p&gt;

&lt;p&gt;Use snapshot exports when the data needs to be queried periodically but doesn’t justify a running instance. For example, exporting a snapshot to S3 in Parquet format allows it to be queried directly with Athena:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds export-db-snapshot &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-snapshot-identifier&lt;/span&gt; campaign-metrics-final-snap &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-engine&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--s3-bucket-name&lt;/span&gt; analytics-historical-data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--iam-role-arn&lt;/span&gt; arn:aws:iam::123456789012:role/RDSExportRole &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--kms-key-id&lt;/span&gt; arn:aws:kms:us-east-1:123456789012:key/abc123xyz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For retention tracking, tag your snapshots with &lt;code&gt;RetentionWindow&lt;/code&gt; and &lt;code&gt;ArchiveStatus&lt;/code&gt;. Then use a scheduled Lambda function to delete anything beyond your defined policy. This keeps storage lean and aligns with internal data governance requirements—without needing a human to remember what’s safe to delete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Treat idle database management like documentation: not glamorous, but essential, and painful when neglected. The teams that stay ahead don’t rely on quarterly cleanups—they build systems that flag drift automatically and make deletion feel safe, not risky. It’s less about tooling and more about culture. Infrastructure that reflects the present, not the past, is easier to operate and trust.&lt;/p&gt;

&lt;p&gt;Cleaning up unused databases reduces more than cost—it removes cognitive load. Every forgotten RDS instance introduces friction: in audits, in migration plans, in monitoring noise. When you've got fewer unknowns, you don’t waste cycles second-guessing what’s still in use. Instead, your team can focus on the infrastructure that actually powers the business.&lt;/p&gt;

&lt;p&gt;The strongest environments aren’t the ones with the most controls—they’re the ones with the fewest surprises. Retiring what doesn’t serve a clear purpose clears the way for faster decisions, simpler security, and cleaner growth.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Find and Safely Remediate Idle Databases on AWS: Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q: What if I suspect a DB is idle but might be used quarterly or yearly?
&lt;/h3&gt;

&lt;p&gt;Start by expanding the observation window. Instead of reviewing week-over-week usage, query CloudWatch for trends over the past 90 to 180 days. Look for recognizable usage spikes tied to business cycles—end-of-quarter reporting, annual audits, or seasonal traffic simulations. If the pattern holds, label the instance accordingly and avoid full remediation.&lt;/p&gt;

&lt;p&gt;For these low-frequency workloads, consider shifting the database to a suspended or paused state if the engine supports it. Aurora Serverless, for example, can be configured to auto-pause after inactivity, which cuts compute costs while preserving availability. If pause isn’t supported, use Systems Manager or EventBridge to implement recurring stop/start routines aligned to expected activity windows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: How can automation help me avoid future idle database sprawl?
&lt;/h3&gt;

&lt;p&gt;The most effective automation happens before the database exists. Integrate tagging requirements into your provisioning pipelines so every new instance carries metadata like &lt;code&gt;Owner&lt;/code&gt;, &lt;code&gt;Project&lt;/code&gt;, and &lt;code&gt;ExpectedTTL&lt;/code&gt;. Then use AWS Config to continuously monitor for missing or misaligned tags and trigger remediation workflows.&lt;/p&gt;

&lt;p&gt;For ongoing detection, implement a scheduled Lambda that pulls metrics like &lt;code&gt;DatabaseConnections&lt;/code&gt; and &lt;code&gt;ReadIOPS&lt;/code&gt; across your RDS estate. Cross-reference those with a DynamoDB table that tracks expected activity profiles. When a database falls outside its expected behavior, tag it for review or trigger a notification for the owning team. This creates a lightweight but persistent idle detection loop that evolves with your infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: Are there any data compliance or regulatory concerns with deleting a DB?
&lt;/h3&gt;

&lt;p&gt;Yes—and they’re often not immediately visible in the AWS resource itself. Compliance risks usually live in the data schema, not the instance metadata. Before any deletion, assess whether the database contains records tied to financial audits, customer interactions, medical logs, or legal holds. These datasets often fall under industry or jurisdictional mandates for long-term retention.&lt;/p&gt;

&lt;p&gt;If data must be preserved, export it to an immutable medium. Use RDS snapshot exports to store the data in columnar formats like Parquet, which supports long-term archival and serverless querying. Apply S3 object tags like &lt;code&gt;DataClassification=Regulated&lt;/code&gt; and configure bucket policies that enforce encryption and access restrictions. This gives you retention without incurring compute costs or leaving services running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: Is there a single best solution for all idle databases?
&lt;/h3&gt;

&lt;p&gt;No, because each idle database represents a different context. Some are no-brainers to delete—test environments with zero connections for 30+ days. Others need to stick around for compliance, but not in their current form. The remediation strategy should reflect how often the data is accessed, what kind of data it holds, and the cost of keeping it online.&lt;/p&gt;

&lt;p&gt;Think in terms of tiers. For databases that are completely dormant, snapshot and delete. For those with intermittent access or unknown status, isolate and shrink. For compliance-hardened workloads, export and archive. The key is to match remediation depth with operational risk—so that the cleanup effort doesn’t create more problems than it solves.&lt;/p&gt;

&lt;p&gt;Managing idle databases isn’t just about cost—it's about clarity, control, and confidence in your infrastructure. When you know what's running and why, you can scale smarter and avoid painful surprises down the line.  &lt;/p&gt;

</description>
      <category>aws</category>
      <category>database</category>
      <category>cloud</category>
      <category>security</category>
    </item>
    <item>
      <title>Backend options for front-end developers - a deep overview</title>
      <dc:creator>Tom Granot</dc:creator>
      <pubDate>Sat, 15 Aug 2020 12:05:16 +0000</pubDate>
      <link>https://forem.com/tomgranot/backend-options-for-front-end-developers-a-deep-overview-4hhb</link>
      <guid>https://forem.com/tomgranot/backend-options-for-front-end-developers-a-deep-overview-4hhb</guid>
      <description>&lt;p&gt;I got into a Twitter chat culminating in &lt;a href="https://twitter.com/leeerob/status/1292804502756655106"&gt;this tweet&lt;/a&gt; with &lt;a href="https://leerob.io/"&gt;Lee Robinson&lt;/a&gt;:&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--P_P-LWGO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1194080814688079872/6qhYKGKC_normal.jpg" alt="Lee Robinson profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Lee Robinson
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @leeerob
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      &lt;a href="https://twitter.com/TomGranot"&gt;@TomGranot&lt;/a&gt; &lt;a href="https://twitter.com/purpleorangeye4"&gt;@purpleorangeye4&lt;/a&gt; I'm searching for a 500-1000 word document explaining tradeoffs at a high level for why you might choose: &lt;br&gt;&lt;br&gt;- Vanilla database&lt;br&gt;- Database client / ORM&lt;br&gt;- Auto-generated API over a database&lt;br&gt;- Fullstack framework&lt;br&gt;&lt;br&gt;It probably depends on what you want to build. That's the hard part.
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      12:46 PM - 10 Aug 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1292804502756655106" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1292804502756655106" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1292804502756655106" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;I love this suggestion for an article. I really do - his tweets suggest that he's truly in a bind about all the possibilities, which means (since he's a prominent developer) that a lot of other, more silent developers are too. He wrote his own version &lt;a href="https://leerob.io/blog/backend"&gt;here&lt;/a&gt;, but I figured I'd roll my own as well.&lt;/p&gt;

&lt;p&gt;Some context: up until recently I was a Site Reliability Engineer - an ops guy, tasked with making sure our entire stack works as it should, with all its different parts behaving nicely. This gives me some understanding of how different pieces fit together, and I think I could shed some light on the darker sides of the stack. &lt;/p&gt;

&lt;p&gt;Lee's article is very practical and down to the point. This article is a bit more "philosophical" in nature, and is aimed at people who want to get a "feel" for what all the different options out there are like. This usually implies more experienced developers, so if you're either just starting out or want the very practical, to the point answers to your questions - &lt;a href="https://leerob.io/blog/backend"&gt;go with Lee&lt;/a&gt;. Otherwise - strap in.&lt;/p&gt;

&lt;h1&gt;
  
  
  What's in a backend?
&lt;/h1&gt;

&lt;p&gt;I get the feeling that when Lee talks about a backend he's talking about a "data machine" - one that knows how to do your regular &lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete"&gt;CRUD&lt;/a&gt; activities, and lets you focus on your front-end logic instead of focusing on operational concerns.&lt;/p&gt;

&lt;p&gt;The backend, from my perspective, is the cornerstone of two - very different - concerns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Running "correct" software - your backend is responding correctly to your requests&lt;/li&gt;
&lt;li&gt;Running "performant" software - your backend is capable of handling the traffic you throw at it without wasting too much resources, in a quick and cost-effective fashion&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Generally speaking, this is also the order of importance - your software first and foremost has to do what it should, and then do it as fast and with as little operational concerns as possible.&lt;/p&gt;

&lt;p&gt;Following Lee's tweet, I am going to enumerate 4 different options, show some examples, and then discuss the tradeoffs. &lt;/p&gt;

&lt;p&gt;I'm making 4 (valid, in my book) assumptions here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We're talking about websites, and not various system services or more low-level applications / Machine Learning / Data Science stuff. Those "other" types of software are usually using a different type of front-end than the ones front-end devs are used to. &lt;a href="https://www.qt.io/"&gt;&lt;code&gt;Qt&lt;/code&gt;&lt;/a&gt; comes to mind for desktop apps, for example.&lt;/li&gt;
&lt;li&gt;We're intentionally disregarding the fact that multiple developers - and DevOps people and DBAs and sysadmins - need to work, maintain and run this software in production. We are talking about a single developer, working on a single application, on their own. The human facet of things plays so, so much into technology selection, and it's way too large a concept to dive into here.&lt;/li&gt;
&lt;li&gt;The "usual" flow of work for front-end devs is "call API, parse data, send to front". That means a lot of different backend APIs, all tailored towards a specific, "small" goal like setting a property of for an object or getting information about a cross-section of objects. &lt;/li&gt;
&lt;li&gt;Most front-end devs use JavaScript and its myriad of frameworks to write their application logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Option 1 - Vanilla Database (Database Client)
&lt;/h1&gt;

&lt;p&gt;This means that your backend is simply a database that you interface with directly. There are basically &lt;a href="https://en.wikipedia.org/wiki/NoSQL#Types_and_examples"&gt;four variants&lt;/a&gt; of databases you can go with here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Key-value Stores - &lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt;, &lt;a href="https://aws.amazon.com/dynamodb/"&gt;DynamoDB&lt;/a&gt;, etc.&lt;/li&gt;
&lt;li&gt;Relational Databases - &lt;a href="https://www.mysql.com/"&gt;MySQL&lt;/a&gt;, &lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt;, etc.&lt;/li&gt;
&lt;li&gt;NoSQL Databases - &lt;a href="https://www.mongodb.com/"&gt;MongoDB&lt;/a&gt;, &lt;a href="https://couchdb.apache.org/"&gt;CouchDB&lt;/a&gt;, etc.&lt;/li&gt;
&lt;li&gt;Graph Databases - Don't, unless you specifically have a need for them (and then you'd probably know everything in this article already).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The choice of database changes the way you interact with it. &lt;a href="https://en.wikipedia.org/wiki/Relational_database"&gt;Relational databases&lt;/a&gt; use SQL, &lt;a href="https://en.wikipedia.org/wiki/NoSQL"&gt;NoSQL&lt;/a&gt; databases have a variety of data models and thus have a variety of ways of interacting with them, and key-value stores usually allow you to &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt; key-value pairs.&lt;/p&gt;

&lt;p&gt;The list above is actually ordered by the level of complexity each database system presents to you, in my opinion. Using a key-value store is more like dealing with &lt;code&gt;localStorage&lt;/code&gt;, so should be somewhat familiar to front-end devs. SQL / NoSQL are.... more tricky.&lt;/p&gt;

&lt;p&gt;There's a misconception in the tweet, by the way - a database client and an ORM are two different things. A client is usually just library that allows you to run commands on the database (read: write SQL queries), whereas an ORM is usually another layer of abstraction above the database itself (read: write JavaScript code). I'll deal with ORMs in Option 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How complicated to deploy?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Relatively easy&lt;/em&gt;&lt;/strong&gt;. Setting up the database is really easy, especially with database addons / plugins by the leading push-to-deploy tools like &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;. The hard thing is choosing &lt;em&gt;which&lt;/em&gt; database to use, maintaining the database, watching that it behaves, optimising it, creating a schema for it, etc. It's the "cleanest" way to go about storing data - no layers of abstraction between you and the database - but it's for people who want to deal with databases (like me!).&lt;/p&gt;

&lt;p&gt;There is so much documentation about databases out there, it's insane. It's really easy to get confused. Choosing a database holds with it a very large set of considerations - most of which are completely irrelevant to the front-end developer.&lt;/p&gt;

&lt;p&gt;I can abstract some of the mystery away by noting that the choice of which database to use mainly depends on where your code runs. Figure out where you want to deploy to, then google for "How to set up a database on X", where "X" is your platform of choice (Heroku, Netlify, etc). Most of the platforms have a huge amount of documentation already, since they want you to come aboard. &lt;/p&gt;

&lt;p&gt;There's also the installation of the client library for that database, but that's usually an &lt;code&gt;npm install&lt;/code&gt; away.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much code do I have to write?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;A large amount (SQL / NoSQL) or a medium amount (key-value stores)&lt;/em&gt;&lt;/strong&gt;. Note that there's no API here. That means that where you would to do a &lt;code&gt;fetch&lt;/code&gt; before, you'd now need to write an SQL query to get the data you want, dispatch it to the database using a client (most databases have JS clients implemented as open-source libraries), then parse the response into form you want the data in. Same goes for updating data, just inversely (you have some data, then need to parse it into an SQL query to dispatch to the database). With data-heavy applications, that can mean hundreds (and often thousands) of different queries with varying length.&lt;/p&gt;

&lt;p&gt;Working with key-value stores is a bit easier, since you're writing &lt;code&gt;JSON&lt;/code&gt;-like (and sometimes actual &lt;code&gt;JSON&lt;/code&gt;) to the database. It still requires defining a general schema for your data, however, or you will quickly have a real mess on your hands.&lt;/p&gt;

&lt;h3&gt;
  
  
  How complex will my code be?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Quite complex (SQL / NoSQL), or not very (key-value stores).&lt;/em&gt;&lt;/strong&gt; I actually wanted to write that using SQL simplifies your code greatly - no extra APIs to learn - but that's assuming that SQL flows through your fingers. Most (good) backend devs I know speak fluent SQL, but from what I gather it's not something front-end tutorials and videos focus on. I'm doing my best to step out of my shoes and into the shoes of a front-end dev, so SQL fluency is not necessarily a common skill.&lt;/p&gt;

&lt;p&gt;That means that any code that has complex SQL queries can be considered complex. Same goes for whatever data structure NoSQL databases use, with the added concern that they are often less represented in online tutorials as their SQL counterparts. There's material out there, sure, just not as in the line of sight as SQL stuff.&lt;/p&gt;

&lt;p&gt;I have to note, however, that key-value stores are relatively straightforward if you're coming from JS, and aren't necessarily foreign-looking to most JavaScript devs, who are used to working with &lt;code&gt;JSON&lt;/code&gt; and JavaScript objects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I would opt for a database only if you really want to understand the lowermost abstraction in your stack that deals with persisting data. If that's not interesting for you, choose one of the other options. &lt;/p&gt;

&lt;h1&gt;
  
  
  Option 2 - an ORM (Object Relational Mapper)
&lt;/h1&gt;

&lt;p&gt;An ORM is another level of abstraction between you and the database. It allows you to call "familiar" constructs (read: objects) to perform common activities, instead of relying on raw queries.&lt;/p&gt;

&lt;p&gt;An example: you want to create a new item, that has a few values for the properties that define it. With an ORM, you would do so by calling the relevant ORM API for items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Item.create({property1: 'value1' , property2: 'value2', property3: 'value3'})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a raw SQL query, you would do it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;property1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;property2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;property3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This saves you a lot of SQL work, but is actually not the same as using a "normal" API endpoint. It's just a more comfortable wrapper around SQL queries, that is not custom-tailored to a specific need.&lt;/p&gt;

&lt;p&gt;In other words, you still work with tables - they're just exposed to you as JavaScript objects. There are much more sophisticated ORMs that read your database schema and do all sorts of magic with it, but at their core - ORMs are just wrappers around tables. They prevent you from dropping down to raw SQL. &lt;/p&gt;

&lt;p&gt;In option 3 I talk about another approach for the same idea, that tackles the same idea from a different approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How complicated to deploy?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Relatively easy&lt;/em&gt;&lt;/strong&gt;. ORMs still require you to deploy a database, and then install an ORM library for your framework of choice or vanilla JS (&lt;a href="https://sequelize.org/"&gt;Sequelize&lt;/a&gt; is an example of a JavaScript ORM). That's not that different from deploying a raw database.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much code do I have to write?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;A large amount (models + accessing the ORM)&lt;/em&gt;&lt;/strong&gt;. Since your ORM doesn't actually know how you want your data to be structured, you need to define Models for your code.  &lt;a href="https://sequelize.org/master/manual/model-basics.html"&gt;Sequlize's docs make a great intro&lt;/a&gt; for understanding what this means in practice, but for the sake of discussion you can think about as creating "virtual" tables. &lt;/p&gt;

&lt;p&gt;This means that you're still doing basically the same thing you were doing with raw SQL queries - but instead of defining the tables in the database and then querying them from your code, you're defining your models in your code and the ORM creates the tables for you. This can take quite a lot of code if you have a lot of tables.&lt;/p&gt;

&lt;p&gt;The rest is interacting with those tables via the ORM - which is usually around same amount of code as using raw SQL queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  How complex will my code be?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Not very&lt;/em&gt;&lt;/strong&gt;. Your code will be entirely JavaScript - no SQL. This provides for a much more native experience. The only "new" thing will be the ORM library's code, which is usually straightforward (&lt;code&gt;Tablename.CRUDAction({propertiesObject}&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This choice is still somewhat verbose, and is basically one step up from interacting with the database directly. Option 3 details a path that offers a somewhat different way of thinking and resembles your current way of working, with REST-style APIs, more closely.&lt;/p&gt;

&lt;h1&gt;
  
  
  Option 3 - Auto-generated API over a database
&lt;/h1&gt;

&lt;p&gt;This option is somewhat tricky to explain, because there are a few technologies that are all considered some variant of "API auto-generation", but are in fact very different things. These include software that turns a database into an API (like &lt;a href="https://github.com/hasura/graphql-engine"&gt;Hasura&lt;/a&gt;), and databases that come with an auto-generated API out of the box (like &lt;a href="https://github.com/apache/couchdb"&gt;CouchDB&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;These are more like "traditional" backend APIs, in the sense that they abstract away the need to deal with the database at all - and instead just give you an API you can &lt;code&gt;fetch&lt;/code&gt; to and from. This means that you get all the information in the format you're used to - &lt;code&gt;JSON&lt;/code&gt; - and there're no parts in the middle.&lt;/p&gt;

&lt;p&gt;Note that this does not mean you are exempt from modelling the data in your database. The auto-generated API still relies on you telling it how is the information you want to use is modelled like. The nice part, though, is that once you model your data you don't really need to touch it anymore. Everything else is done via familiar APIs.&lt;/p&gt;

&lt;p&gt;One comment - there's a technology called &lt;a href="https://graphql.org/"&gt;GraphQL&lt;/a&gt; that allows you to query APIs just like you would query a database, i.e. using a query language. This means you can use a single GraphQL call to the queryroot (a GraphQL system's main API endpoint) instead of mixing-and-matching different, multiple API queries.&lt;/p&gt;

&lt;p&gt;Hasura creates a GraphQL API over a database, while CouchDB only allows you to access the database via an API. It's a tricky differentiation to make, but I would say those are two completely different worlds, and one should not confuse the two. What I'm referring to in this article is Hasura-like services, not CounchDB-like ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How complicated to deploy?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Really easy&lt;/em&gt;&lt;/strong&gt;. Especially with &lt;a href="https://github.com/hasura/graphql-engine"&gt;Hasura&lt;/a&gt; and &lt;a href="https://hasura.io/cloud/"&gt;HasuraCloud&lt;/a&gt;, getting up and running is very fast. The service is there, you model your data and you're good to go. &lt;/p&gt;

&lt;h3&gt;
  
  
  How much code do I have to write?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Probably less than you would have before&lt;/em&gt;.&lt;/strong&gt; An auto-generated API is basically not a change at all from the way you used to work. You call an API exactly like you used before. The only difference is that the source of the API is not some backend code crafted by a developer, but an automated API over your database.&lt;/p&gt;

&lt;p&gt;Especially with GraphQL, you're looking at shaving off a lot of different API calls, which will result in you writing less code. You will have to, however, define your models in your database / HasuraCloud console, which - as you can probably see by now - is part of the cost of playing.&lt;/p&gt;

&lt;p&gt;One comment though: since you're working with a model of the database, expect that building your logic might sometimes be more verbose than what you would have with dedicated backend API endpoints. This really depends on what you are trying to create, and deserves an entirely different discussion. Creating data models is truly an art form, and part of the reason why hardcore programmers are so, so much more efficient than their peers - they're using the correct model for their problem. &lt;/p&gt;

&lt;h3&gt;
  
  
  How complex will my code be?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Generally simple&lt;/em&gt;&lt;/strong&gt;. An auto-generated API is, in many ways, a front-ender's dream come true - an almost full abstraction of the backend. There's no SQL to write and the flow of work is similar to what you're used to - there's an API, right there in front of you, for the taking.&lt;/p&gt;

&lt;p&gt;If you modelled your data correctly before, then the same logic you used previously will probably work here as well. If you're migrating, though, it's probably a good idea to re-think the model and see whether you can simplify it to reduce the number of API calls you're making.&lt;/p&gt;

&lt;p&gt;If your old APIs were very complicated and specific, you might find that this new model allows for much more expressiveness with significantly less code. I dislike generalisations and catchphrases, but these services are a gold mine for most applications.&lt;/p&gt;

&lt;p&gt;GraphQL itself is, however, somewhat foreign even for SQL veterans.  It has a small learning curve, but there is legitimately amazing material out there - &lt;a href="https://www.howtographql.com/"&gt;like this&lt;/a&gt; - that will take you all the way with your existing set of tools and frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you're attempting to abstract away the backend, go with GraphQL over a database, like Hasura.&lt;/p&gt;

&lt;h1&gt;
  
  
  Option 4 - Full Stack Framework
&lt;/h1&gt;

&lt;p&gt;A full-stack JavaScript framework - like &lt;a href="https://redwoodjs.com/videos/tutorial"&gt;Redwood&lt;/a&gt; - combines all you need to get a fully-functional web-app without the hassles of the separation of concerns - namely a backend and a frontend. It's a different type of philosophy, aiming to create a "unified" experience for you as a developer.&lt;/p&gt;

&lt;p&gt;In practice, a full-stack framework is usually a combination of one of the ideas I mentioned before with the other, "normal" front-end parts of the application. Redwood is based around &lt;a href="https://www.prisma.io/"&gt;Prisma&lt;/a&gt;, which is a database toolkit (but you can think of it, for the sake of simplicity, as a type of very advanced and easy to use ORM), and uses GraphQL and React under the hood. The beauty of wrapping all of the relevant tools needed for an application in one bundle comes from the ability to stay in the same "state of mind" all of the way - everything is JavaScript, everything is available from the same "vendor" (i.e. your framework) and generally speaking you can "do it all" on your own.&lt;/p&gt;

&lt;p&gt;If I had to guess, I'd say that this is where the web is going to - a consolidated JS experience for developers, operations people and anyone in between. &lt;/p&gt;

&lt;h2&gt;
  
  
  Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How complicated to deploy?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Relatively easy&lt;/em&gt;&lt;/strong&gt;. Everything is available outside of the box, which means that deploying the framework is as easy as finding a place to host it. Pretty much the same as the other options, albeit with all the documentation and concepts under the same roof - the framework's docs.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much code do I have to write?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Probably less that you would have before&lt;/em&gt;&lt;/strong&gt;. Since you are modelling your own data under the hood, you still need to define how it's going to be built. So writing full-stack code is comprised of defining how your data looks like and then using those definitions to write actual application logic. Pretty similar to the amount of code you would have written in Option 3.&lt;/p&gt;

&lt;h3&gt;
  
  
  How complex will my code be?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Generally simple&lt;/em&gt;&lt;/strong&gt;. Again, it's all JavaScript - but you have to get familiar with the framework's syntax, which might scare away some people afraid of being "boxed" you into the framework. Fear not - Redwood, for example, utilises well-known open-source projects in the mix, so knowledge you gain by using the platform can generally be later transformed into other, adjacent worlds.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Full-stack frameworks are not yet popular enough to be considered the "de facto standard" of the future, but it sure does fell like they're getting there. I would suggest going first with something a bit more established like an auto-generated API (Hasura) and then make your way to a full-stack framework if it becomes too much to handle.&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrapping it all up
&lt;/h1&gt;

&lt;p&gt;We've went on quite a journey here.&lt;/p&gt;

&lt;p&gt;I'd like to sign off with a personal message. I'm a systems guy - I like dealing with the nitty gritty, with trying different deployment options, with looking at why is my memory running out, on destructuring complicated infrastructure and building it all up again. That means I'm a generalist, rather than a specialist.&lt;/p&gt;

&lt;p&gt;That doesn't mean, though, that you have to be one too. There's a whole world of content on both ends of the spectrum. Learn about what interests you most, go deep instead of wide if you want to, and mostly just enjoy the run. There are enough people working on the foundations for your dream project right now - you don't have to build (or even understand) everything yourself.&lt;/p&gt;

&lt;p&gt;It does, however, mean you need to share your knowledge, so that other - oppositely-inclined - people would be able to benefit the same that you have. Spend time writing detailed GitHub issues, and blog posts, and tutorial videos.&lt;/p&gt;

&lt;p&gt;We're all in this together.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Questions? Comments? Hit me up in a private message or leave a comment here.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>database</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Learning with books vs. tutorials</title>
      <dc:creator>Tom Granot</dc:creator>
      <pubDate>Sun, 02 Aug 2020 09:39:51 +0000</pubDate>
      <link>https://forem.com/tomgranot/learning-with-books-vs-tutorials-54db</link>
      <guid>https://forem.com/tomgranot/learning-with-books-vs-tutorials-54db</guid>
      <description>&lt;p&gt;I'm currently re-learning Rust - I picked it up a while back to be able to better debug issues at work, but didn't have the chance to write a lot of production-ready code with it.&lt;/p&gt;

&lt;p&gt;In order to fully understand all the intricacies of the language, I picked up an open-source side project to participate in. But before that, I'm going to go deep on the language's syntax and methodology by reading &lt;a href="https://doc.rust-lang.org/book/"&gt;The Book&lt;/a&gt;, i.e. The Rust Programming Languge.&lt;/p&gt;

&lt;p&gt;What is your preference? Do you run through tutorials and then reach out for project, or do are you a bookworm like me?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>beginners</category>
      <category>learning</category>
      <category>rust</category>
    </item>
    <item>
      <title>Why shall you shell as a webdev? And other terminal topics </title>
      <dc:creator>Tom Granot</dc:creator>
      <pubDate>Wed, 29 Jul 2020 07:13:15 +0000</pubDate>
      <link>https://forem.com/tomgranot/why-shall-you-shell-as-a-webdev-and-other-terminal-topics-3o7g</link>
      <guid>https://forem.com/tomgranot/why-shall-you-shell-as-a-webdev-and-other-terminal-topics-3o7g</guid>
      <description>&lt;p&gt;I have a great affinity for textual interfaces.&lt;/p&gt;

&lt;p&gt;Knowing my way around a terminal has, by far and wide, been the skill with the highest ROI (Return On Investment)  in my toolbelt.&lt;/p&gt;

&lt;p&gt;It enables you to, in no particular order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create highly-customized, used-on-the-daily &lt;a href="https://github.com/k4m4/terminals-are-sexy#readme"&gt;programmer workspaces&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Automate routine tasks with a &lt;a href="https://github.com/alebcay/awesome-shell#command-line-productivity"&gt;single command&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Easily and immediately observe the status of your machine, external services and &lt;a href="https://github.com/agarrharr/awesome-cli-apps"&gt;basically everything else&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Run matrix-style commands:
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--igx1Bgg---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://media1.giphy.com/media/JmJMzlXOiI0dq/giphy.gif" alt="matrix"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Yes, this is a Windows CMD GIF, and not a Linux terminal one. I regret nothing.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Discuss!
&lt;/h2&gt;

&lt;p&gt;I can not recommend learning the terminal enough. It's a real treat, and I think its versatility practically ensures there's a use case for it somewhere in your workflow - even for non-programmers!&lt;/p&gt;

&lt;p&gt;What do you think? Shall everyone shell, or is GUI king of the realm?&lt;/p&gt;

&lt;p&gt;And specifically - how important are terminal skills for the average web developer? &lt;/p&gt;

&lt;h3&gt;
  
  
  Side Note
&lt;/h3&gt;

&lt;p&gt;See the cover image? Try running &lt;code&gt;curl parrot.live&lt;/code&gt; from your terminal, and you can get the party parrot too!&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://github.com/hugomd/parrot.live"&gt;here&lt;/a&gt; for more information.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>linux</category>
      <category>discuss</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Docker shell shortcuts - because writing full commands is hard!</title>
      <dc:creator>Tom Granot</dc:creator>
      <pubDate>Tue, 28 Jul 2020 22:30:31 +0000</pubDate>
      <link>https://forem.com/tomgranot/docker-shell-shortcuts-because-writing-full-commands-is-hard-33h</link>
      <guid>https://forem.com/tomgranot/docker-shell-shortcuts-because-writing-full-commands-is-hard-33h</guid>
      <description>&lt;h2&gt;
  
  
  Containers for fun and profit!
&lt;/h2&gt;

&lt;p&gt;So you're now officially excited about Docker and containers.&lt;/p&gt;

&lt;p&gt;Good! They're an awesome tool to create portable configurations for your software and make sure it works seamlessly across multiple devices.&lt;/p&gt;

&lt;p&gt;If you've been around them long enough, though, you're probably using &lt;a href="https://docs.docker.com/engine/reference/commandline/cli/" rel="noopener noreferrer"&gt;Docker engine's CLI&lt;/a&gt; (command line interface) quite often.&lt;/p&gt;

&lt;p&gt;Commands like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; 1x81hs72k2s9 /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker start container-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Are probably all over your shell history.&lt;/p&gt;

&lt;p&gt;I had the same problem, and so I made a small hack and uploaded it to my slowly-expanding Useful Snippets repository (which you can totally star if you feel like it :) ):&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/TomGranot" rel="noopener noreferrer"&gt;
        TomGranot
      &lt;/a&gt; / &lt;a href="https://github.com/TomGranot/useful-snippets" rel="noopener noreferrer"&gt;
        useful-snippets
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Useful things I made/found over time
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;useful-snippets&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;In chronological order of addition, a bunch of useful stuff kept here for persistence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Base64-Encoded Flag Images (as JSON!) - &lt;a href="https://github.com/TomGranot/useful-snippetsbase64CountriesFlagJson" rel="noopener noreferrer"&gt;link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Some shell snippets for a better Docker experience - &lt;a href="https://github.com/TomGranot/useful-snippetsdocker-bashrc" rel="noopener noreferrer"&gt;link&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TomGranot/useful-snippets" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
 

&lt;h2&gt;
  
  
  Money time
&lt;/h2&gt;

&lt;p&gt;Specifically, if you go &lt;a href="https://github.com/tomgs/useful-snippets/blob/master/docker-bashrc/docker-bashrc.sh" rel="noopener noreferrer"&gt;here&lt;/a&gt; you'll see the following snippets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Get container id of existing container&lt;/span&gt;
dgrep&lt;span class="o"&gt;(){&lt;/span&gt;
    docker ps &lt;span class="nt"&gt;-a&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-c1-12&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# bash into an existing container&lt;/span&gt;
dbash&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; /bin/bash
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# sh into an existing container&lt;/span&gt;
dsh&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; /bin/sh
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Execute something in an existing container&lt;/span&gt;
dex&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Start an existing container&lt;/span&gt;
dstart&lt;span class="o"&gt;(){&lt;/span&gt;
    docker start &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Stop an existing container&lt;/span&gt;
dstop&lt;span class="o"&gt;(){&lt;/span&gt;
    docker stop &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Remove an existing container&lt;/span&gt;
drm&lt;span class="o"&gt;(){&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Stop and remove an existing container&lt;/span&gt;
dsrm&lt;span class="o"&gt;(){&lt;/span&gt;
    dstop &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; drm &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Remove an existing image&lt;/span&gt;
dirm&lt;span class="o"&gt;(){&lt;/span&gt;
    docker image remove &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Get logs of an existing container&lt;/span&gt;
dlog&lt;span class="o"&gt;(){&lt;/span&gt;
    docker logs &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These go inside your shell's configuration file, probably located at &lt;code&gt;~/.bashrc&lt;/code&gt; if you're on one of the Linux distros and older macs, or &lt;code&gt;~/.zshrc&lt;/code&gt; if you're on newer macs.&lt;/p&gt;

&lt;p&gt;After you put those commands inside your shell configuration file, whenever your shell starts up they will become available for use inside your session - just like regular shell commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shell functions as command aliases
&lt;/h2&gt;

&lt;p&gt;Note that what I did up there was define shell &lt;strong&gt;functions&lt;/strong&gt; that perform specific commands, and take &lt;strong&gt;arguments&lt;/strong&gt; just like regular shell commands.&lt;/p&gt;

&lt;p&gt;This goes in contrast to defining shell &lt;em&gt;aliases&lt;/em&gt;, like Nick Taylor does &lt;a href="https://dev.to/nickytonline/my-shell-aliases-1obk"&gt;here&lt;/a&gt; (for the most part), and allows us to pass arguments to our "aliases".&lt;/p&gt;

&lt;p&gt;This was a bit of a mouthful. Let's consider, as an example, the &lt;code&gt;dgrep&lt;/code&gt; function I defined earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get container id of existing container&lt;/span&gt;
dgrep&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    docker ps &lt;span class="nt"&gt;-a&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-c1-12&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dgrep()&lt;/code&gt; is a &lt;em&gt;shell function&lt;/em&gt; that accepts an unlimited amount of arguments (as shell functions do). In order to access the arguments provided to the function inside the function's body, we use the &lt;code&gt;$X&lt;/code&gt; notation, where &lt;code&gt;X&lt;/code&gt; is an integer number.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$0&lt;/code&gt; always refers to the name of the executing command, so if you're running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;my-command first-tom second-tom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;my-command&lt;/code&gt; is defined as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;my-command&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the output will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;my-command
1
2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coming back to our &lt;code&gt;dgrep&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get container id of existing container&lt;/span&gt;
dgrep&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    docker ps &lt;span class="nt"&gt;-a&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-c1-12&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that what it's doing is getting the contents of &lt;a href="https://docs.docker.com/engine/reference/commandline/ps/" rel="noopener noreferrer"&gt;&lt;u&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/u&gt;&lt;/a&gt; (that is a list of all running containers), then &lt;a href="https://dev.to/yashsugandh/piping-in-linux-system-4456"&gt;piping&lt;/a&gt; the input into &lt;a href="https://dev.to/antjanus/demystifying-grep-a1o"&gt;&lt;u&gt;&lt;code&gt;grep&lt;/code&gt;&lt;/u&gt;&lt;/a&gt;, which returns any match to the searched text with the provided argument.&lt;/p&gt;

&lt;p&gt;In our case, it's "grepping" for &lt;code&gt;$1&lt;/code&gt;, which is the first argument provided to &lt;code&gt;dgrep&lt;/code&gt;. It finally pipes &lt;em&gt;that&lt;/em&gt; output into &lt;code&gt;cut -c1-12&lt;/code&gt; that gets the first 12 characters of the piped text (which is, coincidentally, exactly the length of Docker container IDs).&lt;/p&gt;

&lt;p&gt;For example, if you're running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dgrep tom-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then if &lt;code&gt;tom-container&lt;/code&gt; is a running container, &lt;code&gt;docker ps&lt;/code&gt; will return a string containing all the information the Docker engine has about that container.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dgrep&lt;/code&gt; will then pipe it into the &lt;code&gt;cut&lt;/code&gt; command, and print out only the container ID of that container.&lt;/p&gt;

&lt;h2&gt;
  
  
  But why do I need that?
&lt;/h2&gt;

&lt;p&gt;Excellent question. Remember our example from the beginning of the article, where we used the full container ID to refer to the container?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; 1x81hs72k2s9 /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nobody expects you to remember the full names or the IDs of your containers. We can now re-write the command as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;dgrep substring-in-container-name&lt;span class="si"&gt;)&lt;/span&gt; /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;$()&lt;/code&gt; is a &lt;a href="https://bash.cyberciti.biz/guide/What_is_a_Subshell%3F" rel="noopener noreferrer"&gt;subshell&lt;/a&gt; - a special command that executes whatever is provided to it, and sends out the output as text before running the rest of the command.&lt;/p&gt;

&lt;p&gt;In our case, &lt;code&gt;$(dgrep substring-in-container-name)&lt;/code&gt; will find us a container ID based on some substring in the name (like &lt;code&gt;my&lt;/code&gt; in &lt;code&gt;my-very-long-and-hard-to-remember-container-name&lt;/code&gt;), and pass it to the &lt;code&gt;docker exec -ti CONTAINER-ID /bin/bash&lt;/code&gt; command as &lt;code&gt;CONTAINER-ID&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, but that's still kinda long...
&lt;/h2&gt;

&lt;p&gt;You're right! "Bashing" into a container (i.e. running &lt;code&gt;bash&lt;/code&gt; inside that container interactively, which is basically "opening" a terminal to that container) is something we do quite often to debug long-running containers, and to do other fun filesystem shenanigans (&lt;a href="//mailto:tom@kidonpoint.com"&gt;ask me about that if you'd like to know more!&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;In comes &lt;code&gt;dbash()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# bash into an existing container&lt;/span&gt;
dbash&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;dgrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; /bin/bash
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, &lt;code&gt;dbash&lt;/code&gt; is calling the previously mentioned &lt;code&gt;dgrep&lt;/code&gt; in a subshell, and passes that subshell the argument provided to &lt;code&gt;dbash&lt;/code&gt; - namely a substring of the relevant container.&lt;/p&gt;

&lt;p&gt;This eventually lets us do something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbash tom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And get a terminal to the first container on the list of running containers that has &lt;code&gt;tom&lt;/code&gt; in its human-readable name.&lt;/p&gt;

&lt;p&gt;Pretty neat, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Containers are fun. And so is working with the shell!&lt;/p&gt;

&lt;p&gt;I literally &lt;em&gt;just&lt;/em&gt; made a really nice hack using Docker for an &lt;a href="https://github.com/FedericoPonzi/Horust/pull/41" rel="noopener noreferrer"&gt;open-source project&lt;/a&gt; I'm working on with my good friend, Federico. Feel free to take a look if you're into container wizardry!&lt;/p&gt;

&lt;h3&gt;
  
  
  Side note
&lt;/h3&gt;

&lt;p&gt;An observant reader would note that I can save one pipe by using &lt;a href="https://docs.docker.com/config/formatting/" rel="noopener noreferrer"&gt;Docker command formatting&lt;/a&gt;. That reader would be correct, but I think my syntax is simpler and easier to explain to beginners, so I stuck with it for this tutorial (and, honestly, for my own &lt;code&gt;~/.zshrc&lt;/code&gt; as well). &lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>beginners</category>
      <category>shell</category>
    </item>
    <item>
      <title>Quick tip: Increase keyboard repeat rate for faster typing</title>
      <dc:creator>Tom Granot</dc:creator>
      <pubDate>Mon, 27 Jul 2020 21:33:01 +0000</pubDate>
      <link>https://forem.com/tomgranot/quick-tip-increase-keyboard-repeat-rate-for-faster-typing-4d8g</link>
      <guid>https://forem.com/tomgranot/quick-tip-increase-keyboard-repeat-rate-for-faster-typing-4d8g</guid>
      <description>&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;This a short post to remind myself that some fruits are hanging so, so low, that you're a dummy for not thinking about them yourself.&lt;/p&gt;

&lt;p&gt;If you're not a master of keyboard shortcuts to run around textual interfaces (like your code editor or your terminal), you've probably done the following many a time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write many words of text.&lt;/li&gt;
&lt;li&gt;Figure out you made a mistake a few words back.&lt;/li&gt;
&lt;li&gt;Press and hold the left key button until you reach the word you need to fix.&lt;/li&gt;
&lt;li&gt;Fix word.&lt;/li&gt;
&lt;li&gt;Press and hold the right key button until you reach the place you were before.&lt;/li&gt;
&lt;li&gt;Repeat ad infinitum.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are two potential paths for improvement here, both revolving around steps 3 &amp;amp; 5.&lt;/p&gt;

&lt;h2&gt;
  
  
  Become better at keyboard shortcuts
&lt;/h2&gt;

&lt;p&gt;This will &lt;strong&gt;replace&lt;/strong&gt; steps 3 &amp;amp; 5 with a combination of keys that performs the required action.&lt;/p&gt;

&lt;p&gt;This is a worthwhile pursuit, as you will be spending insane amounts of time riding a keyboard. Check out &lt;a href="https://dev.to/designpuddle/27-no-frills-keyboard-shortcuts-every-developer-should-follow-4jd"&gt;this dev.to article&lt;/a&gt; for some great ones.&lt;/p&gt;

&lt;p&gt;It does, however, take some time to get used to keyboard shortcuts, and if you're anything like me - i.e. always changing environments between remote servers, VMs, containers, different local code editors and basically any modern website with WYSISWYG capabilities - then you will have to memorize a large amount of shortcuts. This can get tiring very fast.&lt;/p&gt;

&lt;p&gt;Solution: if &lt;em&gt;you&lt;/em&gt; can't be faster, make your &lt;em&gt;computer&lt;/em&gt; run faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Increase keyboard repeat rate
&lt;/h2&gt;

&lt;p&gt;This will &lt;strong&gt;increase the speed&lt;/strong&gt; of steps 3 &amp;amp; 5 with a neat keyboard trick.&lt;/p&gt;

&lt;p&gt;The keyboard repeat &lt;em&gt;delay&lt;/em&gt; of your machine is the amount of time that the computer waits between each &lt;em&gt;repeat&lt;/em&gt; action you make.&lt;/p&gt;

&lt;p&gt;In our case, it's the amount of time that passes between each "go left" (step 3) or "go right" (step 5) operations.&lt;/p&gt;

&lt;p&gt;If we &lt;em&gt;decrease&lt;/em&gt; that delay, we &lt;em&gt;increase&lt;/em&gt; the rate at which those actions can happen in a sequence. This rate is called the keyboard repeat &lt;em&gt;rate&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A cursor on a machine with a fast keyboard repeat rate looks much, much faster than it does on your current machine. It will basically ping-pong across lines of text, dashing to wherever you need it to be.&lt;/p&gt;

&lt;p&gt;Here's how to employ this trick on different platforms - add a comment if you need some help or find this super-duper-cool like I did! &lt;/p&gt;

&lt;h2&gt;
  
  
  Mac OS
&lt;/h2&gt;

&lt;p&gt;This can be done either graphcially, using System Preferences --&amp;gt; Keyboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkkq57tpz9mb62477q3pb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkkq57tpz9mb62477q3pb.png" alt="The GUI way"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or textually, using the command line - just add the following commands to your shell user's config file, usually &lt;code&gt;~/.zshrc&lt;/code&gt; on newer macs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set a blazingly fast keyboard repeat rate&lt;/span&gt;
defaults write NSGlobalDomain KeyRepeat &lt;span class="nt"&gt;-int&lt;/span&gt; 1
defaults write NSGlobalDomain InitialKeyRepeat &lt;span class="nt"&gt;-int&lt;/span&gt; 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Most Linux distros
&lt;/h2&gt;

&lt;p&gt;Same, except swap &lt;code&gt;~/.zshrc&lt;/code&gt; for &lt;code&gt;~/.bashrc&lt;/code&gt;. Graphical way changes between distros, so I can't really attest to that.&lt;/p&gt;

&lt;p&gt;Also, you're a Linux user. Use your shell! :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows
&lt;/h2&gt;

&lt;p&gt;Untested - see &lt;a href="https://bltt.org/adjust-windows-keyboard-repeat-rate/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Props
&lt;/h2&gt;

&lt;p&gt;Original idea from &lt;a href="https://github.com/mathiasbynens/dotfiles/blob/main/.macos#L154-L156" rel="noopener noreferrer"&gt;here&lt;/a&gt; and the ever-awesome &lt;a href="https://github.com/mathiasbynens" rel="noopener noreferrer"&gt;Mathias Bynens&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>learning</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
