Public/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, [String] $Connection = 'Atws' ) Begin { # Lookup Verbose, WhatIf and other preferences from calling context Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState If (-not($global:AtwsConnection[$Connection].Url)) { Throw [ApplicationException] 'Not connected to Autotask WebAPI. Run Connect-AutotaskWebAPI first.' } Else { $Atws = $global:AtwsConnection[$Connection] } } Process { Write-Verbose ('{0}: Mashing parameters into an array of strings.' -F $MyInvocation.MyCommand.Name) # $Filter should not be a flat string. If it is - fix it! If ($Filter.Count -eq 1 -and $Filter -match ' ' ) { # First, make sure it is a single string and replace parenthesis with our special operator $Filter = $Filter -join ' ' -replace '\(',' -begin ' -replace '\)', ' -end ' # Removing double possible spaces we may have introduced Do {$Filter = $Filter -replace ' ',' '} While ($Filter -match ' ') # Split back in to array, respecting quotes $Words = $Filter.Trim().Split(' ') $Filter = @() $Temp = @() Foreach ($Word in $Words) { If ($Temp.Count -eq 0 -and $Word -match '^[\"\'']') { $Temp += $Word.TrimStart('"''') } ElseIf ($Temp.Count -gt 0 -and $Word -match "[\'\""]$") { $Temp += $Word.TrimEnd("'""") $Filter += $Temp -join ' ' $Temp = @() } ElseIf ($Temp.Count -gt 0) { $Temp += $Word } Else { $Filter += $Word } } } Write-Verbose ('{0}: Checking query for variables that have survived as string' -F $MyInvocation.MyCommand.Name) $NewFilter = @() Foreach ($Word in $Filter) { $Value = $Word # Is it a variable name? If ($Word -match '^\$\{?(\w+:)?(\w+)\}?(\.\w[\.\w]+)?$') { # If present, first group is SCOPE. In the context of this function, scope must be Global, Script or # parent (1). If you used scope 'local' when you called this function, then the scope HERE is parent. $Scope = $Matches[1] If (-not ($Scope) -or $Scope -eq 'local') { $Scope = 1 # Parent } # The variable name MUST be present $VariableName = $Matches[2] # A property tail CAN be present $PropertyTail = $Matches[3] # Check that the variable exists $Variable = Try { Get-Variable -Name $VariableName -Scope $Scope -ValueOnly -ErrorAction Stop } Catch { # If variable scope is Global, but not explicitly mentioned as such, the above line will fail # If scope Script failed, then try Global $Scope = 'Global' Get-Variable -Name $VariableName -Scope $Scope -ValueOnly -ErrorAction SilentlyContinue } If ($Variable) { Write-Verbose ('{0}: Substituting {1} for its value' -F $MyInvocation.MyCommand.Name, $Word) If ($PropertyTail) { # Add properties back $Expression = '$Variable{0}' -F $PropertyTail # Invoke-Expression is considered risky from an SQL injection kind of perspective. But by only # permitting a .dot separated string of [a-zA-Z0-9_] we are PROBABLY safe... $Value = Invoke-Expression -Command $Expression } Else { $Value = $Variable } # Normalize dates. Important to avoid QueryXML problems If ($Value.GetType().Name -eq 'DateTime') {[String]$Value = Get-Date $Value -Format s} } } $NewFilter += $Value } # Squash into a flat array with entity first [Array]$Query = @($Entity) + $NewFilter Write-Verbose ('{0}: Converting query string into QueryXml. String as array looks like: {1}' -F $MyInvocation.MyCommand.Name, $($Query -join ', ')) [xml]$QueryXml = ConvertTo-QueryXML @Query Write-Verbose ('{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)) { $result = @() Write-Verbose ('{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) $lastquery = $atws.query($QueryXml.InnerXml) If ($lastquery.Errors.Count -gt 0) { Foreach ($AtwsError in $lastquery.Errors) { Write-Error $AtwsError.Message } Return } $result += $lastquery.EntityResults $UpperBound = $lastquery.EntityResults[$lastquery.EntityResults.GetUpperBound(0)].id $expression.InnerText = $UpperBound If ($FirstPass) { # Insert looping construct into query [void]$QueryXml.queryxml.query.AppendChild($field) $FirstPass = $False } } Until ($lastquery.EntityResults.Count -lt 500) } } End { Write-Verbose ('{0}: End of function' -F $MyInvocation.MyCommand.Name) Return $result } } |