Public/Disconnect-AvdUserSessions.ps1

function Disconnect-AvdUserSessions {
    <#
    .SYNOPSIS
    Gets the current connect users on an AVD from a specific hostpool.
    .DESCRIPTION
    This function will grab all the logged in users sessions from a specific Azure Virtual Desktop hostpool.
    .PARAMETER HostpoolName
    Enter the AVD Hostpool name
    .PARAMETER ResourceGroupName
    Enter the AVD Hostpool resourcegroup name
    .PARAMETER SessionHostName
    Enter the sessionhosts name
    .PARAMETER SessionHostName
    Enter the user principal name
    .PARAMETER All
    Switch parameter to logoff all sessions on a session host
    .EXAMPLE
    Disconnect-AvdUserSessions -HostpoolName avd-hostpool-personal -ResourceGroupName rg-avd-01 -SessionHostName avd-host-1.avd.domain -LogonName user@domain.com
    .EXAMPLE
    Disconnect-AvdUserSessions -HostpoolName avd-hostpool-personal -ResourceGroupName rg-avd-01 -All
    .EXAMPLE
    Disconnect-AvdUserSessions -HostpoolName avd-hostpool-personal -ResourceGroupName rg-avd-01 -SessionHostName avd-host-1.avd.domain -All
    #>

    [CmdletBinding(DefaultParameterSetName = 'All')]
    param
    (
        [parameter(Mandatory, ParameterSetName = 'All')]
        [parameter(Mandatory, ParameterSetName = 'Hostname')]
        [ValidateNotNullOrEmpty()]
        [string]$HostpoolName,
    
        [parameter(Mandatory, ParameterSetName = 'All')]
        [parameter(Mandatory, ParameterSetName = 'Hostname')]
        [ValidateNotNullOrEmpty()]
        [string]$ResourceGroupName,
    
        [parameter(Mandatory, ParameterSetName = 'Hostname')]
        [ValidateNotNullOrEmpty()]
        [string]$SessionHostName,

        [parameter(ParameterSetName = 'All')]
        [parameter(ParameterSetName = 'Hostname')]
        [ValidateNotNullOrEmpty()]
        [string]$LogonName,

        [parameter(ParameterSetName = 'All')]
        [parameter(ParameterSetName = 'Hostname')]
        [ValidateNotNullOrEmpty()]
        [switch]$All
    )
    Begin {
        Write-Verbose "Start searching session hosts"
        AuthenticationCheck
        $token = GetAuthToken -resource $global:AzureApiUrl
        $apiVersion = "2024-04-03"
        $batchApiUrl = "https://management.azure.com/batch?api-version=2020-06-01"
        $avdParameters = @{
            HostpoolName      = $HostpoolName
            ResourceGroupName = $ResourceGroupName
        }
    }
    Process {
        switch ($PsCmdlet.ParameterSetName) {
            All {
                Write-Verbose 'Using base url for getting all session hosts in $hostpoolName'
            }
            Hostname {
                Write-Verbose "Looking for sessionhost $SessionHostName"
                $avdParameters.Add("sessionHostName", $SessionHostName)
            }
        }
        try {
            $userSessions = Get-AvdUserSessions @avdParameters
        }
        catch {
            Throw "No user sessions found under $Hostpoolname in $ResourceGroupName"
        }
        try {
            # Build batch requests for disconnection
            $batchRequests = @()
            
            if ($all) {
                # Disconnect all user sessions
                Write-Information "Preparing to disconnect $($userSessions.Count) user session(s)" -InformationAction Continue
                
                foreach ($session in $userSessions) {
                    $sessionUrl = $session.id -replace "^https://management\.azure\.com", ""
                    
                    $batchRequests += @{
                        httpMethod = "DELETE"
                        name = [System.Guid]::NewGuid().ToString()
                        requestHeaderDetails = @{
                            commandName = "Microsoft_Azure_WVD.WvdManagerUserDetailsBlade.SessionsTab.gridLogOffCommand.logOffUserSession"
                        }
                        requestHeaderDictionary = @{
                            "User-Agent" = @("AzurePortal/1.0.03193.971")
                        }
                        url = "$sessionUrl" + "?api-version=$apiVersion"
                    }
                }
            }
            else {
                # Disconnect specific user session
                $userSession = $userSessions | Where-Object { $_.properties.userPrincipalName -eq $LogonName }
                
                if (-not $userSession) {
                    throw "User session not found for user: $LogonName"
                }
                
                Write-Information "Preparing to disconnect user session for: $LogonName" -InformationAction Continue
                
                $sessionUrl = $userSession.id -replace "^https://management\.azure\.com", ""
                
                $batchRequests += @{
                    httpMethod = "DELETE"
                    name = [System.Guid]::NewGuid().ToString()
                    requestHeaderDetails = @{
                        commandName = "Microsoft_Azure_WVD.WvdManagerUserDetailsBlade.SessionsTab.gridLogOffCommand.logOffUserSession"
                    }
                    requestHeaderDictionary = @{
                        "User-Agent" = @("AzurePortal/1.0.03193.971")
                    }
                    url = "$sessionUrl" + "?api-version=$apiVersion"
                }
            }
            
            # Execute batch request
            if ($batchRequests.Count -gt 0) {
                $batchRequest = @{
                    requests = $batchRequests
                }
                
                $batchParameters = @{
                    uri = $batchApiUrl
                    Method = "POST"
                    Headers = $token
                    Body = ($batchRequest | ConvertTo-Json -Depth 10)
                }
                
                Write-Verbose "Executing batch disconnect request for $($batchRequests.Count) session(s)"
                $batchResponse = Request-Api @batchParameters
                
                # Process batch response
                $successCount = 0
                $failureCount = 0
                
                foreach ($response in $batchResponse.responses) {
                    if ($response.httpStatusCode -eq 200 -or $response.httpStatusCode -eq 204) {
                        $successCount++
                        Write-Verbose "Successfully disconnected session: $($response.name)"
                    }
                    else {
                        $failureCount++
                        Write-Warning "Failed to disconnect session: $($response.name). Status: $($response.httpStatusCode). Error: $($response.content.error.message)"
                    }
                }
                
                Write-Information "Disconnect Summary: $successCount successful, $failureCount failed" -InformationAction Continue
                
                return @{
                    TotalSessions = $batchRequests.Count
                    SuccessfulDisconnects = $successCount
                    FailedDisconnects = $failureCount
                    BatchResponse = $batchResponse
                }
            }
        }
        catch {
            Throw "Disconnecting users not successful: $_"
        }
    }
}