core/Resources/Scripts/remove_cache.ps1

param(
    [Parameter(Mandatory = $true)]
    [string]$EnvironmentName,

    [Parameter(Mandatory = $true)]
    [string]$Name,
    
    [Parameter(Mandatory = $false)]
    [switch]$NoLogo,
    
    [Parameter(Mandatory = $false)]
    [int]$Port,

    [Parameter(Mandatory = $false)]
    [PSCredential]$Credentials,

    [string]$ScriptsFolderPath = ".\Resources\Scripts"
)

. "$ScriptsFolderPath\dashboard_common.ps1"

function Get-EnvName {
    param (
        [Parameter(Mandatory = $true)]
        [string]$EnvironmentName
    )
    # Get all RGs with EnvironmentName tag
    $all_resource_groups = Get-AzResourceGroup -ErrorAction Stop | Where-Object { $_.Tags -and $_.Tags.ContainsKey("EnvironmentName") } 

    if (-not $all_resource_groups) {
        throw "No resource groups found with tag 'EnvironmentName'."
    }

    # Match the exact EnvironmentName
    $matched_group = $all_resource_groups | Where-Object {
        $_.Tags["EnvironmentName"] -eq $EnvironmentName
    }

    if (-not $matched_group) {
        throw "No resource group found with EnvironmentName = '$EnvironmentName'."
    }

    return $matched_group.ResourceGroupName
}

function Get-VmsWithCache {
    Param(
        [Parameter(Mandatory = $true)]
        [string]$EnvironmentName,

        [Parameter(Mandatory = $true)]
        [string]$Name
    )

    #Filtering VMs using ResourceGroups and Cache Tags
    $vms = Get-AzVM -Status -ResourceGroupName $EnvironmentName -ErrorAction Stop | Where-Object { $_.Tags.ContainsKey("Caches") } 
    
    #Assign Cache Names Array to Vms
    $vms = foreach ($vm in $vms) {
        $vm | Add-Member -NotePropertyName "CachesList" -NotePropertyValue ($vm.Tags["Caches"] -split "," | ForEach-Object { $_.Trim() }) -Force

        $vm
    }

    #If Name is provided, filter by cache tag values
    $vms = foreach ($vm in $vms) {
        if ($vm.CachesList -contains $Name) {
            $vm.CachesList = @($Name)
            $vm 
        }
    }

    return $vms
}

function Get-VMPrivateIps {
    param(
        [Parameter(Mandatory = $true)]
        [array]$VMs,

        [Parameter(Mandatory = $true)]
        [string]$ResourceGroupName
    )


    $VMPrivateIps = foreach ($vm in $VMs) {
        foreach ($nicRef in $vm.NetworkProfile.NetworkInterfaces) {
            $nicName = ($nicRef.Id -split "/")[-1]
            $nic = Get-AzNetworkInterface -Name $nicName -ResourceGroupName $ResourceGroupName -ErrorAction Stop

            [PSCustomObject]@{
                VMName     = $vm.Name
                NicName    = $nic.Name
                PrivateIps = ($nic.IpConfigurations | Select-Object -ExpandProperty PrivateIpAddress)
                OsType     = $vm.StorageProfile.OsDisk.OsType
            }
        }
    }


    return , $VMPrivateIps
}

function Get-LinuxCommand {
    param(
        [Parameter(Mandatory = $true)]
        [object]$machineInfo,
        
        [Parameter(Mandatory = $true)]
        [string]$cacheName
    )

    #Pick First Ip, As It only takes one ip
    $ip = $machineInfo.PrivateIps
    $command = "/opt/ncache/bin/tools/remove-cache $($cacheName) -server `"$ip`""

    if ($Port) { $command += " -port $Port" }
    if ($NoLogo) { $command += " -nologo" }
    if ($Credentials) { 
        # credentials aren�t usually passed like this in Linux,
        # but let's say we format them
        $command += " -userid $($Credentials.UserName)"
    }

    return $command
}

function Get-WindowsCommand {
    param(
        [Parameter(Mandatory = $true)]
        [object]$machineInfo,
        
        [Parameter(Mandatory = $true)]
        [string]$cacheName
    )

    #Pick First Ip, As It only takes one ip
    $ip = $machineInfo.PrivateIps

    $command = "Remove-Cache -Name $($cacheName) -Server `"$ip`" "

    if ($Port) { $command += " -Port $Port" }
    if ($NoLogo) { $command += " -NoLogo" }
    if ($Credentials) { 
        $command += " -UserName $($Credentials.UserName)"
        # you usually wouldn�t echo the password in plain text
        # (NCache would expect a secure string or prompt)
    }

    return $command
}

function Get-CommandForCache {
    param(
        [Parameter(Mandatory = $true)]
        [object]$machineInfo,
        
        [Parameter(Mandatory = $true)]
        [string]$cacheName
    )

    if ($machineInfo.OsType -eq "Windows") {
        return @{
            CommandId = "RunPowerShellScript"
            VMName    = $machineInfo.FirstVMName
            Script    = Get-WindowsCommand -machineInfo $machineInfo -cacheName $cacheName
        }
    }
    elseif ($machineInfo.OsType -eq "Linux") {
        return @{
            CommandId = "RunShellScript"
            VMName    = $machineInfo.FirstVMName
            Script    = Get-LinuxCommand -machineInfo $machineInfo -cacheName $cacheName
        }
    }
    else {
        throw "Unknown OS type for VM: $($Vm.Name)"
    }
}

function Get-MachineInfo {
    param(
        [Parameter(Mandatory = $true)]
        [array]$VMInfo
    )

    if (-not $VMInfo -or $VMInfo.Count -eq 0) {
        throw "VMInfo array is empty."
    }

    $firstVM = $VMInfo[0]

    [PSCustomObject]@{
        FirstVMName = $firstVM.VMName
        PrivateIps  = $firstVM.PrivateIps
        OsType      = $firstVM.OsType
    }
}

function Remove-CacheTagFromVMs {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ResourceGroupName,

        [Parameter(Mandatory = $true)]
        [string]$CacheName,

        [Parameter(Mandatory = $true)]
        [array]$VMs   # array of VM objects with .Name
    )

    foreach ($vm in $VMs) {
        try {
            Write-Output "Removing cache '$CacheName' tag from VM: $($vm.Name)"

            # Get existing VM
            $vmResource = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $vm.Name -ErrorAction Stop

            # Convert tags dictionary to hashtable
            $tags = @{}
            if ($vmResource.Tags) {
                foreach ($k in $vmResource.Tags.Keys) {
                    $tags[$k] = $vmResource.Tags[$k]
                }
            }

            if ($tags.ContainsKey("Caches")) {
                # Split the caches by comma, trim spaces
                $cacheList = $tags["Caches"] -split "," | ForEach-Object { $_.Trim() }

                # Remove the target cache
                $updatedList = $cacheList | Where-Object { $_ -ne $CacheName }

                if ($updatedList.Count -gt 0) {
                    $tags["Caches"] = $updatedList -join ","
                }
                else {
                    # If no caches left, drop the key
                    $tags.Remove("Caches")
                }

                # Apply updated tags
                Set-AzResource -ResourceId $vmResource.Id -Tag $tags -Force -ErrorAction Stop | Out-Null
                Write-Output "Cache '$CacheName' removed from VM '$($vm.Name)'"
            }
            else {
                Write-Output "VM '$($vm.Name)' does not have a 'Caches' tag"
            }
        }
        catch {
            Write-Output "Failed to remove cache '$CacheName' from $($vm.Name): $_"
        }
    }

    Remove-NcAzDashboards -ResourceGroupName $ResourceGroupName -CacheName $CacheName
}


function Invoke-CacheCommand {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ResourceGroupName,

        [Parameter(Mandatory = $true)]
        [object]$Command
    )

    Write-Output "Executing on VM: $($Command.VMName) => $($Command.Script)"

    $result = Invoke-AzVMRunCommand `
        -ResourceGroupName $ResourceGroupName `
        -VMName $Command.VMName `
        -CommandId $Command.CommandId `
        -ScriptString $Command.Script

    # Aggregate all Messages into a single string
    $messages = $result.Value | ForEach-Object { $_.Message }
    $joined = ($messages -join "`n").Trim()

    # Detect error from result codes
    $hasError = $false
    if ($Command.CommandId -eq "RunPowerShellScript" -and $result.Value | Where-Object { $_.Code -match "StdErr" -and -not [string]::IsNullOrWhiteSpace($_.Message) }) {
        $hasError = $true
    }
    elseif ($Command.CommandId -eq "RunShellScript" ) {
        $message = $result.Value[0].Message
        
        if ($message -match "Error:" -or $message -match "error:") {
            $hasError = $true
        }
        
        $parts = $message -split "\[stderr\]"

        # If there's something after [stderr], treat it as error$res
        
        if ($parts.Count -gt 1 -and $parts[1].Trim().Length -gt 0) {
            $hasError = $true
        }
    }

    # Build and return structured PSObject
    $status = if ($hasError) { "error" } else { "success" }

    [PSCustomObject]@{
        status  = $status
        message = $joined
    }
}

function View-CommandResult {
    param(
        [Parameter(Mandatory = $true)]
        [pscustomobject]$Result
    )

    if ($Result.status -eq "error") {
        Write-Host $Result.message -ForegroundColor Red
    }
    else {
        Write-Host $Result.message
    }
}


function ExecuteCommands {
    Write-Output "Commands Execution Started"

    $EnvironmentName = Get-EnvName -EnvironmentName $EnvironmentName

    #Get Vms with Given Caches
    $vms = Get-VmsWithCache -EnvironmentName $EnvironmentName -Name $Name

    #Check If VM Exists or not
    if (-not $vms -or $vms.Count -eq 0) {
        throw "No Server Exists To Remove Cache"
    }

    #Get Private IPs of Vms (VM Name, CachesList, NicName, PrivateIps, OsType)
    $privateIps = Get-VMPrivateIps -VMs $vms -ResourceGroupName $EnvironmentName

    #Get Cache Groups (CacheName, FirstVM, OsType, PrivateIps)
    $machine = Get-MachineInfo -VmInfo $privateIps

    #Get Commands to be Executed on Azure
    $command = Get-CommandForCache -machineInfo $machine -cacheName $Name
    
    #Execute Commands
    $cmdExecution = Invoke-CacheCommand -ResourceGroupName $EnvironmentName -Command $command
    
    if ($cmdExecution.status -eq "success") {
        Write-Output "Removing Tags"
        Remove-CacheTagFromVMs -ResourceGroupName $EnvironmentName -CacheName $Name -VMs $vms
    }

    #Assign Vms With Tags
    View-CommandResult -Result $cmdExecution
}

try {
    if (-not (Get-AzContext)) {
        Connect-AzAccount
        if (Get-AzContext) {
            Write-Output "Created Context, Removing Cache..."
            ExecuteCommands
        }
    }
    else {
        Write-Output "Already have Context, Removing Cache..."
        ExecuteCommands
    }
}
catch {
    Write-Error $($_.Exception.Message)

    Write-Error "Coudn't Remove Cache'"
}