Examples/13-TagManagement.ps1

<#
.SYNOPSIS
    Examples for VergeOS tag management and resource organization.

.DESCRIPTION
    This script demonstrates tag management capabilities:
    - Creating and managing tag categories
    - Creating and managing tags within categories
    - Assigning tags to resources (VMs, networks, tenants)
    - Removing tag assignments
    - Querying resources by tags
    - Common tagging workflows and reporting

.NOTES
    Prerequisites:
    - PowerShell 7.4 or later
    - PSVergeOS module installed
    - Connected to a VergeOS system
#>


# Import the module
Import-Module PSVergeOS

#region Tag Categories
# ============================================================================
# MANAGING TAG CATEGORIES
# ============================================================================

# List all tag categories
Get-VergeTagCategory

# Get a specific tag category by name
Get-VergeTagCategory -Name "Environment"

# View tag categories with their taggable resource types
Get-VergeTagCategory | Format-Table Name, TaggableVMs, TaggableNetworks, TaggableTenants, SingleTagSelection

# Create a tag category for environment classification
New-VergeTagCategory -Name "Environment" `
    -Description "Deployment environment classification" `
    -TaggableVMs `
    -TaggableNetworks `
    -TaggableTenants `
    -SingleTagSelection

# Create a category that allows multiple tags per resource
New-VergeTagCategory -Name "Application" `
    -Description "Application or service tags" `
    -TaggableVMs

# Create a category for cost allocation
New-VergeTagCategory -Name "CostCenter" `
    -Description "Cost center for billing allocation" `
    -TaggableVMs `
    -TaggableTenants `
    -SingleTagSelection `
    -PassThru

# Update a tag category to enable additional resource types
Set-VergeTagCategory -Name "Environment" -TaggableNodes $true -TaggableClusters $true

# Update category description
Set-VergeTagCategory -Name "Application" -Description "Application and service identification"

# List categories that can tag VMs
Get-VergeTagCategory | Where-Object TaggableVMs | Format-Table Name, Description

#endregion

#region Tags
# ============================================================================
# MANAGING TAGS WITHIN CATEGORIES
# ============================================================================

# List all tags
Get-VergeTag

# List tags in a specific category
Get-VergeTag -Category "Environment"

# Get tags using pipeline from category
Get-VergeTagCategory -Name "Environment" | Get-VergeTag

# Find tags by name pattern
Get-VergeTag -Name "Prod*"

# Create environment tags
New-VergeTag -Name "Production" -Category "Environment" -Description "Production workloads"
New-VergeTag -Name "Staging" -Category "Environment" -Description "Pre-production testing"
New-VergeTag -Name "Development" -Category "Environment" -Description "Development and testing"
New-VergeTag -Name "DR" -Category "Environment" -Description "Disaster recovery resources"

# Create application tags
New-VergeTag -Name "WebServer" -Category "Application" -Description "Web server tier"
New-VergeTag -Name "Database" -Category "Application" -Description "Database tier"
New-VergeTag -Name "AppServer" -Category "Application" -Description "Application server tier"
New-VergeTag -Name "LoadBalancer" -Category "Application" -Description "Load balancer"

# Create cost center tags
New-VergeTag -Name "IT-Operations" -Category "CostCenter" -Description "IT Operations budget"
New-VergeTag -Name "Engineering" -Category "CostCenter" -Description "Engineering budget"
New-VergeTag -Name "Marketing" -Category "CostCenter" -Description "Marketing budget"

# Update tag description
Set-VergeTag -Name "Production" -Description "Production environment - critical workloads"

# View tags with their category information
Get-VergeTag | Format-Table Name, CategoryName, Description

#endregion

#region Assigning Tags to Resources
# ============================================================================
# ASSIGNING TAGS TO VMs, NETWORKS, AND TENANTS
# ============================================================================

# Assign a tag to a VM by name
Add-VergeTagMember -Tag "Production" -VM "WebServer01"

# Assign a tag to a VM and get the result
$assignment = Add-VergeTagMember -Tag "Production" -VM "WebServer01" -PassThru
$assignment | Format-Table TagName, ResourceType, ResourceKey

# Assign tags to multiple VMs using pipeline
Get-VergeVM -Name "Web*" | Add-VergeTagMember -Tag "WebServer"
Get-VergeVM -Name "DB*" | Add-VergeTagMember -Tag "Database"

# Assign multiple tags to a single VM
$vm = Get-VergeVM -Name "WebServer01"
Add-VergeTagMember -Tag "Production" -VM $vm
Add-VergeTagMember -Tag "WebServer" -VM $vm
Add-VergeTagMember -Tag "IT-Operations" -VM $vm

# Assign a tag to a network
Add-VergeTagMember -Tag "Production" -Network "DMZ"

# Assign a tag to a tenant
Add-VergeTagMember -Tag "Production" -Tenant "CustomerA"

# Assign tag using generic resource type and key
Add-VergeTagMember -Tag "Production" -ResourceType vms -ResourceKey 123

# Bulk tagging: Tag all VMs in a cluster as Production
Get-VergeVM -Cluster "Prod-Cluster" | ForEach-Object {
    Add-VergeTagMember -Tag "Production" -VM $_
}

#endregion

#region Viewing Tag Assignments
# ============================================================================
# QUERYING TAG ASSIGNMENTS
# ============================================================================

# List all resources with a specific tag
Get-VergeTagMember -Tag "Production"

# List only VMs with a tag
Get-VergeTagMember -Tag "Production" -ResourceType vms

# List networks with a tag
Get-VergeTagMember -Tag "Production" -ResourceType vnets

# Get tag assignments using pipeline
Get-VergeTag -Name "Production" | Get-VergeTagMember

# List all tag assignments for tags in a category
Get-VergeTagCategory -Name "Environment" | Get-VergeTag | Get-VergeTagMember

# View assignments in table format
Get-VergeTagMember -Tag "Production" | Format-Table TagName, ResourceType, ResourceKey, ResourceRef

# Count resources by tag
Get-VergeTag -Category "Environment" | ForEach-Object {
    $members = Get-VergeTagMember -Tag $_.Name
    [PSCustomObject]@{
        Tag      = $_.Name
        Category = $_.CategoryName
        Count    = $members.Count
    }
} | Format-Table

#endregion

#region Removing Tag Assignments
# ============================================================================
# REMOVING TAGS FROM RESOURCES
# ============================================================================

# Remove a tag from a VM by specifying tag and VM
Remove-VergeTagMember -Tag "Development" -VM "WebServer01"

# Remove without confirmation prompt (for scripts)
Remove-VergeTagMember -Tag "Development" -VM "WebServer01" -Confirm:$false

# Remove by tag member key
Remove-VergeTagMember -Key 42 -Confirm:$false

# Remove tag from network
Remove-VergeTagMember -Tag "Production" -Network "DMZ" -Confirm:$false

# Remove all tag assignments for a tag using pipeline
Get-VergeTagMember -Tag "Staging" | Remove-VergeTagMember -Confirm:$false

# Remove specific resource type assignments
Get-VergeTagMember -Tag "Production" -ResourceType vms |
    Where-Object { $_.ResourceKey -eq 123 } |
    Remove-VergeTagMember -Confirm:$false

#endregion

#region Removing Tags and Categories
# ============================================================================
# DELETING TAGS AND TAG CATEGORIES
# ============================================================================

# Remove a tag (this also removes all tag assignments)
Remove-VergeTag -Name "OldTag"

# Remove without confirmation
Remove-VergeTag -Name "Temporary" -Confirm:$false

# Remove all tags in a category
Get-VergeTag -Category "OldCategory" | Remove-VergeTag -Confirm:$false

# Remove a tag category (must have no tags)
Remove-VergeTagCategory -Name "UnusedCategory"

# Safe category removal workflow
$categoryName = "CategoryToRemove"
$tags = Get-VergeTag -Category $categoryName

if ($tags) {
    Write-Warning "Category '$categoryName' has $($tags.Count) tags. Remove tags first."
    # Get-VergeTag -Category $categoryName | Remove-VergeTag -Confirm:$false
} else {
    Remove-VergeTagCategory -Name $categoryName -Confirm:$false
    Write-Host "Category '$categoryName' removed."
}

#endregion

#region Common Tagging Workflows
# ============================================================================
# PRACTICAL TAGGING WORKFLOWS
# ============================================================================

# Workflow: Set up a complete tagging structure for a new environment
function Initialize-VergeTagStructure {
    <#
    .SYNOPSIS
        Creates a standard tagging structure for resource organization.
    #>


    # Create Environment category
    $envCategory = Get-VergeTagCategory -Name "Environment"
    if (-not $envCategory) {
        New-VergeTagCategory -Name "Environment" `
            -Description "Deployment environment" `
            -TaggableVMs -TaggableNetworks -TaggableTenants `
            -SingleTagSelection

        @("Production", "Staging", "Development", "DR") | ForEach-Object {
            New-VergeTag -Name $_ -Category "Environment"
        }
        Write-Host "Created Environment category with tags"
    }

    # Create Application category
    $appCategory = Get-VergeTagCategory -Name "Application"
    if (-not $appCategory) {
        New-VergeTagCategory -Name "Application" `
            -Description "Application tier" `
            -TaggableVMs

        @("Web", "App", "Database", "Cache", "Queue") | ForEach-Object {
            New-VergeTag -Name $_ -Category "Application"
        }
        Write-Host "Created Application category with tags"
    }

    # Create Owner category
    $ownerCategory = Get-VergeTagCategory -Name "Owner"
    if (-not $ownerCategory) {
        New-VergeTagCategory -Name "Owner" `
            -Description "Team or individual responsible" `
            -TaggableVMs -TaggableTenants `
            -SingleTagSelection
        Write-Host "Created Owner category (add team tags as needed)"
    }
}

# Initialize-VergeTagStructure

# Workflow: Tag all untagged VMs with a default environment
function Set-DefaultEnvironmentTag {
    param(
        [string]$DefaultTag = "Development"
    )

    $allVMs = Get-VergeVM
    $envTags = Get-VergeTag -Category "Environment"

    foreach ($vm in $allVMs) {
        # Check if VM has any environment tag
        $hasEnvTag = $false
        foreach ($tag in $envTags) {
            $members = Get-VergeTagMember -Tag $tag.Name -ResourceType vms
            if ($members | Where-Object { $_.ResourceKey -eq $vm.Key }) {
                $hasEnvTag = $true
                break
            }
        }

        if (-not $hasEnvTag) {
            Write-Host "Tagging VM '$($vm.Name)' with '$DefaultTag'"
            Add-VergeTagMember -Tag $DefaultTag -VM $vm
        }
    }
}

# Set-DefaultEnvironmentTag -DefaultTag "Development"

# Workflow: Migrate VMs from one tag to another
function Move-VergeVMTag {
    param(
        [string]$FromTag,
        [string]$ToTag
    )

    $members = Get-VergeTagMember -Tag $FromTag -ResourceType vms

    foreach ($member in $members) {
        Write-Host "Moving VM $($member.ResourceKey) from '$FromTag' to '$ToTag'"
        Remove-VergeTagMember -Key $member.Key -Confirm:$false
        Add-VergeTagMember -Tag $ToTag -ResourceType vms -ResourceKey $member.ResourceKey
    }
}

# Move-VergeVMTag -FromTag "Development" -ToTag "Staging"

#endregion

#region Tag-Based Reporting
# ============================================================================
# REPORTING AND INVENTORY BY TAGS
# ============================================================================

# Generate VM inventory grouped by environment tag
function Get-VMsByEnvironment {
    $envTags = Get-VergeTag -Category "Environment"

    foreach ($tag in $envTags) {
        $members = Get-VergeTagMember -Tag $tag.Name -ResourceType vms

        [PSCustomObject]@{
            Environment = $tag.Name
            VMCount     = $members.Count
            VMKeys      = ($members.ResourceKey -join ', ')
        }
    }
}

Get-VMsByEnvironment | Format-Table -AutoSize

# Detailed VM report with tags
function Get-VMTagReport {
    $vms = Get-VergeVM
    $allTags = Get-VergeTag

    foreach ($vm in $vms) {
        $vmTags = @()

        foreach ($tag in $allTags) {
            $members = Get-VergeTagMember -Tag $tag.Name -ResourceType vms
            if ($members | Where-Object { $_.ResourceKey -eq $vm.Key }) {
                $vmTags += "$($tag.CategoryName):$($tag.Name)"
            }
        }

        [PSCustomObject]@{
            VMName     = $vm.Name
            PowerState = $vm.PowerState
            CPUCores   = $vm.CPUCores
            RAM_GB     = [math]::Round($vm.RAM / 1024, 1)
            Tags       = $vmTags -join '; '
        }
    }
}

Get-VMTagReport | Format-Table -AutoSize

# Export tag report to CSV
# Get-VMTagReport | Export-Csv "vm-tag-report.csv" -NoTypeInformation

# Find untagged VMs (VMs without any environment tag)
function Get-UntaggedVMs {
    param(
        [string]$Category = "Environment"
    )

    $vms = Get-VergeVM
    $categoryTags = Get-VergeTag -Category $Category

    $taggedVMKeys = @()
    foreach ($tag in $categoryTags) {
        $members = Get-VergeTagMember -Tag $tag.Name -ResourceType vms
        $taggedVMKeys += $members.ResourceKey
    }

    $vms | Where-Object { $_.Key -notin $taggedVMKeys } |
        Select-Object Name, PowerState, Cluster, Created
}

Write-Host "`nUntagged VMs (no Environment tag):"
Get-UntaggedVMs -Category "Environment" | Format-Table

# Resource count summary by tag
function Get-TagSummary {
    $tags = Get-VergeTag

    foreach ($tag in $tags) {
        $members = Get-VergeTagMember -Tag $tag.Name

        $summary = $members | Group-Object ResourceType | ForEach-Object {
            "$($_.Name): $($_.Count)"
        }

        [PSCustomObject]@{
            Tag         = $tag.Name
            Category    = $tag.CategoryName
            TotalCount  = $members.Count
            Breakdown   = $summary -join ', '
        }
    }
}

Get-TagSummary | Format-Table -AutoSize

#endregion

#region Tag Compliance Checking
# ============================================================================
# TAG COMPLIANCE AND VALIDATION
# ============================================================================

# Check if all Production VMs have required tags
function Test-ProductionTagCompliance {
    $requiredCategories = @("Environment", "Application", "CostCenter")

    $prodMembers = Get-VergeTagMember -Tag "Production" -ResourceType vms
    $nonCompliant = @()

    foreach ($member in $prodMembers) {
        $vmKey = $member.ResourceKey
        $missingCategories = @()

        foreach ($category in $requiredCategories) {
            $categoryTags = Get-VergeTag -Category $category
            $hasTag = $false

            foreach ($tag in $categoryTags) {
                $tagMembers = Get-VergeTagMember -Tag $tag.Name -ResourceType vms
                if ($tagMembers | Where-Object { $_.ResourceKey -eq $vmKey }) {
                    $hasTag = $true
                    break
                }
            }

            if (-not $hasTag) {
                $missingCategories += $category
            }
        }

        if ($missingCategories.Count -gt 0) {
            $vm = Get-VergeVM -Key $vmKey
            $nonCompliant += [PSCustomObject]@{
                VMName           = $vm.Name
                VMKey            = $vmKey
                MissingCategories = $missingCategories -join ', '
            }
        }
    }

    if ($nonCompliant.Count -eq 0) {
        Write-Host "All Production VMs are compliant with tagging requirements." -ForegroundColor Green
    } else {
        Write-Host "Non-compliant Production VMs:" -ForegroundColor Yellow
        $nonCompliant | Format-Table -AutoSize
    }

    return $nonCompliant
}

# Test-ProductionTagCompliance

#endregion