Private/Get-AtwsData.ps1
<#
.COPYRIGHT Copyright (c) Office Center H�nefoss AS. All rights reserved. Licensed under the MIT license. See https://github.com/officecenter/Autotask/blob/master/LICENSE.md for license information. #> Function Get-AtwsData { <# .SYNOPSIS This function queries the Autotask Web API for entities matching a specified type and filter. .DESCRIPTION This function queries the Autotask Web API for entities matching a specified type and filter. Valid operators: -and, -or Valid comparison operators: -eq, -ne, -lt, -le, -gt, -ge, -isnull, -isnotnull, -isthisday Valid text comparison operators: -contains, -like, -notlike, -beginswith, -endswith, -soundslike Special operators to nest conditions: -begin, -end .INPUTS Nothing. .OUTPUTS Autotask.Entity[]. One or more Autotask entities returned from Autotask Web API. .EXAMPLE Get-AtwsData -Entity Ticket -Filter {id -gt 0} Gets all tickets with an id greater than 0 from Autotask Web API .NOTES NAME: Get-AtwsData .LINK Set-AtwsData New-AtwsData Remove-AtwsData #> [cmdletbinding( SupportsShouldProcess = $True, ConfirmImpact = 'Low' )] [OutputType([PSObject[]])] param ( [Parameter( Mandatory = $True, Position = 0 )] [String] $Entity, [Parameter( Mandatory = $True, ValueFromRemainingArguments = $true, Position = 1 )] [String[]] $Filter ) Begin { # Enable modern -Debug behavior If ($PSCmdlet.MyInvocation.BoundParameters['Debug'].IsPresent) {$DebugPreference = 'Continue'} Write-Debug ('{0}: Begin of function' -F $MyInvocation.MyCommand.Name) If (-not($Script:Atws.Url)) { Throw [ApplicationException] 'Not connected to Autotask WebAPI. Re-import module with valid credentials.' } $Result = @() # Set up TimeZone offset handling If (-not($script:ESTzone)) { $script:ESTzone = [System.TimeZoneInfo]::FindSystemTimeZoneById("Eastern Standard Time") } If (-not($script:ESToffset)) { $Now = Get-Date $ESTtime = [System.TimeZoneInfo]::ConvertTimeFromUtc($Now.ToUniversalTime(), $ESTzone) $script:ESToffset = (New-TimeSpan -Start $ESTtime -End $Now).TotalHours } } Process { # $Filter is usually passed as a flat string. Make sure it is formatted properly If ($Filter.Count -eq 1 -and $Filter -match ' ' ) { $Filter = . Update-AtwsFilter -FilterString $Filter } # Squash into a flat array with entity first [Array]$Query = @($Entity) + $Filter Write-Debug ('{0}: Converting query string into QueryXml. String as array looks like: {1}' -F $MyInvocation.MyCommand.Name, $($Query -join ', ')) [xml]$QueryXml = ConvertTo-QueryXML @Query Write-Debug ('{0}: QueryXml looks like: {1}' -F $MyInvocation.MyCommand.Name, $QueryXml.InnerXml.ToString()) $Caption = 'Get-Atws{0}' -F $Entity $VerboseDescrition = '{0}: About to run a query for Autotask.{1} using Filter {{{2}}}' -F $Caption, $Entity, ($Filter -join ' ') $VerboseWarning = '{0}: About to run a query for Autotask.{1} using Filter {{{2}}}. Do you want to continue?' -F $Caption, $Entity, ($Filter -join ' ') If ($PSCmdlet.ShouldProcess($VerboseDescrition, $VerboseWarning, $Caption)) { Write-Debug ('{0}: Adding looping construct to query to handle more than 500 results.' -F $MyInvocation.MyCommand.Name) # Native XML is rather tedious... $field = $QueryXml.CreateElement('field') $expression = $QueryXml.CreateElement('expression') $expression.SetAttribute('op','greaterthan') $expression.InnerText = 0 $field.InnerText = 'id' [void]$field.AppendChild($expression) $FirstPass = $True Do { Write-Verbose ('{0}: Passing QueryXML to Autotask API' -F $MyInvocation.MyCommand.Name) # Get the first batch - the API returns max 500 items $lastquery = $atws.query($QueryXml.InnerXml) # Handle any errors If ($lastquery.Errors.Count -gt 0) { Foreach ($AtwsError in $lastquery.Errors) { Write-Error $AtwsError.Message } Return } # Add all returned objects to the Result $result += $lastquery.EntityResults # Results are sorted by object Id. The Id of the last object is the highest object id in the result $UpperBound = $lastquery.EntityResults[$lastquery.EntityResults.GetUpperBound(0)].id # Add the higest Id (so far) to the id -gt ? condition $expression.InnerText = $UpperBound # If this is the first pass we append the expression to the query If ($FirstPass) { # Insert looping construct into query [void]$QueryXml.queryxml.query.AppendChild($field) $FirstPass = $False } } # The last query we have to make will have between 0 and 499 items Until ($lastquery.EntityResults.Count -lt 500) } # Datetimeparameters $Fields = Get-AtwsFieldInfo -Entity $Entity $DateTimeParams = $Fields.Where({$_.Type -eq 'datetime'}).Name # Picklists $Picklists = $Fields.Where{$_.IsPickList} # Expand UDFs by default # Normalize dates (convert to local time). EVery datetime field ever returned # By the API is in CEST. Foreach ($Item in $Result) { # Any userdefined fields? If ($Item.UserDefinedFields.Count -gt 0) { # Expand User defined fields for easy filtering of collections and readability Foreach ($UDF in $Item.UserDefinedFields) { # Make names you HAVE TO escape... $UDFName = '#{0}' -F $UDF.Name Add-Member -InputObject $Item -MemberType NoteProperty -Name $UDFName -Value $UDF.Value -Force } } # Adjust TimeZone on all DateTime properties # Dates RETURNED by the API are always in CEST. Add timezone difference # to get local time Foreach ($DateTimeParam in $DateTimeParams) { # Get the datetime value $Value = $Item.$DateTimeParam # Skip if parameter is empty If (-not ($Value)) { Continue } # Yes, you really have to ADD the difference $Item.$DateTimeParam = $Value.AddHours($script:ESToffset) } If ($Script:UsePickListLabels) { # Restore picklist labels Foreach ($Field in $Picklists) { If ($Item.$($Field.Name) -in $Field.PicklistValues.Value) { $Item.$($Field.Name) = ($Field.PickListValues.Where{$_.Value -eq $Item.$($Field.Name)}).Label } } } } } End { Write-Debug ('{0}: End of function' -F $MyInvocation.MyCommand.Name) Return $result } } |