UcsWebUtil.psm1
Function Invoke-UcsWebRequest { Param([Parameter(Mandatory,HelpMessage = '127.0.0.1',ValueFromPipelineByPropertyName,ValueFromPipeline)][ValidatePattern('^([0-2]?[0-9]{1,2}\.){3}([0-2]?[0-9]{1,2})$')][String]$IPv4Address, [Parameter(Mandatory,HelpMessage = 'api/test/example')][String]$ApiEndpoint, [ValidateSet('Get','Post','Put')][String]$Method = 'Get', $Body = $null, [String]$ContentType, [Timespan]$Timeout = (Get-UcsConfig -API Web).Timeout, [PsCredential[]]$Credential = (Get-UcsConfigCredential -API Web -CredentialOnly), [int][ValidateRange(1,100)]$Retries = (Get-UcsConfig -API Web).Retries, [int][ValidateRange(1,65535)]$Port = (Get-UcsConfig -API Web).Port, [Nullable[boolean]]$UseHTTPS = (Get-UcsConfig -API Web).EnableEncryption ) $ThisIPv4Address = $IPv4Address $Protocol = "http" #SSL support has been removed from this invoke. $ThisHost = $ThisIPv4Address $ThisUri = ('{0}://{1}:{2}/{3}' -f $Protocol, $ThisHost, $Port, $ApiEndpoint) #The retry system works by try/catching the command multiple times $RetriesRemaining = $Retries $ThisCredentialIndex = 0 While($RetriesRemaining -gt 0) { #Credential handler. $ThisCredential = $Credential[$ThisCredentialIndex] $Session = New-Object -TypeName Microsoft.PowerShell.Commands.WebRequestSession $Cookie = New-UcsWebCookie -Credential $ThisCredential -Hostname $ThisHost $Session.Cookies.Add($Cookie) #This section attempts to make the request look more real to the phone. As far as I can tell, the phone probably doesn't care. $BasicAuth = ('Basic {0}' -f ( Convert-UcsWebToBase64 -String ('{0}:{1}' -f $ThisCredential.UserName, (ConvertFrom-PsCredential -Credential $ThisCredential)) ) ) $Header = @{ Authorization = $BasicAuth Accept = '*/*' Referer = ('{0}://{1}/index.htm' -f $Protocol, $ThisHost) Origin = ('{0}://{1}' -f $Protocol, $ThisHost) } #Request loop. Try { if($Body.Length -gt 0) { Write-Debug -Message ("Invoking webrequest for `"{0}`" and sending {1}." -f $ThisUri, $Body) $RestOutput = Invoke-WebRequest -Uri $ThisUri -WebSession $Session -Headers $Header -Body $Body -ContentType $ContentType -TimeoutSec $Timeout.TotalSeconds -Method $Method -UseBasicParsing -ErrorAction Stop } else { Write-Debug -Message ("Invoking webrequest for `"{0}`", no body to send." -f $ThisUri) $RestOutput = Invoke-WebRequest -Uri $ThisUri -WebSession $Session -Headers $Header -ContentType $ContentType -TimeoutSec $Timeout.TotalSeconds -Method $Method -UseBasicParsing -ErrorAction Stop } Break } Catch { $RetriesRemaining-- #Deincrement the counter so we remember our state. $ErrorStatusCode = $_.Exception.Response.StatusCode.Value__ #Returns null if it timed out. if($ErrorStatusCode -eq '403' -or $ErrorStatusCode -eq '401') { #Authentication error. $ThisCredentialIndex++ if($ThisCredentialIndex -lt $Credential.Count) { $RetriesRemaining++ #Restore this failure so we can try with our new credential. Write-Debug "Trying new credentials..." } else { $RetriesRemaining = 0 #No credentials left and it's not worth trying. } } if($RetriesRemaining -le 0) { Write-Debug -Message ('Returned error was "{0}".' -f $_) Write-Error -Message ("Couldn't connect to IP {0}." -f $IPv4Address) } else { Write-Debug -Message ("Couldn't connect to IP {0} with error message `"{1}`" {2} retries remaining." -f $IPv4Address, $_, $RetriesRemaining) } } } Return $RestOutput.Content } Function Convert-UcsWebToBase64 { <# .SYNOPSIS Encodes to Polycom's Base64 authentication string #> Param([Parameter(Mandatory)][String]$String) $bytes = [System.Text.Encoding]::ASCII.GetBytes($String) $base64 = [System.Convert]::ToBase64String($bytes) return $base64 } Function New-UcsWebCookie { Param( [Parameter(Mandatory)][String]$Hostname, [Parameter(Mandatory)][PsCredential]$Credential ) $AuthHeader = ('{0}:{1}' -f $Credential.UserName, (ConvertFrom-PsCredential -Credential $Credential)) $Encoded = Convert-UcsWebToBase64 -String $AuthHeader $Cookie = New-Object -TypeName System.Net.Cookie $Cookie.Name = 'Authorization' $Cookie.Value = ('Basic {0}' -f $Encoded) $Cookie.Domain = $Hostname Return $Cookie } Function Get-UcsWebHostname { <# .SYNOPSIS Makes a web request to a Polycom phone's HTTPS address by IP address. Reads the certificate and returns the certificate's hostname. .DESCRIPTION The hostname is the phone's MAC address - so this is one of the easier ways to get the MAC address, and it doesn't require credentials. #> Param( [Parameter(Mandatory,HelpMessage = '127.0.0.1',ValueFromPipelineByPropertyName,ValueFromPipeline)][ValidatePattern('^([0-2]?[0-9]{1,2}\.){3}([0-2]?[0-9]{1,2})$')][String[]]$IPv4Address ) $URI = ('https://{0}' -f $IPv4Address) $Request = [Net.HttpWebRequest]::Create($URI) try { #Make the request but ignore (dispose it) the response, since we only care about the service point $Request.GetResponse().Dispose() } catch [Net.WebException] { if ($_.Exception.Status -ne [Net.WebExceptionStatus]::TrustFailure) { #We ignore trust failures, since we only want the certificate. #Let other exceptions bubble up, or write-error the exception and return from this method Write-Error $_ -ErrorAction Stop } } #The ServicePoint object should now contain the Certificate for the site. $servicePoint = $Request.ServicePoint $Subject = $servicePoint.Certificate.Subject #This now contains something like "CN=0004F28B54F4, O=Polycom Inc." $Matches = $null if($Subject -match '[0-9a-f]{12}') { Return $Matches[0] } else { Write-Error "Couldn't get a result for $IPv4Address." Return $null } } |