Back to Playbooks

Multi-Cloud Tagging Strategy & Enforcement

Comprehensive tagging strategy and automation scripts for consistent resource tagging across Azure, OCI, and AWS with policy enforcement.

12 min read
Multi-CloudFinOpsGovernanceAzureOCIAWSTagging
Share on X

Implement Robust Tagging Across All Clouds

Enable cost allocation, resource management, and compliance tracking with consistent tagging across Azure, OCI, and AWS.

Without proper tagging, you cannot track spending by team, identify resource ownership, or prove compliance with regulatory requirements.

Why Tagging Matters

💸

Cost Allocation is Impossible

Can't track spending by team, project, or environment

Resource Ownership is Unclear

Nobody knows who to contact about a resource

⚖️

Compliance Fails

Can't prove resources meet regulatory requirements

⚠️

Cleanup is Dangerous

Risk deleting production resources

The Universal Tagging Schema

Use these mandatory tags across all clouds:

Tag KeyDescriptionExample ValuesRequired
EnvironmentDeployment environmentprod, staging, dev, qa
OwnerTeam or individual responsibleplatform-team, data-engineering
CostCenterBilling departmentengineering, marketing, ops
ProjectProject or product namecustomer-portal, ml-pipeline
ManagedByAutomation toolterraform, pulumi, manual
CreatedDateISO 8601 date2025-01-12
ExpiryDateWhen to review/delete2025-12-31
DataClassificationSensitivity levelpublic, internal, confidential

Cloud-Specific Tag Examples

Azure

{
  "Environment": "prod",
  "Owner": "platform-team",
  "Cost-Center": "engineering",
  "Project": "api-gateway",
  "Managed-By": "terraform",
  "Created-Date": "2025-01-12"
}

OCI

{
  "Environment": "prod",
  "Owner": "platform-team",
  "CostCenter": "engineering",
  "Project": "api-gateway",
  "ManagedBy": "terraform",
  "CreatedDate": "2025-01-12"
}

Tag Enforcement Policies

Azure Policy

Use Azure Policy to enforce mandatory tags and prevent resource creation without proper tagging:

azure-tag-policy.jsonjson
{
  "properties": {
    "displayName": "Require mandatory tags",
    "policyType": "Custom",
    "mode": "Indexed",
    "description": "Enforces required tags on resources",
    "parameters": {
      "tagNames": {
        "type": "Array",
        "defaultValue": [
          "Environment",
          "Owner",
          "Cost-Center",
          "Project",
          "Managed-By"
        ]
      }
    },
    "policyRule": {
      "if": {
        "anyOf": [
          {
            "field": "[concat('tags[', parameters('tagNames')[0], ']')]",
            "exists": "false"
          }
        ]
      },
      "then": {
        "effect": "deny"
      }
    }
  }
}

OCI Tag Defaults

Create a tag namespace and define required tags in OCI:

oci-tag-setup.shbash
#!/bin/bash
# Create OCI Tag Namespace and Required Tags

COMPARTMENT_ID="ocid1.compartment.oc1..aaa..."
NAMESPACE_NAME="finops"

# Create tag namespace
oci iam tag-namespace create \
  --compartment-id "$COMPARTMENT_ID" \
  --name "$NAMESPACE_NAME" \
  --description "FinOps mandatory tags"

# Define required tags
for TAG in Environment Owner CostCenter Project ManagedBy CreatedDate; do
  oci iam tag create \
    --tag-namespace-id "$NAMESPACE_ID" \
    --name "$TAG" \
    --description "Required: $TAG" \
    --is-required true
done

Terraform Enforcement (All Clouds)

Use Terraform locals to ensure consistent tagging across all resources:

terraform/locals.tfhcl
# Define mandatory tags once
locals {
  mandatory_tags = {
    Environment = var.environment
    Owner       = var.owner_team
    CostCenter  = var.cost_center
    Project     = var.project_name
    ManagedBy   = "terraform"
    CreatedDate = formatdate("YYYY-MM-DD", timestamp())
  }
}

# Azure Resource Example
resource "azurerm_virtual_network" "example" {
  name                = "vnet-${var.project_name}"
  location            = var.location
  resource_group_name = azurerm_resource_group.example.name
  address_space       = ["10.0.0.0/16"]

  tags = merge(
    local.mandatory_tags,
    {
      Type = "Networking"
    }
  )
}

# OCI Resource Example
resource "oci_core_vcn" "example" {
  compartment_id = var.compartment_id
  cidr_block     = "10.0.0.0/16"
  display_name   = "vcn-${var.project_name}"

  freeform_tags = merge(
    local.mandatory_tags,
    {
      Type = "Networking"
    }
  )
}

Automated Tag Auditing Script

This Python script audits your multi-cloud environment and identifies resources with missing mandatory tags:

multicloud_tag_auditor.pypython
#!/usr/bin/env python3
"""
Multi-Cloud Tag Auditor
Checks for missing mandatory tags across Azure, OCI, and AWS
"""

from azure.identity import DefaultAzureCredential
from azure.mgmt.resource import ResourceManagementClient
import oci
import boto3

MANDATORY_TAGS = ['Environment', 'Owner', 'CostCenter', 'Project', 'ManagedBy']

def audit_azure_tags(subscription_id):
    """Audit Azure resource tags"""
    credential = DefaultAzureCredential()
    client = ResourceManagementClient(credential, subscription_id)

    missing_tags = []
    for resource in client.resources.list():
        tags = resource.tags or {}
        missing = [tag for tag in MANDATORY_TAGS if tag not in tags]

        if missing:
            missing_tags.append({
                'cloud': 'Azure',
                'resource_id': resource.id,
                'resource_type': resource.type,
                'missing_tags': missing
            })

    return missing_tags

def audit_oci_tags(compartment_id):
    """Audit OCI resource tags"""
    config = oci.config.from_file()
    search_client = oci.resource_search.ResourceSearchClient(config)

    query = f"query all resources where compartmentId = '{compartment_id}'"
    search_response = search_client.search_resources(
        oci.resource_search.models.StructuredSearchDetails(query=query)
    )

    missing_tags = []
    for resource in search_response.data.items:
        tags = resource.freeform_tags or {}
        missing = [tag for tag in MANDATORY_TAGS if tag not in tags]

        if missing:
            missing_tags.append({
                'cloud': 'OCI',
                'resource_id': resource.identifier,
                'resource_type': resource.resource_type,
                'missing_tags': missing
            })

    return missing_tags

def generate_report(all_missing):
    """Generate report of tag compliance"""
    total_resources = len(all_missing)
    print(f"\n{'='*60}")
    print(f"TAG COMPLIANCE REPORT")
    print(f"{'='*60}")
    print(f"Total non-compliant resources: {total_resources}")

    by_cloud = {}
    for item in all_missing:
        cloud = item['cloud']
        by_cloud[cloud] = by_cloud.get(cloud, 0) + 1

    print(f"\nBy Cloud:")
    for cloud, count in by_cloud.items():
        print(f"  {cloud}: {count} resources")

def main():
    all_missing = []

    # Audit Azure
    azure_sub = "your-subscription-id"
    print("Auditing Azure...")
    all_missing.extend(audit_azure_tags(azure_sub))

    # Audit OCI
    oci_compartment = "ocid1.compartment.oc1..aaa..."
    print("Auditing OCI...")
    all_missing.extend(audit_oci_tags(oci_compartment))

    generate_report(all_missing)

if __name__ == '__main__':
    main()

💡 Pro Tip

Run this audit script weekly as part of your FinOps review process. Export results to CSV for tracking tag compliance over time.

Best Practices

Enforce at Creation

Use policies to prevent untagged resources from being created

Audit Regularly

Run automated audits weekly to catch drift and non-compliance

Keep It Simple

Start with 5-6 mandatory tags, expand only when needed

Implementation Roadmap

1

Week 1: Define Schema

Align with teams on mandatory vs. optional tags

2

Week 2: Tag Existing Resources

Use scripts to bulk-tag current infrastructure

3

Week 3: Deploy Policies

Enable enforcement policies in audit mode first

4

Week 4: Full Enforcement

Switch policies to deny mode, set up automated audits

💰 FinOps Impact

Organizations with >95% tag compliance see 40% faster cost allocation and 60% reduction in orphaned resources. The ROI of tagging automation pays for itself in the first quarter.