RDS License Audit Toolkit

<#
.SYNOPSIS
RDS License Audit Toolkit

.DESCRIPTION
This script performs a comprehensive audit of Remote Desktop Services (RDS) licensing,
including license server configuration, available licenses, and usage statistics.

.NOTES
File Name      : RDSLicenseAuditToolkit.ps1
Author         : [Your Name]
Prerequisite   : PowerShell V5.1 or later, administrator rights, and RDS management tools
Version        : 1.0
Date           : [Current Date]

.EXAMPLE
.\RDSLicenseAuditToolkit.ps1
#>

# Global variables
$global:reportPath = "$env:USERPROFILE\Desktop\RDS_License_Audit_Report_$(Get-Date -Format 'yyyyMMdd_HHmmss').html"
$global:licenseServer = $env:COMPUTERNAME  # Default to local machine

function Show-Menu {
    Clear-Host
    Write-Host "=== RDS License Audit Toolkit ===" -ForegroundColor Cyan
    Write-Host "Current License Server: $global:licenseServer"
    Write-Host "1. Set License Server"
    Write-Host "2. Check License Server Configuration"
    Write-Host "3. List Available Licenses"
    Write-Host "4. Analyze License Usage"
    Write-Host "5. Check RDS Server Configuration"
    Write-Host "6. Verify CAL Compliance"
    Write-Host "7. Review License Policies"
    Write-Host "8. Check License Server Health"
    Write-Host "9. Generate Comprehensive HTML Report"
    Write-Host "10. Exit"
}

function Set-LicenseServer {
    $server = Read-Host "Enter the RDS License Server name (or press Enter for localhost)"
    if ([string]::IsNullOrWhiteSpace($server)) {
        $global:licenseServer = $env:COMPUTERNAME
    } else {
        $global:licenseServer = $server
    }
    Write-Host "License server set to: $global:licenseServer" -ForegroundColor Green
}

function Check-LicenseServerConfiguration {
    Write-Host "`nChecking License Server Configuration..." -ForegroundColor Yellow
    try {
        $config = Invoke-Command -ComputerName $global:licenseServer -ScriptBlock {
            Import-Module RemoteDesktopServices
            $serverInfo = Get-RDLicenseConfiguration
            $serverStatus = Get-RDLicenseServerStatus
            return @{
                ServerInfo = $serverInfo
                ServerStatus = $serverStatus
            }
        }
        
        $result = [PSCustomObject]@{
            Mode = $config.ServerInfo.Mode
            LicensingType = $config.ServerInfo.LicensingType
            IsActivated = $config.ServerStatus.IsActivated
            LastIssuedLicenseDate = $config.ServerStatus.LastIssuedLicenseDate
            GracePeriodDays = $config.ServerStatus.GracePeriodDays
        }
        
        $result | Format-List
        return $result
    }
    catch {
        Write-Host "Error checking license server configuration: $_" -ForegroundColor Red
        return $null
    }
}

function List-AvailableLicenses {
    Write-Host "`nListing Available Licenses..." -ForegroundColor Yellow
    try {
        $licenses = Invoke-Command -ComputerName $global:licenseServer -ScriptBlock {
            Import-Module RemoteDesktopServices
            Get-RDLicense
        }
        
        $licenses | Format-Table -AutoSize
        return $licenses
    }
    catch {
        Write-Host "Error listing available licenses: $_" -ForegroundColor Red
        return $null
    }
}

function Analyze-LicenseUsage {
    Write-Host "`nAnalyzing License Usage..." -ForegroundColor Yellow
    try {
        $usage = Invoke-Command -ComputerName $global:licenseServer -ScriptBlock {
            Import-Module RemoteDesktopServices
            Get-RDLicenseUsage
        }
        
        $usage | Format-Table -AutoSize
        return $usage
    }
    catch {
        Write-Host "Error analyzing license usage: $_" -ForegroundColor Red
        return $null
    }
}

function Check-RDSServerConfiguration {
    Write-Host "`nChecking RDS Server Configuration..." -ForegroundColor Yellow
    try {
        $config = Invoke-Command -ComputerName $global:licenseServer -ScriptBlock {
            Import-Module RemoteDesktopServices
            $deployment = Get-RDDeploymentGatewayConfiguration
            $collection = Get-RDSessionCollection
            return @{
                Deployment = $deployment
                Collection = $collection
            }
        }
        
        $result = [PSCustomObject]@{
            GatewayMode = $config.Deployment.GatewayMode
            CollectionName = $config.Collection.CollectionName
            CollectionDescription = $config.Collection.CollectionDescription
        }
        
        $result | Format-List
        return $result
    }
    catch {
        Write-Host "Error checking RDS server configuration: $_" -ForegroundColor Red
        return $null
    }
}

function Verify-CALCompliance {
    Write-Host "`nVerifying CAL Compliance..." -ForegroundColor Yellow
    try {
        $compliance = Invoke-Command -ComputerName $global:licenseServer -ScriptBlock {
            Import-Module RemoteDesktopServices
            $licenses = Get-RDLicense
            $usage = Get-RDLicenseUsage
            
            $totalLicenses = ($licenses | Measure-Object -Property TotalLicenses -Sum).Sum
            $usedLicenses = ($usage | Measure-Object -Property IssuedLicenses -Sum).Sum
            
            return @{
                TotalLicenses = $totalLicenses
                UsedLicenses = $usedLicenses
                IsCompliant = $totalLicenses -ge $usedLicenses
            }
        }
        
        $result = [PSCustomObject]@{
            TotalLicenses = $compliance.TotalLicenses
            UsedLicenses = $compliance.UsedLicenses
            IsCompliant = $compliance.IsCompliant
            ComplianceStatus = if ($compliance.IsCompliant) { "Compliant" } else { "Non-Compliant" }
        }
        
        $result | Format-List
        return $result
    }
    catch {
        Write-Host "Error verifying CAL compliance: $_" -ForegroundColor Red
        return $null
    }
}

function Review-LicensePolicies {
    Write-Host "`nReviewing License Policies..." -ForegroundColor Yellow
    try {
        $policies = Invoke-Command -ComputerName $global:licenseServer -ScriptBlock {
            Import-Module RemoteDesktopServices
            Get-RDLicenseConfiguration
        }
        
        $result = [PSCustomObject]@{
            Mode = $policies.Mode
            LicensingType = $policies.LicensingType
            PolicyExpirationDays = $policies.PolicyExpirationDays
            PolicyOverrideAllowed = $policies.PolicyOverrideAllowed
        }
        
        $result | Format-List
        return $result
    }
    catch {
        Write-Host "Error reviewing license policies: $_" -ForegroundColor Red
        return $null
    }
}

function Check-LicenseServerHealth {
    Write-Host "`nChecking License Server Health..." -ForegroundColor Yellow
    try {
        $health = Invoke-Command -ComputerName $global:licenseServer -ScriptBlock {
            Import-Module RemoteDesktopServices
            $status = Get-RDLicenseServerStatus
            $service = Get-Service -Name TermServLicensing
            
            return @{
                Status = $status
                ServiceStatus = $service.Status
            }
        }
        
        $result = [PSCustomObject]@{
            IsActivated = $health.Status.IsActivated
            LastIssuedLicenseDate = $health.Status.LastIssuedLicenseDate
            GracePeriodDays = $health.Status.GracePeriodDays
            ServiceStatus = $health.ServiceStatus
        }
        
        $result | Format-List
        return $result
    }
    catch {
        Write-Host "Error checking license server health: $_" -ForegroundColor Red
        return $null
    }
}

function Generate-HTMLReport {
    param([hashtable]$AllResults)

    Write-Host "`nGenerating Comprehensive HTML Report..." -ForegroundColor Yellow
    $reportContent = @"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>RDS License Audit Report</title>
    <style>
        body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 1200px; margin: 0 auto; padding: 20px; }
        h1, h2, h3 { color: #0078D4; }
        table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
        .warning { color: orange; }
        .critical { color: red; }
        .success { color: green; }
    </style>
</head>
<body>
    <h1>RDS License Audit Report</h1>
    <p>Generated on: $(Get-Date)</p>
    <p>License Server: $global:licenseServer</p>

    <h2>License Server Configuration</h2>
    $($AllResults.ServerConfig | ConvertTo-Html -Fragment)

    <h2>Available Licenses</h2>
    $($AllResults.AvailableLicenses | ConvertTo-Html -Fragment)

    <h2>License Usage</h2>
    $($AllResults.LicenseUsage | ConvertTo-Html -Fragment)

    <h2>RDS Server Configuration</h2>
    $($AllResults.RDSConfig | ConvertTo-Html -Fragment)

    <h2>CAL Compliance</h2>
    $($AllResults.CALCompliance | ConvertTo-Html -Fragment)

    <h2>License Policies</h2>
    $($AllResults.LicensePolicies | ConvertTo-Html -Fragment)

    <h2>License Server Health</h2>
    $($AllResults.ServerHealth | ConvertTo-Html -Fragment)
</body>
</html>
"@

    $reportContent | Out-File -FilePath $global:reportPath
    Write-Host "Report generated and saved to: $global:reportPath" -ForegroundColor Green
}

# Main program loop
$allResults = @{}

do {
    Show-Menu
    $choice = Read-Host "`nEnter your choice (1-10)"

    switch ($choice) {
        "1" { Set-LicenseServer }
        "2" { $allResults.ServerConfig = Check-LicenseServerConfiguration }
        "3" { $allResults.AvailableLicenses = List-AvailableLicenses }
        "4" { $allResults.LicenseUsage = Analyze-LicenseUsage }
        "5" { $allResults.RDSConfig = Check-RDSServerConfiguration }
        "6" { $allResults.CALCompliance = Verify-CALCompliance }
        "7" { $allResults.LicensePolicies = Review-LicensePolicies }
        "8" { $allResults.ServerHealth = Check-LicenseServerHealth }
        "9" { Generate-HTMLReport -AllResults $allResults }
        "10" { Write-Host "Exiting program..." -ForegroundColor Yellow; break }
        default { Write-Host "Invalid choice. Please try again." -ForegroundColor Red }
    }

    if ($choice -ne "10") {
        Read-Host "`nPress Enter to continue..."
    }
} while ($choice -ne "10")

This RDS License Audit Toolkit includes:

  1. A menu-driven interface for easy navigation.
  2. Functions to analyze various aspects of RDS licensing:
    • License Server Configuration Check
    • Available Licenses Listing
    • License Usage Analysis
    • RDS Server Configuration Check
    • CAL Compliance Verification
    • License Policies Review
    • License Server Health Check
  3. Option to set a target license server (local or remote)
  4. HTML report generation for easy sharing and viewing of results

Key features:

  • Comprehensive RDS license server configuration analysis
  • Detailed listing of available licenses and their types
  • Analysis of current license usage
  • Verification of Client Access License (CAL) compliance
  • Review of RDS deployment and collection configurations
  • Examination of license policies and server health

This tool is particularly useful for:

  • RDS administrators managing license servers
  • IT professionals auditing RDS environments
  • System administrators troubleshooting RDS licensing issues
  • Anyone needing to quickly gather comprehensive information about RDS licensing configuration and usage

To use this script effectively:

  1. Run PowerShell as an administrator
  2. Ensure you have the Remote Desktop Services PowerShell module installed
  3. Have the necessary permissions to query RDS license information (local admin rights on the license server or appropriate delegated permissions)
  4. Review the generated HTML report for a comprehensive overview of the RDS licensing status and configuration

This script provides a thorough analysis of RDS licensing, helping to identify potential issues, compliance concerns, or areas that need attention. It’s designed to give administrators a quick but comprehensive view of their RDS licensing health and configuration.

RDS Login and Logout Logging Script

<#
.SYNOPSIS
RDS Login and Logout Logging Script

.DESCRIPTION
This script automatically logs user login and logout events for Remote Desktop Services.
It runs continuously as a background job, monitoring the Windows Event Log for relevant events.

.NOTES
File Name      : RDSLoginLogoutLogger.ps1
Author         : [Your Name]
Prerequisite   : PowerShell V3 or later, admin rights on the RDS server
Version        : 1.0
Date           : [Current Date]

.EXAMPLE
Start-Job -FilePath .\RDSLoginLogoutLogger.ps1
#>

# Configuration
$logFilePath = "C:\Logs\RDSLoginLogout.log"
$lastRunFile = "C:\Logs\RDSLoginLogoutLastRun.txt"

# Ensure log directory exists
$logDir = Split-Path $logFilePath -Parent
if (-not (Test-Path $logDir)) {
    New-Item -ItemType Directory -Path $logDir | Out-Null
}

# Function to write log entries
function Write-Log {
    param (
        [string]$Message
    )
    
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logEntry = "$timestamp - $Message"
    Add-Content -Path $logFilePath -Value $logEntry
}

# Function to get the last run time
function Get-LastRunTime {
    if (Test-Path $lastRunFile) {
        return Get-Content $lastRunFile
    }
    return (Get-Date).AddDays(-1).ToString("o")  # Default to 1 day ago if no last run time
}

# Function to save the last run time
function Save-LastRunTime {
    param (
        [DateTime]$LastRunTime
    )
    $LastRunTime.ToString("o") | Set-Content $lastRunFile
}

# Main logging loop
try {
    Write-Log "RDS Login/Logout logging started."

    while ($true) {
        $lastRunTime = Get-LastRunTime
        $currentTime = Get-Date

        # Query for login events
        $loginEvents = Get-WinEvent -FilterHashtable @{
            LogName = 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational'
            ID = 21  # Event ID for session logon
            StartTime = $lastRunTime
        } -ErrorAction SilentlyContinue

        # Query for logout events
        $logoutEvents = Get-WinEvent -FilterHashtable @{
            LogName = 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational'
            ID = 23  # Event ID for session logoff
            StartTime = $lastRunTime
        } -ErrorAction SilentlyContinue

        # Process login events
        foreach ($event in $loginEvents) {
            $username = $event.Properties[0].Value
            $sessionId = $event.Properties[1].Value
            Write-Log "User logged in: $username (Session ID: $sessionId)"
        }

        # Process logout events
        foreach ($event in $logoutEvents) {
            $username = $event.Properties[0].Value
            $sessionId = $event.Properties[1].Value
            Write-Log "User logged out: $username (Session ID: $sessionId)"
        }

        # Save the current time as the last run time
        Save-LastRunTime $currentTime

        # Wait for a minute before the next check
        Start-Sleep -Seconds 60
    }
}
catch {
    Write-Log "An error occurred: $_"
}
finally {
    Write-Log "RDS Login/Logout logging stopped."
}

To use this script:

  1. Save the script as RDSLoginLogoutLogger.ps1 in a suitable location on your RDS server.
  2. Modify the $logFilePath and $lastRunFile variables at the beginning of the script if you want to change the default log locations.
  3. To run the script as a background job, open PowerShell as an administrator and use the following command:
    Start-Job -FilePath C:\Path\To\RDSLoginLogoutLogger.ps1
    Replace C:\Path\To\ with the actual path where you saved the script.
  4. To check the status of the job: Get-Job
  5. To stop the job when needed: Stop-Job -Id <JobId>
  6. Replace <JobId> with the ID of the job from the Get-Job command.

Key features of this script:

  1. Continuous Monitoring: Runs as a background job, continuously checking for new login and logout events.
  2. Efficient Event Querying: Uses the last run time to query only for new events since the last check.
  3. Separate Log File: Logs events to a dedicated file for easy review and analysis.
  4. Error Handling: Includes basic error handling to log any issues that occur during execution.
  5. Low Resource Usage: Checks for new events every minute, balancing timeliness with system resource usage.

Notes:

  • This script needs to be run with administrator privileges on the RDS server.
  • The script creates a log file and a last run time file. Ensure the specified paths are accessible and writable.
  • For long-term use, consider implementing a log rotation mechanism to manage log file sizes.
  • You may need to adjust the event IDs (21 for login, 23 for logout) if your RDS environment uses different event IDs for these actions.
  • Always test the script in a non-production environment before deploying it to production servers.

This script provides a robust solution for automatically logging RDS login and logout events, which can be valuable for security auditing, user activity tracking, and compliance purposes.