UcsWeb.psm1

Function Get-UcsWebConfiguration
{
  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,
    [String[]]$IgnoreSources = ('ParsedHtml', 'RawContent', 'RawContentStream', 'StatusDescription', 'Headers', 'BaseResponse', 'Content', '#comment'),
    [String[]]$IgnoreParameters = ('Length'),
    [Hashtable]$SourceNameOverride = @{
      'CALL_SERVER'   = 'sip'
      'CONFIG_FILES'  = 'config'
      'DEVICE_SETTINGS' = 'device'
      'PHONE_LOCAL'   = 'local'
      'WEB'           = 'web'
    },
    [Int]$Retries = $Script:DefaultRetries
  )

  BEGIN {
    $ParameterList = New-Object -TypeName System.Collections.ArrayList

  }  PROCESS {
    #Structure:
    <# Foreach IpAddress
        Foreach SourceId (configuration Id)
        Foreach Source in the response (should only be one per)
        Foreach Parameter in each source
    #>

    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        $Results = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'Utilities/configuration/phoneBackup' -ErrorAction Stop
        $Results = [Xml]$Results
        $Results = $Results.PHONE_BACKUP
        $Sources = ($Results |
          Get-Member -ErrorAction Stop |
          Where-Object -Property MemberType -EQ -Value Property |
        Where-Object -Property Name -NotIn -Value $IgnoreSources)
      }
      Catch
      {
        Write-Debug -Message "Skipping $ThisIPv4Address. $_"
        Continue
      }

      Foreach($Source in $Sources)
      {
        $SourceName = $Source.Name

        #Choose a displayname for the source if it's in the hashtable..
        $SourceDisplayName = $SourceNameOverride[$SourceName]
        if($SourceDisplayName.Length -lt 1)
        {
          $SourceDisplayName = $SourceName
        }
        $SourceParameters = $Results.$SourceName

        Try
        {
          $ParameterNames = ($SourceParameters |
            Get-Member -ErrorAction Stop |
          Where-Object -Property MemberType -EQ -Value Property).Name
          $ParameterNames = $ParameterNames | Where-Object -FilterScript {
            $_ -notin $IgnoreParameters
          }
        }
        Catch
        {
          Write-Debug -Message "We had an issue with $SourceName. Skipping..."
          Continue
        }
        Foreach ($ParameterName in $ParameterNames)
        {
          $ParameterValue = $SourceParameters.$ParameterName

          $ThisParameter = $ParameterName | Select-Object -Property @{
            Name       = 'Parameter'
            Expression = {
              $ParameterName
            }
          }, @{
            Name       = 'Value'
            Expression = {
              $ParameterValue
            }
          }, @{
            Name       = 'Source'
            Expression = {
              $SourceDisplayName
            }
          }, @{
            Name       = 'IPv4Address'
            Expression = {
              $ThisIPv4Address
            }
          }

          $null = $ParameterList.Add($ThisParameter)
        }
      }
    }
  } END {
    $ParameterList = $ParameterList | Sort-Object -Property IPv4Address, Parameter, Source
    Return $ParameterList
  }
}

Function Get-UcsWebParameter
{
  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 = 'A UCS parameter, such as "Up.Timeout."',ValueFromPipelineByPropertyName)][String[]]$Parameter)
  BEGIN {
    $ParameterOutput = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach($ThisIPv4Address in $IPv4Address)
    {
      $ThisParameterSet = Get-UcsWebConfiguration -IPv4Address $ThisIPv4Address

      $null = $ParameterOutput.Add(($ThisParameterSet | Where-Object -Property Parameter -In -Value $Parameter))
    }
  } END {
    Return $ParameterOutput
  }
}

Function Set-UcsWebParameter
{
  [CmdletBinding(SupportsShouldProcess,ConfirmImpact = 'High')]
  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 = 'A valid UCS parameter, such as Up.Timeout',ValueFromPipelineByPropertyName)][String]$Parameter,
    [Parameter(Mandatory,HelpMessage = 'A valid value for the specified parameter.')][String]$Value,
  [Switch]$PassThru)

  BEGIN {
    $ParameterOutput = New-Object -TypeName System.Collections.ArrayList
    Write-Warning -Message "Not working yet, haven't figured out how to submit the file."
    #TODO: Build a way to send a file.
    #Value accepts boolean values and converts them to something UCS can understand.
    if($Value -eq $true)
    {
      $Value = '1'
    }
    elseif($Value -eq $false)
    {
      $Value = '0'
    }

    $XMLFileOutput = @"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PHONE_CONFIG>
<WEB
$Parameter="$Value"
/>
</PHONE_CONFIG>
"@

  } PROCESS {
    Foreach($ThisIPv4Address in $IPv4Address)
    {
      $ThisResponse = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'form-submit/Utilities/configuration/importFile' -Method Put -ContentType 'application/octet-stream'

      $null = $ParameterOutput.Add($ThisResponse)
    }
  } END {
    if($PassThru -eq $true)
    {
      Return $ParameterOutput
    }
  }
}

Function Get-UcsWebLyncStatus
{
  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)

  BEGIN {
    $OutputArray = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #Actual: http://172.21.84.89/Utilities/LyncStatusXml
        $Results = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'Utilities/LyncStatusXml' -ErrorAction Stop
        $Results = [Xml]$Results
        $Results = $Results.LYNC_STATUS_INFO
        $Results = $Results | Select-Object -Property BOSS_ADMIN, CCCP, EWS, MOH, LYNC_PARAMETERS, O365, QOE, BTOEPCPAIRING, @{
          Name       = 'IPv4Address'
          Expression = {
            $ThisIPv4Address
          }
        }

        $null = $OutputArray.Add($Results)
      }
      Catch
      {
        Write-Error -Message $_
        Continue #Skip this item.
      }
    }

  } END {
    Return $OutputArray
  }
}
Function Get-UcsWebConfigurationOld
{
  #The difference between this and the other is that this retrieves one at a time - using the backup capability, we get all of the data, all at once.
  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,
    [Int[]]$ConfigId = (-1, 1, 2, 5, 6),
    [String[]]$IgnoreSources = ('ParsedHtml', 'RawContent', 'RawContentStream', 'StatusDescription', 'Headers', 'BaseResponse', 'Content', 'All', '#comment'),
    [String[]]$IgnoreParameters = ('Length'),
    [Int]$Retries = $Script:DefaultRetries
  )

  BEGIN {
    $ParameterList = New-Object -TypeName System.Collections.ArrayList

  }  PROCESS {
    #Structure:
    <# Foreach IpAddress
        Foreach SourceId (configuration Id)
        Foreach Source in the response (should only be one per)
        Foreach Parameter in each source
    #>

    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      $IsOnline = $false
      $RemainingRetries = $Retries
      Foreach($SourceId in $ConfigId)
      {
        Try
        {
          $Results = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint "Utilities/configuration/exportFile?source=$SourceId" -ErrorAction Stop
          $Results = [Xml]$Results
          $Results = $Results.PHONE_CONFIG
          $Sources = ($Results |
            Get-Member -ErrorAction Stop |
            Where-Object -Property MemberType -EQ -Value Property |
          Where-Object -Property Name -NotIn -Value $IgnoreSources)
          $IsOnline = $true #If we get here, we must have had a valid attempt, so we can set the flag that will prevent the retry mechanism from skipping this device.
        }
        Catch
        {
          Write-Debug -Message "Skipping config Id $SourceId for $ThisIPv4Address."
          $RemainingRetries--
          if($RemainingRetries -le 0 -and $IsOnline -eq $false)
          {
            Write-Debug -Message 'No retries remaining'
            Break #Leave the SourceId loop.
          }
          Continue
        }

        Foreach($Source in $Sources)
        {
          $SourceName = $Source.Name
          $SourceParameters = $Results.$SourceName

          Try
          {
            $ParameterNames = ($SourceParameters |
              Get-Member -ErrorAction Stop |
            Where-Object -Property MemberType -EQ -Value Property).Name
            $ParameterNames = $ParameterNames | Where-Object -FilterScript {
              $_ -notin $IgnoreParameters
            }
          }
          Catch
          {
            Write-Debug -Message "We had an issue with $SourceName. Skipping..."
            Continue
          }
          Foreach ($ParameterName in $ParameterNames)
          {
            $ParameterValue = $SourceParameters.$ParameterName

            $ThisParameter = $ParameterName | Select-Object -Property @{
              Name       = 'Parameter'
              Expression = {
                $ParameterName
              }
            }, @{
              Name       = 'Value'
              Expression = {
                $ParameterValue
              }
            }, @{
              Name       = 'Source'
              Expression = {
                $SourceName
              }
            }, @{
              Name       = 'SourceId'
              Expression = {
                $SourceId
              }
            }, @{
              Name       = 'IPv4Address'
              Expression = {
                $ThisIPv4Address
              }
            }

            $null = $ParameterList.Add($ThisParameter)
          }
        }
      }
    }
  } END {
    $ParameterList = $ParameterList | Sort-Object -Property IPv4Address, Parameter, Source
    Return $ParameterList
  }
}

Function Get-UcsWebFirmware
{
  <#
      .PARAMETER Latest
      Returns only the most recent firmware available for this phone model.
  #>

  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(ParameterSetName = 'CustomServer')][String]$CustomServerUrl = '',
    [Switch]$Latest,
    [Timespan]$Timeout = (New-TimeSpan -Seconds 30)
  )

  BEGIN {

    $AvailableVersions = New-Object -TypeName System.Collections.ArrayList

    if($PSCmdlet.ParameterSetName -eq 'CustomServer')
    {
      $ServerType = 'customserver'
    }
    else
    {
      $ServerType = 'plcmserver'
    }
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      #Actual request from a phone: http://10.92.10.48/Utilities/softwareUpgrade/getAvailableVersions?type=plcmserver&_=1498851105686
      $UnixTime = Get-UcsUnixTime

      Try
      {
        #Get the provisioning server info.
        $ServerInfo = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'Utilities/softwareUpgrade/getProvisioningServer' -ErrorAction Stop
        $ServerInfo = $ServerInfo.Split(';')
        $HardwareId = $ServerInfo |
        Where-Object -FilterScript {
          $_ -like 'HARDWARE_ID=*'
        } |
        Select-Object -First 1
        $HardwareId = $HardwareId.Substring($HardwareId.IndexOf('=') + 1)
        $HardwareRev = $ServerInfo |
        Where-Object -FilterScript {
          $_ -like 'HARDWARE_REV=*'
        } |
        Select-Object -First 1
        $HardwareRev = $HardwareRev.Substring($HardwareRev.IndexOf('=') + 1)
      }
      Catch
      {
        $HardwareId = $null
        $HardwareRev = $null
      }
      Try
      {
        if($PSCmdlet.ParameterSetName -eq 'CustomServer')
        {
          #If it's a custom server, we have to send the URL to the phone before asking for the update.
          $Body = @{
            CUSTOM_SERVER = "$CustomServerUrl"
          }
          $null = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'form-submit/Utilities/softwareUpgrade/updateCustomServer' -Method Post -Body $Body -ErrorAction -ContentType Stop
        }

        $Result = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint "Utilities/softwareUpgrade/getAvailableVersions?type=$ServerType&_=$UnixTime" -ErrorAction Stop -Timeout $Timeout


        #The response sometimes has garbage before the first tag, so we need to drop it.
        $Result = $Result
        $FirstBracket = $Result.IndexOf('<')
        $Result = $Result.Substring($FirstBracket,$Result.Length - $FirstBracket)
        $Result = ([Xml]$Result).PHONE_IMAGES.REVISION.PHONE_IMAGE
      } Catch
      {
        Write-Debug -Message "Skipped $ThisIPv4Address due to error $_."
      }

      if($Latest -eq $true)
      {
        Write-Debug ('{0} results were returned but the -Latest parameter was included, so dropping all but last one.' -f $Result.Count)
        $Result = $Result | Select-Object -Last 1
      }

      Foreach($Version in $Result)
      {
        Try
        {
          $ThisOutput = $Version | Select-Object -Property @{
            Name       = 'FirmwareRelease'
            Expression = {
              $_.Version
            }
          }, @{
            Name       = 'HardwareId'
            Expression = {
              $HardwareId
            }
          }, @{
            Name       = 'HardwareRev'
            Expression = {
              $HardwareRev
            }
          }, @{
            Name       = 'UpdateUri'
            Expression = {
              $_.Path
            }
          }, @{
            Name       = 'IPv4Address'
            Expression = {
              $ThisIPv4Address
            }
          }

          $null = $AvailableVersions.Add($ThisOutput)
        } Catch
        {
          Write-Debug -Message 'Skipped a version due to a parsing error.'
        }
      }
    }
  } END {
    Return $AvailableVersions
  }
}

Function Update-UcsWebFirmware
{
  [CmdletBinding(SupportsShouldProcess,ConfirmImpact = 'High')]
  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,ValueFromPipelineByPropertyName)][ValidatePattern('^https?://.+$')][String]$UpdateUri)

  BEGIN {
    $StatusResult = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #Actual URL: http://10.92.10.48/form-submit/Utilities/softwareUpgrade/upgrade
        <#$Body = @{
            URLPath = "$UpdateUri"
            serverType = 'plcmserver'
        }#>

        $EncodedURL = [System.Web.HttpUtility]::UrlEncode($UpdateUri)
        $Body = ('URLPath={0}&serverType={1}' -f $EncodedURL,'plcmserver')

        if($PSCmdlet.ShouldProcess(('{0}' -f $ThisIPv4Address)))
        {
          $Result = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'form-submit/Utilities/softwareUpgrade/upgrade' -Body $Body -Method Post -ContentType 'application/x-www-form-urlencoded' -ErrorAction Stop
        }

        $null = $StatusResult.Add($Result)
      } Catch
      {
        Write-Error -Message "Skipped $ThisIPv4Address due to error $_" -Category DeviceError
      }
    }
  } END {
    Return $StatusResult
  }
}

Function Restart-UcsWebPhone
{
  [CmdletBinding(SupportsShouldProcess,ConfirmImpact = 'High')]
  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,
  [String][ValidateSet('Reboot','Restart')]$Type = 'Reboot')

  BEGIN {
    $StatusResult = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #Actual URL: http://10.92.10.48/form-submit/Reboot http://10.92.10.48/form-submit/Restart

        $Result = $null
        if($PSCmdlet.ShouldProcess(('{0}' -f $ThisIPv4Address)))
        {
          $Result = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint "form-submit/$Type" -Method Post -ContentType 'application/x-www-form-urlencoded' -ErrorAction Stop
        }

        $null = $StatusResult.Add($Result)
      } Catch
      {
        Write-Debug -Message "Skipped $ThisIPv4Address due to error $_."
        Write-Error -Message "Couldn't restart $ThisIPv4Address."
      }
    }
  } END {
    Return $StatusResult
  }
}

Function Reset-UcsWebConfiguration
{
  #sets special long timeout for this operation, as it takes a while to reply.
  [CmdletBinding(SupportsShouldProcess,ConfirmImpact = 'High')]
  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,
  [Timespan]$Timeout = (New-TimeSpan -Seconds 20))

  BEGIN {
    $StatusResult = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #Actual URL: http://10.92.10.160/form-submit/Utilities/restorePhoneToFactory

        if($PSCmdlet.ShouldProcess($ThisIPv4Address,'Reset to factory settings'))
        {
          $Result = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'form-submit/Utilities/restorePhoneToFactory' -Method Post -ContentType 'application/x-www-form-urlencoded' -Timeout $Timeout -ErrorAction Stop
        }

        $null = $StatusResult.Add($Result)
      } Catch
      {
        Write-Debug -Message "Skipped $ThisIPv4Address due to error $_."
      }
    }
  } END {
    Return $StatusResult
  }
}

Function Get-UcsWebLyncSignInStatus
{
  [CmdletBinding(DefaultParameterSetName="Normal")]
  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(ParameterSetName="Normal")][String][ValidateSet('None','SignedOut','SignedIn','SigningOut','SigningIn','PasswordChanged')]$WaitFor = 'None',
    [Parameter(ParameterSetName="Raw")][String][ValidateSet('None','UNREGISTERED','SIGNED_IN','SIGNING_OUT','SIGNING_IN','PASS_CHANGED')]$WaitForRaw = 'None',
    [Switch]$InvertWaitFor,
    [Int][ValidateRange(1,3600)]$TimeoutSeconds = 120
  )

  BEGIN
  {
    $ResultOutput = New-Object System.Collections.ArrayList
    $StartTime = Get-Date #Used for calculation of "WaitFor"
    $EndTime = $StartTime.AddSeconds($TimeoutSeconds)

    if($PSCmdlet.ParameterSetName -eq 'Normal')
    {
      Switch($WaitFor)
      {
        'NONE' { $WaitForString = 'NONE'}
        'SignedOut' { $WaitForString = 'UNREGISTERED' }
        'SignedIn' { $WaitForString = 'SIGNED_IN' }
        'SigningOut' { $WaitForString = 'SIGNING_OUT' }
        'SigningIn' { $WaitForString = 'SIGNING_IN' }
        'PasswordChanged' { $WaitForString = 'PASS_CHANGED' }
        default { Write-Error "Invalid option provided: $WaitFor" -ErrorAction Stop} #This should never happen.
      }
    }
    else
    {
      $WaitForString = $WaitForRaw
    }
  }

  PROCESS
  {
    Foreach($ThisIPv4Address in $IPv4Address)
    {
      $ContinueWaiting = $true

      While($ContinueWaiting -eq $true)
      {
        Try
        {
          $UnixTime = Get-UcsUnixTime
          $SigninStatus = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint "Settings/lyncSignInStatus?_=$UnixTime" -ErrorAction Stop
        }
        Catch
        {
          $SigninStatus = "UNAVAILABLE"
          Write-Error "Could not get sign in status for $ThisIPv4Address."
          $ContinueWaiting = $false #Regardless of our type, this is a dead-end, so stop checking.
        }

        #Check if we've met what we're waiting for.
        if($WaitForString -eq 'None')
        {
          $ContinueWaiting = $false
        }
        elseif($WaitForString -eq $SigninStatus -and $InvertWaitFor -eq $false)
        {
          $ContinueWaiting = $false
        }
        elseif($WaitForString -ne $SigninStatus -and $InvertWaitFor -eq $true)
        {
          $ContinueWaiting = $false
        }
        elseif( (Get-Date) -gt $EndTime)
        {
          $ContinueWaiting = $false
          Write-Warning "Timeout expired while waiting for $ThisIPv4Address."
        }
        else
        {
          Start-Sleep -Seconds 1 #Delay to prevent hammering the phone too much.
        }
      }

      $ThisOutput = $ThisIPv4Address | Select-Object @{Name="IPv4Address";Expression={$ThisIPv4Address}},@{Name="SignInStatus";Expression={$SigninStatus}}
      $null = $ResultOutput.Add($ThisOutput)
    }
  }

  END
  {
    Return $ResultOutput
  }
}
Function Register-UcsWebLyncUser
{
  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,ParameterSetName = 'PIN')][String][ValidatePattern('^\d+$')]$Extension,
    [Parameter(Mandatory,ParameterSetName = 'PIN')][String][ValidatePattern('^\d+$')]$PIN,
    [Parameter(Mandatory,ParameterSetName = 'Credential')][String][ValidatePattern('^\d+$')]$Address,
    [Parameter(Mandatory,ParameterSetName = 'Credential')][String][ValidatePattern('^\d+$')]$Domain,
    [Parameter(ParameterSetName = 'Credential')][pscredential]$Credential,
    [Switch]$Force,
    [Switch]$Wait
  )

  BEGIN {
    $StatusResult = New-Object -TypeName System.Collections.ArrayList

    #$EncodedPassword = [System.Web.HttpUtility]::UrlEncode($Password)
    $AuthTypeId = 3 #3 represents PIN authentication.
    if($PSCmdlet.ParameterSetName -eq 'PIN')
    {
      $AuthTypeId = 3
      $Body = @{
        authType  = "$AuthTypeId"
        extension = "$Extension"
        pin       = "$PIN"
      }
    }
    elseif($PSCmdlet.ParameterSetName -eq 'Credential')
    {
      $AuthTypeId = 2
      $Body = @{
        authType = "$AuthTypeId"
        address  = "$Address"
        domain   = "$Domain"
        username = $Credential.UserName
        password = ConvertFrom-SecureString -SecureString $Credential.Password
      }
    }
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #Actual URL: http://10.92.10.48/form-submit/Settings/lyncSignIn
        $CurrentSignInStatus = Get-UcsWebLyncSignInStatus -IPv4Address $ThisIPv4Address -ErrorAction SilentlyContinue
        $DoSignIn = $true

        if($CurrentSignInStatus.SignInStatus -eq "SIGNED_IN")
        {
          if($Force)
          {
            Write-Warning "$ThisIPv4Address was signed in. Sign-in has been unregistered."
            Unregister-UcsWebLyncUser -IPv4Address $ThisIPv4Address -Wait -Confirm:$false
          }
          else
          {
            Write-Error "$ThisIPv4Address is currently signed in. Use the -Force flag to automatically unregister current sign-ins."
            $DoSignIn = $false
          }
        }
        elseif($CurrentSignInStatus.SignInStatus -eq "SIGNING_IN")
        {
          if($Force)
          {
            Write-Warning "$ThisIPv4Address was in the process of signing in. Sign-in has been cancelled and restarted."
            Stop-UcsWebLyncSignIn -IPv4Address $ThisIPv4Address
            $null = Get-UcsWebLyncSignInStatus -IPv4Address $ThisIPv4Address -WaitFor SignedOut
          }
          else
          {
            Write-Error "$ThisIPv4Address is currently in the process of signing in. Use the -Force flag to automatically cancel current sign-ins."
            $DoSignIn = $false
          }
        }
        elseif($CurrentSignInStatus.SignInStatus -ne "UNREGISTERED")
        {
          if($Force)
          {
            Write-Warning "$ThisIPv4Address was not ready to sign in. Sign-in has been cancelled and restarted."
            Unregister-UcsWebLyncUser -IPv4Address $ThisIPv4Address -Wait -Confirm:$false
          }
          else
          {
            Write-Error "$ThisIPv4Address is not currently ready for sign-in. Use the -Force flag to automatically cancel current sign-ins."
            $DoSignIn = $false
          }
        }

        if($DoSignIn)
        {
          $Result = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'form-submit/Settings/lyncSignIn' -Method Post -ContentType 'application/x-www-form-urlencoded' -Body $Body -ErrorAction Stop
          $null = $StatusResult.Add($ThisIPv4Address) #We only get to this line if the first line doesn't fail.
        }
      } Catch
      {
        Write-Error -Message "Skipped $ThisIPv4Address due to error $_."
      }

      if($Result -ne '' -and $null -ne $Result)
      {
        #The phone usually responds with nothing if a sign-in succeeds.
        Write-Error ('Sign-in request failed for {0} with error ''{1}.''' -f $ThisIPv4Address,$Result)
      }
    }
  } END {
    if($Wait)
    {
      #The wait flag allows a user to instruct the script to wait to exit until all phones have signed in.
      #As a nice side effect, this also allows us to throw an error if the sign-in fails for any reason.
      #We batch together all the phones to minimize wait time - this way, if you have 100 phones, there may be almost no waiting.
      Foreach ($ThisIPv4Address in $StatusResult)
      {
        $SigninStatus = Get-UcsWebLyncSignInStatus -IPv4Address $ThisIPv4Address -WaitFor SigningIn -InvertWaitFor

        if($SigninStatus.SignInStatus -ne 'SIGNED_IN')
        {
          Write-Error ('Sign-in request failed for {0}. Bad credentials?' -f $ThisIPv4Address) -Category AuthenticationError
        }
      }
    }
  }
}

Function Unregister-UcsWebLyncUser
{
  [CmdletBinding(SupportsShouldProcess,ConfirmImpact = 'High')]
  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,
  [Switch]$Wait)

  BEGIN {
    $SuccessPhones = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #Actual URL: http://172.21.7.19/form-submit/Settings/lyncSignOut

        if($PSCmdlet.ShouldProcess($ThisIPv4Address,'Sign out user'))
        {
          $Result = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'form-submit/Settings/lyncSignOut' -Method Post -ContentType 'application/x-www-form-urlencoded' -ErrorAction Stop
        }
      }
      Catch
      {
        Write-Error -Message "Skipped $ThisIPv4Address due to error $_."
      }


      if($Result -ne "SIGNING_OUT")
      {
        Write-Error ('Sign-out request failed for {0} with error ''{1}.''' -f $ThisIPv4Address,$Result)
      }
      else
      {
        $null = $SuccessPhones.Add($ThisIPv4Address)
      }
    }
  } END {
    if($Wait)
    {
      $null = Get-UcsWebLyncSignInStatus -IPv4Address $SuccessPhones -WaitFor SignedOut
    }
  }
}

Function Stop-UcsWebLyncSignIn
{
  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)
  BEGIN {
    $null = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #Actual URL: http://10.92.10.48/form-submit/Settings/lyncCancelSignIn

        $SigninStatus = Get-UcsWebLyncSignInStatus -IPv4Address $ThisIPv4Address

        if($SigninStatus.SignInStatus -eq 'SIGNING_IN')
        {
          $Result = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'form-submit/Settings/lyncCancelSignIn' -Method Post -ContentType 'application/x-www-form-urlencoded' -ErrorAction Stop
        }
        else
        {
          Write-Error "No sign-in to cancel for $ThisIPv4Address."
        }
      } Catch
      {
        Write-Debug -Message "Skipped $ThisIPv4Address due to error $_."
      }

      if($Result -ne '' -and $null -ne $Result)
      {
        Write-Error ('Sign-in cancel request failed for {0} with error ''{1}''' -f $ThisIPv4Address,$Result)
      }
    }
  } END {
  }
}

Function Get-UcsWebAuditLog
{
  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)
  BEGIN {
    $AllResult = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #Actual URL: http://10.92.10.160/Diagnostics/log?value=app&dummyParam=1498860013020
        $UnixTime = Get-UcsUnixTime
        $Result = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint "Diagnostics/log?value=audit&dummyParam=$UnixTime" -ErrorAction Stop
        $SplitString = $Result.Split("`r`n") | Where-Object -FilterScript {
          $_.Length -gt 2
        }
      } Catch
      {
        Write-Debug -Message "Skipped $ThisIPv4Address due to error $_."
      }

      Foreach ($Line in $SplitString)
      {
        Try
        {
          $SplitAuditLine = $Line.Split('|')
          $TimedateString = $SplitAuditLine[0].Trim(' ')
          $MacAddress = $SplitAuditLine[1]
          $Message = $SplitAuditLine[2]

          $DateString = $TimedateString.Substring(0,6)
          $YearString = $TimedateString.Substring(($TimedateString.Length - 4))
          $null = $TimedateString -match '\d{2}:\d{2}:\d{2}'
          $TimeString = $Matches[0]

          $TimedateString = ('{0} {1} {2}' -f $DateString, $YearString, $TimeString)
          $Date = Get-Date $TimedateString

          $ThisResult = 1 | Select-Object -Property @{
            Name       = 'Date'
            Expression = {
              $Date
            }
          }, @{
            Name       = 'MacAddress'
            Expression = {
              $MacAddress
            }
          }, @{
            Name       = 'IPv4Address'
            Expression = {
              $ThisIPv4Address
            }
          }, @{
            Name       = 'Message'
            Expression = {
              $Message
            }
          }

          $null = $AllResult.Add($ThisResult)
        } Catch
        {
          Write-Debug -Message "Skipped $Line due to error $_"
        }
      }
    }
  } END {
    Return $AllResult
  }
}

Function Get-UcsWebLog
{
  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,
  [String][ValidateSet('app','boot')]$LogType = 'app')
  BEGIN {
    $AllResult = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #Actual URL: http://10.92.10.160/Diagnostics/log?value=app&dummyParam=1498860013020
        $UnixTime = Get-UcsUnixTime
        $Result = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint "Diagnostics/log?value=$LogType&dummyParam=$UnixTime" -ErrorAction Stop
        $SplitString = $Result.Split("`r`n") | Where-Object -FilterScript {
          $_.Length -gt 2
        }
      } Catch
      {
        Write-Debug -Message "Skipped $ThisIPv4Address due to error $_."
      }

      $TheseResults = New-UcsLog -LogString $SplitString -LogType $LogType -IPv4Address $ThisIPv4Address
      Foreach($ThisResult in $TheseResults)
      {
        $null = $AllResult.Add($ThisResult)
      }
    }
  } END {
    Return $AllResult
  }
}

Function Clear-UcsWebLog
{
  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)][String][ValidateSet('app','boot')]$LogType,
  [Switch]$PassThru)
  BEGIN {
    $AllResult = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #Actual URL: http://10.92.10.160/Diagnostics/log?value=boot&clear=1&dummyParam=1499810229667
        $UnixTime = Get-UcsUnixTime
        $Result = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint "Diagnostics/log?value=$LogType&clear=1&dummyParam=$UnixTime" -ErrorAction Stop

        $null = $AllResult.Add($Result)
      } Catch
      {
        Write-Debug -Message "Skipped $ThisIPv4Address due to error $_."
      }
    }
  } END {
    if($PassThru -eq $true)
    {
      Return $AllResult
    }
  }
}

Function Get-UcsWebLyncSignIn
{
  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)
  BEGIN {
    $AllOutput = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach($ThisIPv4Address in $IPv4Address)
    {
      #Signin status is from http://10.92.10.160/Settings/lyncSignInStatus?_=1499803468177
      #Cached credentials from http://10.92.10.160/Settings/lyncCachedCredentials?_=1499803468135

      $UnixTime = Get-UcsUnixTime
      Try
      {
        $SigninStatus = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint "Settings/lyncSignInStatus?_=$UnixTime" -ErrorAction Stop
      }
      Catch
      {
        Write-Error -Message "Couldn't connect to $ThisIPv4Address."
        Continue
      }

      if($SigninStatus -eq 'SIGNED_IN')
      {
        $SignedIn = $true
      }
      else
      {
        $SignedIn = $false
      }

      $ThisOutput = $SignedIn | Select-Object -Property @{
        Name       = 'Registered'
        Expression = {
          $SignedIn
        }
      }, @{
        Name       = 'IPv4Address'
        Expression = {
          $ThisIPv4Address
        }
      }

      if($SignedIn -eq $true)
      {
        Try
        {
          $Credentials = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint "Settings/lyncCachedCredentials?_=$UnixTime" -ErrorAction Stop
        }
        Catch
        {
          Write-Error -Message "Couldn't retrieve sign-in information for $ThisIPv4Address."
          Continue
        }
        $Credentials = ConvertFrom-Json -InputObject $Credentials

        if($Credentials.isUsingCfg -eq 'false')
        {
          $IsUsingCfg = $false
        }
        else
        {
          $IsUsingCfg = $true
        }

        $SipAddress = $null
        if($Credentials.address.length -gt 2)
        {
          $SipAddress = ('sip:{0}' -f $Credentials.address)
        }

        $ThisOutput = $ThisOutput | Select-Object -Property *, @{
          Name       = 'SipAddress'
          Expression = {
            $SipAddress
          }
        }, @{
          Name       = 'Domain'
          Expression = {
            $Credentials.domain
          }
        }, @{
          Name       = 'Username'
          Expression = {
            $Credentials.user
          }
        }, @{
          Name       = 'Extension'
          Expression = {
            $Credentials.extension
          }
        }, @{
          Name       = 'IsUsingConfig'
          Expression = {
            $IsUsingCfg
          }
        }

        if($Credentials.Extension.Length -gt 0)
        {
          #We're using PIN auth and therefore aren't using Domain or User.
          $ThisOutput = $ThisOutput | Select-Object -Property * -ExcludeProperty Domain, Username
        }
        else
        {
          #We're using user auth and therefore aren't using Domain or User.
          $ThisOutput = $ThisOutput | Select-Object -Property * -ExcludeProperty Extension
        }


      }
      $null = $AllOutput.Add($ThisOutput) #Add to the collection.
    }
  } END {
    Return $AllOutput
  }
}

Function Get-UcsWebDeviceInfo
{
  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
    )

  BEGIN {
    $AllPhoneInfo = New-Object -TypeName System.Collections.ArrayList
  } PROCESS {
    Foreach($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        #http://10.92.10.160/home.htm
        $ThisResult = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'home.htm' -ErrorAction Stop
      }
      Catch
      {
        Write-Error -Message "Couldn't connect to $ThisIPv4Address."
        Continue
      }


      $Content = $ThisResult

      $Matches = $null
      $null = $Content -match "(\d+[A-Z]?\.){3}\d{4,}[A-Z]*" #We're looking for the specific format of the software version string, which seems to always be 1.1.1.1234 or similar.
      $FirmwareRelease = $Matches[0]

      $Matches = $null
      $null = $Content -match '(?<=\n\s*)(\w+\s)+\d+'
      $Model = $Matches[0].Trim("`r`n ")

      $Matches = $null
      $null = $Content -match '(?<=\s*)\d{4}-\d{5}-\d{3}'
      $HardwareId = $Matches[0].Trim("`r`n ")

      $Matches = $null
      $null = $Content -match "(?<=$HardwareId\sRev:)\w"
      $HardwareRev = $Matches[0].Trim("`r`n ")

      $Matches = $null
      $null = $Content -match '[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}'
      $MacAddress = (($Matches[0].Trim("`r`n ")).Replace(':','')).ToLower()

      $LastReboot = (Get-UcsWebLastReboot -IPv4Address $ThisIPv4Address -ErrorAction SilentlyContinue).LastReboot

      $ThisResult = 1 | Select-Object -Property @{
        Name       = 'MacAddress'
        Expression = {
          $MacAddress
        }
      }, @{
        Name       = 'Model'
        Expression = {
          $Model
        }
      }, @{
        Name       = 'HardwareId'
        Expression = {
          $HardwareId
        }
      }, @{
        Name       = 'HardwareRev'
        Expression = {
          $HardwareRev
        }
      }, @{
        Name       = 'FirmwareRelease'
        Expression = {
          $FirmwareRelease
        }
      }, @{
        Name       = 'LastReboot'
        Expression = {
          $LastReboot
        }
      }, @{
        Name       = 'IPv4Address'
        Expression = {
          $ThisIPv4Address
        }
      }
      $null = $AllPhoneInfo.Add($ThisResult)
    }
  } END {
    Return $AllPhoneInfo
  }
}

Function Get-UcsWebProvisioningInfo {
  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)

  BEGIN
  {
    $OutputObject = New-Object System.Collections.ArrayList
  }
  PROCESS
  {
    Foreach ($ThisIPv4Address in $IPv4Address)
    {
      $ProvisioningServer = $null
      Try
      {
        $Matches = $null
        $ProvUserType = Get-UcsWebParameter -IPv4Address $ThisIPv4Address -Parameter ('device.prov.user','device.prov.serverType')
        $ProvUser = $ProvUserType | Where-Object Parameter -eq "device.prov.user"
        $ProvType = $ProvUserType | Where-Object Parameter -eq "device.prov.serverType"
        $Logs = Get-UcsWebLog -IPv4Address $ThisIPv4Address -LogType app -ErrorAction Stop
        $Logs = $Logs | Where-Object Message -like "Provisioning server address is*" | Select-Object -Last 1
        $null = $Logs.Message -match '(?<=Provisioning server address is).+(?=\.)'
        if($Logs.Count -eq 0) {
          #Try something else if the first thing didn't work.
          $Logs = $Logs | Where-Object Message -like "Prov|Server*is unresponsive" | Select-Object -Last 1
          $null = $Logs.Message -match "(?<=Prov|Server ').+(?='.+)"
        }

        $ProvisioningServer = $Matches[0].Trim(' ')
        #Remove a leading FTP:// or similar.
        $ProvisioningServer = $ProvisioningServer.Replace('/','\') #Make all slashes the same.
        $ProvisioningServerIndex = $ProvisioningServer.LastIndexOf('\') + 1
        if($ProvisioningServerIndex -gt 0)
        {
          $ProvisioningServer = $ProvisioningServer.Substring($ProvisioningServerIndex)
        }

        $ThisOutput = $ProvisioningServer | Select-Object @{Name="ProvServerAddress";Expression={$ProvisioningServer}},@{Name="ProvServerUser";Expression={$ProvUser.Value}},@{Name="ProvServerType";Expression={$ProvType.Value}},@{Name="IPv4Address";Expression={$ThisIPv4Address}}
        $null = $OutputObject.Add($ThisOutput)
      }
      Catch
      {
        Write-Error "Couldn't get provisioning server for $ThisIPv4Address"
      }
    }
  }
  END
  {
    Return $OutputObject
  }
}

Function Get-UcsWebLastReboot {
  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,
  [Timespan]$BootLogClockSkew = (New-Timespan -Seconds 300))
  Begin
  {
    $Reboots = New-Object System.Collections.ArrayList
  }
  Process
  {
    Foreach($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        foreach($LogType in ('app','boot')) {
          $LastReboot = $null


          if($Model -like "*Trio*" -and $LogType -eq 'boot') {
            Write-Debug "Skipping boot logs for $ThisIPv4Address because it was detected as $Model."
            Continue
          }

          $TheseLogs = Get-UcsWebLog -IPv4Address $ThisIPv4Address -LogType $LogType -ErrorAction Stop
          if($LogType -eq 'boot')
          {
            Foreach($Log in $TheseLogs)
            {
              #Apply a clock skew to the boot logs to correct for differences between reported times from logs and the REST API official time.
              $Log.DateTime = $Log.DateTime + $BootLogClockSkew
            }
          }

          $Logs = $TheseLogs | Sort-Object -Property DateTime
          $FirstLog = $Logs | Where-Object DateTime -ne $null | Where-Object Level -eq "*" | Where-Object Message -like "Initial log entry.*" | Select-Object -Last 1
          $LastReboot = $FirstLog.DateTime

          if($null -ne $LastReboot) {
            Break #Leave this loop, we've found the lastreboot.
          }
        }
      }
      Catch
      {
        $LastReboot = $null
        Write-Error "Couldn't get a LastReboot for $ThisIPv4Address. Error was $_."
      }

      if($null -ne $LastReboot) {
        $ThisOutput = $LastReboot | Select-Object @{Name="LastReboot";Expression={$LastReboot}},@{Name="IPv4Address";Expression={$ThisIPv4Address}}
        $null = $Reboots.Add($ThisOutput)
      }
    }
  }
  End
  {
    Return $Reboots
  }
}

Function Reset-UcsWebParameter {
  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 = 'A UCS parameter, such as "Up.Timeout."',ValueFromPipelineByPropertyName)][String[]]$Parameter)

  Begin
  {
  }
  Process
  {
    Foreach($ThisIPv4Address in $IPv4Address)
    {
      Try
      {
        $FormBody = $null
        Foreach($ThisParameter in $Parameter)
        {
          $FormBody += @{$ThisParameter=$null}
        }

        #http://10.92.10.48/form-submit/resetToDefault
        $null = Invoke-UcsWebRequest -IPv4Address $ThisIPv4Address -ApiEndpoint 'form-submit/resetToDefault' -Method Post -Body $FormBody -ContentType 'application/x-www-form-urlencoded' -ErrorAction Stop
      }
      Catch
      {
        Write-Error "Couldn't reset parameter on $ThisIPv4Address."
      }
    }
  }
  End
  {
  }
}