Back to Blog

Cloud FinOps in 2026: Cost Tagging, Rightsizing, and Eliminating Waste

Control cloud costs with FinOps: AWS cost allocation tags, rightsizing recommendations, reserved instance strategy, Terraform cost estimation, and eliminating common waste patterns.

Viprasol Tech Team
July 19, 2026
12 min read

Cloud FinOps in 2026: Cost Tagging, Rightsizing, and Eliminating Waste

Most engineering teams have no idea what their cloud infrastructure costs per feature, per team, or per customer. They get a monthly AWS bill and try to attribute it after the fact — which is nearly impossible without a tagging strategy designed upfront.

FinOps (Financial Operations) is the discipline of making cloud spend visible, accountable, and optimizable. This post covers the three fundamentals: tagging everything consistently, rightsizing underutilized resources, and finding the quick wins that typically cut 20–40% of cloud spend without touching architecture.


The Cost Visibility Problem

Monthly AWS bill: $47,000

Questions you can't answer without tagging:
- How much does the payments feature cost to run?
- Which team is responsible for the $8,000 data transfer line item?
- What does it cost to serve one customer?
- Which environment (dev/staging/prod) is most expensive?
- Are we being charged for resources no one is using?

Without tags, the bill is a lump sum. With proper tagging, every dollar is attributable.


Tagging Strategy: The Minimum Viable Taxonomy

# Mandatory tags on every AWS resource
Required tags:
  Environment:   "production" | "staging" | "development" | "sandbox"
  Team:          "platform" | "payments" | "growth" | "data"
  Service:       "orders-api" | "auth-service" | "analytics-pipeline"
  CostCenter:    "engineering" | "data-science" | "infrastructure"
  ManagedBy:     "terraform" | "manual" | "cdk"

Optional but recommended:
  Customer:      customer ID (for customer-specific infrastructure)
  Feature:       feature flag or project name
  Expiry:        ISO date (for sandbox/temporary resources)

Enforcing Tags with AWS Config

# terraform/tag-enforcement.tf
resource "aws_config_config_rule" "required_tags" {
  name = "required-tags"

  source {
    owner             = "AWS"
    source_identifier = "REQUIRED_TAGS"
  }

  input_parameters = jsonencode({
    tag1Key   = "Environment"
    tag2Key   = "Team"
    tag3Key   = "Service"
    tag4Key   = "CostCenter"
  })

  scope {
    compliance_resource_types = [
      "AWS::EC2::Instance",
      "AWS::RDS::DBInstance",
      "AWS::ElasticLoadBalancingV2::LoadBalancer",
      "AWS::S3::Bucket",
      "AWS::ECS::Service",
      "AWS::Lambda::Function",
    ]
  }
}

# Alert when untagged resources are created
resource "aws_config_remediation_configuration" "auto_tag" {
  config_rule_name = aws_config_config_rule.required_tags.name
  target_type      = "SSM_DOCUMENT"
  target_id        = "AWS-AddTagsToResources"
  automatic        = false  # Don't auto-tag; alert instead

  parameter {
    name         = "AutomationAssumeRole"
    static_value = aws_iam_role.config_remediation.arn
  }
}

Tagging in Terraform: Default Tags

# providers.tf — default tags on all AWS resources
provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      Environment = var.environment
      ManagedBy   = "terraform"
      Team        = var.team
      CostCenter  = var.cost_center
      TerraformWorkspace = terraform.workspace
    }
  }
}

# Resource-specific tags supplement defaults
resource "aws_ecs_service" "orders_api" {
  name    = "orders-api"
  # ... config ...

  tags = {
    Service = "orders-api"
    # Default tags (Environment, Team, etc.) auto-applied by provider
  }
}

Tagging GitHub Actions Resources

# .github/workflows/deploy.yml — tag resources created in CI
env:
  AWS_DEFAULT_TAGS: |
    {
      "Environment": "${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}",
      "Team": "platform",
      "Service": "${{ github.event.repository.name }}",
      "DeployedBy": "github-actions",
      "CommitSha": "${{ github.sha }}"
    }

☁️ Is Your Cloud Costing Too Much?

Most teams overspend 30–40% on cloud — wrong instance types, no reserved pricing, bloated storage. We audit, right-size, and automate your infrastructure.

  • AWS, GCP, Azure certified engineers
  • Infrastructure as Code (Terraform, CDK)
  • Docker, Kubernetes, GitHub Actions CI/CD
  • Typical audit recovers $500–$3,000/month in savings

Cost Attribution with AWS Cost Explorer API

// scripts/cost-report.ts — weekly cost report per team
import {
  CostExplorerClient,
  GetCostAndUsageCommand,
  Granularity,
} from '@aws-sdk/client-cost-explorer';

const client = new CostExplorerClient({ region: 'us-east-1' });

interface TeamCost {
  team: string;
  totalCost: number;
  services: { service: string; cost: number }[];
}

async function getTeamCosts(startDate: string, endDate: string): Promise<TeamCost[]> {
  const response = await client.send(
    new GetCostAndUsageCommand({
      TimePeriod: { Start: startDate, End: endDate },
      Granularity: Granularity.MONTHLY,
      GroupBy: [
        { Type: 'TAG', Key: 'Team' },
        { Type: 'DIMENSION', Key: 'SERVICE' },
      ],
      Metrics: ['UnblendedCost'],
      Filter: {
        Not: {
          Dimensions: {
            Key: 'RECORD_TYPE',
            Values: ['Credit', 'Refund'],
          },
        },
      },
    }),
  );

  const teamMap = new Map<string, TeamCost>();

  for (const result of response.ResultsByTime ?? []) {
    for (const group of result.Groups ?? []) {
      const [teamTag, service] = group.Keys ?? [];
      const team = teamTag.replace('Team$', '') || 'untagged';
      const cost = parseFloat(group.Metrics?.UnblendedCost?.Amount ?? '0');

      if (!teamMap.has(team)) {
        teamMap.set(team, { team, totalCost: 0, services: [] });
      }

      const teamData = teamMap.get(team)!;
      teamData.totalCost += cost;
      teamData.services.push({ service, cost });
    }
  }

  return Array.from(teamMap.values()).sort((a, b) => b.totalCost - a.totalCost);
}

// Run weekly and post to Slack
async function sendWeeklyCostReport() {
  const end = new Date().toISOString().split('T')[0];
  const start = new Date(Date.now() - 7 * 86400000).toISOString().split('T')[0];

  const costs = await getTeamCosts(start, end);

  const total = costs.reduce((s, t) => s + t.totalCost, 0);
  const untagged = costs.find((t) => t.team === 'untagged');

  const message = [
    `*Weekly Cloud Cost Report (${start}${end})*`,
    `Total: $${total.toFixed(2)}`,
    '',
    '*By Team:*',
    ...costs
      .filter((t) => t.team !== 'untagged')
      .map((t) => `  • ${t.team}: $${t.totalCost.toFixed(2)}`),
    '',
    untagged
      ? `⚠️ Untagged resources: $${untagged.totalCost.toFixed(2)} — review and tag`
      : '✅ No untagged resources',
  ].join('\n');

  await slack.chat.postMessage({ channel: '#engineering-costs', text: message });
}

Rightsizing: Finding Underutilized Resources

EC2 / ECS CPU and Memory

# scripts/rightsizing_scan.py
import boto3
from datetime import datetime, timedelta
from dataclasses import dataclass

cloudwatch = boto3.client('cloudwatch', region_name='us-east-1')
ec2 = boto3.client('ec2', region_name='us-east-1')

@dataclass
class RightsizingRecommendation:
    resource_id: str
    resource_type: str
    current_type: str
    avg_cpu_pct: float
    avg_memory_pct: float
    recommendation: str
    monthly_savings: float

def get_ec2_utilization(instance_id: str, days: int = 14) -> dict:
    end = datetime.utcnow()
    start = end - timedelta(days=days)

    def get_metric(metric_name: str) -> float:
        response = cloudwatch.get_metric_statistics(
            Namespace='AWS/EC2',
            MetricName=metric_name,
            Dimensions=[{'Name': 'InstanceId', 'Value': instance_id}],
            StartTime=start,
            EndTime=end,
            Period=86400,  # Daily
            Statistics=['Average'],
        )
        datapoints = response['Datapoints']
        if not datapoints:
            return 0.0
        return sum(d['Average'] for d in datapoints) / len(datapoints)

    return {
        'cpu': get_metric('CPUUtilization'),
        'network_in': get_metric('NetworkIn'),
    }

def scan_for_oversized_instances() -> list[RightsizingRecommendation]:
    recommendations = []

    instances = ec2.describe_instances(
        Filters=[{'Name': 'instance-state-name', 'Values': ['running']}]
    )

    for reservation in instances['Reservations']:
        for instance in reservation['Instances']:
            instance_id = instance['InstanceId']
            instance_type = instance['InstanceType']

            util = get_ec2_utilization(instance_id)
            avg_cpu = util['cpu']

            # Flag if consistently below 15% CPU
            if avg_cpu < 15:
                # Suggest smaller instance type
                current_cost = INSTANCE_COSTS.get(instance_type, 0)
                suggested_type = suggest_smaller(instance_type)
                suggested_cost = INSTANCE_COSTS.get(suggested_type, 0)
                monthly_savings = (current_cost - suggested_cost) * 730  # hours/month

                recommendations.append(RightsizingRecommendation(
                    resource_id=instance_id,
                    resource_type='EC2',
                    current_type=instance_type,
                    avg_cpu_pct=avg_cpu,
                    avg_memory_pct=0,  # Requires CloudWatch agent
                    recommendation=f"Downsize to {suggested_type}",
                    monthly_savings=monthly_savings,
                ))

    return sorted(recommendations, key=lambda r: r.monthly_savings, reverse=True)

# Instance cost map (on-demand, us-east-1, 2026 pricing)
INSTANCE_COSTS = {
    'm7i.large':   0.1008,
    'm7i.xlarge':  0.2016,
    'm7i.2xlarge': 0.4032,
    'm7i.4xlarge': 0.8064,
    'c7i.large':   0.0850,
    'c7i.xlarge':  0.1700,
    'r7i.large':   0.1260,
    'r7i.xlarge':  0.2520,
}

def suggest_smaller(instance_type: str) -> str:
    DOWNSIZE_MAP = {
        'm7i.2xlarge': 'm7i.xlarge',
        'm7i.xlarge':  'm7i.large',
        'c7i.2xlarge': 'c7i.xlarge',
        'c7i.xlarge':  'c7i.large',
        'r7i.2xlarge': 'r7i.xlarge',
        'r7i.xlarge':  'r7i.large',
    }
    return DOWNSIZE_MAP.get(instance_type, instance_type)

⚙️ DevOps Done Right — Zero Downtime, Full Automation

Ship faster without breaking things. We build CI/CD pipelines, monitoring stacks, and auto-scaling infrastructure that your team can actually maintain.

  • Staging + production environments with feature flags
  • Automated security scanning in the pipeline
  • Uptime monitoring + alerting + runbook automation
  • On-call support handover docs included

Common Cloud Waste Patterns

Waste PatternHow to DetectTypical Savings
Idle EC2 instances (<5% CPU 2+ weeks)CloudWatch CPU metrics$50–$500/month each
Unattached EBS volumesaws ec2 describe-volumes --filters State=available$0.10/GB/month
Unused Elastic IPsNot associated with a running instance$0.005/hour = $3.6/month each
Old EBS snapshotsSnapshots >90 days with no associated AMI$0.05/GB/month
Idle RDS instances<1 connection/day for 2+ weeks$100–$2,000/month
Oversized RDSCPU <10%, memory <20%30–60% of DB cost
Data transfer from wrong regionCost Explorer: Data Transfer lineOften 10–20% of total bill
CloudWatch Logs retentionLogs never expire$0.03/GB/month storage
NAT Gateway data processingRoute traffic via S3/DynamoDB endpoints$0.045/GB

Automated Waste Scan

#!/bin/bash
# scripts/find-waste.sh

echo "=== Unattached EBS Volumes ==="
aws ec2 describe-volumes \
  --filters "Name=status,Values=available" \
  --query 'Volumes[*].{ID:VolumeId,Size:Size,Created:CreateTime,Tags:Tags}' \
  --output table

echo "=== Unused Elastic IPs ==="
aws ec2 describe-addresses \
  --query 'Addresses[?AssociationId==null].{IP:PublicIp,AllocationId:AllocationId}' \
  --output table

echo "=== Old Snapshots (>90 days) ==="
aws ec2 describe-snapshots \
  --owner-ids self \
  --query "Snapshots[?StartTime<='$(date -d '90 days ago' -u +%Y-%m-%dT%H:%M:%SZ)'].{ID:SnapshotId,Size:VolumeSize,Date:StartTime}" \
  --output table

echo "=== CloudWatch Log Groups (no retention set) ==="
aws logs describe-log-groups \
  --query 'logGroups[?!retentionInDays].{Name:logGroupName,Size:storedBytes}' \
  --output table

Reserved Instances and Savings Plans

CommitmentDiscount vs On-DemandBest For
No commitment0%Dev/sandbox
1-year Compute Savings Plan40–50%Predictable workloads
3-year Compute Savings Plan55–66%Long-term baseline
1-year EC2 Reserved (standard)40–55%Specific instance type lock-in
Spot Instances70–90%Stateless, interruption-tolerant

Strategy for 2026: Cover your steady-state compute baseline with 1-year Compute Savings Plans (flexible across EC2, Fargate, Lambda). Use Spot for batch jobs, training, and CI workers. Keep 20–30% on-demand for burst capacity.


Expected Savings: Typical FinOps Audit

Company SizeAnnual AWS SpendCommon WasteExpected Savings
Early startup$50KOversized dev instances15–25%
Series A$200KNo reservations, idle resources25–40%
Series B$1MData transfer, unused services20–35%
Enterprise$5M+Reserved coverage gaps, rightsizing15–30%

A disciplined FinOps program typically delivers 20–35% reduction in the first 90 days, then 5–10% annually through continuous optimization.


Working With Viprasol

Our infrastructure team runs cloud cost audits and implements FinOps programs — from tagging strategy through Reserved Instance purchasing and continuous rightsizing automation.

What we deliver:

  • Tagging taxonomy design and Terraform enforcement
  • AWS Cost Explorer attribution by team, service, and environment
  • Automated waste scan (unattached volumes, idle instances, old snapshots)
  • Rightsizing analysis with monthly savings estimates
  • Reserved Instance / Savings Plan purchasing recommendation

Discuss your cloud cost reduction goalsCloud infrastructure services


Share this article:

About the Author

V

Viprasol Tech Team

Custom Software Development Specialists

The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 100+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement. Based in India, serving clients globally.

MT4/MT5 EA DevelopmentAI Agent SystemsSaaS DevelopmentAlgorithmic Trading

Need DevOps & Cloud Expertise?

Scale your infrastructure with confidence. AWS, GCP, Azure certified team.

Free consultation • No commitment • Response within 24 hours

Viprasol · Big Data & Analytics

Making sense of your data at scale?

Viprasol builds end-to-end big data analytics solutions — ETL pipelines, data warehouses on Snowflake or BigQuery, and self-service BI dashboards. One reliable source of truth for your entire organisation.