PSTANSS.psm1
$script:ModuleRoot = $PSScriptRoot $script:ModuleVersion = (Import-PowerShellDataFile -Path "$($script:ModuleRoot)\PSTANSS.psd1").ModuleVersion # Detect whether at some level dotsourcing was enforced $script:doDotSource = Get-PSFConfigValue -FullName PSTANSS.Import.DoDotSource -Fallback $false if ($PSTANSS_dotsourcemodule) { $script:doDotSource = $true } <# Note on Resolve-Path: All paths are sent through Resolve-Path/Resolve-PSFPath in order to convert them to the correct path separator. This allows ignoring path separators throughout the import sequence, which could otherwise cause trouble depending on OS. Resolve-Path can only be used for paths that already exist, Resolve-PSFPath can accept that the last leaf my not exist. This is important when testing for paths. #> # Detect whether at some level loading individual module files, rather than the compiled module was enforced $importIndividualFiles = Get-PSFConfigValue -FullName PSTANSS.Import.IndividualFiles -Fallback $false if ($PSTANSS_importIndividualFiles) { $importIndividualFiles = $true } if (Test-Path (Resolve-PSFPath -Path "$($script:ModuleRoot)\..\.git" -SingleItem -NewChild)) { $importIndividualFiles = $true } if ("<was compiled>" -eq '<was not compiled>') { $importIndividualFiles = $true } function Import-ModuleFile { <# .SYNOPSIS Loads files into the module on module import. .DESCRIPTION This helper function is used during module initialization. It should always be dotsourced itself, in order to proper function. This provides a central location to react to files being imported, if later desired .PARAMETER Path The path to the file to load .EXAMPLE PS C:\> . Import-ModuleFile -File $function.FullName Imports the file stored in $function according to import policy #> [CmdletBinding()] Param ( [string] $Path ) $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path).ProviderPath if ($doDotSource) { . $resolvedPath } else { $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($resolvedPath))), $null, $null) } } #region Load individual files if ($importIndividualFiles) { # Execute Preimport actions foreach ($path in (& "$ModuleRoot\internal\scripts\preimport.ps1")) { . Import-ModuleFile -Path $path } # Import all internal functions foreach ($function in (Get-ChildItem "$ModuleRoot\internal\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) { . Import-ModuleFile -Path $function.FullName } # Import all public functions foreach ($function in (Get-ChildItem "$ModuleRoot\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) { . Import-ModuleFile -Path $function.FullName } # Execute Postimport actions foreach ($path in (& "$ModuleRoot\internal\scripts\postimport.ps1")) { . Import-ModuleFile -Path $path } # End it here, do not load compiled code below return } #endregion Load individual files #region Load compiled code <# This file loads the strings documents from the respective language folders. This allows localizing messages and errors. Load psd1 language files for each language you wish to support. Partial translations are acceptable - when missing a current language message, it will fallback to English or another available language. #> Import-PSFLocalizedString -Path "$($script:ModuleRoot)\en-us\*.psd1" -Module 'PSTANSS' -Language 'en-US' function Assert-CacheRunspaceRunning { <# .Synopsis Assert-CacheRunspaceRunning .DESCRIPTION Check cache validation runspace on status .EXAMPLE PS C:\> Assert-CacheRunspaceRunning Check cache validation runspace on status .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] Param( ) Write-PSFMessage -Level Debug -Message "Check cache validationRunspace" if ([TANSS.Cache]::StopValidationRunspace -eq $true) { Write-PSFMessage -Level Debug -Message "ValidationRunspace is stopped. Going to start the runspace again" # force to stop the runspace [TANSS.Cache]::StopValidationRunspace = $true Get-PSFRunspace -Name "TANSS.LookupValidation" | Stop-PSFRunspace # Restart the runspace try { [TANSS.Cache]::StopValidationRunspace = $false Start-PSFRunspace -Name "TANSS.LookupValidation" -ErrorAction Stop -ErrorVariable invokeError } catch { Stop-PSFFunction -Message "Error Starting ValidationRunspace. Unknown module behaviour. Please restart your powershell console!" -EnableException $true -Exception $invokeError -Tag "RunSpace" throw $invokeError } } } function ConvertFrom-Base64StringWithNoPadding( [string]$Data ) { <# .SYNOPSIS Helper function build valid Base64 strings from JWT access tokens .DESCRIPTION Helper function build valid Base64 strings from JWT access tokens .PARAMETER Data The Token to convert .EXAMPLE PS C:\> ConvertFrom-Base64StringWithNoPadding -Data $data build valid base64 string the content from variable $data #> $Data = $Data.Replace('-', '+').Replace('_', '/') switch ($Data.Length % 4) { 0 { break } 2 { $Data += '==' } 3 { $Data += '=' } default { throw New-Object ArgumentException('data') } } [System.Convert]::FromBase64String($Data) } function ConvertFrom-JWTtoken { <# .SYNOPSIS Converts access tokens to readable objects .DESCRIPTION Converts access tokens to readable objects .PARAMETER TokenText The Token to convert .EXAMPLE PS C:\> ConvertFrom-JWTtoken -Token $TokenText Converts the content from variable $TokenText to an object #> [cmdletbinding()] param( [Parameter(Mandatory = $true)] [string] $TokenText ) # Validate as per https://tools.ietf.org/html/rfc7519 - Access and ID tokens are fine, Refresh tokens will not work if ((-not $TokenText.Contains(".")) -or (-not $TokenText.StartsWith("eyJ"))) { $msg = "Invalid data or not an access/refresh token. $($TokenText)" Stop-PSFFunction -Message $msg -Tag "JWT" -EnableException $true -Exception ([System.Management.Automation.RuntimeException]::new($msg)) } # Split the token in its parts $tokenParts = $TokenText.Split(".") # Work on header $tokenHeader = [System.Text.Encoding]::UTF8.GetString( (ConvertFrom-Base64StringWithNoPadding $tokenParts[0]) ) $tokenHeaderJSON = $tokenHeader | ConvertFrom-Json # Work on payload $tokenPayload = [System.Text.Encoding]::UTF8.GetString( (ConvertFrom-Base64StringWithNoPadding $tokenParts[1]) ) $tokenPayloadJSON = $tokenPayload | ConvertFrom-Json # Work on signature $tokenSignature = ConvertFrom-Base64StringWithNoPadding $tokenParts[2] # Output $resultObject = [PSCustomObject]@{ "alg" = $tokenHeaderJSON.alg "typ" = $tokenHeaderJSON.typ "kid" = $tokenHeaderJSON.kid "sub" = $tokenPayloadJSON.sub "exp" = [datetime]::new(1970, 1, 1, 0, 0, 0, 0, [DateTimeKind]::Utc).AddSeconds($tokenPayloadJSON.exp).ToLocalTime() "type" = $tokenPayloadJSON.type "signature" = $tokenSignature } $resultObject } function ConvertFrom-NameCache { <# .Synopsis ConvertFrom-NameCache .DESCRIPTION Convert Name to ID from cached TANSS.Lookup values .PARAMETER Name Name to convert into ID .PARAMETER Id Id to convert into Name .PARAMETER Type Lookup type where the name should convert from .EXAMPLE PS C:\> ConvertFrom-NameCache -Name "User X" -Type "Employee" Example .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "FromName", SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] Param( [Parameter( ParameterSetName = "FromName", Mandatory = $true )] [string] $Name, [Parameter( ParameterSetName = "FromId", Mandatory = $true )] [int] $Id, [Parameter(Mandatory = $true)] [ValidateSet("Companies", "Contracts", "CostCenters", "Departments", "Employees", "OrderBys", "Phases", "Tags", "Tickets", "TicketStates", "TicketTypes", "VacationAbsenceSubTypes", "VacationTypesPredefinedApi")] [string] $Type ) $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" switch ($parameterSetName) { "FromName" { Write-PSFMessage -Level Verbose -Message "Start converting '$($Name)' of type '$($Type)' to ID" if ( ([TANSS.Lookup]::$Type).ContainsValue($Name) ) { foreach ($key in [TANSS.Lookup]::$Type.Keys) { if ([TANSS.Lookup]::$Type[$key] -like $Name) { Write-PSFMessage -Level Verbose -Message "Found ID '$key' for name '$($Name)' of type '$($Type)'" return $key } } } else { Write-PSFMessage -Level Error -Message "Unable to convert '$($Name)' of type '$($Type)' in ID. Name is not in present in cache." } } "FromId" { Write-PSFMessage -Level Verbose -Message "Start converting ID '$($Id)' of type '$($Type)' to name" if ( ([TANSS.Lookup]::$Type).ContainsKey("$($Id)") ) { $output = [TANSS.Lookup]::$Type["$($Id)"] Write-PSFMessage -Level Verbose -Message "Found '$output' with ID '$($Id)' of type '$($Type)'" return $output } else { Write-PSFMessage -Level Error -Message "Unable to convert '$($Id)' of type '$($Type)' into Name. Id is not in present in cache." } } Default { Stop-PSFFunction -Message "Unhandeled ParameterSetName. Developers mistake." -EnableException $true throw } } } Function ConvertFrom-UnixEpochTime { <# .SYNOPSIS Converts UNIX Epoch Time to DateTime object .DESCRIPTION Converts UNIX Epoch Time to DateTime object .PARAMETER EpochTime The time value to convert .PARAMETER UTC convert the given Epoch without following the lcoal timezone .EXAMPLE PS C:\> ConvertFrom-UnixEpochTime -EpochTime "1641769200" Converts the content from variable $TokenText to an object #> param( # Parameter help description [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [int[]] $EpochTime, [switch] $UTC ) Process { foreach ($item in $EpochTime) { if ($UTC) { ([datetime]'1/1/1970').AddSeconds($item) } else { [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($item)) } } } } function Format-ApiPath { <# .Synopsis Format-ApiPath .DESCRIPTION Ensure the right format and the existense of api prefix in the given path .PARAMETER Path Path to format .PARAMETER QueryParameter A hashtable for all the parameters to the api route .EXAMPLE PS C:\> Format-ApiPath -Path $ApiPath Api path data from variable $ApiPath will be tested and formatted. .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] Param( [Parameter(Mandatory = $true)] [string] $Path, [hashtable] $QueryParameter ) # Start Function Write-PSFMessage -Level System -Message "Formatting API path '$($Path)'" # receive module cental configuration for prefix on api path (default is 'backend/') $apiPrefix = Get-PSFConfigValue -FullName 'PSTANSS.API.RestPathPrefix' -Fallback "" # remove no more need slashes $apiPath = $Path.Trim('/') # check on API path prefix if (-not $ApiPath.StartsWith($apiPrefix)) { $ApiPath = $apiPrefix + $ApiPath Write-PSFMessage -Level Debug -Message "Add API prefix, formatting path to '$($ApiPath)'" } else { Write-PSFMessage -Level Debug -Message "Prefix API path already present" } # If specified, process hashtable QueryParameters to valid parameters into uri if ($MyInvocation.BoundParameters['QueryParameter'] -and $QueryParameter) { Write-PSFMessage -Level Debug -Message "Add query parameters '$([string]::Join("' ,'", $QueryParameter.Keys))'" $apiPath = "$($apiPath)?" $i = 0 foreach ($key in $QueryParameter.Keys) { if ($i -gt 0) { $apiPath = "$($apiPath)&" } if ("System.Array" -in ($QueryParameter[$Key]).psobject.TypeNames) { $parts = $QueryParameter[$Key] | ForEach-Object { "$($key)=$($_)" } $apiPath = "$($apiPath)$([string]::Join("&", $parts))" } else { $apiPath = "$($apiPath)$($key)=$($QueryParameter[$Key])" } $i++ } } # Output Result $ApiPath = $ApiPath.TrimEnd("?") $ApiPath } function Invoke-CacheRefresh { <# .Synopsis Invoke-CacheRefresh .DESCRIPTION Invokes api calls to fill mostly used lookup values .PARAMETER Token AccessToken object to register as default connection for TANSS .EXAMPLE PS C:\> Invoke-CacheRefresh -Token $token Example .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] Param( [Parameter(Mandatory = $true)] [TANSS.Connection] $Token ) Write-PSFMessage -Level Verbose -Message "Start updating lookup cache from current tickets in TANSS" -Tag "Cache" $tickets = @() $tickets += Get-TANSSTicket -MyTickets -Token $token $tickets += Get-TANSSTicket -NotAssigned -Token $token $tickets += Get-TANSSTicket -AllTechnician -Token $token Write-PSFMessage -Level Verbose -Message "Built cache from $($tickets.count) tickets" -Tag "Cache" $null = Get-TANSSVacationAbsenceSubType -Token $token $null = Get-TANSSDepartment -Token $token $null = Get-TANSSTicketStatus -Token $token $null = Get-TANSSTicketType -Token $token } function Invoke-TANSSTokenCheck { <# .Synopsis Test a TANSS connection- oder service-token .DESCRIPTION Tests validity for a TANSS.Connection object .PARAMETER Token TANSS.Connection Token object to check on .PARAMETER NoRefresh Indicates that the function will not try to update the specified token .PARAMETER DoNotRegisterConnection Do not register the connection as default connection .PARAMETER PassThru Outputs the token to the console, even when the register switch is set .EXAMPLE PS C:\> Invoke-TANSSTokenCheck -Token $Token Test the TANSS.Connection object from variable $Token for validity If the token has a lifetime under 5 percent, the function will try to update the token. If the token matches the registered token within the module, the updated token will also be registered. .EXAMPLE PS C:\> Invoke-TANSSTokenCheck -Token $Token -NoRefresh Test the TANSS.Connection object from variable $Token for validity, but will NOT try to update the token. Considered for testing ServiceTokes, that can't be updated .EXAMPLE PS C:\> Invoke-TANSSTokenCheck -Token $Token -DoNotRegisterConnection -PassThru Test the TANSS.Connection object from variable $Token for validity. If the token has a lifetime under 5 percent, the function will try to update the token, but not registered as the standard token for the module. Instead, the token will be outputted to the console. .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "Default", SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] [OutputType([TANSS.Connection])] Param( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [TANSS.Connection] $Token, [Parameter(ParameterSetName = "NoRefresh")] [switch] $NoRefresh, [Parameter(ParameterSetName = "Default")] [switch] $DoNotRegisterConnection, [switch] $PassThru ) begin { $registeredToken = Get-TANSSRegisteredAccessToken } process { # General validity check if (-not $Token.IsValid) { Stop-PSFFunction -Message "$($Token.EmployeeType) token for '$($Token.UserName)' on $($Token.Server) is not valid" -Tag "AccessToken", "InvalidToken" -EnableException $true -PSCmdlet $pscmdlet } # Lifetime check if ($Token.PercentRemaining -lt 5) { Write-PSFMessage -Level Warning -Message "$($Token.EmployeeType) token for '$($Token.UserName)' on $($Token.Server) is about to expire in $($Token.TimeRemaining.Minutes) min" -Tag "AccessToken", "InvalidToken" if ((-not $NoRefresh) -and $Token.RefreshToken) { Write-PSFMessage -Level Verbose -Message "Going to try a token refresh" -Tag "AccessToken" # Compile parameters for Token refresh $paramUpdateTANSSAccessToken = @{ "Token" = $Token "NoCacheRefresh" = $true "PassThru" = $true } if ((([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($registeredToken.AccessToken))) -notlike [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token.AccessToken))) -or $DoNotRegisterConnection) { $paramUpdateTANSSAccessToken.add("DoNotRegisterConnection", $false) } else { $paramUpdateTANSSAccessToken.add("DoNotRegisterConnection", $true) } $newToken = Update-TANSSAccessToken @paramUpdateTANSSAccessToken # Output result if ($PassThru) { $newToken } } else { Write-PSFMessage -Level Important -Message "Please aquire a new token as soon as possible" -Tag "AccessToken", "NoAccessTokenRefresh" } } # Output if if((-not $newToken) -and $PassThru) { $Token } } end {} } function Push-DataToCacheRunspace { <# .Synopsis Push-DataToCacheRunspace .DESCRIPTION Push meta information to runspace cache .PARAMETER MetaData The metadata PSCusomobject to push to Cache .EXAMPLE PS C:\> Push-DataToCacheRunspace -MetaData $response.meta Push meta information to runspace cache .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding( SupportsShouldProcess = $false, PositionalBinding = $true, ConfirmImpact = 'Low' )] Param( [Alias("Meta", "Data")] $MetaData ) Write-PSFMessage -Level Debug -Message "Pushing data to cache validationRunspace" [TANSS.Cache]::Data.Add((New-Guid), $MetaData) } function Update-CacheLookup { <# .Synopsis Update-CacheLookup .DESCRIPTION Update a cache lookup hashtable with an object .PARAMETER LookupName Name of LokkupClass where to update .PARAMETER Id The Id to of the record to cache .PARAMETER Name The name of the record to cache .EXAMPLE PS C:\> Update-CacheLookup -LookupName "Departments" -Id $department.Id -Name $department.Name Update or insert the key from variable $department.Id of the cache-lookup-hashtable [TANSS.Lookup]::Departments with the name $department.Name .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding( SupportsShouldProcess = $false, PositionalBinding = $true, ConfirmImpact = 'Low' )] Param( [string] $LookupName, [int] $Id, [string] $Name ) if ([TANSS.Lookup]::$LookupName["$($Id)"] -notlike $Name) { if ([TANSS.Lookup]::$LookupName["$($Id)"]) { Write-PSFMessage -Level Debug -Message "Update existing id '$($Id)' in [TANSS.Lookup]::$($LookupName) with value '$($Name)'" -Tag "Cache", $LookupName [TANSS.Lookup]::$LookupName["$($Id)"] = $Name } else { Write-PSFMessage -Level Debug -Message "Insert in [TANSS.Lookup]::$($LookupName): $($Id) - '$($($Name))'" -Tag "Cache", $LookupName ([TANSS.Lookup]::$LookupName).Add("$($Id)", $Name) } } } function ConvertFrom-TANSSTimeStampParameter { <# .Synopsis ConvertFrom-TANSSTimeStampParameter .DESCRIPTION Convert display names for Type & State parameter into api texts .PARAMETER Text If not specified, the registered default token from within the module is going to be used .PARAMETER TextType Specifies if the text is a timestampe "state" or "type" .EXAMPLE PS C:\> ConvertFrom-TANSSTimeStampParameter -Text "Coming" -TextType "State" Outputs "On" as a "comming state for TANSS api .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( PositionalBinding = $true, ConfirmImpact = 'Low' )] Param( [Parameter( Mandatory=$true, ValueFromPipeline=$true )] [ValidateSet("Coming", "Leaving", "StartPause", "EndPause", "Work", "Inhouse", "Errand", "Vacation", "Illness", "PaidAbsence", "UnpaidAbsence", "Overtime", "Support")] [string] $Text, [Parameter( Mandatory=$true )] [ValidateSet("State", "Type")] [String] $TextType ) begin {} process { Write-PSFMessage -Level Debug -Message "Start converting '$($Text)' as '$($TextType)' to ApiText value" $apiText = "" switch ($TextType) { "State" { switch ($Text) { "Coming" { $apiText = "ON" } "Leaving" { $apiText = "OFF" } "StartPause" { $apiText = "PAUSE_START" } "EndPause" { $apiText = "PAUSE_END" } Default { Stop-PSFFunction -Message "Unhandeled pattern for parameter Text. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } } "Type" { switch ($Text) { "Work" { $apiText = "WORK" } "Inhouse" { $apiText = "INHOUSE" } "Errand" { $apiText = "ERRAND" } "Vacation" { $apiText = "VACATION" } "Illness" { $apiText = "ILLNESS" } "PaidAbsence" { $apiText = "ABSENCE_PAID" } "UnpaidAbsence" { $apiText = "ABSENCE_UNPAID" } "Overtime" { $apiText = "OVERTIME" } "Support" { $apiText = "DOCUMENTED_SUPPORT" } Default { Stop-PSFFunction -Message "Unhandeled pattern for parameter Text. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } } Default { Stop-PSFFunction -Message "Unhandeled pattern for parameter TextType. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } # Output Write-PSFMessage -Level Debug -Message "'$($Text)' as '$($TextType)' converted into ApiText value: $($apiText) done" $apiText } end {} } function Connect-TANSS { <# .Synopsis Connect-TANSS .DESCRIPTION Connect to TANSS Service .PARAMETER Server Name of the service to connect to .PARAMETER Credential The credentials to login .PARAMETER LoginToken If the user needs an -application specific- login token for MFA, this field must be set as well .PARAMETER Protocol Specifies if the connection is done with http or https .PARAMETER DoNotRegisterConnection Do not register the connection as default connection .PARAMETER NoCacheInit Do not query current existing tickets and various types to fill cache data for lookup types .PARAMETER PassThru Outputs the token to the console, even when the register switch is set .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> Connect-TANSS -Server "tanss.company.com" -Credential (Get-Credential "username") Connects to "tanss.company.com" via HTTPS protocol and the specified credentials. Connection will be set as default connection for any further action. .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] [CmdletBinding( DefaultParameterSetName = 'Credential', SupportsShouldProcess = $false, PositionalBinding = $true, ConfirmImpact = 'Medium' )] [OutputType([TANSS.Connection])] Param( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Alias("ComputerName", "Hostname", "Host", "ServerName")] [String] $Server, [Parameter( Mandatory = $true, ParameterSetName = 'Credential' )] [System.Management.Automation.PSCredential] $Credential, [Parameter(ParameterSetName = 'Credential')] [string] $LoginToken, [ValidateSet("HTTP", "HTTPS")] [ValidateNotNullOrEmpty()] [String] $Protocol = "HTTPS", [Alias('NoRegistration')] [Switch] $DoNotRegisterConnection, [switch] $NoCacheInit, [switch] $PassThru ) begin { $ApiPath = Format-ApiPath -Path "api/v1/user/login" } process { if ($protocol -eq 'HTTP') { Write-PSFMessage -Level Important -Message "Unsecure $($protocol) connection with possible security risk detected. Please consider switch to HTTPS!" -Tag "Connection" $prefix = 'http://' } else { Write-PSFMessage -Level System -Message "Using secure $($protocol) connection." -Tag "Connection" $prefix = 'https://' } if ($Server -match '//') { if ($Server -match '\/\/(?<Server>(\w+|\.)+)') { $Server = $Matches["Server"] } Remove-Variable -Name Matches -Force -Verbose:$false -Debug:$false -Confirm:$false } if ($PsCmdlet.ParameterSetName -eq 'Credential') { if (($credential.UserName.Split('\')).count -gt 1) { $userName = $credential.UserName.Split('\')[1] } else { $userName = $credential.UserName } Write-PSFMessage -Level Verbose -Message "Authenticate user '$($userName)' to service '$($Prefix)$($server)'" -Tag "Connection", "Authentication" $param = @{ "Uri" = "$($prefix)$($server)/$($ApiPath)" "Headers" = @{ "user" = $userName "password" = $credential.GetNetworkCredential().Password "logintoken" = "$($LoginToken)" } "Verbose" = $false "Debug" = $false "ErrorAction" = "Stop" "ErrorVariable" = "invokeError" } try { $response = Invoke-RestMethod @param } catch { Stop-PSFFunction -Message "Error invoking rest call on service '$($Prefix)$($server)'. $($invokeError)" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet } if ($response.meta.text -like "Unsuccesful login attempt") { $msgText = "$($response.meta.text) to service '$($Prefix)$($server)'. Maybe wrong password" if (-not $LoginToken) { $msgText = "$($msgText) or LoginToken (OTP) is needed" } else { $msgText = "$($msgText) or LoginToken (OTP) wrong/expired" } Stop-PSFFunction -Message $msgText -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet } if (-not $response.content.apiKey) { Stop-PSFFunction -Message "Something went wrong on authenticating user $($userName). No apiKey found in response. Unable login to service '$($Prefix)$($server)'" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet } } Write-PSFMessage -Level System -Message "Creating TANSS.Connection" -Tag "Connection" $token = [TANSS.Connection]@{ Server = "$($Prefix)$($Server)" UserName = $userName EmployeeId = $response.content.employeeId EmployeeType = $response.content.employeeType AccessToken = ($response.content.apiKey | ConvertTo-SecureString -AsPlainText -Force) RefreshToken = ($response.content.refresh | ConvertTo-SecureString -AsPlainText -Force) Message = $response.meta.text TimeStampCreated = Get-Date TimeStampExpires = [datetime]::new(1970, 1, 1, 0, 0, 0, 0, [DateTimeKind]::Utc).AddSeconds($response.content.expire).ToLocalTime() TimeStampModified = Get-Date } if (-not $NoCacheInit) { Invoke-CacheRefresh -Token $token } if (-not $DoNotRegisterConnection) { # Make the connection the default connection for further commands Register-TANSSAccessToken -Token $token Write-PSFMessage -Level Significant -Message "Connected to service '($($token.Server))' as '$($token.UserName)' as default connection" -Tag "Connection" if ($PassThru) { Write-PSFMessage -Level System -Message "Outputting TANSS.Connection object" -Tag "Connection" $token } } else { Write-PSFMessage -Level Significant -Message "Connected to service '($($token.Server))' as '$($token.UserName)', outputting TANSS.Connection" -Tag "Connection" $token } } end { } } function Find-TANSSObject { <# .Synopsis Find-TANSSObject .DESCRIPTION Find a object via global search in TANSS The search has to be initiated on one of three areas. (Company, Employees, Tickets) .PARAMETER Company Initiate a search in the company area of TANSS .PARAMETER Employee Initiate a search within the employee/person database of TANSS .PARAMETER TicketPreview Initiate a search in the tickets of TANSS .PARAMETER Text The Text (id or name) to seach for .PARAMETER ShowInactive Search company records that are marked as inactive By default, only companies that are marked as "active" This is bound to company search only .PARAMETER ShowLocked Search company records that are marked as locked By default, only companies that are marked as "Unlocked" This is bound to company search only .PARAMETER CompanyId Return tickets or employees of the specified company id This is bound to ticket- and employee-search only .PARAMETER CompanyName Return tickets or employees of the specified company name This is bound to ticket- and employee-search only .PARAMETER Status Return "All", only "Active" or only "Inactive" employees. .PARAMETER GetCategories If true, categories will be fetches as well. The names are given in the "linked entities"-"employeeCategories" Default is $false .PARAMETER GetCallbacks If true, expected callbacks will be fetched as well Default is $false .PARAMETER PreviewContentMaxChars If defined, it overrides the to preview the content Default values within the api is 60 .PARAMETER ResultSize The amount of objects the query will return To avoid long waitings while query a large number of items, the api by default only query an amount of 100 items within one call .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .EXAMPLE PS C:\> Find-TANSSObject -Company -Text "Customer X" Search for "Customer X" within all company data .EXAMPLE PS C:\> Find-TANSSObject -TicketPreview -Text "Issue Y" Search for "Issue Y" within all tickets .EXAMPLE PS C:\> "Mister T" | Find-TANSSObject -Employee Search "Mister T" in the employee records of all companies .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "Company", PositionalBinding = $true, SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] [OutputType([TANSS.TicketPreview], [TANSS.Company], [TANSS.EmployeeSearched])] Param( [Parameter(ParameterSetName = "Company")] [switch] $Company, [Parameter(ParameterSetName = "Employee-UserFriendly")] [Parameter(ParameterSetName = "Employee-ApiNative")] [switch] $Employee, [Parameter(ParameterSetName = "Ticket-UserFriendly")] [Parameter(ParameterSetName = "Ticket-ApiNative")] [Alias("Ticket")] [switch] $TicketPreview, [Parameter(ParameterSetName = "Company", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Parameter(ParameterSetName = "Employee-UserFriendly", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Parameter(ParameterSetName = "Employee-ApiNative", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Parameter(ParameterSetName = "Ticket-UserFriendly", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Parameter(ParameterSetName = "Ticket-ApiNative", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateLength(2, [int]::MaxValue)] [string[]] $Text, [Parameter(ParameterSetName = "Company")] [switch] $ShowInactive, [Parameter(ParameterSetName = "Company")] [switch] $ShowLocked, [Parameter(ParameterSetName = "Employee-ApiNative")] [Parameter(ParameterSetName = "Ticket-ApiNative")] [int] $CompanyId, [Parameter(ParameterSetName = "Employee-UserFriendly", Mandatory = $true)] [Parameter(ParameterSetName = "Ticket-UserFriendly", Mandatory = $true)] [string] $CompanyName, [Parameter(ParameterSetName = "Employee-UserFriendly")] [Parameter(ParameterSetName = "Employee-ApiNative")] [ValidateSet("All", "Active", "Inactive")] [string] $Status = "All", [Parameter(ParameterSetName = "Employee-UserFriendly")] [Parameter(ParameterSetName = "Employee-ApiNative")] [bool] $GetCategories = $false, [Parameter(ParameterSetName = "Employee-UserFriendly")] [Parameter(ParameterSetName = "Employee-ApiNative")] [bool] $GetCallbacks = $false, [Parameter(ParameterSetName = "Ticket-UserFriendly")] [Parameter(ParameterSetName = "Ticket-ApiNative")] [int] $PreviewContentMaxChars, [Parameter(ParameterSetName = "Company")] [Parameter(ParameterSetName = "Employee-UserFriendly")] [Parameter(ParameterSetName = "Employee-ApiNative")] [Parameter(ParameterSetName = "Ticket-UserFriendly")] [Parameter(ParameterSetName = "Ticket-ApiNative")] [int] $ResultSize, [Parameter(ParameterSetName = "Company")] [Parameter(ParameterSetName = "Employee-UserFriendly")] [Parameter(ParameterSetName = "Employee-ApiNative")] [Parameter(ParameterSetName = "Ticket-UserFriendly")] [Parameter(ParameterSetName = "Ticket-ApiNative")] [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning $apiPath = Format-ApiPath -Path "api/v1/search" if ((-not $ResultSize) -or ($ResultSize -eq 0)) { $ResultSize = 100 } if ($Status) { switch ($Status) { "All" { $inactive = $true } "Active" { $inactive = $false } "Inactive" { $inactive = $true } Default { Stop-PSFFunction -Message "Unhandeled Status. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } } if($MyInvocation.BoundParameters['CompanyName'] -and $CompanyName) { $CompanyId = ConvertFrom-NameCache -Name CompanyName -Type "Companies" if(-not $CompanyId) { Write-PSFMessage -Level Warning -Message "No Id for company '$($Company)' found. Ticket will be created with blank value on CompanyId" } } } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" switch ($parameterSetName) { "Company" { foreach ($textItem in $Text) { $body = @{ areas = @("COMPANY") query = $textItem.replace("*", "") configs = @{ company = @{ maxResults = $ResultSize } } } $response = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $body -Token $Token if ($response.content.companies) { $countCompanyAll = ([array]($response.content.companies)).count Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - $($countCompanyAll) records returned" if (-not $ShowInactive) { $countCompanyFiltered = ([array]($response.content.companies | Where-Object { $_.inactive -like "False" })).Count Write-PSFMessage -Level Verbose -Message "Filtering companies marked as inactive - keeping $($countCompanyFiltered) of $($countCompanyAll) records" } else { $countCompanyFiltered = $countCompanyAll } if (-not $ShowLocked) { $countCompanyFiltered = $countCompanyFiltered - ([array]($response.content.companies | Where-Object { $_.lockout -like "True" })).Count Write-PSFMessage -Level Verbose -Message "Filtering companies marked as locked - keeping $($countCompanyFiltered) of $($countCompanyAll) records" } foreach ($companyItem in $response.content.companies) { # Filtering if(-not $ShowInactive) { if($companyItem.inactive -like "True") { continue } } if(-not $ShowLocked) { if($companyItem.lockout -like "True") { continue } } # Output data [TANSS.Company]@{ BaseObject = $companyItem Id = $companyItem.id } } } else { Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - no records returned from global search" } } } {$_ -like "Employee-ApiNative" -or $_ -like "Employee-UserFriendly"} { foreach ($textItem in $Text) { $body = @{ areas = @("EMPLOYEE") query = $textItem configs = @{ employee = @{ maxResults = $ResultSize } } } if($CompanyId) { $body.configs.employee.Add("companyId", $CompanyId) } if($Status) { $body.configs.employee.Add("inactive", $inactive) } if("GetCategories" -in $PSCmdlet.MyInvocation.BoundParameters.Keys) { $body.configs.employee.Add("categories", $GetCategories) } if("GetCallbacks" -in $PSCmdlet.MyInvocation.BoundParameters.Keys) { $body.configs.employee.Add("callbacks", $GetCallbacks) } $response = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $body -Token $Token if ($response.content.employees) { $countEmployeeAll = ([array]($response.content.employees)).count Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - $($countEmployeeAll) records returned" Push-DataToCacheRunspace -MetaData $response.meta foreach ($employeeItem in $response.content.employees) { # Output data [TANSS.EmployeeSearched]@{ BaseObject = $employeeItem Id = $employeeItem.id } } } else { Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - no records returned from global search" } } } {$_ -like "Ticket-ApiNative" -or $_ -like "Ticket-UserFriendly"} { foreach ($textItem in $Text) { $body = @{ areas = @("TICKET") query = $textItem configs = @{ employee = @{ maxResults = $ResultSize } } } if($CompanyId) { $body.configs.employee.Add("companyId", $CompanyId) } if($PreviewContentMaxChars) { $body.configs.employee.Add("previewContentMaxChars", $PreviewContentMaxChars) } $response = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $body -Token $Token if ($response.content.Tickets) { $countTicketsAll = ([array]($response.content.Tickets)).count Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - $($countTicketsAll) records returned" Push-DataToCacheRunspace -MetaData $response.meta foreach ($ticketItem in $response.content.Tickets) { # Output data [TANSS.TicketPreview]@{ BaseObject = $ticketItem Id = $ticketItem.id } } } else { Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - no records returned from global search" } } } Default { Stop-PSFFunction -Message "Unhandeled ParameterSetName. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } } end {} } function Get-TANSSDepartment { <# .Synopsis Get-TANSSDepartment .DESCRIPTION Get department from TANSS .PARAMETER Id Id of the department to get .PARAMETER Name Name of the department to get .PARAMETER IncludeEmployeeId Include assigned employees .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .EXAMPLE PS C:\> Get-TANSSDepartment Get departments from TANSS .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "All", SupportsShouldProcess = $false, PositionalBinding = $true, ConfirmImpact = 'Low' )] [OutputType([TANSS.Department])] Param( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "ById" )] [int[]] $Id, [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "ByName" )] [string[]] $Name, [switch] $IncludeEmployeeId, [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning if ($IncludeEmployeeId) { Write-PSFMessage -Level Verbose -Message "IncludeEmployeeId switch is specified, going to ask for linked IDs" -Tag "Department", "IncludeEmployeeId" $deparmentsWithEmployeeId = Invoke-TANSSRequest -Type GET -ApiPath "api/v1/companies/departments?withEmployees=true" -Token $Token | Select-Object -ExpandProperty content $departments = foreach ($department in $departments) { [array]$_employeeIds = $deparmentsWithEmployeeId | Where-Object id -like $department.id | Select-Object -ExpandProperty employeeIds $department | Add-Member -MemberType NoteProperty -Name employeeIds -Value $_employeeIds $department } Remove-Variable -Name deparmentsWithEmployeeId, _employeeIds, deparment -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false -ErrorAction:Ignore -WarningAction:Ignore -InformationAction:Ignore } else { $departments = Invoke-TANSSRequest -Type GET -ApiPath "api/v1/employees/departments" -Token $Token | Select-Object -ExpandProperty content } [array]$filteredDepartments = @() } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" switch ($parameterSetName) { "ById" { foreach ($item in $Id) { $filteredDepartments += $departments | Where-Object id -eq $item } } "ByName" { foreach ($item in $Name) { $filteredDepartments += $departments | Where-Object name -like $item } } "All" { $filteredDepartments = $departments } Default { Stop-PSFFunction -Message "Unhandled ParameterSet '$($parameterSetName)', developers mistake" -EnableException $true -Cmdlet $pscmdlet -Tag "Department", "SwitchException", "ParameterSet" } } } end { $filteredDepartments = $filteredDepartments | Sort-Object name, id -Unique Write-PSFMessage -Level Verbose -Message "Going to return $($filteredDepartments.count) departments" -Tag "Department", "Output" foreach ($department in $filteredDepartments) { Write-PSFMessage -Level System -Message "Working on department '$($department.name)' with id '$($department.id)'" -Tag "Department" # put id and name to cache lookups Update-CacheLookup -LookupName "Departments" -Id $department.Id -Name $department.Name # output result [TANSS.Department]@{ Baseobject = $department Id = $department.id } } } } function Get-TANSSRegisteredAccessToken { <# .Synopsis Get-TANSSRegisteredAccessToken .DESCRIPTION Retrieve the registered LoginToken for default TANSS connection .EXAMPLE PS C:\> Get-TANSSRegisteredAccessToken Retrieve the registered LoginToken for TANSS .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] Param( ) begin {} process { Write-PSFMessage -Level Verbose -Message "Retrieving the registered LoginToken for '$($script:TANSSToken.UserName)' on '$($script:TANSSToken.Server)'" -Tag "AccessToken" $script:TANSSToken } end {} } function Invoke-TANSSRequest { <# .Synopsis Invoke-TANSSRequest .DESCRIPTION Invoke a API request to TANSS Server .PARAMETER Type Type of web request .PARAMETER ApiPath Uri path for the REST call in the API .PARAMETER QueryParameter A hashtable for all the parameters to the api route .PARAMETER AdditionalHeader Hashtable with additional values to put in the header of the request .PARAMETER Body The body as a hashtable for the request .PARAMETER Pdf if a PDF should be queried, this switch must be specified .PARAMETER BodyForceArray Tells the function always to invoke the data in the body as a JSON array formatted string .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> Invoke-TANSSRequest -Type GET -ApiPath "api/v1/something" Invoke a GET request to API with path api/v1/something by using the default registered token within the module .EXAMPLE PS C:\> Invoke-TANSSRequest -Type GET -ApiPath "api/v1/something" -Token $Token Invoke a GET request to API with path api/v1/something by using the explicit token from the variale $Token .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = 'Default', SupportsShouldProcess = $true, PositionalBinding = $true, ConfirmImpact = 'Medium' )] param ( [Parameter(Mandatory = $true)] [ValidateSet("GET", "POST", "PUT", "DELETE")] [string] $Type, [Parameter(Mandatory = $true)] [string] $ApiPath, [hashtable] $QueryParameter, [hashtable[]] $Body, [hashtable] $AdditionalHeader, [switch] $Pdf, [switch] $BodyForceArray, [TANSS.Connection] $Token ) begin { } process { } end { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Invoke-TANSSTokenCheck -Token $Token $ApiPath = Format-ApiPath -Path $ApiPath -QueryParameter $QueryParameter # Body if ($Body) { $bodyData = $Body | ConvertTo-Json -Compress } else { $bodyData = $null } if ($BodyForceArray -and (-not ([string]$bodyData).StartsWith("["))) { $bodyData = "[$($bodyData)]" } # Header $header = @{ "apiToken" = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token.AccessToken)) } if ($Pdf) { $header.Add("Accept", "pdf") } if ($MyInvocation.BoundParameters['AdditionalHeader'] -and $AdditionalHeader) { foreach ($key in $AdditionalHeader.Keys) { $header.Add($key, $AdditionalHeader[$key]) } } # Invoke request $param = [ordered]@{ "Uri" = "$($Token.Server)/$($ApiPath)" "Headers" = $header "Body" = $bodyData "Method" = $Type "ContentType" = 'application/json; charset=UTF-8' "Verbose" = $false "Debug" = $false "ErrorAction" = "Stop" "ErrorVariable" = "invokeError" } if ($pscmdlet.ShouldProcess("$($Type) web REST call against URL '$($param.Uri)'", "Invoke")) { Write-PSFMessage -Level Verbose -Message "Invoke $($Type) web REST call against URL '$($param.Uri)'" -Tag "TANSSApiRequest" -Data @{ "body" = $bodyData; "Method" = $Type } try { $response = Invoke-RestMethod @param Write-PSFMessage -Level System -Message "API Response: $($response.meta.text)" -Tag "TANSSApiRequest", "SuccessfulRequest" -Data @{ "response" = $response } } catch { if ($invokeError[0].Message.StartsWith("{")) { $response = $invokeError[0].Message | ConvertFrom-Json -ErrorAction SilentlyContinue } if ($response) { Write-PSFMessage -Level Error -Message "$($response.Error.text) - $($response.Error.localizedText)" -Exception $response.Error.type -Tag "TANSSApiRequest", "FailedRequest", "REST call $($Type)" -Data @{ "Message" = $invokeError[0].Message } -PSCmdlet $pscmdlet -ErrorRecord $invokeError[0].ErrorRecord } else { Write-PSFMessage -Level Error -Message "$($invokeError[0].Source) ($($invokeError[0].HResult)): $($invokeError[0].Message)" -Exception $invokeError[0].InnerException -Tag "TANSSApiRequest", "FailedRequest", "REST call $($Type)" -ErrorRecord $invokeError[0].ErrorRecord -PSCmdlet $pscmdlet -Data @{ "Message" = $invokeError[0].Message } } return } # Output $response } } } function New-TANSSServiceToken { <# .Synopsis Create a new (user unspecific) api service access token object .DESCRIPTION Create a new api service access token object. Apart from the regular user login, there are aspects and api routes, that are only available via explicit service token. This function allows you to create a service token to give to other functions, that require such a token. .PARAMETER Server Name of the service the token is generated from .PARAMETER ServiceToken A API token generated within Tanss to access specific TANSS modules explicit via API service as a non-employee-account. For security reaons, the parameter only accept secure strings. Please avoid plain-text for sensitive informations! To generate secure strings use: $ServiceTokenSecureString = Read-Host -AsSecureString .PARAMETER Protocol Specifies if the service connection is done with http or https .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> $tanssServiceToken = New-TANSSServiceToken -Server "tanss.corp.company.com" -ServiceToken $ServiceTokenSecureString Outputs a ServiceToken as a TANSS.Connection object for "tanss.corp.company.com" with the api key from the variable $ServiceTokenSecureString API variable $ServiceTokenSecureString hast to be a securestring. ($ServiceTokenSecureString = Read-Host -AsSecureString) .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "Default", SupportsShouldProcess = $true, PositionalBinding = $true, ConfirmImpact = 'Medium' )] [OutputType([TANSS.Connection])] Param( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Alias("ComputerName", "Hostname", "Host", "ServerName")] [ValidateNotNull()] [String] $Server, [Parameter( Mandatory = $true, ValueFromPipeline = $true )] [Alias("ApiKey", "Password", "AccessToken", "Token")] [securestring] $ServiceToken, [ValidateSet("HTTP", "HTTPS")] [ValidateNotNullOrEmpty()] [String] $Protocol = "HTTPS" ) begin { } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" # Ensure Prefix if ($protocol -eq 'HTTP') { Write-PSFMessage -Level Important -Message "Unsecure $($protocol) connection with possible security risk detected. Please consider switch to HTTPS!" -Tag "ServiceToken" $prefix = 'http://' } else { Write-PSFMessage -Level System -Message "Using secure $($protocol) connection." -Tag "ServiceToken" $prefix = 'https://' } # Validate Server Parameter to avoid accidentally input bearer token information in $Server try { $null = ConvertFrom-JWTtoken -TokenText $Server $serverIsTokenObject = $true } catch { $serverIsTokenObject = $false } if (($Server.StartsWith("Bearer")) -or ($Server.Length -gt 256) -or ($serverIsTokenObject)) { if ($Server.Length -gt 10) { $textlength = $Server.Length / 2 } elseif ($Server.Length -gt 5) { $textlength = 4 } else { $textlength = 2 } Stop-PSFFunction -Message "The specified Server '$($Server.Substring(0, $textlength))****' looks like a service token. ServiceToken has to be piped in as a SecureString or has to be specified via parameter '-ServiceToken'. For security reason, please don't use plaintext for sensitive information." -EnableException $true -Cmdlet $pscmdlet -Tag "ServiceToken" } # Read JWT from service token $TokenText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ServiceToken)) if ($TokenText.StartsWith("Bearer")) { Write-PSFMessage -Level System -Message "Found Bearer token information. Going to extract JWT" -Tag "ServiceToken" $TokenText = $TokenText.split(" ")[-1] } Write-PSFMessage -Level Verbose -Message "Reading JWT information from serviceToken" -Tag "ServiceToken" $tokenInfo = ConvertFrom-JWTtoken -TokenText $TokenText # Create ServiceToken if (($tokenInfo.typ -like "JWT") -and $Server -and $prefix) { if ($pscmdlet.ShouldProcess("Service token for '$($UserName)'", "New")) { Write-PSFMessage -Level System -Message "Creating TANSS.Connection with service token" -Tag "ServiceToken" $serviceTokenObject = [TANSS.Connection]@{ Server = "$($Prefix)$($Server)" UserName = $tokenInfo.sub EmployeeId = 0 EmployeeType = "ServiceAccessToken" AccessToken = $ServiceToken RefreshToken = $null Message = "Explizit specified API token. May not work with all functions!" TimeStampCreated = (Get-Date) TimeStampExpires = $tokenInfo.exp TimeStampModified = (Get-Date) } Invoke-TANSSTokenCheck -Token $serviceTokenObject -NoRefresh # output result $serviceTokenObject } } else { Write-PSFMessage -Level Important -Message "Unable to create TANSS ServiceToken object with specified ServiceToken '$($TokenText.Substring(0,10))*****'" -Tag "ServiceToken" } } end {} } function Register-TANSSAccessToken { <# .Synopsis Register-TANSSAccessToken .DESCRIPTION Register the AccessToken as default connection setting for TANSS .PARAMETER Token AccessToken object to register as default connection for TANSS .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> Register-TANSSAccessToken -Token $Token Register the LoginToken from variable $Token as a default connection for TANSS .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'Medium' )] Param( [Parameter(Mandatory = $true)] [TANSS.Connection] $Token ) begin {} process { if ($pscmdlet.ShouldProcess("AccessToken for $($Token.UserName) on '$($Token.Server)'", "Register")) { Write-PSFMessage -Level Verbose -Message "Registering AccessToken for $($Token.UserName) on '$($Token.Server)' valid until '$($Token.TimeStampExpires)'" -Tag "AccessToken" $script:TANSSToken = $Token } } end {} } function Update-TANSSAccessToken { <# .Synopsis Update-TANSSAccessToken .DESCRIPTION Updates the AccessToken from a refreshToken for TANSS connection By defaault, the new Access is registered to as default connection .PARAMETER NoCacheRefresh Do not requery tickets and various types to fill cache data for lookup types .PARAMETER DoNotRegisterConnection Do not register the connection as default connection .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .PARAMETER PassThru Outputs the new token to the console .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> Update-TANSSAccessToken Updates the AccessToken from the default connection and register it as new AccessToken on default Connection .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] [CmdletBinding( SupportsShouldProcess = $true, PositionalBinding = $true, ConfirmImpact = 'Medium' )] Param( [TANSS.Connection] $Token, [Alias('NoRegistration')] [Switch] $DoNotRegisterConnection, [switch] $NoCacheRefresh, [switch] $PassThru ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning } process { if ($Token.RefreshToken) { $refreshTokenInfo = ConvertFrom-JWTtoken -TokenText ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token.RefreshToken))).split(" ")[1] } else { Stop-PSFFunction -Message "Invalid Token specified. No refreshToken found" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet } Write-PSFMessage -Level Verbose -Message "Checking RefreshToken from TANSS.Connection of $($Token.UserName) on '$($Token.Server)'" -Tag "AccessToken", "Connection", "Authentication" if ( (Get-Date) -ge $refreshTokenInfo.exp ) { Stop-PSFFunction -Message "RefreshToken expired. Unable to refresh with current token. Please use Connect-TANSS to login again" -Tag "Connection", "Authentication" return } if ($pscmdlet.ShouldProcess("AccessToken from TANSS.Connection of $($Token.UserName) on '$($Token.Server)' with RefreshToken valid until '$($refreshTokenInfo.exp)'", "Update")) { $apiPath = Format-ApiPath -Path "api/v1/tickets/own" Write-PSFMessage -Level Verbose -Message "Updating AccessToken from TANSS.Connection of $($Token.UserName) on '$($Token.Server)' with RefreshToken valid until '$($refreshTokenInfo.exp)'" -Tag "AccessToken" $param = @{ "Uri" = "$($Token.Server)/$($ApiPath)" "Headers" = @{ "refreshToken" = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token.RefreshToken)) } "Verbose" = $false "Debug" = $false "ErrorAction" = "Stop" "ErrorVariable" = "invokeError" } try { $response = Invoke-RestMethod @param } catch { Stop-PSFFunction -Message "Error invoking rest call on service '$($Token.Server)'. $($invokeError)" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet } if ($response.meta.text -notlike "Welcome, your ApiToken is 4 hours valid.") { Stop-PSFFunction -Message "$($response.meta.text) to service '$($Token.Server)'. Apperantly, refreshToken is not valid" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet } if (-not $response.content.apiKey) { Stop-PSFFunction -Message "Something went wrong on authenticating user $($Token.UserName). No apiKey found in response. Unable to refresh token from connection '$($Token.Server)'" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet } Write-PSFMessage -Level System -Message "Creating TANSS.Connection from refreshed AccessToken" -Tag "Connection" $token = [TANSS.Connection]@{ Server = $Token.Server UserName = $Token.UserName EmployeeId = $response.content.employeeId EmployeeType = $response.content.employeeType AccessToken = ($response.content.apiKey | ConvertTo-SecureString -AsPlainText -Force) RefreshToken = ($response.content.refresh | ConvertTo-SecureString -AsPlainText -Force) Message = $response.meta.text TimeStampCreated = $Token.TimeStampCreated TimeStampExpires = [datetime]::new(1970, 1, 1, 0, 0, 0, 0, [DateTimeKind]::Utc).AddSeconds($response.content.expire).ToLocalTime() TimeStampModified = Get-Date } if (-not $NoCacheRefresh) { Invoke-CacheRefresh -Token $token } if (-not $DoNotRegisterConnection) { # Make the connection the default connection for further commands Write-PSFMessage -Level Significant -Message "Updating AccessToken for service '($($token.Server))' as '$($token.UserName)' and register it as default connection" -Tag "Connection" Register-TANSSAccessToken -Token $token if ($PassThru) { Write-PSFMessage -Level System -Message "Outputting TANSS.Connection object" -Tag "Connection" $token } } else { Write-PSFMessage -Level Significant -Message "Updating AccessToken for service '($($token.Server))' as '$($token.UserName)'" -Tag "Connection" $token } } } end {} } function Get-TANSSEmployee { <# .Synopsis Get-TANSSEmployee .DESCRIPTION Get employees out of TANSS service. You can pipe in IDs o employee objects to get refreshed data out of the service You can also pipe in company objects to receive employees of that company .PARAMETER EmployeeId The ID of the employee to get from TANSS service .PARAMETER Employee A TANSS.Employee object to query again from the service .PARAMETER CompanyId The ID of the company to get employees from .PARAMETER Company A passed in TANSS.Company object to query employees from .PARAMETER CompanyName The name of the company to query employees from .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .EXAMPLE PS C:\> Get-TANSSEmployee -EmployeeId 2 Get the employee with ID 2 (usually the first employee created in TANSS) .EXAMPLE PS C:\> $employee | Get-TANSSEmployee Query the employee from variable $employee again .EXAMPLE PS C:\> Get-TANSSEmployee -CompanyId 100000 Get all employees from company ID 100000 (your own company) .EXAMPLE PS C:\> $company | Get-TANSSEmployee Get all employees from company $company .EXAMPLE PS C:\> Get-TANSSEmployee -CompanyName "Company X" Get all employees from "Company X" .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "Employee_ApiNative", SupportsShouldProcess = $false, PositionalBinding = $true, ConfirmImpact = 'Low' )] [OutputType([TANSS.Employee])] Param( [Parameter( ParameterSetName = "Employee_ApiNative", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [Alias("Id")] [int[]] $EmployeeId, [Parameter( ParameterSetName = "Employee_UserFriendly", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [tanss.employee] $Employee, [Parameter( ParameterSetName = "Company_ApiNative", ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [int[]] $CompanyId, [Parameter( ParameterSetName = "Company_UserFriendly", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [tanss.company] $Company, [Parameter( ParameterSetName = "Company_UserFriendlyByName", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [string[]] $CompanyName, [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" # If objects are piped in -> collect IDs switch ($parameterSetName) { "Employee_UserFriendly" { $EmployeeId = $Employee.Id } "Company_UserFriendly" { $CompanyId = $Company.Id } "Company_UserFriendlyByName" { $CompanyId = foreach ($_name in $CompanyName) { Find-TANSSObject -Company -Text $_name -Token $Token -ShowLocked | Where-Object name -like $_name | Select-Object -ExpandProperty Id } } } switch ($parameterSetName) { { $_ -like "Employee_*" } { Write-PSFMessage -Level System -Message "Query personal employee record. $(([array]$EmployeeId).count) record(s)" foreach ($id in $EmployeeId) { Write-PSFMessage -Level Verbose -Message "Working on employee id $($id)" $response = Invoke-TANSSRequest -Type GET -ApiPath "api/v1/employees/$($id)" -Token $Token if ($response.meta.text -like "Object found") { # Cache refresh Push-DataToCacheRunspace -MetaData $response.meta Write-PSFMessage -Level Verbose -Message "Output employee $($response.content.name)" $output = [TANSS.Employee]@{ BaseObject = $response.content Id = $response.content.id } # ToDo: Filtering - add parameters (Isactive, FilterName, ) # Output result $output } else { Write-PSFMessage -Level Error -Message "API returned no data" } } } { $_ -like "Company_*" } { Write-PSFMessage -Level System -Message "Query corporate employee record. $(([array]$CompanyId).count) record(s)" foreach ($id in $CompanyId) { Write-PSFMessage -Level Verbose -Message "Query employee(s) for company id $($id)" $response = Invoke-TANSSRequest -Type GET -ApiPath "api/v1/companies/$($id)/employees" -Token $Token if ($response.meta.text -like "Object found") { # Cache refresh Push-DataToCacheRunspace -MetaData $response.meta foreach ($responseItem in $response.content) { Write-PSFMessage -Level Verbose -Message "Output corporate employee $($responseItem.name)" $output = [TANSS.Employee]@{ BaseObject = $responseItem Id = $responseItem.id } # ToDo: Filtering - add parameters (Isactive, FilterName, ) # Output result $output } } else { Write-PSFMessage -Level Error -Message "API returned no data" } } } Default { Stop-PSFFunction -Message "Unhandeled ParameterSetName. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } } end {} } function Get-TANSSTechnician { <# .Synopsis Get-TANSSTechnician .DESCRIPTION Gets all technicians of this system from default TANSS connection .PARAMETER Id ID of the technician to get (client side filtering) .PARAMETER Name Name of the technician to get (client side filtering) .PARAMETER FreelancerCompanyId If this parameter is given, also fetches the freelancers of this company. By default all users with a license are treated as "TANSS technicians". .PARAMETER ExcludeRestrictedLicenseUser Do not show account/ users / technicians with limited licenses .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .EXAMPLE PS C:\> Get-TANSSTechnician Gets all technicians of this system .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] [OutputType([TANSS.Employee])] Param( [String[]] $Name, [int[]] $Id, [int[]] $FreelancerCompanyId, [switch] $ExcludeRestrictedLicenseUser, [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } $apiPath = Format-ApiPath -Path "api/v1/employees/technicians" Assert-CacheRunspaceRunning if (-not $FreelancerCompanyId) { $FreelancerCompanyId = 0 } } process { $response = @() $response += foreach ($companyId in $FreelancerCompanyId) { $queryParameter = @{} if($MyInvocation.BoundParameters['ExcludeRestrictedLicenseUser'] -and $ExcludeRestrictedLicenseUser) { $queryParameter.Add("restrictedLicenses", $false) } else { $queryParameter.Add("restrictedLicenses", $true) } if ($companyId -ne 0) { Write-PSFMessage -Level System -Message "FreelancerCompanyId specified, compiling body to query freelancers of company '$($companyId)'" -Tag "Technician", "Freelancer" $queryParameter.Add("FreelancerCompanyId", $companyId) } $invokeParam = @{ "Type" = "GET" "ApiPath" = (Format-ApiPath -Path $apiPath -QueryParameter $queryParameter) "Token" = $Token } Invoke-TANSSRequest @invokeParam Remove-Variable -Name queryParameter, invokeParam -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore -InformationAction Ignore } if ($response) { Write-PSFMessage -Level Verbose -Message "Found $(($response.content).count) technicians" -Tag "Technician" foreach ($responseItem in $response) { # Output result foreach ($technician in $responseItem.content) { # Do filtering on name if ($MyInvocation.BoundParameters['Name'] -and $Name) { $filterSuccess = $false foreach ($filterName in $Name) { if ($technician.Name -like $filterName) { $filterSuccess = $true } } # if filter does not hit, continue with next technician if ($filterSuccess -eq $false) { continue } } # Do filtering on id if ($MyInvocation.BoundParameters['Id'] -and $Id) { $filterSuccess = $false foreach ($filterId in $Id) { if ([int]($technician.id) -eq $filterId) { $filterSuccess = $true } } # if filter does not hit, continue with next technician if ($filterSuccess -eq $false) { continue } } # Query details Write-PSFMessage -Level Verbose -Message "Getting details of '$($technician.name)' (Id $($technician.id))" -Tag "Technician" $invokeParam = @{ "Type" = "GET" "ApiPath" = (Format-ApiPath -Path "api/v1/employees/$($technician.id)") "Token" = $Token } $employeeResponse = Invoke-TANSSRequest @invokeParam if ($employeeResponse) { Push-DataToCacheRunspace -MetaData $employeeResponse.meta foreach ($employeeItem in $employeeResponse.content) { Write-PSFMessage -Level Debug -Message "Found '$($employeeItem.Name)' with id $($employeeItem.id)" # Output data [TANSS.Employee]@{ BaseObject = $employeeItem Id = $employeeItem.id } } } else { Stop-PSFFunction -Message "Unexpected error searching '$($employeeResponse.content.name)' with ID '$($technician.id)'. TANSS is unable to find details of employee" -EnableException $true -Cmdlet $pscmdlet } } } } else { Write-PSFMessage -Level Warning -Message "No technicians found." -Tag "Technician" } } end { } } function New-TANSSEmployee { <# .Synopsis New-TANSSEmployee .DESCRIPTION Create a new employee in TANSS .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE New-TANSSEmployee -Name "User, Test" Create a new employee (as technician) in your own company .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "Userfriendly", SupportsShouldProcess = $true, PositionalBinding = $true, ConfirmImpact = 'Medium' )] [OutputType([TANSS.Employee])] param ( # Fullname of the employee [Parameter( ParameterSetName = "ApiNative", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Parameter( ParameterSetName = "Userfriendly", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Alias("Fullname", "Displayname")] [string] $Name, # first name of the employee [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("Givenname")] [string] $FirstName, # Last name of the employee [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("Surname")] [string] $LastName, # Id of the salutation for this employee [Parameter(ParameterSetName = "ApiNative")] [Alias("IdSalutation")] [Int] $SalutationId = 0, # Id of the department which tis employee is assigned to [Parameter(ParameterSetName = "ApiNative")] [Alias("IdDepartment")] [Int] $DepartmentId, # Name of the department which tis employee is assigned to [Parameter(ParameterSetName = "Userfriendly")] [string] $Department, # location / room of the employee [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("Location")] [string] $Room, # Main telephone number [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("TelephoneNumber")] [string] $Phone, # e-Mail address [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("EmailAddress")] [string] $Email, # if this employee is assigned to a specific car, the id goes here [Parameter(ParameterSetName = "ApiNative")] [Alias("IdCar")] $CarId = 0, # mobile telephone number [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("MobilePhone")] [string] $Mobile, # initials for this employee [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [string] $Initials, # Working hour model for this employee [Parameter(ParameterSetName = "ApiNative")] [Alias("IdWorkingHourModel")] [int] $WorkingHourModelId = 0, # Id of the accounting type for this employee [Parameter(ParameterSetName = "ApiNative")] [Alias("IdAccountingType")] [int] $accountingTypeId = 0, # Private telephone number [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("PrivatePhoneNumber")] [string] $PrivateNumber, # True if the employee is active [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("Active")] [bool] $IsActive = $true, # Identification number in foreign ERP system [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [string] $ERPNumber, # Fax number [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("PersonalFaxNumber")] [string] $Fax, # Role for this employee [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [string] $Role, # if the employee uses a title, the id goes here [Parameter(ParameterSetName = "ApiNative")] [Alias("IdTitle")] [int] $TitleId = 0, # Language which this employee uses [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [string] $Language, # Second telephone number [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("TelephoneNumberTwo")] [string] $Phone2, # second mobile telephone number [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("MobileNumberTwo")] [string] $Mobile2, # Employee birthday date [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [Alias("BirthdayDate", "DateBirthday", "DateOfBirth")] [datetime] $Birthday, # Company id of the ticket. Name is stored in the "linked entities" - "companies". Can only be set if the user has access to the company [Parameter(ParameterSetName = "ApiNative")] [Alias("CompanyAssignments", "IdCompany")] [int[]] $CompanyId, # Company name where the ticket should create for. Can only be set if the user has access to the company [Parameter(ParameterSetName = "Userfriendly")] [Alias("Company")] [String[]] $CompanyName, [Parameter(ParameterSetName = "ApiNative")] [Parameter(ParameterSetName = "Userfriendly")] [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning $apiPath = Format-ApiPath -Path "api/v1/employees" if ($Department) { $DepartmentId = ConvertFrom-NameCache -Name $Department -Type "Departments" if (-not $DepartmentId) { Write-PSFMessage -Level Warning -Message "No Id for department '$($Department)' found. Employee will be created with blank value on departmentId" #todo implement API call for departments $DepartmentId = 0 } } if ($CompanyName) { $CompanyId = foreach ($companyItem in $CompanyName) { $_id = ConvertFrom-NameCache -Name $companyItem -Type Companies if (-not $_id) { Write-PSFMessage -Level Warning -Message "No Id for company '$($companyItem)' found. Employee will be created without assignment to this company" } else { $_id } } Remove-Variable -Name _id -Force -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore } if (-not $CompanyId) { $_name = ConvertFrom-NameCache -Id 100000 -Type "Companies" Write-PSFMessage -Level Important -Message "No company specified. Employee will be created within your own company $(if($_name) { "($($_name)) "})as a technician" $CompanyId = 100000 Remove-Variable -Name _name -Force -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore } } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" if ($parameterSetName -like "Userfriendly" -and (-not $Name)) { Write-PSFMessage -Level Error -Message "No name specified" continue } #region rest call prepare if ($Birthday) { $_birthday = Get-Date -Date $Birthday -Format "yyyy-MM-dd" } else { $_birthday = "0000-00-00" } [array]$_companies = foreach ($CompanyIdItem in $CompanyId) { @{ "companyId" = $CompanyIdItem } } if (-not $LastName) { $nameParts = $Name.split(",").Trim() if ($nameParts.Count -eq 2) { # Assuming schema "<Lastname>, <Firstname>" $LastName = $nameParts[0] } elseif ($nameParts.Count -eq 1) { $nameParts = $Name.split(" ").Trim() if ($nameParts.Count -eq 2) { # Assuming schema "<Firstname> <Lastname>" $LastName = $nameParts[1] } else { $LastName = $Name } } else { $LastName = $Name } } Remove-Variable -Name nameParts -Force -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore if (-not $FirstName) { $nameParts = $Name.split(",").Trim() if ($nameParts.Count -eq 2) { # Assuming schema "<Lastname>, <Firstname>" $FirstName = $nameParts[1] } elseif ($nameParts.Count -eq 1) { $nameParts = $Name.split(" ").Trim() if ($nameParts.Count -eq 2) { # Assuming schema "<Firstname> <Lastname>" $FirstName = $nameParts[0] } else { $FirstName = "" } } else { $FirstName = "" } } Remove-Variable -Name nameParts -Force -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore if (-not $Initials -and $FirstName -and $LastName) { $_initials = "$(([string]$FirstName)[0])$(([string]$LastName)[0])" } else { $_initials = $Initials } $body = [ordered]@{ "id" = 0 "name" = "$Name" "firstName" = "$FirstName" "lastName" = "$LastName" "salutationId" = $SalutationId "departmentId" = $DepartmentId "room" = "$Room" "telephoneNumber" = "$Phone" "emailAddress" = "$Email" "carId" = $CarId "mobilePhone" = "$Mobile" "initials" = "$_initials" "workingHourModelId" = $WorkingHourModelId "accountingTypeId" = $accountingTypeId "privatePhoneNumber" = "$PrivateNumber" "active" = $IsActive "erpNumber" = "$ERPNumber" "personalFaxNumber" = "$Fax" "role" = "$Role" "titleId" = $TitleId "language" = "$Language" "telephoneNumberTwo" = "$Phone2" "mobileNumberTwo" = "$Mobile2" "birthday" = "$_birthday" "companyAssignments" = $_companies } #endregion rest call prepare if ($pscmdlet.ShouldProcess("Employee '$($Name)' on companyID '$([string]::Join(", ", $CompanyId))'", "New")) { Write-PSFMessage -Level Verbose -Message "Creating new employee '$($Name)' on companyID '$([string]::Join(", ", $CompanyId))'" -Tag "Employee" -Data $body $response = Invoke-TANSSRequest -Type POST -ApiPath $apiPath -Body $body -Token $Token if ($response) { Write-PSFMessage -Level Verbose -Message "API Response: $($response.meta.text)" Push-DataToCacheRunspace -MetaData $response.meta [TANSS.Employee]@{ BaseObject = $response.content Id = $response.content.id } } else { Write-PSFMessage -Level Error -Message "Error creating employee, no response from API" } } } end { } } function Get-TANSSProject { <# .Synopsis Get-TANSSProject .DESCRIPTION Get all projects from TANSS .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .EXAMPLE PS C:\> Get-TANSSProject Get all projects from TANSS .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidMultipleTypeAttributes", "")] [CmdletBinding( SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] [OutputType([TANSS.Ticket])] param ( [TANSS.Connection] $Token ) begin { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-TANSSTicket', [System.Management.Automation.CommandTypes]::Function) $scriptCmd = {& $wrappedCmd -Project @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline() $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try { $steppablePipeline.Process($_) } catch { throw } } end { try { $steppablePipeline.End() } catch { throw } } } function Get-TANSSProjectPhase { <# .Synopsis Get-TANSSProjectPhase .DESCRIPTION Get phases of a project in TANSS .PARAMETER ProjectID The ID of the poject to receive phases from .PARAMETER Project TANSS.Project object to receive phases from .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .EXAMPLE PS C:\> Get-TANSSProjectPhase -ProjectId 10 Get phases out of project 10 .EXAMPLE PS C:\> $projects | Get-TANSSTicketActivity Get all phases of projects in variable $tickets .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "ByProject", SupportsShouldProcess = $false, PositionalBinding = $true, ConfirmImpact = 'Low' )] [OutputType([TANSS.ProjectPhase])] Param( [Parameter( ParameterSetName = "ByProjectId", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [Alias("Id")] [int[]] $ProjectID, [Parameter( ParameterSetName = "ByProject", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [TANSS.Project[]] $Project, [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" if ($parameterSetName -like "ByProject") { $inputobjectProjectCount = ([array]$Project).Count Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectProjectCount) project$(if($inputobjectProjectCount -gt 1){'s'})" -Tag "ProjectPhase", "CollectInputObjects" [array]$ProjectID = $Project.id } foreach ($projectIdItem in $ProjectID) { Write-PSFMessage -Level Verbose -Message "Working on project ID $($projectIdItem)" -Tag "ProjectPhase", "Query" # build api path $apiPath = Format-ApiPath -Path "api/v1/projects/$projectIdItem/phases" # query content $response = Invoke-TANSSRequest -Type GET -ApiPath $apiPath -Token $Token Push-DataToCacheRunspace -MetaData $response.meta Write-PSFMessage -Level Verbose -Message "$($response.meta.text): Received $($response.meta.properties.extras.count) VacationEntitlement records in year $($Year)" -Tag "ProjectPhase", "Query" # create output foreach ($phase in $response.content) { # output object [TANSS.ProjectPhase]@{ BaseObject = $phase Id = $phase.id } # cache Lookup refresh [TANSS.Lookup]::Phases[$phase.id] = $phase.name } } } end {} } function New-TANSSProject { <# .Synopsis New-TANSSProject .DESCRIPTION Creates a project in TANSS, that can contain multiple tickets .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> New-TANSSProject -Title "A new project" Create a project with title "A new project" .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidMultipleTypeAttributes", "")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "")] [CmdletBinding( DefaultParameterSetName = "Userfriendly", SupportsShouldProcess = $true, PositionalBinding = $true, ConfirmImpact = 'Medium' )] [OutputType([TANSS.Ticket])] param ( # Company id of the ticket. Name is stored in the "linked entities" - "companies". Can only be set if the user has access to the company [Parameter(ParameterSetName="ApiNative")] [int] $CompanyId, # Company name where the ticket should create for. Can only be set if the user has access to the company [Parameter(ParameterSetName="Userfriendly")] [String] $Company, # If the ticket has a remitter, the id goes here. Name is stored in the "linked entities" - "employees" [Parameter(ParameterSetName="ApiNative")] [Alias('ClientId')] [int] $RemitterId, # If the ticket has a remitter/client, the name of the client [Parameter(ParameterSetName="Userfriendly")] [String] $Client, # gives infos about how the remitter gave the order. Infos are stored in the "linked entities" - "orderBys" [Parameter(ParameterSetName="ApiNative")] [int] $OrderById, # gives infos about how the Client gave the order. [Parameter(ParameterSetName="Userfriendly")] [string] $OrderBy, # The title / subject of the ticket [Parameter( ParameterSetName="Userfriendly", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Parameter( ParameterSetName="ApiNative", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [string] $Title, # The content / description of the ticket [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [Alias('content')] [string] $Description, # External ticket id (optional) [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [Alias('extTicketId')] [string] $ExternalTicketId, # id of employee which ticket is assigned to. Name is stored in "linked entities" - "employees" [Parameter(ParameterSetName="ApiNative")] [Alias('assignedToEmployeeId')] [int] $EmployeeIdAssigned, # Name of the employee the ticket is assigned to [Parameter(ParameterSetName="Userfriendly")] [String] $EmployeeAssigned, # id of department the ticket is assigned to. Name is stored in "linked entities" - "departments" [Parameter(ParameterSetName="ApiNative")] [Alias('assignedToDepartmentId')] [int] $DepartmentIdAssigned, # Name of the department the ticket is assigned to [Parameter(ParameterSetName="Userfriendly")] [String] $Department, # id of the ticket state. Name is give in "linked entities" - "ticketStates" [Parameter(ParameterSetName="ApiNative")] [int] $StatusId, # The name of the ticket status [Parameter(ParameterSetName="Userfriendly")] [String] $Status, # id of the ticket type. Name is give in "linked entities" - "ticketTypes" [Parameter(ParameterSetName="ApiNative")] [int] $TypeId, # The name of the ticket type [Parameter(ParameterSetName="Userfriendly")] [String] $Type, # if ticket is assigned to device / employee, linktype is given here [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [Alias('LinkTypeId')] [int] $AssignmentId, # If ticket has a deadline, the date is given here [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [Alias('deadlineDate')] [datetime] $Deadline, # if true, this ticket is a "repair ticket" [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [Alias('repair')] [bool] $IsRepair = $false, # if ticket has a due date, the timestamp is given here [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [datetime] $DueDate, # Determines the "attention" flag state of a ticket [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [ValidateSet("NO", "YES", "RESUBMISSION", "MAIL")] [string] $Attention = "NO", # If the ticket has an installation fee, this value is true [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [ValidateSet("NO", "YES", "NO_PROJECT_INSTALLATION_FEE")] [string] $InstallationFee = "NO", # Sets the installation fee drive mode. If it is set to NONE then the system config parameter "leistung.ip.fahrzeit_berechnen" will be used. If the company from the ticket has an installation fee drive mode set then that will be used instead of the system config parameter. [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [ValidateSet("NONE", "DRIVE_INCLUDED", "DRIVE_EXCLUDED")] [string] $InstallationFeeDriveMode = "NONE", #Amount for the installation fee [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [int] $InstallationFeeAmount, # If true, the ticket shall be billed separately [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [bool] $SeparateBilling = $false, # if the ticket has a service cap, here the amount is given [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [int] $ServiceCapAmount, # linkId of the relationship (if ticket has a relation) [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [int] $RelationshipLinkId, # linkTypeId of the relationship (if ticket has a relation) [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [int] $RelationshipLinkTypeId, # if the ticket as a resubmission date set, this is given here [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [datetime] $ResubmissionDate, # If a resubmission text is set, this text is returned here [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [string] $ResubmissionText, # Number of estimated minutes which is planned for the ticket [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [int] $EstimatedMinutes, # Determines wether the ticket is assigned to a local ticket admin or not # NONE: "normal" ticket # LOCAL_ADMIN: ticket is assigned to a local ticket admin # TECHNICIAN: local ticket admin has forwarded the ticket to a technician [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [ValidateSet("NONE", "LOCAL_ADMIN", "TECHNICIAN")] [string] $LocalTicketAdminFlag = "NONE", # if the ticket is assigned to a local ticket admin, this represents the employee (local ticket admin) who is assigned for this ticket [Parameter(ParameterSetName="ApiNative")] [int] $LocalTicketAdminEmployeeId, # if the ticket is assigned to a local ticket admin, this represents the name of the employee (local ticket admin) who is assigned for this ticket [Parameter(ParameterSetName="Userfriendly")] [ValidateNotNullOrEmpty] [string] $EmployeeTicketAdmin, # Sets the order number [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [string] $OrderNumber, # If the ticket has a reminder set, the timestamp is returned here [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [datetime] $Reminder, # When persisting a ticket, you can also send a list of tag assignments which will be assigned to the ticket [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [string[]] $Tags, [Parameter(ParameterSetName="ApiNative")] [Parameter(ParameterSetName="Userfriendly")] [TANSS.Connection] $Token ) begin { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('New-TANSSTicket', [System.Management.Automation.CommandTypes]::Function) $scriptCmd = {& $wrappedCmd -IsProject $true @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline() $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try { $steppablePipeline.Process($_) } catch { throw } } end { try { $steppablePipeline.End() } catch { throw } } } function New-TANSSProjectPhase { <# .Synopsis Add a project phase into a project .DESCRIPTION Add a project phase into a project .PARAMETER ProjectID The ID of the poject where to create the specified phase .PARAMETER Project TANSS.Project object where to create the specified phase .PARAMETER Name The name of the phase .PARAMETER Rank The rank of the phase, when there are other phases in the project .PARAMETER StartDate The (planned) starting date of the phase .PARAMETER EndDate The (planned) ending date of the phase .PARAMETER RequiredPrePhasesComplete Indicates that all tickets in the previous phase has to be finished to open this phase Default: $false .PARAMETER BillingType Set the behavour how to bill tickets. Values are available via TabCompletion. Possible are: "InstantBillSupportActivities" = Support activities of sub-tickets are immediately billable "BillClosedTicketOnly" = Only Support activities of completed sub-tickets are billable "BillOnlyWhenAllTicketsClosed" = Phase may only be billed when all sub-tickets have been completed Default: "InstantBillSupportActivities" .PARAMETER ClearanceMode Set the behavour how support activities in tickets for the phase are treated. Values are available via TabCompletion. Possible are: "Default" = Setting from TANSS application base preferences is used "ReleaseSupportOfUnresolvedTickets" = support activities in open tickets can be released "LockSupportsOfUnresolvedTickets" = support activities in open tickets can not be released Default: "Default" .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> New-TANSSProjectPhase -Project $project -Name "Phase X" Creates "Phase X" within the project in the variable $project .EXAMPLE PS C:\> $project | New-TANSSProjectPhase -Name "Phase X" Creates "Phase X" within the project in the variable $project .EXAMPLE PS C:\> New-TANSSProjectPhase -ProjectId $project.id -Name "Phase X", "Phase Y", "Phase Z" Creates 3 phases ("Phase X", "Phase Y", "Phase Z") within the project in the variable $project .EXAMPLE PS C:\> "Phase X", "Phase Y", "Phase Z" | New-TANSSProjectPhase -ProjectId $project.id Creates 3 phases ("Phase X", "Phase Y", "Phase Z") within the project in the variable $project .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "ByProject", SupportsShouldProcess = $true, PositionalBinding = $true, ConfirmImpact = 'Medium' )] [OutputType([TANSS.ProjectPhase])] Param( [Parameter( ParameterSetName = "ByPhaseName", Mandatory = $true )] [Alias("Id")] [int] $ProjectID, [Parameter( ParameterSetName = "ByProject", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [TANSS.Project] $Project, [Parameter( ValueFromPipeline = $true, Mandatory = $true )] [string[]] $Name, [int] $Rank, [datetime] $StartDate, [datetime] $EndDate, [bool] $RequiredPrePhasesComplete = $false, [ValidateSet("InstantBillSupportActivities", "BillClosedTicketOnly", "BillOnlyWhenAllTicketsClosed")] [string] $BillingType = "InstantBillSupportActivities", [ValidateSet("Default", "ReleaseSupportOfUnresolvedTickets", "LockSupportsOfUnresolvedTickets")] [String] $ClearanceMode = "Default", [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning $apiPath = Format-ApiPath -Path "api/v1/projects/phases" # Translate BillingType into api value if ($BillingType) { switch ($BillingType) { "InstantBillSupportActivities" { $_billingType = "DEFAULT" } "BillClosedTicketOnly" { $_billingType = "TICKET_MUST_BE_CLOSED" } "BillOnlyWhenAllTicketsClosed" { $_billingType = "ALL_TICKETS_MUST_BE_CLOSED" } Default { Stop-PSFFunction -Message "Unhandeled Value in BillingType. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } } # Translate BillingType into api value if ($ClearanceMode) { switch ($ClearanceMode) { "Default" { $_clearanceMode = "DEFAULT" } "ReleaseSupportOfUnresolvedTickets" { $_clearanceMode = "DONT_CLEAR_SUPPORTS" } "LockSupportsOfUnresolvedTickets" { $_clearanceMode = "MAY_CLEAR_SUPPORTS" } Default { Stop-PSFFunction -Message "Unhandeled Value in ClearanceMode. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } } } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" if ($parameterSetName -like "ByProject") { $ProjectID = $Project.id Write-PSFMessage -Level System -Message "Identified ID '$($ProjectID) for project $($Project.Title)" -Tag "ProjectPhase", "CollectInputObjects" } # Create body object for api call foreach ($nameItem in $Name) { Write-PSFMessage -Level Verbose -Message "Working new phase '$($nameItem)' for project ID $($ProjectID)" -Tag "ProjectPhase", "New" $phaseToCreate = [ordered]@{ projectId = $ProjectID name = $nameItem closedPrePhasesRequired = $RequiredPrePhasesComplete billingType = $_billingType clearanceMode = $_clearanceMode } if ($Rank) { $phaseToCreate.add("rank", $Rank) } if ($StartDate) { $_startDate = [int32][double]::Parse((Get-Date -Date $StartDate.ToUniversalTime() -UFormat %s)) $phaseToCreate.add("startDate", $_startDate) } if ($EndDate) { $_endDate = [int32][double]::Parse((Get-Date -Date $EndDate.ToUniversalTime() -UFormat %s)) $phaseToCreate.add("endDate", $_endDate) } # Create phase if ($pscmdlet.ShouldProcess("phase '$($nameItem)' in project id $($ProjectID)", "New")) { Write-PSFMessage -Level Verbose -Message "New phase '$($nameItem)' in project id $($ProjectID)" -Tag "ProjectPhase", "New" # invoke api call $response = Invoke-TANSSRequest -Type POST -ApiPath $apiPath -Body $phaseToCreate -Token $Token Write-PSFMessage -Level Verbose -Message "$($response.meta.text): $($response.content.name) (Rank: $($response.content.rank), Id: $($response.content.id))" -Tag "ProjectPhase", "New", "CreatedSuccessfully" # create output [TANSS.ProjectPhase]@{ BaseObject = $response.content Id = $response.content.id } # cache Lookup refresh [TANSS.Lookup]::Phases[$response.content.id] = $response.content.name } } } end {} } function Remove-TANSSProject { <# .Synopsis Remove-TANSSProject .DESCRIPTION Delete a project in TANSS .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> Remove-TANSSProject -ID 100 Remove project with ticketID 100 from TANSS .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidMultipleTypeAttributes", "")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "")] [CmdletBinding( DefaultParameterSetName = 'ById', SupportsShouldProcess = $true, PositionalBinding = $true, ConfirmImpact = 'High' )] [OutputType([TANSS.Ticket])] param ( # TANSS Ticket object to remove [Parameter( ParameterSetName = "ByInputObject", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [TANSS.Ticket[]] $InputObject, # Id of the ticket to remove [Parameter( ParameterSetName = "ById", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Alias("ProjectId", "Project","TicketId", "Ticket")] [int[]] $Id, [TANSS.Connection] $Token ) begin { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Remove-TANSSTicket', [System.Management.Automation.CommandTypes]::Function) $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline() $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try { $steppablePipeline.Process($_) } catch { throw } } end { try { $steppablePipeline.End() } catch { throw } } } function Remove-TANSSProjectPhase { <# .Synopsis Remove-TANSSProjectPhase .DESCRIPTION Removes a project phase from TANSS .PARAMETER InputObject TANSS.ProjectPhase object to remove .PARAMETER Force Process the removal quietly. .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> $phase | Remove-TANSSProjectPhase Remove project phase in variable $phase from TANSS .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( SupportsShouldProcess = $true, PositionalBinding = $true, ConfirmImpact = 'High' )] Param( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Alias("Phase", "ProjectPhase")] [TANSS.ProjectPhase[]] $InputObject, [switch] $Force, [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning } process { $processRemoval = $false foreach ($projectPhase in $InputObject) { # Check on Force parameter, otherwise process shouldprocess if ($Force) { $processRemoval = $true } else { if ($pscmdlet.ShouldProcess("ProjectPhase Id $($projectPhase.Id) '$($projectPhase.Name)' from project '$($projectPhase.Project)' (Id $($projectPhase.ProjectId))", "Remove")) { $processRemoval = $true } } if ($processRemoval) { Write-PSFMessage -Level Verbose -Message "Remove ProjectPhase Id $($projectPhase.Id) '$($projectPhase.Name)' from project '$($projectPhase.Project)' (Id $($projectPhase.ProjectId))" -Tag "ProjectPhase", "Remove" # Remove projectPhase $apiPath = Format-ApiPath -Path "api/v1/projects/phases/$($projectPhase.Id)" Invoke-TANSSRequest -Type DELETE -ApiPath $apiPath -Token $Token -Confirm:$false -ErrorVariable "invokeError" # cache Lookup refresh [TANSS.Lookup]::Phases.Remove($($projectPhase.Id)) } } } end {} } function Set-TANSSProjectPhase { <# .Synopsis Modify a phase inside project .DESCRIPTION Modify a phase inside project .PARAMETER Phase TANSS.ProjectPhase object to set .PARAMETER PhaseID The ID of the poject phase to set .PARAMETER Project TANSS.Project where to modify a phase .PARAMETER PhaseName The name of the phase to modify .PARAMETER Name The (new) name for the phase to set .PARAMETER Rank The order of the phase in relation to other project phases .PARAMETER StartDate The (planned) starting date of the phase .PARAMETER EndDate The (planned) ending date of the phase .PARAMETER RequiredPrePhasesComplete Indicates that all tickets in the previous phase has to be finished to open this phase Default: $false .PARAMETER BillingType Set the behavour how to bill tickets. Values are available via TabCompletion. Possible are: "InstantBillSupportActivities" = Support activities of sub-tickets are immediately billable "BillClosedTicketOnly" = Only Support activities of completed sub-tickets are billable "BillOnlyWhenAllTicketsClosed" = Phase may only be billed when all sub-tickets have been completed Default: "InstantBillSupportActivities" .PARAMETER ClearanceMode Set the behavour how support activities in tickets for the phase are treated. Values are available via TabCompletion. Possible are: "Default" = Setting from TANSS application base preferences is used "ReleaseSupportOfUnresolvedTickets" = support activities in open tickets can be released "LockSupportsOfUnresolvedTickets" = support activities in open tickets can not be released Default: "Default" .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .PARAMETER PassThru Outputs the result to the console .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> Set-TANSSProjectPhase -PhaseID $phase.Id -Name "NewPhaseName X" Rename the phase from variable $phase to "NewPhaseName X" $Phase has to be filled with a .EXAMPLE PS C:\> $phase | Set-TANSSProjectPhase -Name "NewPhaseName X" Rename the phase from variable $phase to "NewPhaseName X" $Phase has to be filled with a .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "ByPhase", SupportsShouldProcess = $true, PositionalBinding = $true, ConfirmImpact = 'Medium' )] [OutputType([TANSS.ProjectPhase])] Param( [Parameter( ParameterSetName = "ByPhaseId", ValueFromPipeline = $true, Mandatory = $true )] [Alias("Id")] [int] $PhaseID, [Parameter( ParameterSetName = "ByPhase", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [TANSS.ProjectPhase] $Phase, [Parameter( ParameterSetName = "ByProject", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [TANSS.Ticket] $Project, [Parameter( ParameterSetName = "ByProject", Mandatory = $true )] [string] $PhaseName, [ValidateNotNullOrEmpty()] [Alias("NewName")] [string] $Name, [ValidateNotNullOrEmpty()] [int] $Rank, [ValidateNotNullOrEmpty()] [datetime] $StartDate, [ValidateNotNullOrEmpty()] [datetime] $EndDate, [bool] $RequiredPrePhasesComplete, [ValidateSet("InstantBillSupportActivities", "BillClosedTicketOnly", "BillOnlyWhenAllTicketsClosed")] [string] $BillingType, [ValidateSet("Default", "ReleaseSupportOfUnresolvedTickets", "LockSupportsOfUnresolvedTickets")] [String] $ClearanceMode, [switch] $PassThru, [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning # Translate BillingType into api value if ($BillingType) { switch ($BillingType) { "InstantBillSupportActivities" { $_billingType = "DEFAULT" } "BillClosedTicketOnly" { $_billingType = "TICKET_MUST_BE_CLOSED" } "BillOnlyWhenAllTicketsClosed" { $_billingType = "ALL_TICKETS_MUST_BE_CLOSED" } Default { Stop-PSFFunction -Message "Unhandeled Value in BillingType. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } } # Translate BillingType into api value if ($ClearanceMode) { switch ($ClearanceMode) { "Default" { $_clearanceMode = "DEFAULT" } "ReleaseSupportOfUnresolvedTickets" { $_clearanceMode = "DONT_CLEAR_SUPPORTS" } "LockSupportsOfUnresolvedTickets" { $_clearanceMode = "MAY_CLEAR_SUPPORTS" } Default { Stop-PSFFunction -Message "Unhandeled Value in ClearanceMode. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } } } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" # ParameterSet handling if ($parameterSetName -like "ByPhase") { $PhaseID = $Phase.id $projectID = $Phase.ProjectId Write-PSFMessage -Level System -Message "Identified ID '$($PhaseID) for project phase $($Phase.Name) within project '$($Phase.Project)' (Id: $($projectID))" -Tag "ProjectPhase", "CollectInputObjects" } if ($parameterSetName -like "ByProject") { $projectID = $Project.Id $Phase = Get-TANSSProjectPhase -ProjectID $Project.Id -Token $Token | Where-Object Name -Like $PhaseName if (-not $Phase) { Write-PSFMessage -Level Error -Message "Phase '$($PhaseName)' not found in project '$($Project.Title)' (Id: $($Project.Id))" -Tag "ProjectPhase", "CollectInputObjects" -Data @{"Project" = $Project } continue } $PhaseID = $Phase.Id Write-PSFMessage -Level System -Message "Identified ID '$($PhaseID) for project phase $($Phase.Name) within project $($Phase.Project)" -Tag "ProjectPhase", "CollectInputObjects" } # Create body object for api call Write-PSFMessage -Level Verbose -Message "Going to set phase ID $($PhaseID)" -Tag "ProjectPhase", "Set" $paramFormatApiPath = @{ "Path" = "api/v1/projects/phases/$($PhaseID)" } $paramFormatApiPathQueryParameter = @{} $phaseToSet = [ordered]@{ Id = $PhaseID #projectId = $ProjectID } if ($ProjectID) { $phaseToSet.add("projectId", $ProjectID) } if ($Name) { $phaseToSet.add("name", $Name) } if ($RequiredPrePhasesComplete) { $phaseToSet.add("closedPrePhasesRequired", $RequiredPrePhasesComplete) } if ($BillingType) { $phaseToSet.add("billingType", $_billingType) } if ($ClearanceMode) { $phaseToSet.add("clearanceMode", $_clearanceMode) } if ($Rank) { $phaseToSet.add("rank", $Rank) } if ($StartDate) { $_startDate = [int32][double]::Parse((Get-Date -Date $StartDate.ToUniversalTime() -UFormat %s)) $phaseToSet.add("startDate", $_startDate) $paramFormatApiPathQueryParameter["adjustStart"] = $true $paramFormatApiPathQueryParameter["adjustEnd"] = $true } if ($EndDate) { $_endDate = [int32][double]::Parse((Get-Date -Date $EndDate.ToUniversalTime() -UFormat %s)) $phaseToSet.add("endDate", $_endDate) $paramFormatApiPathQueryParameter["adjustStart"] = $true $paramFormatApiPathQueryParameter["adjustEnd"] = $true } if ($paramFormatApiPathQueryParameter["adjustStart"] -or $paramFormatApiPathQueryParameter["adjustEnd"]) { $paramFormatApiPath.Add("QueryParameter", $paramFormatApiPathQueryParameter) } $apiPath = Format-ApiPath @paramFormatApiPath # Create phase $response = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $phaseToSet -Token $Token Write-PSFMessage -Level Verbose -Message "$($response.meta.text): $($response.content.adjustedPhases.name) (Rank: $($response.content.adjustedPhases.rank), Id: $($response.content.adjustedPhases.id))" -Tag "ProjectPhase", "Set", "Modify" # create output i($PassThru) { foreach($_phase in $response.content.adjustedPhases) { # object [TANSS.ProjectPhase]@{ BaseObject = $_phase Id = $_phase.id } # cache Lookup refresh [TANSS.Lookup]::Phases[$_phase.id] = $_phase.name } } } end { $null = Get-TANSSProject -Token $Token } } function Get-TANSSTicket { <# .Synopsis Get-TANSSTicket .DESCRIPTION Gat a ticket from TANSS service .PARAMETER Id The ticket Id (one or more) to query .PARAMETER CompanyId Get all tickets of company Id .PARAMETER MyTickets Get all tickets assigned to the authenticated account .PARAMETER NotAssigned Get all tickets not assigned to somebody .PARAMETER AllTechnician Get all tickets assigned to somebody .PARAMETER RepairTickets Get tickets marked as repair tickets .PARAMETER NotIdentified Get all unidentified tickets .PARAMETER Project Get tickets marked as a project .PARAMETER LocalTicketAdmin Get all tickets specified for a ticket admin .PARAMETER TicketWithTechnicianRole Get all tickets with a technician role .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .EXAMPLE PS C:\> Get-TANSSTicket Get all tickets assinged to the authenticated user .EXAMPLE PS C:\> Get-TANSSTicket -Id 100 Get ticket with Id 100 .EXAMPLE PS C:\> Get-TANSSTicket -CompanyId 12345 Get all tickets of company Id 12345 .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")] [CmdletBinding( DefaultParameterSetName = 'MyTickets', SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] [OutputType([TANSS.Ticket], [TANSS.Project])] Param( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "TicketId" )] [int[]] $Id, [Parameter( Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = "CompanyId" )] [int[]] $CompanyId, [Parameter( Mandatory = $false, ParameterSetName = "MyTickets" )] [Alias("Own", "OwnTickets", "MyOwn", "NyOwnTickets")] [switch] $MyTickets, [Parameter( Mandatory = $true, ParameterSetName = "NotAssigned" )] [Alias("General", "GeneralTickets")] [switch] $NotAssigned, [Parameter( Mandatory = $true, ParameterSetName = "AllTechnician" )] [Alias("TicketsAllTechnicians", "Assigned", "AssignedTickets")] [switch] $AllTechnician, [Parameter( Mandatory = $true, ParameterSetName = "RepairTickets" )] [switch] $RepairTickets, [Parameter( Mandatory = $true, ParameterSetName = "NotIdentified" )] [switch] $NotIdentified, [Parameter( Mandatory = $true, ParameterSetName = "Projects" )] [switch] [Alias("AllProjects")] $Project, [Parameter( Mandatory = $true, ParameterSetName = "LocalTicketAdmin" )] [Alias("AssignedToTicketAdmin")] [switch] $LocalTicketAdmin, [Parameter( Mandatory = $true, ParameterSetName = "TicketWithTechnicianRole" )] [switch] $TicketWithTechnicianRole, [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" $response = @() # Construct query switch ($parameterSetName) { "TicketId" { # in case of "Query by Id" -> Do the query now $response += foreach ($ticketId in $Id) { Invoke-TANSSRequest -Type GET -ApiPath "api/v1/tickets/$($ticketId)" -Token $Token } } "CompanyId" { # in case of "Query by CompanyId" -> Do the query now $response += foreach ($_companyId in $CompanyId) { Invoke-TANSSRequest -Type GET -ApiPath "api/v1/tickets/company/$($_companyId)" -Token $Token } } "MyTickets" { $apiPath = Format-ApiPath -Path "api/v1/tickets/own" } "NotAssigned" { $apiPath = Format-ApiPath -Path "api/v1/tickets/general" } "AllTechnician" { $apiPath = Format-ApiPath -Path "api/v1/tickets/technician" } "RepairTickets" { $apiPath = Format-ApiPath -Path "api/v1/tickets/repair" } "NotIdentified" { $apiPath = Format-ApiPath -Path "api/v1/tickets/notIdentified" } "Projects" { $apiPath = Format-ApiPath -Path "api/v1/tickets/projects" } "LocalTicketAdmin" { $apiPath = Format-ApiPath -Path "api/v1/tickets/localAdminOverview" } "TicketWithTechnicianRole" { $apiPath = Format-ApiPath -Path "api/v1/tickets/withRole" } Default { Stop-PSFFunction -Message "Unhandeled ParameterSetName. Developers mistake." -EnableException $true -Cmdlet $pscmdlet } } # Do the constructed query, as long, as variable apiPath has a value if ($apiPath) { $response += Invoke-TANSSRequest -Type GET -ApiPath $apiPath -Token $Token } if ($response) { Write-PSFMessage -Level Verbose -Message "Found $(($response.content).count) tickets" # Push meta to cache runspace foreach ($responseItem in $response) { Push-DataToCacheRunspace -MetaData $responseItem.meta # Output result foreach($ticket in $responseItem.content) { if($parameterSetName -like "Projects") { [TANSS.Project]@{ BaseObject = $ticket Id = $ticket.id } } else { [TANSS.Ticket]@{ BaseObject = $ticket Id = $ticket.id } } } } } else { Write-PSFMessage -Level Warning -Message "No tickets found." -Tag "Ticket" } } end { } } function Get-TANSSTicketActivity { <# .Synopsis Get-TANSSTicketActivity .DESCRIPTION Retreive support activities within a ticket .PARAMETER TicketID The ID of the ticket to receive activities of .PARAMETER Ticket TANSS.Ticket object to receive activities of .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .EXAMPLE PS C:\> Get-TANSSTicketActivity -TicketID 555 Get all support activities from ticket 555 .EXAMPLE PS C:\> $tickets | Get-TANSSTicketActivity Get all support activities from all tickets from variable $tickets .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "ByTicketId", SupportsShouldProcess = $false, PositionalBinding = $true, ConfirmImpact = 'Low' )] Param( [Parameter( ParameterSetName = "ByTicketId", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [int[]] $TicketID, [Parameter( ParameterSetName = "ByTicket", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [TANSS.Ticket[]] $Ticket, [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" if ($parameterSetName -like "ByTicket") { $inputobjectTicketCount = ([array]$Ticket).Count Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectTicketCount) ticket$(if($inputobjectTicketCount -gt 1){'s'})" -Tag "TicketComment", "CollectInputObjects" [array]$TicketID = $Ticket.id } foreach ($ticketIdItem in $TicketID) { Write-PSFMessage -Level Verbose -Message "Working on ticket ID $($ticketIdItem)" -Tag "TicketComment", "Query" Get-TANSSTicketContent -TicketID $ticketIdItem -Type "Activity" -Token $Token | Select-Object -ExpandProperty Object } } end { } } function Get-TANSSTicketComment { <# .Synopsis Get-TANSSTicketComment .DESCRIPTION Retreive the ticket comments .PARAMETER TicketID The ID of the ticket to receive comments of .PARAMETER Ticket TANSS.Ticket object to receive comments of .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .EXAMPLE PS C:\> Get-TANSSTicketComment -TicketID 555 Get all comments from ticket 555 .EXAMPLE PS C:\> $tickets | Get-TANSSTicketComment Get all comments from all tickets from variable $tickets .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "ByTicketId", SupportsShouldProcess = $false, PositionalBinding = $true, ConfirmImpact = 'Low' )] Param( [Parameter( ParameterSetName = "ByTicketId", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [int[]] $TicketID, [Parameter( ParameterSetName = "ByTicket", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [TANSS.Ticket[]] $Ticket, [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" if ($parameterSetName -like "ByTicket") { $inputobjectTicketCount = ([array]$Ticket).Count Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectTicketCount) ticket$(if($inputobjectTicketCount -gt 1){'s'})" -Tag "TicketComment", "CollectInputObjects" [array]$TicketID = $Ticket.id } foreach ($ticketIdItem in $TicketID) { Write-PSFMessage -Level Verbose -Message "Working on ticket ID $($ticketIdItem)" -Tag "TicketComment", "Query" Get-TANSSTicketContent -TicketID $ticketIdItem -Type "Comment" -Token $Token | Select-Object -ExpandProperty Object } } end { } } function Get-TANSSTicketContent { <# .Synopsis Get-TANSSTicketContent .DESCRIPTION Retreive the various entries from a ticket. Entries can be a comment, activity, mail, document, image .PARAMETER TicketID The ID of the ticket to receive comments of .PARAMETER Ticket TANSS.Ticket object to receive comments of .PARAMETER Type Specifies the type of content you want to get Available types: "All" = everthing within the ticket "Comment" = only comments within the ticket "Activity" = only activites within the ticket "Mail" = = only mails within the ticket "Document" = uploaded documents within the ticket "Image" = uploaded images within the ticket .PARAMETER Token The TANSS.Connection token to access api If not specified, the registered default token from within the module is going to be used .EXAMPLE PS C:\> Get-TANSSTicketContent -TicketID 555 Get everthing out of ticket 555 .EXAMPLE PS C:\> $tickets | Get-TANSSTicketContent -Type "Mail" Get only malis out of all tickets from variable $tickets .NOTES Author: Andreas Bellstedt .LINK https://github.com/AndiBellstedt/PSTANSS #> [CmdletBinding( DefaultParameterSetName = "ByTicketId", SupportsShouldProcess = $false, PositionalBinding = $true, ConfirmImpact = 'Low' )] Param( [Parameter( ParameterSetName = "ByTicketId", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [int[]] $TicketID, [Parameter( ParameterSetName = "ByTicket", ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true )] [TANSS.Ticket[]] $Ticket, [ValidateNotNullOrEmpty()] [ValidateSet("All", "Comment", "Activity", "Mail", "Document", "Image")] [string[]] $Type = "All", [TANSS.Connection] $Token ) begin { if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken } Assert-CacheRunspaceRunning } process { $parameterSetName = $pscmdlet.ParameterSetName Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" if ($parameterSetName -like "ByTicket") { $inputobjectTicketCount = ([array]$Ticket).Count Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectTicketCount) ticket$(if($inputobjectTicketCount -gt 1){'s'})" -Tag "TicketContent", "CollectInputObjects" [array]$TicketID = $Ticket.id } foreach ($ticketIdItem in $TicketID) { Write-PSFMessage -Level Verbose -Message "Working on ticket ID $($ticketIdItem)" -Tag "TicketContent", "Query" $content = @() # Get documents, if included in Type filter if ( ($Type | Where-Object { $_ -in @("All", "Document") }) ) { Write-PSFMessage -Level Verbose -Message "Getting documents from ticket $($ticketIdItem)" -Tag "TicketContent", "Query", "QueryDocuments" $apiPath = Format-ApiPath -Path "api/v1/tickets/$ticketIdItem/documents" $response = Invoke-TANSSRequest -Type GET -ApiPath $apiPath -Token $Token Push-DataToCacheRunspace -MetaData $response.meta if ($response.content) { Write-PSFMessage -Level Verbose -Message "Found $($response.content.count) documents" -Tag "TicketContent", "Query", "QueryDocuments" $content += foreach ($document in $response.content) { # create output objects, but first query download uri # build api path $apiPath = Format-ApiPath -Path "api/v1/tickets/$ticketIdItem/documents/$($document.id)" |