Private/NamedPipe-Client.ps1
|
#Requires -Version 5.1 <# .SYNOPSIS Named pipe client helper functions for MakeMeAdminCLI. .DESCRIPTION Provides functions for client-side communication with the MakeMeAdminCLI named pipe server. Handles connection, request/response, timeouts, and error handling for non-elevated client processes. .NOTES Author: MakeMeAdminCLI Version: 1.0.0 #> function Connect-MakeMeAdminPipe { <# .SYNOPSIS Establishes a connection to the MakeMeAdminCLI named pipe server. .DESCRIPTION Creates a named pipe client connection to the MakeMeAdminCLI service. Handles connection timeouts and provides meaningful error messages when the service is not running. .PARAMETER PipeName The name of the named pipe to connect to. Defaults to the configured pipe name. .PARAMETER TimeoutMilliseconds The timeout in milliseconds for the connection attempt. Defaults to 5000. .OUTPUTS System.IO.Pipes.NamedPipeClientStream or $null on failure. .EXAMPLE $pipe = Connect-MakeMeAdminPipe if ($pipe) { # Use the pipe $pipe.Dispose() } #> [CmdletBinding()] [OutputType([System.IO.Pipes.NamedPipeClientStream])] param( [string]$PipeName, [int]$TimeoutMilliseconds = 5000 ) # Get pipe name from config if not specified if (-not $PipeName) { try { $config = Get-MakeMeAdminConfig $PipeName = $config.PipeName } catch { $PipeName = "MakeMeAdminCLI" } } try { # Create the named pipe client $pipeClient = New-Object System.IO.Pipes.NamedPipeClientStream( ".", # Server name (local) $PipeName, # Pipe name [System.IO.Pipes.PipeDirection]::InOut, # Direction [System.IO.Pipes.PipeOptions]::None # Options ) # Attempt to connect with timeout Write-Verbose "Connecting to named pipe '$PipeName'..." $pipeClient.Connect($TimeoutMilliseconds) $pipeClient.ReadMode = [System.IO.Pipes.PipeTransmissionMode]::Message Write-Verbose "Connected to named pipe successfully." return $pipeClient } catch [System.TimeoutException] { Write-Error "Connection timeout: MakeMeAdminCLI service is not responding. Is the service running?" return $null } catch [System.IO.FileNotFoundException] { Write-Error "MakeMeAdminCLI service is not running. Run 'Install-MakeMeAdminCLI' as administrator to start the service." return $null } catch [System.UnauthorizedAccessException] { Write-Error "Access denied connecting to MakeMeAdminCLI service. You may not have permission to use this service." return $null } catch { Write-Error "Failed to connect to MakeMeAdminCLI service: $($_.Exception.Message)" return $null } } function Send-PipeRequest { <# .SYNOPSIS Sends a request to the MakeMeAdminCLI service via named pipe. .DESCRIPTION Sends a JSON-formatted request through the named pipe and returns the parsed JSON response. Handles serialization, timeouts, and errors. .PARAMETER Request A hashtable or PSCustomObject containing the request data. Expected format: @{ action = "add|remove|status"; duration = 15; username = "DOMAIN\user" } .PARAMETER TimeoutSeconds The timeout in seconds for the entire operation. Defaults to 30. .OUTPUTS PSCustomObject containing the response from the service, or $null on error. .EXAMPLE $response = Send-PipeRequest -Request @{ action = "add"; duration = 30 } if ($response.success) { Write-Host "Admin rights granted until $($response.expiresAt)" } #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Mandatory)] [object]$Request, [int]$TimeoutSeconds = 30 ) $pipeClient = $null $reader = $null $writer = $null try { # Connect to the pipe $pipeClient = Connect-MakeMeAdminPipe -TimeoutMilliseconds ($TimeoutSeconds * 1000) if ($null -eq $pipeClient) { return $null } # Create reader and writer $writer = New-Object System.IO.StreamWriter($pipeClient) $writer.AutoFlush = $true $reader = New-Object System.IO.StreamReader($pipeClient) # Serialize and send the request $jsonRequest = $Request | ConvertTo-Json -Compress Write-Verbose "Sending request: $jsonRequest" $writer.WriteLine($jsonRequest) # Read the response with timeout $readTask = $reader.ReadLineAsync() $completed = $readTask.Wait([TimeSpan]::FromSeconds($TimeoutSeconds)) if (-not $completed) { Write-Error "Response timeout: MakeMeAdminCLI service did not respond in time." return $null } $jsonResponse = $readTask.Result if ([string]::IsNullOrWhiteSpace($jsonResponse)) { Write-Error "Empty response received from MakeMeAdminCLI service." return $null } Write-Verbose "Received response: $jsonResponse" # Parse and return the response $response = $jsonResponse | ConvertFrom-Json return $response } catch { Write-Error "Error communicating with MakeMeAdminCLI service: $($_.Exception.Message)" return $null } finally { # Clean up resources if ($null -ne $reader) { try { $reader.Dispose() } catch { } } if ($null -ne $writer) { try { $writer.Dispose() } catch { } } if ($null -ne $pipeClient) { try { $pipeClient.Dispose() } catch { } } } } function Test-ServiceRunning { <# .SYNOPSIS Tests if the MakeMeAdminCLI service is running. .DESCRIPTION Attempts to connect to the named pipe to verify the service is available. This is a quick check that doesn't send any requests. .OUTPUTS Boolean indicating whether the service is available. .EXAMPLE if (Test-ServiceRunning) { Write-Host "Service is running" } else { Write-Host "Service is not running" } #> [CmdletBinding()] [OutputType([bool])] param() $pipeClient = $null try { $config = Get-MakeMeAdminConfig $pipeName = $config.PipeName $pipeClient = New-Object System.IO.Pipes.NamedPipeClientStream( ".", $pipeName, [System.IO.Pipes.PipeDirection]::InOut, [System.IO.Pipes.PipeOptions]::None ) # Quick connection test with short timeout $pipeClient.Connect(1000) return $true } catch { return $false } finally { if ($null -ne $pipeClient) { try { $pipeClient.Dispose() } catch { } } } } function Get-CurrentUsername { <# .SYNOPSIS Gets the current user's full username in DOMAIN\User format. .DESCRIPTION Returns the current Windows identity name, which is used for requests to the MakeMeAdminCLI service. .OUTPUTS String containing the username in DOMAIN\User format. .EXAMPLE $username = Get-CurrentUsername # Returns something like "CONTOSO\jdoe" #> [CmdletBinding()] [OutputType([string])] param() return [System.Security.Principal.WindowsIdentity]::GetCurrent().Name } function Test-IsElevated { <# .SYNOPSIS Tests if the current PowerShell session is running elevated (as admin). .DESCRIPTION Checks if the current process has administrator privileges. .OUTPUTS Boolean indicating whether the session is elevated. .EXAMPLE if (Test-IsElevated) { Write-Host "Running as administrator" } #> [CmdletBinding()] [OutputType([bool])] param() $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object System.Security.Principal.WindowsPrincipal($identity) return $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) } # Export module members (when dot-sourced from module) if ($MyInvocation.MyCommand.ScriptBlock.Module) { Export-ModuleMember -Function @( 'Connect-MakeMeAdminPipe', 'Send-PipeRequest', 'Test-ServiceRunning', 'Get-CurrentUsername', 'Test-IsElevated' ) } |