TechDirect.psm1
<#
.NOTES -------------------------------------------------------------------------------- Code generated by: SAPIEN Technologies, Inc., PowerShell Studio 2020 v5.7.171 Generated on: 2/21/2020 11:02 AM Generated by: dz053479 Organization: CernerWorks -------------------------------------------------------------------------------- .DESCRIPTION Script generated by PowerShell Studio 2020 #> <# =========================================================================== Created with: SAPIEN Technologies, Inc., PowerShell Studio 2019 v5.6.167 Created on: 9/3/2019 8:37 AM Created by: D.Z. Organization: N/A Filename: TechDirect.psm1 ------------------------------------------------------------------------- Module Name: TechDirect =========================================================================== #> function Connect-TechDirect { <# .SYNOPSIS This function will acquire an Access Token from TechDirect .DESCRIPTION This function will acquire an Access Token from TechDirect for use with TechDirect Warranty and Self-Dispatch APIs .PARAMETER Warranty Used to incidate a request for a Warranty API Token .PARAMETER Dispatch Used to incidate a request for a Self-Dispatch API Token .PARAMETER Sandbox Used to request a token using the sandbox environment. Sandbox is not available for the Warranty API. .PARAMETER ClientID Client ID provided by TechDirect for your API access. .PARAMETER ClientSecret Client Secret provided by TechDirect for your API access. .EXAMPLE PS C:\> Connect-TechDirect -ClientID 'ABCDEFGHIJKL' -ClientSecret 'ZYXWVUTSRQPONML' .EXAMPLE PS C:\> Connect-TechDirect -ClientID 'ABCDEFGHIJKL' -ClientSecret 'ZYXWVUTSRQPONML' -Sandbox .NOTES Additional information about the function. #> [CmdletBinding(DefaultParameterSetName = 'Warranty', ConfirmImpact = 'None', PositionalBinding = $true, SupportsPaging = $false, SupportsShouldProcess = $false)] param ( [Parameter(ParameterSetName = 'Warranty', Position = 1)] [switch]$Warranty, [Parameter(ParameterSetName = 'Dispatch', Position = 1)] [switch]$Dispatch, [Parameter(ParameterSetName = 'Dispatch', Position = 2)] [switch]$Sandbox, [Parameter(Mandatory = $true)] [string]$ClientID, [Parameter(Mandatory = $true)] [string]$ClientSecret ) $Bytes = [System.Text.Encoding]::UTF8.GetBytes($($ClientID + ":" + $ClientSecret)) $Base64Key = [System.Convert]::ToBase64String($Bytes) $Header = @{ Authorization = "Basic $Base64Key" } $Body = "grant_type=client_credentials" if ($sandbox) { $Endpoint = "apigtwb2cnp.us.dell.com" } else { $Endpoint = "apigtwb2c.us.dell.com" } Try { $Token = Invoke-RestMethod "https://$Endpoint/auth/oauth/v2/token" -Method Post -Body $Body -ContentType "application/x-www-form-urlencoded" -Headers $Header if ($sandbox) { $Token | Add-Member -MemberType NoteProperty -Name sandbox -Value $True } else { $Token | Add-Member -MemberType NoteProperty -Name sandbox -Value $False } $Token | Add-Member -MemberType NoteProperty "expire_time" -Value (Get-Date).AddSeconds($Token.expires_in) -Force if (!(Test-Path "$env:LOCALAPPDATA\TechDirect\")) { New-Item -Path "$env:LOCALAPPDATA\TechDirect\" -ItemType directory -Force | Out-Null } if ($Warranty) { $Token | Add-Member -MemberType NoteProperty "expire_time" -Value (Get-Date).AddSeconds($Token.expires_in) -Force $Token | ConvertTo-Json | Out-File $env:LOCALAPPDATA\TechDirect\Token-W.json -Force } elseif ($Dispatch) { $Token | Add-Member -MemberType NoteProperty "expire_time" -Value (Get-Date).AddSeconds($Token.expires_in) -Force $Token | ConvertTo-Json | Out-File $env:LOCALAPPDATA\TechDirect\Token-D.json -Force } Write-Output "`r`nSuccessfully acquired TechDirect token.`r`n" return $Token } catch { $result = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $response = $reader.ReadToEnd(); $statuscode = $_.Exception.Response.StatusCode.value__ $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($(($Response | ConvertFrom-Json).error_description), "$statuscode", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } function Get-TDAccessToken { [CmdletBinding()] param ( [switch]$Warranty, [switch]$Dispatch ) if ($Warranty) { if (Test-Path "$env:LOCALAPPDATA\TechDirect\Token-W.json") { try { $TokenInfo = Get-Content $env:LOCALAPPDATA\TechDirect\Token-W.json | ConvertFrom-Json } catch { $ErrCategory = [system.management.automation.errorcategory]::SecurityError $ErrorRecord = New-Object System.Management.Automation.ErrorRecord("Token does not exist or is corrupted, use 'Connect-TechDirect' to acquire a token", "", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } else { $ErrCategory = [system.management.automation.errorcategory]::SecurityError $ErrorRecord = New-Object System.Management.Automation.ErrorRecord("Token does not exist or is corrupted, use 'Connect-TechDirect' to acquire a token", "", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } elseif ($Dispatch) { if (Test-Path "$env:LOCALAPPDATA\TechDirect\Token-D.json") { try { $TokenInfo = Get-Content $env:LOCALAPPDATA\TechDirect\Token-D.json | ConvertFrom-Json } catch { $ErrCategory = [system.management.automation.errorcategory]::SecurityError $ErrorRecord = New-Object System.Management.Automation.ErrorRecord("Token does not exist or is corrupted, use 'Connect-TechDirect' to acquire a token", "", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } else { $ErrCategory = [system.management.automation.errorcategory]::SecurityError $ErrorRecord = New-Object System.Management.Automation.ErrorRecord("Token does not exist or is corrupted, use 'Connect-TechDirect' to acquire a token", "", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } else { return } $UTCOffset = [System.TimeZoneInfo]::Local.GetUtcOffset((get-date)).totalminutes $TokenInfo.expire_time = $TokenInfo.expire_time.AddMinutes($UTCOffset) $TokenInfo.expires_in = [math]::Round((New-timespan (get-date) $TokenInfo.expire_time).TotalSeconds, 0) if ($TokenInfo.expire_time -le (get-date)) { $ErrCategory = [system.management.automation.errorcategory]::AuthenticationError $ErrorRecord = New-Object System.Management.Automation.ErrorRecord("Token is expired, use 'Connect-TechDirect' to acquire a new token", "", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) return } return $TokenInfo } function Format-DispatchPart { param ([string]$PartNumber, [string]$PPID, [int]$Quantity) $part = "<api:PartInfo>`r`n" $Part += "`t<api:PartNumber>$PartNumber</api:PartNumber>`r`n" if ($PPID) { $Part += "`t<api:PPID>$PPID</api:PPID>`r`n" } else { $Part += "`t<api:PPID/>`r`n" } $Part += "`t<api:Quanitity>$Quantity</api:Quanitity>`r`n" $Part += "</api:PartInfo>" return $part } function Format-DispatchAttachment { param ( [string]$Description, [string]$FilePath ) Begin { Add-Type -AssemblyName "System.Web" } Process { $FileName = (Get-Item $FilePath).Name if ($Description -eq "") { $Description = $FileName } $MIMEType = [System.Web.MimeMapping]::GetMimeMapping("$FilePath") $FileData = Convert-FileToBase64 $FilePath $XML = @" <api:AttachmentInfo> <api:Description>$Description</api:Description> <api:FileName>$FileName</api:FileName> <api:MIMEType>$MIMEType</api:MIMEType> <api:Data>$FileData</api:Data> </api:AttachmentInfo> "@ return $XML } } function Get-TechnicianStatus { <# .SYNOPSIS Check Technician Status. .DESCRIPTION Provides account level details. Account should be active and not expired for API transactions to complete. .PARAMETER Credentials Credential Object containing the Username/Password of the Technician .EXAMPLE $Credentials = (Get-Credential) PS C:\> Get-TechnicianStatus -Credentials $Credentials #> [CmdletBinding(ConfirmImpact = 'None', SupportsPaging = $false, SupportsShouldProcess = $false)] param ( [Parameter(Mandatory = $false)] [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:") ) $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password $XML = @" <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com"> <soapenv:Header/> <soapenv:Body> <api:CheckUser> <api:CheckUserRequest> <api:Login>$Username</api:Login> <api:Password>$Password</api:Password> </api:CheckUserRequest> </api:CheckUser> </soapenv:Body> </soapenv:Envelope> "@ $DellToken = Get-TDAccessToken -Dispatch if ($DellToken.sandbox) { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" } else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" } $Headers = @{ Authorization = "Bearer $($DellToken.access_token)" SOAPAction = "http://api.dell.com/IDispatchService/CheckUser" } try { $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml" $Result = $Response.Envelope.Body.CheckUserResponse.CheckUserResult.LoginResult ####### We have to parse the XML response manually, since Powershell doesn't want to convert it to JSON natively $JSON = [pscustomobject]@{ FullName = $Result.FullName Role = $Result.Role PasswordExpirationDate = [datetime]$Result.PasswordExpirationDate Inactive = $Result.Inactive Locked = $Result.Locked } return $JSON } catch { $result = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $response = $reader.ReadToEnd(); $ResponseMessage = ([xml]$Response).Envelope.Body.Fault $statuscode = $_.Exception.Response.StatusCode.value__ $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } function Get-TechnicianInfo { <# .SYNOPSIS Get detailed information about Technician .DESCRIPTION A detailed description of the Get-TechnicianInfo function. .PARAMETER Credentials Credential Object containing the Username/Password of the Technician .EXAMPLE PS C:\> Get-TechnicianInfo .NOTES Additional information about the function. #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:") ) $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password $XML = @" <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com"> <soapenv:Header/> <soapenv:Body> <api:CheckLogin> <api:CheckLoginRequest> <api:Login>$Username</api:Login> <api:Password>$Password</api:Password> </api:CheckLoginRequest> </api:CheckLogin> </soapenv:Body> </soapenv:Envelope> "@ $DellToken = Get-TDAccessToken -Dispatch if ($DellToken.sandbox) { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" } else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" } $Headers = @{ Authorization = "Bearer $($DellToken.access_token)" SOAPAction = "http://api.dell.com/IDispatchService/CheckLogin" } Try { $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml" $Result = $Response.Envelope.Body.CheckLoginResponse.CheckLoginResult.LoginResult ####### We have to parse the XML response manually, since Powershell doesn't want to convert it to JSON natively $Relationships = $Result.Relationships.RelationshipInfo $Groups = @() foreach ($Relationship in $Relationships) { $Group = [pscustomobject]@{ BranchName = $Relationship.BranchName CustomerName = $Relationship.CustomerName Track = $Relationship.Track } $Groups += $Group } $Certs = $Result.Certificates.CertificateInfo $Certifications = @() foreach ($cert in $Certs) { $Certificate = [pscustomobject]@{ Certificate = $cert.certificate ExpirationDate = $cert.ExpirationDate } $Certifications += $Certificate } $JSON = [pscustomobject]@{ FullName = $Result.FullName Role = $Result.Role PasswordExpirationDate = [datetime]$Result.PasswordExpirationDate HomeBranch = $Result.HomeBranch Relationships = $Groups Certificates = $Certifications DSP = $Result.DSP } return $JSON } catch { $result = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $response = $reader.ReadToEnd(); $ResponseMessage = ([xml]$Response).Envelope.Body.Fault $statuscode = $_.Exception.Response.StatusCode.value__ $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } function Invoke-DispatchRequest { <# .SYNOPSIS Initiate dispatch with TechDirect .DESCRIPTION Function to initiate a request for parts. This request is asynchronous, a Work Order is returned on successfully transacting with the Dispatch system. The WO number can then be queried to track the status of the Work Order. 'Get-DispatchStatus' will be the primary method to track status of an WO. 'Get-Dispatches' function can be used for querying more than one WO. For successful processing of a WO always submit - i. Contact information ii. Shipping information iii. Parts information iv. Service tag of the failed system v. Troubleshooting notes and evidence of failure. .PARAMETER Credentials Credential Object containing the Username/Password of the Technician .PARAMETER TechEmail Contains the email of the technician responsible for the dispatch. In the case of a logistics user this may be different from the login email .PARAMETER Branch Contains the branch associated with the dispatch. Note that the technician must be authorized to dispatch parts from this branch .PARAMETER Customer Contains the customer associated with the dispatch. The customer must have a valid relationship with the branch in order to successfully dispatch .PARAMETER Track Contains the track associated with the Branch to Customer relationship (this can be obtained using 'Get-TechnicianInfo') .PARAMETER ServiceTag The service tag associated with the dispatch .PARAMETER PrimaryContactName The primary contact associated with the dispatch. .PARAMETER PrimaryContactPhone The primary contact phone number for the dispatch .PARAMETER PrimaryContactEmail The primary contact email associated with the dispatch. .PARAMETER AlternateContactName Additional/Alternate contact name .PARAMETER AlternateContactPhone Additional/Alternate contact number .PARAMETER ReferencePONumber Optional purchase order or internal reference number .PARAMETER AddressBookName The name of a personal or company address book entry. If this is specified the detail address parameters are not allowed. .PARAMETER CountryISOCode ISO country code for the ship-to address .PARAMETER City The ship-to city .PARAMETER State The ship-to state .PARAMETER ZipCode The ship-to zip or postal code .PARAMETER AddressLine1 The first ship-to address line .PARAMETER AddressLine2 The second ship-to address line .PARAMETER AddressLine3 The third ship-to address line .PARAMETER TimeZone A description of the TimeZone parameter. .PARAMETER RequestCompleteCare A true/false parameter indicating if accidental damage applies to this dispatch .PARAMETER RequestReturnToDepot A true/false parameter indicating if return to depot applies to this dispatch .PARAMETER RequestOnSiteTechnician A true/false parameter indicating if an onsite technician is requested .PARAMETER TroubleshootingNote Contains troubleshooting notes, limited to 1000 characters .PARAMETER Parts An array of part information associated with the dispatch. This is limited to a maximum of 4 parts per dispatch Use 'Get-PartsByServiceTag' to query replaceable parts; Use 'Format-DispatchPart' to create each part; .PARAMETER Attachments An array of attachments to include with the dispatch. Use 'Format-DispatchAttachment' to create each attachment. PS C:\> $attachments = @( (Format-DispatchAttachment -filepath 'c:\temp\file.png' -description 'This thing is broken') (Format-DispatchAttachment -filepath 'c:\temp\secondfile.jpg' -description 'This is broken too') ) .EXAMPLE PS C:\> $attachments = @( (Format-DispatchAttachment -filepath 'c:\temp\file.png' -description 'This thing is broken') (Format-DispatchAttachment -filepath 'c:\temp\secondfile.jpg' -description 'This is broken too') ) PS C:\> $Parts = @( (Format-DispatchPart -PartName ) PS C:\> Invoke-DispatchRequest -Credentials -Email 'Value2' -Branch 'Value3' -Customer 'Value4' -Track 'Value5' -ServiceTag 'Value6' -PrimaryContactName 'Value7' -PrimaryContactPhone 'Value8' -PrimaryContactEmail 'Value9' -CountryISOCode 'Value10' -City 'Value11' -State 'Value12' -ZipCode 'Value13' -AddressLine1 'Value14' -AddressLine2 'Value15' -AddressLine3 'Value16' -TimeZone 'Value17' -RequestCompleteCare $value18 -RequestReturnToDepot $value19 -RequestOnSiteTechnician $value20 -TroubleshootingNote 'Value21' .EXAMPLE PS C:\> $paramInvokeDispatchRequest = @{ Credentials = $Credentials TechEmail = "apiuser_us_tech01@uatmail.com" Branch = "US Group" Customer = "Round Rock Customer" Track = "Tier 1" ServiceTag = "BR5424J" PrimaryContactName = "One,Technician" PrimaryContactPhone = "1111111111" PrimaryContactEmail = "apiuser_us_tech01@uatmail.com" CountryISOCode = $true AddressLine1 = "1" AddressLine2 = "Main" AddressLine3 = "Street" City = "New York" State = "NY" ZipCode = "11111" TimeZone = "US/Central" RequestCompleteCare = $false RequestReturnToDepot = $false RequestOnSiteTechnician = $false TroubleshootingNote = "THIS IS A TEST" Attachments = @( (Format-DispatchAttachment -FilePath 'c:\Temp\file (1).xlsx') (Format-DispatchAttachment -FilePath "c:\Temp\file.xlsx" -Description "This is a file") ) Parts = @((Format-DispatchPart -PartNumber "MBD" -Quantity 1 -PPID "2148242621834394587")) } PS C:\> Invoke-DispatchRequest @paramInvokeDispatchRequest #> [CmdletBinding(DefaultParameterSetName = 'No Address Book', SupportsPaging = $false, SupportsShouldProcess = $false)] [OutputType([pscustomobject])] param ( [Parameter(Mandatory = $false, Position = 1)] [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:"), [Parameter(Mandatory = $true, Position = 2)] [Alias('Email')] [string]$TechEmail, [Parameter(Mandatory = $true, Position = 3)] [string]$Branch, [Parameter(Mandatory = $true, Position = 4)] [Alias('CustomerName')] [string]$Customer, [Parameter(Mandatory = $true, Position = 5)] [string]$Track, [Parameter(Mandatory = $true, Position = 6)] [Alias('SerialNumber')] [string]$ServiceTag, [Parameter(Mandatory = $true, Position = 7)] [string]$PrimaryContactName, [Parameter(Mandatory = $true, Position = 8)] [string]$PrimaryContactPhone, [Parameter(Mandatory = $true, Position = 9)] [string]$PrimaryContactEmail, [Parameter(Position = 10)] [string]$AlternateContactName, [Parameter(Position = 11)] [string]$AlternateContactPhone, [Parameter(Mandatory = $false, Position = 12)] [string]$ReferencePONumber, [Parameter(ParameterSetName = 'Using Address Book', Position = 13)] [string]$AddressBookName, [Parameter(ParameterSetName = 'No Address Book', Position = 13)] [string]$CountryISOCode, [Parameter(ParameterSetName = 'No Address Book', Mandatory = $false, Position = 14)] [string]$City, [Parameter(ParameterSetName = 'No Address Book', Mandatory = $false, Position = 15)] [string]$State, [Parameter(ParameterSetName = 'No Address Book', Mandatory = $false, Position = 16)] [string]$ZipCode, [Parameter(ParameterSetName = 'No Address Book', Mandatory = $false, Position = 17)] [string]$AddressLine1, [Parameter(ParameterSetName = 'No Address Book', Mandatory = $false, Position = 18)] [string]$AddressLine2, [Parameter(ParameterSetName = 'No Address Book', Mandatory = $false, Position = 19)] [string]$AddressLine3, [Parameter(ParameterSetName = 'No Address Book', Mandatory = $false, Position = 20)] [string]$TimeZone, [Parameter(Mandatory = $true, Position = 21)] [boolean]$RequestCompleteCare, [Parameter(Mandatory = $true, Position = 22)] [boolean]$RequestReturnToDepot, [Parameter(Mandatory = $true, Position = 23)] [boolean]$RequestOnSiteTechnician, [Parameter(Mandatory = $true, Position = 24)] [ValidateLength(0, 1000)] [string]$TroubleshootingNote, [Parameter(Mandatory = $true, Position = 25)] [array]$Parts, [Parameter(Position = 26)] [array]$Attachments ) $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password $XML = @" <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com"> <soapenv:Header/> <soapenv:Body> <api:CreateDispatch> <api:CreateDispatchRequest> <api:Login>$Username</api:Login> <api:Password>$Password</api:Password> <api:Dispatch> <api:TechEmail>$TechEmail</api:TechEmail> <api:Branch>$Branch</api:Branch> <api:Customer>$Customer</api:Customer> <api:Track>$Track</api:Track> <api:ServiceTag>$ServiceTag</api:ServiceTag> <api:PrimaryContactName>$PrimaryContactName</api:PrimaryContactName> <api:PrimaryContactPhone>$PrimaryContactPhone</api:PrimaryContactPhone> <api:PrimaryContactEmail>$PrimaryContactEmail</api:PrimaryContactEmail>`r`n "@ if ($AlternateContactName) { $XML += @" <api:AlternativeContactName>$AlternateContactName</api:AlternativeContactName> <api:AlternativeContactPhone>$AlternateContactPhone</api:AlternativeContactPhone>`r`n "@ } else { $XML += @" <api:AlternativeContactName/> <api:AlternativeContactPhone/>`r`n "@ } $XML += @" <api:ShipToAddress>`r`n "@ if ($AddressBookName) { $xml += @" <api:AddressBookName>$AddressBookName</api:AddressBookName>`r`n "@ } else { $xml += @" <api:AddressBookName/>`r`n "@ } $XML += @" <api:CountryISOCode>$CountryISOCode</api:CountryISOCode> <api:City>$City</api:City> <api:State>$State</api:State> <api:ZipPostalCode>$ZipCode</api:ZipPostalCode> <api:AddressLine1>$AddressLine1</api:AddressLine1>`r`n "@ if ($AddressLine2) { $XML += @" <api:AddressLine2>$AddressLine2</api:AddressLine2>`r`n "@ } else { $xml += "`t`t`t`t`t`t`t<api:AddressLine2/>`r`n" } if ($AddressLine3) { $xml += @" <api:AddressLine3>$AddressLine3</api:AddressLine3>`r`n "@ } else { $xml += "`t`t`t`t`t`t`t<api:AddressLine3/>`r`n" } $XML += @" <api:TimeZone>$TimeZone</api:TimeZone> </api:ShipToAddress> <api:ReferencePONumber>$ReferencePONumber</api:ReferencePONumber> <api:RequestCompleteCare>$($RequestCompleteCare.ToString().ToLower())</api:RequestCompleteCare> <api:RequestReturnToDepot>$($RequestReturnToDepot.ToString().ToLower())</api:RequestReturnToDepot> <api:RequestOnSiteTechnician>$($RequestOnSiteTechnician.ToString().ToLower())</api:RequestOnSiteTechnician> <api:TroubleshootingNote>$TroubleshootingNote</api:TroubleshootingNote> <api:OverrideDPSType/>`r`n "@ if ($Parts) { $XML += @" <api:Parts> $($Parts -join "`r`n") </api:Parts>`r`n "@ } else { $XML += "<api:Parts/>`r`n" } if ($Attachments) { $XML += @" <api:Attachments> $($Attachments -join "`r`n") </api:Attachments>`r`n "@ } else { $XML += "<api:Attachments/>`r`n" } $XML += @" </api:Dispatch> </api:CreateDispatchRequest> </api:CreateDispatch> </soapenv:Body> </soapenv:Envelope> "@ $DellToken = Get-TDAccessToken -Dispatch if ($DellToken.sandbox) { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" } else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" } $Headers = @{ Authorization = "Bearer $($DellToken.access_token)" SOAPAction = "http://api.dell.com/IDispatchService/CreateDispatch" } try { $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml" $Result = $Response.Envelope.Body.CreateDispatchResponse.CreateDispatchResult.DispatchCreateResult ####### We have to parse the XML response manually, since Powershell doesn't want to convert it to JSON natively $JSON = [pscustomobject]@{ DispatchID = $Result.DispatchID DispatchCode = $Result.DispatchCode Status = $Result.Status } return $JSON } catch { $result = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $response = $reader.ReadToEnd(); $ResponseMessage = ([xml]$Response).Envelope.Body.Fault $statuscode = $_.Exception.Response.StatusCode.value__ $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } function Get-DispatchStatus { <# .SYNOPSIS Get the status of a dispatch .DESCRIPTION The primary function to track status of the work order. Failure to provide sufficient failure evidence will result in rejection of dispatches. However, there can be other reasons for rejection. Status codes 'DSP', 'Issues' and 'QUE' indicate the WO has been approved by Dell for dispatch. A Dell dispatch number will be available for these status codes. Status code 'Shipped Parts' indicates the requested part(s) has/have been dispatched. Status code 'Dispatch Denied' indicates the request has been denied by Dell. Further action is required by the user on this dispatch request. They can contact Dell using phone or other means to proceed with the transaction. They can also use the ReSubmitDispatch() method to resubmit the request with more information. If a WO is denied more than three times, it is no longer valid and a new WO must be submitted. .PARAMETER Credentials Credential Object containing the Username/Password of the Technician .PARAMETER Code The Dispatch Code to query the status of. .EXAMPLE PS C:\> Get-DispatchStatus -Code 'Value1' .NOTES Additional information about the function. #> [CmdletBinding(ConfirmImpact = 'None', SupportsPaging = $false, SupportsShouldProcess = $false)] param ( [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:"), [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('DispatchCode')] [string]$Code ) begin { $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password $DellToken = Get-TDAccessToken -Dispatch if ($DellToken.sandbox) { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" } else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" } $Headers = @{ Authorization = "Bearer $($DellToken.access_token)" SOAPAction = "http://api.dell.com/IDispatchService/GetDispatchStatus" } } process { $XML = @" <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com"> <soapenv:Header/> <soapenv:Body> <api:GetDispatchStatus> <api:GetDispatchStatusRequest> <api:Login>$Username</api:Login> <api:Password>$Password</api:Password> <api:DispatchCode>$Code</api:DispatchCode> </api:GetDispatchStatusRequest> </api:GetDispatchStatus> </soapenv:Body> </soapenv:Envelope> "@ try { $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml" $Result = $Response.Envelope.Body.GetDispatchStatusResponse.GetDispatchStatusResult.DispatchStatusResult ####### We have to parse the XML response manually, since Powershell doesn't want to convert it to JSON natively $JSON = [pscustomobject]@{ Result = $Result.Result Status = $Result.Status DPSNumber = $Result.DPSNumber DispatchCode = $Result.DispatchCode OrderDeniedReason = $Result.OrderDeniedReason } return $JSON } catch { $result = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $response = $reader.ReadToEnd(); $ResponseMessage = ([xml]$Response).Envelope.Body.Fault $statuscode = $_.Exception.Response.StatusCode.value__ $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } } function Get-PartsByServiceTag { <# .SYNOPSIS Get Replaceable Parts .DESCRIPTION Provides the list of all replaceable parts for a particular service tag. Use this function to query the list of parts that can be requested for dispatch. This function should be called only once per service tag and the results should be cached at the customer system. Output of this function will be a mandatory input for 'Invoke-DispatchRequest'. .PARAMETER Credentials Credential Object containing the Username/Password of the Technician .PARAMETER ServiceTag The Service Tag to get part info for. .EXAMPLE PS C:\> Get-PartsByServiceTag -ServiceTag 'BR5424J' .EXAMPLE PS C:\> 'BR5424J' | Get-PartsByServiceTag -Credentials $Credentials #> [CmdletBinding(ConfirmImpact = 'None', SupportsPaging = $false, SupportsShouldProcess = $false)] [OutputType([pscustomobject])] param ( [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:"), [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('SerialNumber')] [string]$ServiceTag ) begin { $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password $DellToken = Get-TDAccessToken -Dispatch if ($DellToken.sandbox) { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" } else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" } $Headers = @{ Authorization = "Bearer $($DellToken.access_token)" SOAPAction = "http://api.dell.com/IDispatchService/GetPartsbyServiceTag" } } process { $XML = @" <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com"> <soapenv:Header/> <soapenv:Body> <api:GetPartsbyServiceTag> <api:GetPartsbyServiceTag> <api:Login>$Username</api:Login> <api:Password>$Password</api:Password> <api:ServiceTag>$ServiceTag</api:ServiceTag> </api:GetPartsbyServiceTag> </api:GetPartsbyServiceTag> </soapenv:Body> </soapenv:Envelope> "@ try { $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml" $Result = $Response.Envelope.Body.GetPartsbyServiceTagResponse.GetPartsbyServiceTagResult.PartsbyTagResult ####### We have to parse the XML response manually, since Powershell doesn't want to convert it to JSON natively #Ignore these parts, they're not things that would typically be replaced in any laptop $PartList = $Result.Parts.PartInformation $Parts = @() foreach ($Part in $PartList) { #Create a temporary object for each part, then add it in to the array of parts $tmp = [pscustomobject]@{ PartTypeCode = $Part.PartTypeCode PartNumber = $part.PartNumber PartDescription = $Part.PartDescription } $Parts += $tmp } #Return to Depot isn't in the response, even though it's in the documentation $tmp = [pscustomobject]@{ PartTypeCode = "Accessory" PartNumber = "RTD" PartDescription = "Return to Depot" } $Parts += $tmp $JSON = [pscustomobject]@{ Model = $Result.Model ModelDescription = $Result.ModelDescription Parts = $parts } return $JSON } catch { $result = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $response = $reader.ReadToEnd(); $ResponseMessage = ([xml]$Response).Envelope.Body.Fault $statuscode = $_.Exception.Response.StatusCode.value__ switch -Wildcard ($response) { "*Missing parameters*"{ $ErrCategory = [system.management.automation.errorcategory]::InvalidArgument } default{ $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation } } $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } } function Get-BulkDispatch { <# .SYNOPSIS Provide Status Information in Bulk .DESCRIPTION Provides status information for a batch of work orders .PARAMETER Credentials Credential Object containing the Username/Password of the Technician .PARAMETER CreatedFromDate Contains a date constraint to limit the result set. Work Orders will only be returned if they were created after this date. .EXAMPLE PS C:\> Get-Dispatches -CreatedFromDate "01/01/2019" This command will return all dispatches created since 1 January 2019 00:00. .EXAMPLE PS C:\> Get-Dispatches -CreatedFromDate (Get-Date).addDays(-7) This command will return all dispatches created in the last 7 days, at the current time of day. #> [CmdletBinding(ConfirmImpact = 'None', SupportsPaging = $false, SupportsShouldProcess = $false)] [OutputType([pscustomobject])] param ( [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:"), [Parameter(Mandatory = $true)] [datetime]$CreatedFromDate ) $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password $FormattedDate = (Get-date $CreatedFromDate -Format o) $XML = @" <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com"> <soapenv:Header/> <soapenv:Body> <api:BulkDispatchesInquiry> <api:BulkDispatchesInquiryRequest> <api:Login>$Username</api:Login> <api:Password>$Password</api:Password> <api:CreatedFromDate>$FormattedDate</api:CreatedFromDate> <api:InStatuses></api:InStatuses> <api:Scope>All</api:Scope> </api:BulkDispatchesInquiryRequest> </api:BulkDispatchesInquiry> </soapenv:Body> </soapenv:Envelope> "@ $DellToken = Get-TDAccessToken -Dispatch if ($DellToken.sandbox) { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" } else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" } $Headers = @{ Authorization = "Bearer $($DellToken.access_token)" SOAPAction = "http://api.dell.com/IDispatchService/BulkDispatchesInquiry" } try { $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml" $Result = $Response.Envelope.Body.BulkDispatchesInquiryResponse.BulkDispatchesInquiryResult.DispatchInquiryResult.DispatchInquiryResult $Dispatches = @() foreach ($Dispatch in $Result) { $tmp = [pscustomobject]@{ Code = $Dispatch.Code DellDispatchNumber = $Dispatch.DellDispatchNumber Status = $Dispatch.Status } $Dispatches += $tmp } $Dispatches | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Name "Status Value" -Value $(Convert-TDStatusCode $_.status) } return $Dispatches } catch { $result = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $response = $reader.ReadToEnd(); $ResponseMessage = ([xml]$Response).Envelope.Body.Fault $statuscode = $_.Exception.Response.StatusCode.value__ $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } function Get-DeviceWarranty { <# .SYNOPSIS Get warranty information .DESCRIPTION Get the warranty information for a Dell device by providing one or more service tags .PARAMETER ServiceTag One or more Service Tags to get Warranty information for. (Max. 100) .EXAMPLE PS C:\> Get-DeviceWarranty -ServiceTags @("B68BK79","OE6W9MA") .EXAMPLE PS C:\> Get-DeviceWarranty -ServiceTags "B68BK79" #> [CmdletBinding(SupportsPaging = $false, SupportsShouldProcess = $false)] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateCount(0, 100)] [Alias('ServiceTags')] [array]$ServiceTag ) process { $URI = "https://apigtwb2c.us.dell.com/PROD/sbil/eapi/v5/assets?servicetags=" $URI = $URI + $($ServiceTag -join ",") $Token = (Get-TDAccessToken -Warranty) $Headers = @{ Authorization = "Bearer $($token.access_token)" } $response = Invoke-RestMethod -Method GET -Uri $URI -ContentType "application/json" -Headers $Headers $response | ForEach-Object {$_.shipdate = [datetime]$_.shipdate} return $response } } function Redo-DispatchRequest { <# .SYNOPSIS Resubmit a Dispatch Request .DESCRIPTION If a Dispatch is denied by Dell, further action is required on this dispatch request. When you check the status of SR using Get-DispatchStatus and get "Dispatch Denied", you can re submit the request adding more details. .PARAMETER Credentials Credential Object containing Technician Username/Password .PARAMETER Code The Dispatch Code associated with the denied dispatch .PARAMETER TroubleshootingNote The troubleshooting notes for this dispatch. This should explain, in detail, the reason for each part request. .PARAMETER Parts The parts requested in this dispatch. Use 'Format-DispatchPart' to create the xml object for each part. .EXAMPLE PS C:\> Redo-DispatchRequest -DispatchCode 'SR999999999' .NOTES Additional information about the function. #> [CmdletBinding(ConfirmImpact = 'Low', SupportsPaging = $false, SupportsShouldProcess = $false)] param ( [Parameter(Mandatory = $false)] [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:"), [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias('DispatchCode')] [string]$Code, [ValidateNotNullOrEmpty()] [string]$TroubleshootingNote, [ValidateCount(1, 4)] [array]$Parts ) $Username = ($PSboundparameters.Credentials).GetNetworkCredential().UserName $Password = ($PSboundparameters.Credentials).GetNetworkCredential().Password $XML = @" <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com"> <soapenv:Header/> <soapenv:Body> <api:ResubmitDispatch> <api:ResubmitDispatchRequest> <api:Login>$Username</api:Login> <api:Password>$Password</api:Password> <api:Dispatch> <api:DispatchCode>$Code</api:DispatchCode> <api:TroubleshootingNote>$TroubleshootingNote</api:TroubleshootingNote> <api:Parts> $Parts </api:Parts> </api:Dispatch> </api:ResubmitDispatchRequest> </api:ResubmitDispatch> </soapenv:Body> </soapenv:Envelope> "@ $DellToken = Get-TDAccessToken -Dispatch if ($DellToken.sandbox) { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" } else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" } $Headers = @{ Authorization = "Bearer $($DellToken.access_token)" SOAPAction = "http://api.dell.com/IDispatchService/ResubmitDispatch" } try { $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml" $Result = $Response.Envelope.Body.ResubmitDispatchResponse.ResubmitDispatchResult.DispatchCreateResult if ($Result.notes) { $ErrCategory = [system.management.automation.errorcategory]::InvalidData $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($Result.Notes.string), " ", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } else {return $Result} } catch { $result = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $response = $reader.ReadToEnd(); $ResponseMessage = ([xml]$Response).Envelope.Body.Fault $statuscode = $_.Exception.Response.StatusCode.value__ $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "", $ErrCategory, $Null) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } #region Helper Functions function Convert-FileToBase64 { param ( [Parameter(ValueFromPipeline = $true)] $Filepath ) process { $bufferSize = 9000 # should be a multiplier of 3 $buffer = New-Object byte[] $bufferSize $reader = [System.IO.File]::OpenRead($Filepath) $writer = "" $bytesRead = 0 do { $bytesRead = $reader.Read($buffer, 0, $bufferSize); $writer += ([Convert]::ToBase64String($buffer, 0, $bytesRead)) } while ($bytesRead -eq $bufferSize); $reader.Dispose() return $writer } } function Convert-TDStatusCode { <# .SYNOPSIS Convert a status code to text .DESCRIPTION Convert a status code to its text value. .PARAMETER Status The Staus Code to convert .EXAMPLE PS C:\> Convert-TDStatusCode -Status 'HOLD' #> [CmdletBinding(ConfirmImpact = 'None', SupportsPaging = $false, SupportsShouldProcess = $false)] [OutputType([string])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('StatusCode')] [string]$Status ) begin { $Codes = @{ "Request submitted to Dell and now under review" = @( "2H SBD" "APJ Queue" "BIL ERROR" "CAR" "Complete Care" "HOLD" "L2 Review" "L2 DISPATCH" "NBD" "NON LATIN 1" "NPO" "Parts Review" "Pending Supervisor" "PND" "Request Submitted" "Request Resubmitted" "Returned to Depot" "SBD" "SBD4" "TAG/CUSTOMER MISMATCH" ) "Claim Submitted" = @( "Claim Submitted" "Parts Returned/Claim Submitted" ) "CAD Deferred" = @("CAD-Deferred") "FED Queue" = @("FED") "Moved to Claims Process" = "Moved_to_Claims_Process" "Defective Part Received" = "Defective Part Received" "Request has been denied by Dell" = "Dispatch Denied" "Request has been approved by Dell" = @( "Issued" "DSP" "ORD" "QUE" ) "Create Dispatch/Place Request" = "START" "Pending Request 2" = "Pending 2" "Request has not been submitted to Dell" = "Pending Request" "Shipped Parts" = "Shipped Parts" } } Process { $codes.keys | % { if ($codes.$_ -contains "$status") { return $_ } } } } #endregion Helper Functions # SIG # Begin signature block # MIIQ/wYJKoZIhvcNAQcCoIIQ8DCCEOwCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBoP4YRYKYZrzY8 # rXzlRevdO++1X7/caBnq0aECWn0fKaCCC+8wggMIMIIB8KADAgECAhAeLeTkRNs8 # gEMIvpGunxhLMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNVBAMMEURaMDUzNDc5X0Nv # ZGVTaWduMB4XDTIwMDIwNjE5MDUzNloXDTIxMDIwNjE5MjUzNlowHDEaMBgGA1UE # AwwRRFowNTM0NzlfQ29kZVNpZ24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK # AoIBAQD0az+EFRw2OiqtWw74lG8NlvxrQ695XftwhBkiNUyAk7hno3dJASd0Dp7Q # FhwS7JiGNBjVZEmoM7YbvPF3ePC4N7fYImvmRoHTeB8zXH9/VTFRTg2jrDXq5UpR # ppL3fGVxGQuB3+YrxmWPGqlaNS4N3uUmS0XOlEseJ97bmsp4orwOwxK/NjBx15bC # IGyD4XiTRpy3/fdKUHJA61N11umpr914sMiXEY2kR6M+1olbKZA0ABfMxJl2oZGx # UQcApKsdYg2/yDfBMAGxBrzCaJKAVUqCR6KTWaOJ1dTvkB60Y7RE6jdAixyitxbU # kaCpTcjgL6CRR18+imLKEbZtyHQ9AgMBAAGjRjBEMA4GA1UdDwEB/wQEAwIHgDAT # BgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUMRbqaTD35MDPR4hmHkrBnaTp # v6UwDQYJKoZIhvcNAQELBQADggEBAMrAn7J8yHlxcaEuRbRjwCafkZeQldpSl+cJ # 9YJwSlqpBQporOKg5qAGZCLuXqIB/efIxyZTfmrAFRBSaxudptMce1JeA5YpJqew # 9ZNdpgsB2PFcQhB9CXTja6gmaTlsRbmV8Td6xrfexga3LbtOlGIY0Ji5rAC6MeWE # tUTaCKNmcyEwb40HKBygmEUWEy2ol+r9GXC5XSoJMeSq3MTw1n8YXPbmnD6r5C23 # pWtaAgmd4/msj4bhxDhr92l6EbGWfLrDNxT+Kt2pdbF7fS1HkMCLPG4KB7XssY53 # gkJ67BGqNGOeoBs6YbQ7qRyn673U5IJkmKN7INfRbumuIOz8cnEwggQVMIIC/aAD # AgECAgsEAAAAAAExicZQBDANBgkqhkiG9w0BAQsFADBMMSAwHgYDVQQLExdHbG9i # YWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UE # AxMKR2xvYmFsU2lnbjAeFw0xMTA4MDIxMDAwMDBaFw0yOTAzMjkxMDAwMDBaMFsx # CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTEwLwYDVQQD # EyhHbG9iYWxTaWduIFRpbWVzdGFtcGluZyBDQSAtIFNIQTI1NiAtIEcyMIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqpuOw6sRUSUBtpaU4k/YwQj2RiPZ # RcWVl1urGr/SbFfJMwYfoA/GPH5TSHq/nYeer+7DjEfhQuzj46FKbAwXxKbBuc1b # 8R5EiY7+C94hWBPuTcjFZwscsrPxNHaRossHbTfFoEcmAhWkkJGpeZ7X61edK3wi # 2BTX8QceeCI2a3d5r6/5f45O4bUIMf3q7UtxYowj8QM5j0R5tnYDV56tLwhG3NKM # vPSOdM7IaGlRdhGLD10kWxlUPSbMQI2CJxtZIH1Z9pOAjvgqOP1roEBlH1d2zFuO # BE8sqNuEUBNPxtyLufjdaUyI65x7MCb8eli7WbwUcpKBV7d2ydiACoBuCQIDAQAB # o4HoMIHlMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1Ud # DgQWBBSSIadKlV1ksJu0HuYAN0fmnUErTDBHBgNVHSAEQDA+MDwGBFUdIAAwNDAy # BggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFsc2lnbi5jb20vcmVwb3NpdG9y # eS8wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9iYWxzaWduLm5ldC9y # b290LXIzLmNybDAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpjmove4t0bvDANBgkq # hkiG9w0BAQsFAAOCAQEABFaCSnzQzsm/NmbRvjWek2yX6AbOMRhZ+WxBX4AuwEIl # uBjH/NSxN8RooM8oagN0S2OXhXdhO9cv4/W9M6KSfREfnops7yyw9GKNNnPRFjbx # vF7stICYePzSdnno4SGU4B/EouGqZ9uznHPlQCLPOc7b5neVp7uyy/YZhp2fyNSY # BbJxb051rvE9ZGo7Xk5GpipdCJLxo/MddL9iDSOMXCo4ldLA1c3PiNofKLW6gWlk # KrWmotVzr9xG2wSukdduxZi61EfEVnSAR3hYjL7vK/3sbL/RlPe/UOB74JD9IBh4 # GCJdCC6MHKCX8x2ZfaOdkdMGRE4EbnocIOM28LZQuTCCBMYwggOuoAMCAQICDCRU # uH8eFFOtN/qheDANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJCRTEZMBcGA1UE # ChMQR2xvYmFsU2lnbiBudi1zYTExMC8GA1UEAxMoR2xvYmFsU2lnbiBUaW1lc3Rh # bXBpbmcgQ0EgLSBTSEEyNTYgLSBHMjAeFw0xODAyMTkwMDAwMDBaFw0yOTAzMTgx # MDAwMDBaMDsxOTA3BgNVBAMMMEdsb2JhbFNpZ24gVFNBIGZvciBNUyBBdXRoZW50 # aWNvZGUgYWR2YW5jZWQgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC # ggEBANl4YaGWrhL/o/8n9kRge2pWLWfjX58xkipI7fkFhA5tTiJWytiZl45pyp97 # DwjIKito0ShhK5/kJu66uPew7F5qG+JYtbS9HQntzeg91Gb/viIibTYmzxF4l+lV # ACjD6TdOvRnlF4RIshwhrexz0vOop+lf6DXOhROnIpusgun+8V/EElqx9wxA5tKg # 4E1o0O0MDBAdjwVfZFX5uyhHBgzYBj83wyY2JYx7DyeIXDgxpQH2XmTeg8AUXODn # 0l7MjeojgBkqs2IuYMeqZ9azQO5Sf1YM79kF15UgXYUVQM9ekZVRnkYaF5G+wcAH # dbJL9za6xVRsX4ob+w0oYciJ8BUCAwEAAaOCAagwggGkMA4GA1UdDwEB/wQEAwIH # gDBMBgNVHSAERTBDMEEGCSsGAQQBoDIBHjA0MDIGCCsGAQUFBwIBFiZodHRwczov # L3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzAJBgNVHRMEAjAAMBYGA1Ud # JQEB/wQMMAoGCCsGAQUFBwMIMEYGA1UdHwQ/MD0wO6A5oDeGNWh0dHA6Ly9jcmwu # Z2xvYmFsc2lnbi5jb20vZ3MvZ3N0aW1lc3RhbXBpbmdzaGEyZzIuY3JsMIGYBggr # BgEFBQcBAQSBizCBiDBIBggrBgEFBQcwAoY8aHR0cDovL3NlY3VyZS5nbG9iYWxz # aWduLmNvbS9jYWNlcnQvZ3N0aW1lc3RhbXBpbmdzaGEyZzIuY3J0MDwGCCsGAQUF # BzABhjBodHRwOi8vb2NzcDIuZ2xvYmFsc2lnbi5jb20vZ3N0aW1lc3RhbXBpbmdz # aGEyZzIwHQYDVR0OBBYEFNSHuI3m5UA8nVoGY8ZFhNnduxzDMB8GA1UdIwQYMBaA # FJIhp0qVXWSwm7Qe5gA3R+adQStMMA0GCSqGSIb3DQEBCwUAA4IBAQAkclClDLxA # CabB9NWCak5BX87HiDnT5Hz5Imw4eLj0uvdr4STrnXzNSKyL7LV2TI/cgmkIlue6 # 4We28Ka/GAhC4evNGVg5pRFhI9YZ1wDpu9L5X0H7BD7+iiBgDNFPI1oZGhjv2Mbe # 1l9UoXqT4bZ3hcD7sUbECa4vU/uVnI4m4krkxOY8Ne+6xtm5xc3NB5tjuz0PYbxV # fCMQtYyKo9JoRbFAuqDdPBsVQLhJeG/llMBtVks89hIq1IXzSBMF4bswRQpBt3yS # br5OkmCCyltk5lXT0gfenV+boQHtm/DDXbsZ8BgMmqAc6WoICz3pZpendR4PvyjX # CSMN4hb6uvM0MYIEZjCCBGICAQEwMDAcMRowGAYDVQQDDBFEWjA1MzQ3OV9Db2Rl # U2lnbgIQHi3k5ETbPIBDCL6Rrp8YSzANBglghkgBZQMEAgEFAKBMMBkGCSqGSIb3 # DQEJAzEMBgorBgEEAYI3AgEEMC8GCSqGSIb3DQEJBDEiBCCbF3321rczN0mauIXE # oJJqmcSwb21Ogp20qUzFycLnjzANBgkqhkiG9w0BAQEFAASCAQA2r/l75Myj3Lpr # e5kT3ki1ITr3eyntHMp94cunTMVN8FJJQf2lKLD5hLZsaVz3j0HiETE+vB8EGuWp # dh9EvbHuqckTxHE86l/CdmNUlaGwYtBCFxlBL+kD640V1m9oISWn05g8f1Wl7//h # rU1QoUiQnE8TZ2VKsJPKDIsr9Q76mbq3bkWnDn3MMjicLX+PWaE/lBxvDx1M9KsV # l0smA8puHogaQXhat5Vtt87oFGoOINOPkKhlLK/mGb2OTJ84UlEmcbzEPQyti66p # SjcK98LpzU36INYRxPx79xcPDNkUMp8e4uyz7BKkbJRAzMfggcJ1Y3dPNGN8RQHa # s1ruOIkIoYICuTCCArUGCSqGSIb3DQEJBjGCAqYwggKiAgEBMGswWzELMAkGA1UE # BhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExMTAvBgNVBAMTKEdsb2Jh # bFNpZ24gVGltZXN0YW1waW5nIENBIC0gU0hBMjU2IC0gRzICDCRUuH8eFFOtN/qh # eDANBglghkgBZQMEAgEFAKCCAQwwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc # BgkqhkiG9w0BCQUxDxcNMjAwMjIxMTcwMjM5WjAvBgkqhkiG9w0BCQQxIgQgh+d8 # 21v9WDGf8Pg0A2eHfwHfvXLBiIAIkq/Ca/EaMdowgaAGCyqGSIb3DQEJEAIMMYGQ # MIGNMIGKMIGHBBQ+x2bV1NRy4hsfIUNSHDG3kNlLaDBvMF+kXTBbMQswCQYDVQQG # EwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTExMC8GA1UEAxMoR2xvYmFs # U2lnbiBUaW1lc3RhbXBpbmcgQ0EgLSBTSEEyNTYgLSBHMgIMJFS4fx4UU603+qF4 # MA0GCSqGSIb3DQEBAQUABIIBADrgOBt9TZx0eh7Y6d4B4I5kxy7aignacksLZrE3 # rv1g5wOT8DeT73lxoXyWxSVhGkAlX0voFJwRWRaK7+sxIAqRxWrqGnb4OgK8s/Xb # mA/NIEUk7Fo3ZZSnPp0ZTbrZChUWfrTgBU1WdlESbpbFtbdhjnAjvN350sytUpfv # 77cgnr671juv8j9eg3Pw939JYc4Dq0dG/gk7cV3ViP7VP5akA/wewDCEyLhN9v0V # Wqn1F3WDHX4JUSZRmtf8tPX8P0gr3ImyxtcfzYcYI4ztpatuwzyk4VMXyVCgKySP # fg690WIKfb+An0eKgRb4jNvQQ75fthW25ziZU4NMCTGHyEA= # SIG # End signature block |