PSIdoitNG.psm1
#Region '.\Private\Convert-PropertyToArray.ps1' -1 function Convert-PropertyToArray { <# .SYNOPSIS Convert-PropertyToArray .DESCRIPTION This function converts a PowerShell object with properties into an array of objects. .PARAMETER InputObject The input object to be converted. Details see example. .OUTPUTS Returns an array of objects, each containing the property name and value. .EXAMPLE Convert-PropertyToArray -InputObject $inputObject [PSCustomObject] @{ C__OBJTYPE__SERVICE = 'System Service'; C__OBJTYPE__APPLICATION = 'Application'; ... } into an array of the following definition [PSCustomObject] @{ Name = 'C__OBJTYPE__SERVICE'; Value = 'System Service'; } .... and so on #> [CmdletBinding()] param ( [PSCustomObject]$InputObject ) $result = foreach ($property in $InputObject.PSObject.Properties) { [PSCustomObject]@{ Name = $property.Name Value = $property.Value } } return $result } #EndRegion '.\Private\Convert-PropertyToArray.ps1' 41 #Region '.\Private\ConvertFrom-CustomCategory.ps1' -1 function ConvertFrom-CustomCategory { <# .SYNOPSIS Converts a custom category object into a more user-friendly format. .DESCRIPTION ConvertFrom-CustomCategory gives a more user-friendly representation of a custom category object. It retrieves category information and formats the properties into a custom object with more readable property names. The PSCustom property names are derived from the category information titles, cleaning up any non-alphanumeric characters. .PARAMETER InputObject The input object to convert. .PARAMETER CategoryObject The category object containing the category information to use for conversion. .EXAMPLE $inputObject = Get-IdoitObject -ObjId 12345 $objTypeCatList = Get-IdoitObjectTypeCategory -ObjId $Id -ErrorAction Stop | Where-Object { $_.const -eq 'C__CATG__CUSTOM_FIELDS_COMPONENT' } $newObject = ConvertFrom-CustomCategory -InputObject $inputObject -CategoryObject $objTypeCatList This would be an explicit example of how to use the ConvertFrom-CustomCategory function. .EXAMPLE $inputObject = Get-IdoitObject -ObjId 12345 $newObject = Get-IdoitCategory -ObjId 12345 -Category 'C__CATG__CUSTOM_FIELDS_COMPONENT' This is an example, where ConvertFrom-CustomCategory is used implicitly by Get-IdoitCategory. .NOTES The converted names get all non-alphanumeric characters replaced with an underscore. If the category information is not available, the original property names from the input object are retained. Category properties that are only for UI purposes (like 'hr' or 'html') are skipped. To make Set-IdoitCategory work easier, the original property names are stored in a special property called 'psid_custom'. The 'psid_custom' property is a hashtable that maps the new property names to the original property names. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [PSObject]$InputObject, [Parameter(Mandatory = $true)] [PSObject] $CategoryObject ) begin { } process { $catInfo = Get-IdoitCategoryInfo -Category $CategoryObject.const -ErrorAction Stop $result = [ordered]@{ objId = $InputObject.objId id = $InputObject.id psid_custom = @{} # indicates that this is a modified custom category object, stores the names } foreach ($property in $catInfo.PSObject.Properties) { if ($catInfo.PSObject.Properties[$property.Name]) { $newPropName = $property.Value.title -replace '[^a-zA-Z0-9]', '_' # If the property seems to be just for UI purposes, we will skip it if ($property.Value.ui.type -in 'hr','html') { continue } if ('' -eq $newPropName) { continue # this seems to happen for the category name? } # $InputObject | Add-Member -MemberType NoteProperty -Name $property.Name -Value $catInfo.PSObject.Properties[$property.Name].Value -Force $result.Add($newPropName, $InputObject.($property.name).Title) $result.psid_custom.Add($newPropName, $property.Name) # store the original name for later reference } else { # If the property is not defined in the category info, we will add it as a NoteProperty with value from InputObject $result.Add($property.Name, $InputObject[$property.Name]) $result.psid_custom.Add($property.Name, $property.Name) } } [PSCustomObject] $result } end { } } #EndRegion '.\Private\ConvertFrom-CustomCategory.ps1' 85 #Region '.\Private\ModuleStartup.ps1' -1 function ModuleStartup { <# .SYNOPSIS Initializes the module by setting up the script parameters. .DESCRIPTION This function is called when the module is loaded. It sets up the script parameters and initializes the module. .EXAMPLE ModuleStartup This will initialize the module and set up the script parameters. .NOTES The function will be inserted into the .psm1 as any other function. At the end of the file, the function will be called to initialize the module. So the code will be executed when the module is loaded. #> [CmdletBinding()] param() $Script:IdoItParams = @{} $Script:IdoItParams['Connection'] = @{ Uri = $null Username = $null Password = $null ApiKey = $null SessionId = $null } } ModuleStartup #EndRegion '.\Private\ModuleStartup.ps1' 31 #Region '.\Private\NewDynamicParameter.ps1' -1 function NewDynamicParameter { <# .SYNOPSIS Create a new dynamic parameter. .DESCRIPTION This function creates a new dynamic parameter with the specified name, parameter set name, and attributes. .PARAMETER Name The name of the dynamic parameter. .PARAMETER ParametersetName The name of the parameter set to which the dynamic parameter belongs. .PARAMETER Mandatory Indicates whether the dynamic parameter is mandatory. .PARAMETER ParameterType The type of the dynamic parameter. .PARAMETER ValidateSet An array of valid values for the dynamic parameter. If not provided, the parameter will not have a validation set. .OUTPUTS Returns a RuntimeDefinedParameter object representing the dynamic parameter. .EXAMPLE $dynParam = NewDynamicParameter -Name 'MyDynamicParam' -ParametersetName 'MyParameterSet' -Mandatory $true -ParameterType 'System.String' -ValidateSet 'Value1', 'Value2' This example creates a new dynamic parameter named 'MyDynamicParam' with the specified attributes. .NOTES #> [CmdletBinding()] [OutputType([System.Management.Automation.RuntimeDefinedParameter])] param ( [Parameter(Mandatory = $true)] [string] $Name, [string] $ParametersetName = '', [boolean] $Mandatory = $false, [string] $ParameterType = 'System.String', [string[]] $ValidateSet = $null ) $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attribute = New-Object System.Management.Automation.ParameterAttribute $attribute.Mandatory = $Mandatory $attribute.ParameterSetName = $ParametersetName $attributeCollection.Add($attribute) if ($null -ne $ValidateSet) { $attribute = New-Object System.Management.Automation.ValidateSetAttribute($ValidateSet) $attributeCollection.Add($attribute) } $dynParam = New-Object System.Management.Automation.RuntimeDefinedParameter($Name, $ParameterType, $attributeCollection) Write-Output $dynParam } #EndRegion '.\Private\NewDynamicParameter.ps1' 64 #Region '.\Private\ValidateProperties.ps1' -1 function ValidateProperties { <# .SYNOPSIS Validates properties for a given category. .DESCRIPTION This function checks if the provided properties are valid for the specified category in i-doit. It validates the properties against the category's information and returns a hashtable of valid properties. If any property is invalid, it issues a warning and removes that property from the returned hashtable. Reason to validate locally? Field type dialog_plus would create a new entry if was not defined in the category. To avoid accidental creation of new entries because of a typo, we validate the properties first. .PARAMETER Category The category to validate properties against. This should be a valid i-doit category identifier. .PARAMETER Properties A hashtable of properties to validate. The keys should be property names and the values should be the corresponding values. .PARAMETER ExcludeProperties An array of property names to exclude from validation. These properties will be included in the returned hashtable without validation. .EXAMPLE $properties = @{ 'f_popup_c_17289168067044910' = 'Job / Schnittstelle' # custom field; select one from a list 'f_popup_c_17289128195752470' = @('SQL Server','Biztalk') # custom field; multiselect } $validProperties = ValidateProperties -Category 'C__CATG__CUSTOM_FIELDS_KOMPONENTE' -Properties $properties This example validates the properties for the category 'C__CATG__CUSTOM_FIELDS_KOMPONENTE' and returns a hashtable of valid properties. .NOTES #> [CmdletBinding()] [OutputType([Hashtable])] param ( [Parameter(Mandatory = $true)] [string] $Category, [Parameter(Mandatory = $true)] [hashtable] $Properties, [string[]] $ExcludeProperties = @( 'objID', 'id', 'psid_custom', 'entry', 'Category' ) ) $atLeastOneFailed = $false $validProperties = @{} $catInfo = Get-IdoitCategoryInfo -Category $Category -ErrorAction Stop $keysToCheck = @($Properties.Keys.GetEnumerator()) foreach ($key in $keysToCheck) { Write-Verbose "Validating property '$key' for category '$($Category)'" if ($ExcludeProperties -contains $key) { $validProperties[$key] = $Properties[$key] Write-Verbose "Excluding property '$key' from validation for category '$($Category)'" continue } if ($null -eq $catInfo.$key) { Write-Warning "Property '$key' is not a valid property for category '$($Category)'. Skipping it." continue } if ($catInfo.$key.data.readonly) { Write-Verbose "Property '$key' is read-only for category '$($Category)'. Skipping it." $Properties.Remove($key) continue } switch -Regex ($catInfo.$key.info.type) { 'dialog' { # type "dialog", "dialog_plus" if ($null -eq $Properties[$key]) { continue } $thisDialogOptions = Get-IdoitDialog -params @{ category = $Category; property = $key } -ErrorAction Stop $valid = $thisDialogOptions.title -contains $Properties[$key] if (-not $valid) { $atLeastOneFailed = $true Write-Warning "Value '$($Properties[$key])' for property '$key' is not valid for category '$($Category)'. Valid values are: $($thisDialogOptions.title -join ', ')" continue # we leave it to the caller to break the loop by using -ErrorAction Stop } $validProperties[$key] = $Properties[$key] } 'multiselect' { # type "multiselect"; never so "multiselect_plus" in the field, just a guess $thisDialogOptions = Get-IdoitDialog -params @{ category = $Category; property = $key } -ErrorAction Stop foreach ($thisKey in $Properties[$key]) { $valid = $thisDialogOptions.title -contains $thisKey if (-not $valid) { $atLeastOneFailed = $true Write-Warning "Value '$thisKey' for property '$key' is not valid for category '$Category'. Valid values are: $($thisDialogOptions.title -join ', ')" continue # we leave it to the caller to break the loop by using -ErrorAction Stop } $validProperties[$key] = $Properties[$key] } } Default { # For other types, we assume the value is valid $validProperties[$key] = $Properties[$key] } } } if ($atLeastOneFailed) { Write-Error "One or more properties failed validation for category '$Category'. Please check the warnings above." } Write-Output $validProperties } #EndRegion '.\Private\ValidateProperties.ps1' 106 #Region '.\Public\Connect-IdoIt.ps1' -1 function Connect-IdoIt { <# .SYNOPSIS Connect-to Idoit API. .DESCRIPTION Connect-to Idoit API. This function is used to connect to the Idoit API and authenticate the user. .PARAMETER Uri The Uri to the idoit JSON-RPC API. should be like http[s]://your.i-doit.host/src/jsonrpc.php .PARAMETER Credential User with appropiate permissions to access the cmdb. .PARAMETER Username The username to connect to the Idoit API. .PARAMETER Password The password to connect to the Idoit API. The password is passed as a SecureString. .PARAMETER ApiKey This is the apikey you define in idoit unter Settings-> Interface-> JSON-RPC API to access the api .EXAMPLE PS> Connect-IdoIt -Uri 'https://test.uri' -Credential $credential -ApiKey 'TestApiKey' This will connect to the Idoit API using the provided Uri and Credential. The result of the login will be returned. .EXAMPLE PS> Connect-IdoIt -Uri 'https://test.uri' -Username 'TestUser' -Password (ConvertTo-SecureString 'TestPassword' -AsPlainText -Force) -ApiKey 'TestApiKey' This will connect to the Idoit API using the provided Uri, Username, Password and ApiKey. The result of the login will be returned. .NOTES #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidGlobalVars", "")] [Cmdletbinding()] Param( [Parameter(Mandatory = $True, ParameterSetName = "PSCredential")] [Parameter(Mandatory = $True, ParameterSetName = "UserPasswordApiKey")] [String] $Uri, [Parameter(Mandatory = $true, ParameterSetName = "PSCredential")] [System.Management.Automation.PSCredential] $Credential, [Parameter(Mandatory = $True, ParameterSetName = "UserPasswordApiKey")] [String] $Username, [Parameter(Mandatory = $True, ParameterSetName = "UserPasswordApiKey")] [SecureString] $Password, [Parameter(Mandatory = $True, ParameterSetName = "PSCredential")] [Parameter(Mandatory = $True, ParameterSetName = "UserPasswordApiKey")] [String] $ApiKey ) If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' } switch ($PSCmdlet.ParameterSetName) { "UserPasswordApiKey" { $Script:IdoItParams["Connection"] = @{ Uri = $Uri Username = $Username Password = $Password ApiKey = $ApiKey } } "PSCredential" { $Script:IdoItParams["Connection"] = @{ Uri = $Uri Username = $Credential.UserName Password = $Credential.GetNetworkCredential().Password ApiKey = $ApiKey } } Default { Throw " Invalid parameter set $($PSCmdlet.ParameterSetName) specified." } } $Headers = @{ "Content-Type" = "application/json" "X-RPC-Auth-Username" = $Script:IdoItParams["Connection"].Username "X-RPC-Auth-Password" = $Script:IdoItParams["Connection"].Password } $splatInvoke = @{ Uri = $Script:IdoItParams["Connection"].Uri Headers = $Headers Method = "idoit.login" Params = @{} } $resultObj = Invoke-IdoIt @splatInvoke -ErrorAction Stop $result = [pscustomobject]@{ Account = $resultObj.name ClientName = $resultObj.'client-name' ClientId = $resultObj.'client-id' } $Script:IdoItParams["Connection"].SessionId = $resultObj.'session-id' # According to the i-doit API docs, this should return a version string. # As of version 33 is used in our environment, tt seems to be an integer. So we need to convert it to a string. $versionString = (Get-IdoItVersion).Version Try { # ugly way to check whether the [string] is an integer if ("$versionString" -match '^\d+$') { $versionString = [Version]::new($versionString, 0) } $null = [Version]$versionString } Catch { Throw "IdoIt version is not a valid version string: $versionString" } $result } #EndRegion '.\Public\Connect-IdoIt.ps1' 108 #Region '.\Public\ConvertFrom-MappingFile.ps1' -1 function ConvertFrom-MappingFile { <# .SYNOPSIS Convert a file containing a mapping of I-doit categories to a PSObject .DESCRIPTION The content of the file and convert it to a PSObject for later use as mapping between I-doit categories and PowerShell objects. Allowed file formats are yaml and JSON. For converting yaml files, the module 'powershell-yaml' is required. .PARAMETER Path The path to the file containing the mapping. .EXAMPLE ConvertFrom-MappingFile -Path 'C:\path\to\mapping.yaml' Returns an object with the mapping defined in the file. .EXAMPLE ConvertFrom-MappingFile -Path 'C:\path\to\mapping.json' Returns an object with the mapping defined in the file. .OUTPUTS PCustomObject #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [string] $Path ) begin { if (-not (Test-Path $Path -ErrorAction SilentlyContinue)) { throw [System.IO.FileNotFoundException] "Mapping file not found ($Path)." } } process { $Content = Get-Content -Path $Path -Raw if ($Path.EndsWith('.yaml')) { $rawMapping = ConvertFrom-Yaml -Yaml $Content -Ordered -ErrorAction Stop | ConvertTo-Yaml -JsonCompatible | ConvertFrom-Json -AsHashtable } elseif ($Path.EndsWith('.json')) { $rawMapping = $Content | ConvertFrom-Json } else { throw "Unsupported file format: $Path" } # loop through the keys defining all object map definitions $MappingList = foreach ($key in $rawMapping.keys) { $definition = $rawMapping[$key] $obj = [PSCustomObject]@{ Name = $key PSType = $definition.PSType IdoitObjectType = $definition.IdoitObjectType Mapping = @() } # loop through each category in the definition $thisMappings = foreach ($catKey in $definition.Category.Keys) { $mapOneCategory = [PSCustomObject]@{ Category = $catKey PropertyList = @() } # loop through each PSProperty/property mapping in this category foreach ($attributeDef in ($definition.Category.$catKey).GetEnumerator()) { $thisDefinition = [PSCustomObject]@{ PSProperty = $attributeDef.Value.PSProperty iAttribute = $attributeDef.Key Action = $attributeDef.Value.Action GetScript = $null DisplayFormat = $null Update = $attributeDef.Value.Update -eq $true } if ($null -eq $thisDefinition.PSProperty) { $thisDefinition.PSProperty = $thisDefinition.iAttribute # if no property is defined, it uses the same name } if ('ScriptAction' -eq $thisDefinition.Action) { if ($null -eq $attributeDef.Value.GetScript) { Write-Error "No GetScript defined for attribute '$($attributeDef.Key)' in category '$catKey'." -ErrorAction Stop } $thisDefinition.GetScript = [scriptblock]::Create($attributeDef.Value.GetScript) } if (-not [string]::IsNullOrEmpty($attributeDef.Value.DisplayFormat)) { $thisDefinition.DisplayFormat = $attributeDef.Value.DisplayFormat } $mapOneCategory.PropertyList += $thisDefinition } $mapOneCategory } $obj.Mapping = $thisMappings $obj } $MappingList } end { } } #EndRegion '.\Public\ConvertFrom-MappingFile.ps1' 99 #Region '.\Public\Disconnect-IdoIt.ps1' -1 function Disconnect-IdoIt { <# .SYNOPSIS Disconnect-IdoIt logs out of the IdoIt API-Session. .DESCRIPTION Disconnect-IdoIt logs out of the IdoIt API-Session. .EXAMPLE PS> Disconnect-IdoIt This will disconnect from idoit .NOTES #> Try { Invoke-IdoIt -Method "idoit.logout" -Params @{} $Script:IdoItParams["Connection"].SessionId = $null } Catch { Throw } } #EndRegion '.\Public\Disconnect-IdoIt.ps1' 23 #Region '.\Public\Get-IdoItCategory.ps1' -1 function Get-IdoItCategory { <# .SYNOPSIS Get category properties and values for a given object id and category. .DESCRIPTION Get-IdoItCategory retrieves all category properties and values for a given object id and category. Custom properties can be converted to a more user-friendly format (except if RawCustomCategory is set). .PARAMETER ObjId The object id of the object for which you want to retrieve category properties and values. Alias: Id .PARAMETER Category The category constant name for which you want to retrieve properties and values. Alias: Const This parameter is dynamic and will be populated based on the object type of the specified Id. If the Id is not specified, all available categories will be returned. If an Id is used, where no object type is defined, the parameter will not be populated. .PARAMETER Status The status of the category. Default is 2 (active). .PARAMETER UseCustomTitle If this switch is set, the custom category will be converted to a more user-friendly format. .EXAMPLE PS> Get-IdoItCategory -ObjId 12345 -Category 'C__CATG__CPU' Retrieves a list of items of the category 'C__CATG__CPU' and its values for the object with id 12345. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [int] $ObjId, # dynamic parameter # [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] # [Alias('Const')] # [string] $Category, [Parameter(Position = 2, ParameterSetName = "Id")] [int] $Status = 2, [Switch] $UseCustomTitle ) DynamicParam { #region Category: if user has entered an Id, try to get defined categories for this object if ($ObjId -gt 0) { $obj = Get-IdoitObject -ObjId $ObjId -ErrorAction SilentlyContinue if ($null -ne $obj) { $objCategoryList = Get-IdoitObjectTypeCategory -Type $obj.objecttype -ErrorAction SilentlyContinue } if ($null -ne $objCategoryList) { $validCatConstList = $objCategoryList | Select-Object -ExpandProperty const } } else { if ($null -eq $validCatConstList) { # default: deliver all constants $validCatConstList = (Get-IdoItConstant | Where-Object Type -in ('GlobalCategory','SpecificCategory')).Name } } $dynParamCategory = NewDynamicParameter -Name 'Category' -ParameterType 'System.String' -ValidateSet $validCatConstList -Mandatory $true $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $RuntimeParameterDictionary.Add('Category', $dynParamCategory) #endregion return $RuntimeParameterDictionary } begin { } process { $params = @{ objID = $ObjId category = $PSBoundParameters['Category'] status = $Status } Try { $result = Invoke-Idoit -Method 'cmdb.category.read' -Params $params } Catch { $ex = $_ if ($ex.Exception.Message -match 'Virtual category') { # Write-Warning "Skipping virtual category: $($params.category)" return } Throw $_ } # This one more wierd things of the i-doit API: # If the category is not defined for the object, it returns an empty array with only the objId and id properties. # But sometimes it returns an empty object without any properties. # If the category is defined, it returns an array with the properties of the category. # We will normalize the two "empty" result in returning $null # I really hate this solution, but didnt find a better way to handle this. if ( @($result).Count -eq 0 ) { $result = $null } if ( @($result.PSObject.Properties).count -le 2 ) { # if the result has max two properties, it is an empty category $result = $null } if ($null -ne $result) { foreach ($item in $result) { if ($UseCustomTitle) { # is it a custom category? # TODO: It is not very efficient to do all the stuff for each object. Check that later. $objTypeCatList = Get-IdoitObjectTypeCategory -ObjId $ObjId -ErrorAction Stop $objTypeCat = $objTypeCatList | Where-Object { $_.const -eq $params.category } if ($null -eq $objTypeCat) { Write-Error "ObjectTypeCategory '$($params.category)' not found for object with ID $Id." return } $isCustom = $objTypeCat.type -eq 'custom' if ($isCustom) { $item = ConvertFrom-CustomCategory -InputObject $item -CategoryObject $objTypeCat } } $item.PSObject.TypeNames.Insert(0, 'Idoit.Category') $item | Add-Member -MemberType NoteProperty -Name 'Category' -Value $params.category -Force $item } } } end { } } #EndRegion '.\Public\Get-IdoItCategory.ps1' 130 #Region '.\Public\Get-IdoitCategoryInfo.ps1' -1 Function Get-IdoitCategoryInfo { <# .SYNOPSIS Get-IdoitCategoryInfo .DESCRIPTION Get-IdoItCategoryInfo lets you discover all available category properties for a given category id. The list corresponds to the properties you will receive for each returned object after calling the cmdb.category.read method. .PARAMETER Category Look for category info by category name. This is the most common way to get category info. .PARAMETER CatgId Look for category info by category id of a global category. .PARAMETER CatsId Look for category info by category id of a s(?) category. .EXAMPLE PS>Get-IdoitCategoryInfo -Category 'C__CATG__CPU' Gives you detailed Info about every possible categaory value of this object. E.g. for 'C__CATG__CPU' you get title, manifacturer, type, frequency, cores, etc. ... cores: @{title=CPU cores; check=; info=; data=; ui=} @{ title = 'CPU cores'; check = @{ mandatory = 'False' }; info = @{ primary_field = 'False'; type = 'int'; backward = 'False'; title = 'LC__CMDB__CATG__CPU_CORES'; description = 'CPU cores' }; data = @{ type = 'int'; readonly = 'False'; index = 'False'; field = 'isys_catg_cpu_list__cores' }; ui = @{ type = 'text'; params = @{ p_strPlaceholder = 0; default = 0; p_strClass = 'input-mini' }; default = 1; id = 'C__CATG__CPU_CORES' } }; .NOTES One more example of the incosistency of the i-doit API: Searching by category name does return the name as well. Searching for the same category by id does not return the name. #> Param ( [Parameter(Mandatory = $True, ParameterSetName="Category")] [String]$Category, [Parameter(Mandatory = $True, ParameterSetName="CatgId")] [int]$CatgId, [Parameter(Mandatory = $True, ParameterSetName="CatsId")] [int]$CatsId ) $params = @{} Switch ($PSCmdlet.ParameterSetName) { "Category" { $params.Add("category", $Category); break } "CatgId" { $params.Add("catgID",$CatgId); break } "CatsId" { $params.Add("catsID",$CatsId); break } } $result = Invoke-IdoIt -Method "cmdb.category_info.read" -Params $params -ErrorAction Stop $result.PSObject.Typenames.Add('Idoit.CategoryInfo') $result | Add-Member -MemberType NoteProperty -Name Category -Value $Category -Force return $result } #EndRegion '.\Public\Get-IdoitCategoryInfo.ps1' 59 #Region '.\Public\Get-IdoItConstant.ps1' -1 Function Get-IdoItConstant { <# .SYNOPSIS Get-IdoItConstant .DESCRIPTION It retrieve all constants from the API (objTypes, categories, global and specific categories). .OUTPUTS Returns a list Type of constant, name and value. .EXAMPLE Get-IdoItConstant Returns all the constants. .NOTES #> [CmdletBinding()] $params = @{} $result = Invoke-IdoIt -Method "idoit.constants" -Params $params # one of the things, which are wierd in this API # result is an object having a property by type of constant # e.g. $result.objectTypes, $result.Categories.G, $result.Categories.S, recordStates, ... # Each of this properties is set up as a object having a property by contstant name # and a value of the text (or title?) of that constant Convert-PropertyToArray -InputObject $result.objectTypes | Select-Object @{ name='Type'; Expression={'ObjectType'} }, Name, Value Convert-PropertyToArray -InputObject $result.recordStates | Select-Object @{ name='Type'; Expression={'RecordState'} }, Name, Value Convert-PropertyToArray -InputObject $result.Categories.G | Select-Object @{ name='Type'; Expression={'GlobalCategory'} }, Name, Value Convert-PropertyToArray -InputObject $result.Categories.S | Select-Object @{ name='Type'; Expression={'SpecificCategory'} }, Name, Value Convert-PropertyToArray -InputObject $result.Categories.g_custom | Select-Object @{ name='Type'; Expression={'CustomCategory'} }, Name, Value Convert-PropertyToArray -InputObject $result.relationTypes | Select-Object @{ name='Type'; Expression={'RelationType'} }, Name, Value Convert-PropertyToArray -InputObject $result.staticObjects | Select-Object @{ name='Type'; Expression={'StaticObject'} }, Name, Value } #EndRegion '.\Public\Get-IdoItConstant.ps1' 36 #Region '.\Public\Get-IdoitDialog.ps1' -1 function Get-IdoitDialog { <# .SYNOPSIS Get the dialog for a specific category and property from i-doit. .DESCRIPTION This function retrieves the dialog for a specific category and property. It returns a list of options available for the specified category and property. .PARAMETER Category The category name (title) for which the dialog is requested. Example: 'C__CATG__CPU' .PARAMETER Property The property name (title) for which the dialog is requested. Example: 'manufacturer' .PARAMETER params A hashtable containing the parameters for the dialog. The hashtable should contain at least the keys 'category' and 'property'. Example: @{ category='C__CATG__CPU'; property='manufacturer' } .EXAMPLE Get-IdoitDialog -params @{ category='C__CATG__CPU'; property='manufacturer' } Retrieves the dialog options for the CPU category and manufacturer property. .NOTES #> [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Default')] [ValidateNotNullOrEmpty()] [string] $Category, # category id, e.g. 'C__CATG__CPU' [Parameter(Mandatory = $true, ParameterSetName = 'Default')] [ValidateNotNullOrEmpty()] [string] $Property, # property id, e.g. 'manufacturer' [Parameter(Mandatory = $true, ParameterSetName = 'ByParams')] [ValidateNotNullOrEmpty()] [hashtable] $params # parameters for the dialog, e.g. @{ category='C__CATG__CPU'; property='manufacturer' } ) begin { } process { if ($PSCmdlet.ParameterSetName -eq 'Default') { $params = @{ category = $Category property = $Property } } $apiResult = Invoke-IdoIt -Endpoint 'cmdb.dialog' -Params $params foreach ($dialog in $apiResult) { $dialog | Add-Member -MemberType NoteProperty -Name 'ParentId' -Value $Id -Force $dialog.PSObject.TypeNames.Insert(0, 'Idoit.IdoitDialogItem') Write-Output $dialog } } end { } } #EndRegion '.\Public\Get-IdoitDialog.ps1' 67 #Region '.\Public\Get-IdoitLocationTree.ps1' -1 function Get-IdoitLocationTree { <# .SYNOPSIS Get the next location tree from i-doit .DESCRIPTION This function retrieves the location tree from i-doit. The root location is returned if no Id is specified. .PARAMETER Id The Id of the location to retrieve the tree from. If 0 is specified, the root location is returned. .EXAMPLE Get-IdoitLocationTree -Id 0 Retrieves the root location. .EXAMPLE Get-IdoitLocationTree -Id 1 Retrieves the location tree starting from the location with Id 1 (root). >#> [CmdletBinding()] param ( [int] $Id # 0 returns the root location ) begin { } process { $apiResult = Invoke-IdoIt -Endpoint 'cmdb.location_tree' -Params @{ id = $Id } foreach ($location in $apiResult) { $location.PSObject.TypeNames.Insert(0, 'PSIdoitNG.Location') $location | Add-Member -MemberType NoteProperty -Name 'ParentId' -Value $Id -Force -PassThru } } end { } } #EndRegion '.\Public\Get-IdoitLocationTree.ps1' 45 #Region '.\Public\Get-IdoitMappedObject.ps1' -1 function Get-IdoitMappedObject { <# .SYNOPSIS Get an object that is created based on a iAttribute map. .DESCRIPTION Get an object that is created based on a iAttribute map. The mapping is defined in the property map. This allows to construct Powershell objects combining different I-doit categories and their values. .PARAMETER ObjId The object ID of the I-doit object. .PARAMETER MappingName The name of the mapping to be used for the object creation. This is a name of a mapping registered with Register-IdoitCategoryMap. .PARAMETER PropertyMap The property map that defines how the I-doit categories and their values should be mapped to the properties of the resulting object. .EXAMPLE Assume that 37 is the Id of a persons object. $propertyMap = @{ PSType = 'MyUser' IdoitObjectType = 'C__OBJTYPE__PERSON' Mapping = @( @{ Category = 'C__CATS__PERSON'; PropertyList = @( @{ Property = 'Id'; iAttribute = 'Id' }, @{ Property = 'Name'; iAttribute = 'Title' } ) } ) } $result = Get-IdoitMappedObject -ObjId 37 -PropertyMap $propertyMap #> [CmdletBinding(DefaultParameterSetName = 'MappingName')] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory = $true)] # [Alias('Id')] [int] $ObjId, [Parameter(Mandatory = $true, ParameterSetName = 'MappingName')] [ValidateNotNullOrEmpty()] [Alias('Name')] $MappingName, [Parameter(Mandatory = $true, ParameterSetName = 'PropertyMap')] $PropertyMap ) begin { if ($PSCmdlet.ParameterSetName -eq 'MappingName') { if ($null -eq $Script:IdoitCategoryMaps -or -not $Script:IdoitCategoryMaps.ContainsKey($MappingName)) { Write-Error "No category map registered for name '$MappingName'. Use Register-IdoitCategoryMap to register a mapping." -ErrorAction Stop } $PropertyMap = $Script:IdoitCategoryMaps[$MappingName] } } process { $obj = Get-IdoItObject -ObjId $ObjId if ($null -eq $obj) { return } $resultObj = @{ ObjId = $obj.Id PSTypeName = 'Idoit.MappedObject' } foreach ($propMap in $PropertyMap) { foreach ($thisMapping in $propMap.Mapping) { $catValues = Get-IdoItCategory -ObjId $obj.Id -Category $thisMapping.Category if ($null -eq $catValues) { Write-Verbose "No values found for category $($thisMapping.Category) in object $($obj.Id)." } foreach ($propListItem in $thisMapping.PropertyList) { # I-doit iAttribute values can be simpley types or a hashtable with keys depending on the iAttribute it is representing # For the later once, the if the iAttribute "name" has to be 'iAttribute.field'. This is the "deep key" to get the value $attr, $field, $index = $propListItem.iAttribute -split '\.' if ($attr -eq '*' -and $field -notmatch '^$|^[0-9]+$') { Write-Error "The iAttribute '$($propListItem.iAttribute)' is not supported. It must be '*' or be a simple key." } if ($attr -eq '*') { # pass the whole category object # char & would bei nicer, but would collide with merge key support of powershell-yaml $thisCatValue = $catValues } elseif (-not [string]::IsNullOrEmpty($field)) { # if the iAttribute is a deep key, the value is extracted from the hashtable $thisCatValue = $catValues.$attr.$field } else { # if the iAttribute is a simple key, the value is extracted from the hashtable $thisCatValue = $catValues.$attr } # manage simple values if ([string]::IsNullOrEmpty($propListItem.Action)) { if ($thisCatValue -is [System.Array]) { $resultObj.Add($propListItem.PSProperty, @($thisCatValue)) } else { $resultObj.Add($propListItem.PSProperty, $thisCatValue) } continue } # manage predefined actions if ($propListItem.Action -is [string]) { switch ($propListItem.Action) { 'Sum' { $resultObj.Add($propListItem.PSProperty, ( $thisCatValue |Measure-Object -Sum | Select-Object -ExpandProperty Sum)) break } 'Count' { $resultObj.Add($propListItem.PSProperty, ($thisCatValue | Measure-Object).Count) break } 'ScriptAction' { if (-not $propListItem.GetScript) { Write-Warning "No script action defined for property $($propListItem.PSProperty) in mapping $($thisMapping.Category)" continue } # scriptblock parameter: if iAttribute if it is defined else the whole object is passed to the action Try { $result = $propListItem.GetScript.InvokeReturnAsIs(@($thisCatValue)) } catch { $result = $_ | Out-String } $resultObj.Add($propListItem.PSProperty, $result) continue } '' { $resultObj.Add($propListItem.PSProperty, $thisCatValue) } Default { Write-Warning "Unknown action: [$($_)]" } } continue } } } } $result = ([PSCustomObject]$resultObj)[0] # casting to [PSObject] returns an arraylist, but we want a single object if ($null -ne $PropertyMap.PSType) { $result.PSObject.TypeNames.Insert(0, $PropertyMap.PSType) } $result } end { } } #EndRegion '.\Public\Get-IdoitMappedObject.ps1' 151 #Region '.\Public\Get-IdoitObject.ps1' -1 function Get-IdoitObject { <# .SYNOPSIS Get-IdoitObject returns an object from the i-doit API or $null. .DESCRIPTION Get-IdoitObject returns an object or $null. .EXAMPLE Get-IdoitObject -ObjId 540 .PARAMETER ObjId The id of the object you want to retrieve from the i-doit API. #> [cmdletBinding(ConfirmImpact = 'Low')] [OutputType([Object])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] # [Alias('Id')] [int] $ObjId ) process { $apiResult = Invoke-Idoit -Method 'cmdb.object.read' -Params @{ id = $ObjId } if ($null -eq $apiResult.id) { # is it a null array? Write-Error -Message "Object not found Id=$ObjId" return } $apiResult.PSObject.TypeNames.Insert(0, 'Idoit.Object') $apiResult | Add-Member -MemberType NoteProperty -Name 'ObjId' -Value $ObjId -Force $apiResult } } #EndRegion '.\Public\Get-IdoitObject.ps1' 36 #Region '.\Public\Get-IdoitObjectTree.ps1' -1 function Get-IdoitObjectTree { <# .SYNOPSIS Retrieves the full object tree for a given i-doit object ID. .DESCRIPTION This cmdlet retrieves the object tree for a specified i-doit object ID, including its categories and properties. It excludes categories specified in the `ExcludeCategory` parameter. If you have installed the module "PwshSpectreConsole" you can get a nice view of the results by using: Format-SpectreJson -Data (Get-IdoitObjectTree -ObjId 37) -Depth 5 .PARAMETER ObjId The ID of the i-doit object for which to retrieve the tree. .PARAMETER ExcludeCategory An array of category constants to exclude from the results. Default is 'C__CATG__LOGBOOK'. .PARAMETER IncludeEmptyCategories A switch to include empty categories in the output. .EXAMPLE Get-IdoitObjectTree -ObjId 37 Id Title ObjectType Categories -- ----- ---------- ---------- 37 This Title 53 {@{Category=C__CATG__OVERVIEW; Properties=}, @{Category=C__CATG__RELATION; Properties=System.Object[]}, @{Category=C__CATG__M... .EXAMPLE PSIdoitNG> Get-IdoitObjectTree -Id 37 | Select-Object -Expanded Categories -------- ---------- C__CATG__OVERVIEW @{id=4; objID=37} C__CATG__RELATION {@{id=15; objID=53; object1=; object2=; relation_type=; weighting=; itservice=; description=}, @{id=63; objID=156; objec... C__CATG__MAIL_ADDRESSES @{id=353; objID=37; title=thisTitle@somewhere; primary_mail=thisTitle@somewhere; primary=; descrip... C__CATG__GLOBAL @{id=37; objID=37; title=thisTitle; status=; created=2024-10-09 12:56:39; created_by=SomeBody; changed=2025-05-... C__CATS__PERSON @{id=11; objID=37; title=thisTitle@somewhere; salutation=; first_name=First; last_name=Last; academic_de... PSIdoitNG> $x.Categories[3].Properties id : 37 objID : 37 title : This Title status : @{id=2; title=Normal; const=; title_lang=LC__CMDB__RECORD_STATUS__NORMAL} created : 2024-10-09 12:56:39 created_by : SomeBody changed : 2025-05-27 23:10:18 changed_by : automation purpose : category : sysid : SYSID_1733071461 cmdb_status : @{id=6; title=in operation; const=C__CMDB_STATUS__IN_OPERATION; title_lang=LC__CMDB_STATUS__IN_OPERATION} type : @{id=53; title=Persons; const=C__OBJTYPE__PERSON; title_lang=LC__CONTACT__TREE__PERSON} tag : description : #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [int] $ObjId, [string[]] $ExcludeCategory = 'C__CATG__LOGBOOK', [switch] $IncludeEmptyCategories ) begin { } process { $obj = Get-IdoitObject -ObjId $ObjId if ($null -eq $obj) { Write-Error "Object with ID $ObjId not found." return } $catList = Get-IdoItObjectTypeCategory -Type $obj.objecttype if ($null -eq $catList) { Write-Error "No categories found for object with ID $ObjId." return } $result = [PSCustomObject]@{ Id = $ObjId Title = $obj.title ObjectType = $obj.objecttype Categories = @() } foreach ($category in $catList) { if ($category.const -in $ExcludeCategory) { continue } $catValues = Get-IdoItCategory -ObjId $ObjId -Category $category.const -ErrorAction Stop # an logically empty result contains objId, id plus the category const added by Get-IdoitCategory if ( $null -eq $catValues -and -not $IncludeEmptyCategories ) { continue } # $catValues | ft -GroupBy RefCategory -AutoSize -Wrap $result.Categories += [PSCustomObject]@{ Category = $category.const Properties = $catValues } } $result } end { } } #EndRegion '.\Public\Get-IdoitObjectTree.ps1' 109 #Region '.\Public\Get-IdoItObjectType.ps1' -1 function Get-IdoItObjectType { <# .SYNOPSIS Get-IdoItObjectType retrieves object types from i-doit. .DESCRIPTION Get-IdoItObjectType retrieves object types from i-doit. You can specify the object type by its ID or title. .PARAMETER TypeId The ID of the object type to retrieve. This parameter is mandatory. .PARAMETER Const The title of the object type to retrieve. This parameter is mandatory. .PARAMETER Enabled If specified, only enabled object types will be retrieved. .PARAMETER Skip The number of object types to skip before returning results. This is useful for pagination. The default value is 0. .PARAMETER Limit The maximum number of object types to return. This is useful for pagination. The default value is 100. .EXAMPLE Get-IdoItObjectType -TypeId 1 Retrieves the object type with ID 1.^ .EXAMPLE Get-IdoItObjectType -Const "C_OBJTYPE_SERVICE","C_OBJTYPE_SERVER" Retrieves the object types with titles "C_OBJTYPE_SERVICE" and "C_OBJTYPE_SERVER". .EXAMPLE Get-IdoItObjectType -Enabled Retrieves all enabled object types. .EXAMPLE Get-IdoItObjectType -Limit 20 Get-IdoItObjectType -Skip 20 -Limit 20 Retrieves the the first 20 object types and then the next 20 object types. .DESCRIPTION Get-IdoItObjectType retrieves object types from i-doit. You can specify the object type by its ID or title. #> [CmdletBinding()] Param ( # [Alias('Id')] [Int[]] $TypeId, [Alias('Title')] [string[]] $Const, [Switch] $Enabled, [int] $Skip, [Int] $Limit ) #checkCmdbConnection Process { $params= @{} $filter = @{} foreach ($PSBoundParameter in $PSBoundParameters.Keys) { switch ($PSBoundParameter) { "TypeId" { $filter.Add("ids", @($TypeId)) break } "Const" { $filter.Add("titles", @($Const)) # sadly: the API param is title, searched are const strings break } "Enabled" { $filter.Add("enabled", 1) break } "Limit" { if ($Skip -gt 0) { $params.Add("limit", "$Skip,$Limit") } else { $params.Add("limit", $Limit) } break } } } $params.Add("filter", $filter) $result = Invoke-IdoIt -Method "cmdb.object_types.read" -Params $params $result | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Name "TypeId" -Value $_.id -Force $_.PSObject.TypeNames.Add('Idoit.ObjectType') } Return $result } } #EndRegion '.\Public\Get-IdoItObjectType.ps1' 100 #Region '.\Public\Get-IdoItObjectTypeCategory.ps1' -1 Function Get-IdoItObjectTypeCategory { <# .SYNOPSIS Get-IdoItObjectTypeCategory .DESCRIPTION Gets all the categories that the specified object type is constructed of. .PARAMETER ObjId Object ID for which the categories should be returned. If this parameter is specified, the type of the object will be determined first. .PARAMETER TypeId Object type for which the categories should be returned. This can be a string or an integer. .OUTPUTS Returns a collection of IdoIt.ObjectTypeCategory objects. Each object represents a category. .EXAMPLE PS> Get-IdoItObjectTypeCategory -Type 'C__OBJTYPE__SERVER' This will get all categories that are assigned to the ObjectType 'Server' [PSCustomObject] @{ id = 31; title = 'Overview page'; const = 'C__CATG__OVERVIEW'; multi_value = 0; source_table = 'isys_catg_overview' } [PSCustomObject] @{ id = 42; title = 'Drive'; const = 'C__CATG__DRIVE'; multi_value = 1; source_table = 'isys_catg_drive' } ... .EXAMPLE PS> Get-IdoItObjectTypeCategory -Type 1 This will get all categories that are assigned to the ObjectType with ID 1. .EXAMPLE PS> Get-IdoItObjectTypeCategory -ObjId 540 This will get all categories for that object id (Server with ID 540). .NOTES #> [CmdletBinding(DefaultParameterSetName = 'ObjectType')] [OutputType([System.Object[]])] Param ( [Parameter (Mandatory = $false, ValueFromPipelineByPropertyName = $True, ParameterSetName = 'ObjectId')] [ValidateNotNullOrEmpty()] [int] $ObjId, [Parameter (Mandatory = $True, ValueFromPipeline = $True, ParameterSetName = 'ObjectType')] [ValidateNotNullOrEmpty()] # [Alias('TypeId','Id')] $TypeId ) Process { $params = @{} switch ($PSCmdlet.ParameterSetName) { 'ObjectId' { # if we have an object id, we need to get the type first $obj = Get-IdoItObject -ObjId $ObjId $TypeId = $obj.objecttype } 'ObjectType' { # do nothing, type is already set } } $params.Add("type", $TypeId) $result = Invoke-IdoIt -Method "cmdb.object_type_categories.read" -Params $params #idoit delivers two arrays, depending of global or specific categories. From a PowerShell #point of view this is ugly - so we flatten the result into one PSObject. ForEach ($thisProperty In $result.PSObject.Properties) { ForEach ($subProperty In $result.($thisProperty.Name)) { if ([string]::IsNullOrEmpty($subProperty.type)) { # older API version seems not to deliver the type? $subProperty | Add-Member -MemberType NoteProperty -Name "type" -Value $thisProperty.Name } $subProperty.PsObject.TypeNames.Insert(0,'IdoIt.ObjectTypeCategory') $subProperty } } } } #EndRegion '.\Public\Get-IdoItObjectTypeCategory.ps1' 80 #Region '.\Public\Get-IdoItObjectTypeGroup.ps1' -1 Function Get-IdoItObjectTypeGroup { <# .SYNOPSIS Get-IdoItObjectTypeGroup .DESCRIPTION Gets all the object type groups that are available in the i-doit CMDB. .EXAMPLE Get-IdoItObjectTypeGroup Return all object type groups. .NOTES #> [CmdletBinding()] Param () $result = Invoke-IdoIt -Method "cmdb.object_type_groups.read" $result | ForEach-Object { $_.PSObject.TypeNames.Insert(0, 'IdoIt.ObjectTypeGroup') } Write-Output $result } #EndRegion '.\Public\Get-IdoItObjectTypeGroup.ps1' 24 #Region '.\Public\Get-IdoItVersion.ps1' -1 Function Get-IdoItVersion { <# .SYNOPSIS Get the version of the Idoit instance .DESCRIPTION This function retrieves the version of the Idoit instance. Since some version of the API, it does return a integer value and not a version string. .EXAMPLE Get-IdoItVersion This will retrieve the version of the Idoit instance. .NOTES #> $result = Invoke-IdoIt -Method "idoit.version" -Params @{} return $result | Select-Object version, type, step } #EndRegion '.\Public\Get-IdoItVersion.ps1' 15 #Region '.\Public\Invoke-IdoIt.ps1' -1 Function Invoke-IdoIt { <# .SYNOPSIS Invoke-IdoIt API request to the i-doit RPC Endpoint .DESCRIPTION This function is calling the IdoIt API. The result is returned as a PSObject. .PARAMETER Endpoint This parameter the method yout want to call at the RPC Endpoint (see https://kb.i-doit.com/de/i-doit-add-ons/api/index.html). From my personal point of view, at the time of writing the documentation is not very good. .PARAMETER Params Hashtable creating request body with the methods parameters (https://kb.i-doit.com/de/i-doit-add-ons/api/index.html). The following additional parameters are inserted into the request body: ApiKey, Request id, Version .PARAMETER Headers Optional parameter if default headers should be overwritten (e.g. when logging into a new session). .PARAMETER Uri The Uri if the API Endpoint. .PARAMETER Version The version of the API. Default is 2.0 .PARAMETER ApiKey The API key to be used. Default is the one used in Connect. .EXAMPLE $result = -Method "idoit.logout" -Params @{} .NOTES To trace the API calls, set the global variable $Global:IdoitApiTrace to @(). For every API call, a new entry is added to the array. The entry contains the following properties: Endpoint: The endpoint called Request: The request body Response: The response body (if response was successful) Time: The time of the call Exception: The exception thrown (if any) #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidGlobalVars", "")] [CmdletBinding()] Param ( [Parameter( Mandatory = $True )] [ValidateNotNullOrEmpty()] [Alias('Method')] [String] $Endpoint, [ValidateNotNull()] [Hashtable] $Params = @{}, [Hashtable] $Headers = @{"Content-Type" = "application/json"; "X-RPC-Auth-Session" = $Script:IdoItParams["Connection"].SessionId}, [String] $Uri = $Script:IdoItParams["Connection"].Uri, [string] $Version = "2.0", [string] $ApiKey = $Script:IdoItParams["Connection"].ApiKey ) $Params['apikey'] = $ApiKey $body = @{ "method" = $Endpoint "version" = $Version "id" = [Guid]::NewGuid() "params" = $Params } $bodyJson = ConvertTo-Json -InputObject $body -Depth 4 Try { $apiResult = Invoke-RestMethod -Uri $Uri -Method Post -Body $bodyJson -Headers $Headers # remove quotes from integer values $apiResult = ($apiResult | ConvertTo-Json -Depth 10) -replace '(?m)"([0-9]+)"','$1' | ConvertFrom-Json if ($null -ne $Global:IdoitApiTrace) { $Global:IdoitApiTrace += [PSCustomObject]@{ Endpoint = $Endpoint Request = [PSCustomObject]$body Response = $apiResult Time = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") } } } Catch { if ($null -ne $Global:IdoitApiTrace) { $Global:IdoitApiTrace += [PSCustomObject]@{ Endpoint = $Endpoint Request = [PSCustomObject]$body Exception = $_ Time = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") } } if ($_.CategoryInfo.Reason -eq 'UriFormatException') { # check if the login was missing if ([string]::IsNullOrEmpty($Script:IdoItParams["Connection"].SessionId)) { Write-Error -Message "No valid session found. Please check your API connection." -ErrorAction Stop } } Throw $_ } If ($apiResult.PSObject.Properties.Name -Contains 'Error') { $errMsg = "Error $($apiResult.Error.Code) - $($apiResult.error.data.Description) - $($apiResult.error.message)" Write-Error $errMsg } else { If ( $body.Id -ne $apiResult.id) { Throw "Request id mismatch. Expected value was $RequestID but it is $($apiResult.id)" } if ($apiResult.result.PSObject.Properties.Name -Contains 'Success') { # cast success to boolean $apiResult.result.success = $apiResult.result.success -eq 'True' } $apiResult.result } } #EndRegion '.\Public\Invoke-IdoIt.ps1' 116 #Region '.\Public\New-IdoitObject.ps1' -1 function New-IdoitObject { <# .SYNOPSIS Create a new i-doit object. .DESCRIPTION This function creates a new object in i-doit with the specified name, type, category, purpose, status, and description. It checks if an object with the same name already exists, and if so, it throws an error unless the `-AllowDuplicates` switch is set. .PARAMETER Name The name of the object to create. This is a mandatory parameter. Alias: Title .PARAMETER ObjectType The type of the object to create. .PARAMETER Category An array of categories to assign to the object. This is optional. .PARAMETER Purpose The purpose of the object. Valid entries can be found in the i-doit documentation. .PARAMETER Status The status of the object. Valid entries can be found in the i-doit documentation. .PARAMETER Description A description for the object. This is optional. .PARAMETER AllowDuplicates A switch to allow creating an object with the same name as an existing object. If this switch is not set, the function will throw an error if an object with the same name already exists. .EXAMPLE New-IdoitObject -Name "New Server" -ObjectType "C__OBJTYPE__SERVER" -Category "C__CATG__GLOBAL" -Purpose "In production" -Status 'C__CMDB_STATUS__IN_OPERATION' -Description "This is a new server object." This command creates a new server object in i-doit with the specified name, type, category, purpose, status, and description. .NOTES The function is intended to create *new* object of a specific type. If you want to create an object with the same name as an existing object, use the `-AllowDuplicates` switch. If you want to update an existing object, use the `Set-IdoitObject` function instead. #> [CmdletBinding(SupportsShouldProcess = $True)] param ( [Parameter( Mandatory = $True, ValueFromPipelineByPropertyName = $True, Position = 0)] [ValidateNotNullOrEmpty()] [Alias( 'Title' )] [string] $Name, [Parameter( Mandatory = $True, ValueFromPipelineByPropertyName = $True)] [ValidateNotNullOrEmpty()] [string] $ObjectType, [Parameter( ValueFromPipelineByPropertyName = $True)] [ValidateNotNullOrEmpty()] [string[]] $Category, [Parameter( ValueFromPipelineByPropertyName = $True)] [ValidateNotNullOrEmpty()] [string] $Purpose, [Parameter( ValueFromPipelineByPropertyName = $True)] [ValidateNotNullOrEmpty()] [Alias( 'cmbd_status')] [string] $Status, [Parameter( ValueFromPipelineByPropertyName = $True)] [ValidateNotNullOrEmpty()] [string] $Description, [Parameter( ValueFromPipelineByPropertyName = $True)] [Switch] $AllowDuplicates ) begin { } process { # by default, an object of this type and name should not exist if (-not $AllowDuplicates) { $obj = Search-IdoItObject -Conditions @{"property" = "C__CATG__GLOBAL-title"; "comparison" = "="; "value" = $Name} -ErrorAction SilentlyContinue if ($null -ne $obj) { Write-Error "An object with the name '$Name' already exists. Use -AllowDuplicates to create a new object with the same name." return } } $params = @{ title = $Name type = $ObjectType } if ($Category.Count -gt 0) { $params.categories = @($Category) # use @(..) to ensure the value is an array } if ('' -ne $Purpose) { $params.purpose = $Purpose } if ('' -ne $Status) { $params.status = $Status } if ('' -ne $Description) { $params.description = $Description } if ($PSCmdlet.ShouldProcess("Creating object '$Name' of type '$ObjectType'")) { $apiResult = Invoke-IdoIt -Method 'cmdb.object.create' -Params $params if ($apiResult -and 'True' -eq $apiResult.success) { $ret = [PSCustomObject]@{ ObjId = $apiResult.id } Write-Output $ret } else { Write-Error "Failed to create object. Error: $($apiResult.message)" } } } end { } } #EndRegion '.\Public\New-IdoitObject.ps1' 119 #Region '.\Public\Register-IdoitCategoryMap.ps1' -1 function Register-IdoitCategoryMap { <# .SYNOPSIS Registers a mapping of Idoit categories from a YAML file or directory. .DESCRIPTION This function reads a YAML file or all YAML files in a specified directory to register for Idoit category mappings. .PARAMETER Path The path to the YAML file or directory containing mapping files. .PARAMETER Recurse Indicates whether to search subdirectories for YAML files. .PARAMETER Force Indicates whether to overwrite existing mappings. .EXAMPLE Register-IdoitCategoryMap -Path 'C:\Path\To\MappingFile.yaml' Registers the mapping from the specified YAML file. .EXAMPLE Register-IdoitCategoryMap -Path 'C:\Path\To\MappingDirectory' -Recurse Registers all YAML mapping files found in the specified directory and its subdirectories. .EXAMPLE Register-IdoitCategoryMap -Path 'C:\Path\To\MappingFile.yaml' -Force Registers the mapping from the specified YAML file, overwriting any existing mapping with the same name. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string[]] $Path, [switch] $Recurse, [switch] $Force ) begin { if (-not (Test-Path -Path $Path)) { Write-Error "Path $Path for category map file or directory does not exist." -ErrorAction Stop } if (Test-Path -Path $Path -PathType Container) { $Path = Get-ChildItem -Path $Path -Filter '*.yaml' -Recurse:$Recurse } if ($null -eq $Script:IdoitCategoryMaps) { $Script:IdoitCategoryMaps = @{} } } process { foreach ($item in $Path) { $mapping = ConvertFrom-MappingFile -Path $item if ($mapping) { foreach ($obj in $mapping) { $rootName = $obj.Name if ($Script:IdoitCategoryMaps.ContainsKey($rootName) -and -not $Force) { Write-Warning "Category map for '$rootName' already exists. Use -Force to overwrite." continue } $Script:IdoitCategoryMaps[$rootName] = $obj } } } } end { } } #EndRegion '.\Public\Register-IdoitCategoryMap.ps1' 73 #Region '.\Public\Remove-IdoitCategory.ps1' -1 function Remove-IdoitCategory { <# .SYNOPSIS Remove an i-doit object category entry. .DESCRIPTION This function removes an entry from a category of an object. Currently the API only supports removal of entries of multi-value categories. .PARAMETER ObjId The ID of the i-doit object. .PARAMETER Category The category of the i-doit object from which the entry should be removed. .PARAMETER EntryId The ID of the entry to be removed from the category. .EXAMPLE Remove-IdoitCategory -ObjId 12345 -Category 'C__CATG__MEMORY' -EntryId 12 #> [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] # [Alias('Id')] [int] $ObjId, [Parameter(Mandatory = $true)] [string] $Category, [Parameter(Mandatory = $true)] [ValidateRange(1, [int]::MaxValue)] [int] $EntryId ) begin { } process { if (-not $PSCmdlet.ShouldProcess("Idoit Object with ID $Id in category '$Category'", "Remove")) { Write-Verbose "Skipping removal of object with ID $Id in category '$Category' due to ShouldProcess." return } $params = @{ objID = $ObjId category = $Category id = $EntryId # I hate this API } $apiEndpoint = 'cmdb.category.delete' $apiResult = Invoke-IdoIt -Endpoint $apiEndpoint -Params $params Write-Output $apiResult } end { } } #EndRegion '.\Public\Remove-IdoitCategory.ps1' 59 #Region '.\Public\Remove-IdoitObject.ps1' -1 function Remove-IdoitObject { <# .SYNOPSIS Removes an IdoIT object. .DESCRIPTION This function removes an IdoIT object by its ID and specified method. The used method must correspond to the objects current state (see I-doit documentation). .PARAMETER ObjId The ID of the object to be removed. .PARAMETER Method The method to use for removing the object. Valid values are 'Archive', 'Delete', 'Purge', 'QuickPurge'. Default is 'Archive'. .EXAMPLE Remove-IdoitObject -ObjId 12345 -Method 'Archive' This command will archive the object with ID 12345. .EXAMPLE Remove-IdoitObject -ObjId 12345 -Method 'QuickPurge'. This command will quickly purge the object from the database. This one uses a different API endpoint. #> [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] # [Alias('Id')] [int] $ObjId, [ValidateSet('Archive', 'Delete','Purge','QuickPurge','')] [string] $Method = 'Archive' ) begin { } process { if (-not $PSCmdlet.ShouldProcess("Idoit Object with ID $ObjId", "Remove using method $Method")) { Write-Output [PSCustomObject]@{ Success = $true; Message = "Operation dummy true due to -Whatif." } return } $params = @{ id = $ObjId } $apiEndpoint = 'cmdb.object.delete' switch ($Method) { 'Archive' { $params.status = 'C__RECORD_STATUS__ARCHIVED' } 'Delete' { $params.status = 'C__RECORD_STATUS__DELETED' } 'Purge' { $params.status = 'C__RECORD_STATUS__PURGE' } 'QuickPurge' { $apiEndpoint = 'cmdb.object.quick_purge' } } $apiResult = Invoke-IdoIt -Endpoint $apiEndpoint -Params $params Write-Output $apiResult } end { } } #EndRegion '.\Public\Remove-IdoitObject.ps1' 63 #Region '.\Public\Search-IdoitObject.ps1' -1 function Search-IdoItObject { <# .SYNOPSIS Searches for objects in the i-doit CMDB based on specified conditions. .DESCRIPTION This cmdlet allows you to search for objects in the i-doit CMDB by providing an array of conditions. The conditions are passed as an array of hashtable entries. Against the usual naming of the functions, it implements "cmdb.condition.read". .PARAMETER Conditions An array of hashtable entries defining the search conditions. Each hashtable should include keys like "property", "operator", and "value". .PARAMETER Query A string representing the a simple search query. It will find all objects that match the query. This might be used to get a quick overview of objects in the i-doit CMDB. .EXAMPLE PS> Search-IdoItObject -Conditions @( @{ "property" = "C__CATG__GLOBAL-title"; "comparison" = "like"; "value" = "*r540*" }, @{ "property" = "C__CATG__GLOBAL-type"; "comparison" = "="; "value" = "5" } ) This will search for objects where the title contains "Server" and the type is "Server". id title sysid type created updated type_title type_icon type_group_title status -- ----- ----- ---- ------- ------- ---------- --------- ---------------- ------ 540 server540 SYSID_1730365404 5 2024-10-31 09:54:24 2025-05-15 16:05:08 Server /cmdb/object-type/image/5 2 .NOTES API version 33 behaviour (or some other?) Be aware that some files are case sensitive! I know, that this is not the best practice, but I don't know who designed this. not case sensitive (title): Search-IdoItObject -Conditions @{"property" = "C__CATG__GLOBAL-title"; "comparison" = "="; "value" = "yOuR-Server"} returns records but case sensitive (type) : Search-IdoItObject -Conditions @{"property" = "C__CATG__GLOBAL-type"; "comparison" = "="; "value" = "C__OBJTYPE__SERVER"} does not return records Receiving error message like "Failed to execute the search: Error code -32099 ..." This might happen, if you want to select a field, which is not part of the database table your (implicit) searching. E.g. Search-IdoItObject -Conditions @{"property" = "C__CATG__GLOBAL-type"; "comparison" = "="; "value" = "5"} | ft returns the field type_title id title sysid type created updated type_title type_icon type_group_title status -- ----- ----- ---- ------- ------- ---------- --------- ---------------- ------ 540 server540 SYSID_1730365404 5 2024-10-31 09:54:24 2025-04-27 06:52:30 Server /cmdb/object-type/image/5 2 Sorry, it seems not possible to search for a field of this name Search-IdoItObject -Conditions @{"property" = "C__CATG__GLOBAL-type_title"; "comparison" = "="; "value" = "Server"} | ft returns an error message like this: Exception: C:\Users\wagnerw\Lokal\Github\psidoit\psidoit\Public\Search-IdoItObject.ps1:49:17 Line | 49 | Throw "Failed to execute the search: $_" | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | Failed to execute the search: Error code -32099 - i-doit system error: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use | near ')' at line 7 - #> [CmdletBinding()] param ( [Parameter(Mandatory=$true, ParameterSetName='Conditions')] [hashtable[]]$Conditions, [Parameter(Mandatory=$true, ParameterSetName='Query')] [string]$Query ) process { try { switch ($PSCmdlet.ParameterSetName) { 'Conditions' { $result = Invoke-IdoIt -Endpoint 'cmdb.condition.read' -Params @{ conditions = $Conditions } $result = $result | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Name 'objId' -Value $_.id -Force $_.PSObject.TypeNames.Insert(0, 'IdoIt.ConditionalSearchResult') $_ } Write-Output $result } 'Query' { $result = Invoke-IdoIt -Endpoint 'idoit.search' -Params @{ q = $Query } $result = $result | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Name 'objId' -Value $_.id -Force $_.PSObject.TypeNames.Insert(0, 'IdoIt.QuerySearchResult') $_ } Write-Output $result } } } catch { Throw "Failed to execute the search: $_" } } } #EndRegion '.\Public\Search-IdoitObject.ps1' 97 #Region '.\Public\Set-IdoItCategory.ps1' -1 Function Set-IdoItCategory { <# .SYNOPSIS Set category properties and values for a given object id and category. .DESCRIPTION Set-IdoItCategory sets all category properties and values for a given object id and category. .PARAMETER InputObject An object that contains the properties to be set in the category. If this is used, *all* properties are set to their respective values. .PARAMETER ObjId The object id of the object for which you want to set category properties and values. Alias: Id .PARAMETER Category The category constant name for which you want to set properties and values. Alias: Const This is a dynamic parameter and will be set based on the objects type. .PARAMETER Data A hashtable containing the data to be set in the category. The keys of the hashtable should match the property names of the category. .EXAMPLE PS> Set-IdoItCategory -ObjId 12345 -Category 'C__CATG__CPU' -Data @{title = 'New Title'; status = 1} #> [CmdletBinding( SupportsShouldProcess = $True, DefaultParameterSetName = 'Id' )] Param ( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'InputObject' )] [PSObject] $InputObject, [Parameter( Mandatory = $True, ValueFromPipelineByPropertyName = $True, Position = 0, ParameterSetName = 'Id' )] [ValidateNotNullOrEmpty()] # [Alias( 'Id' )] [Int] $ObjId, # dynamic parameter # [String] $Category, [Parameter( Mandatory = $True, ParameterSetName = 'Id' )] [Hashtable] $Data ) DynamicParam { #region Category: if user has entered an Id, try to get defined categories for this object if ($ObjId -gt 0) { $obj = Get-IdoitObject -ObjId $ObjId -ErrorAction SilentlyContinue if ($null -eq $obj) { return } $objCategoryList = Get-IdoitObjectTypeCategory -Type $obj.objecttype -ErrorAction SilentlyContinue if ($null -eq $objCategoryList) { return } $validCatConstList = $objCategoryList | Select-Object -ExpandProperty const if ($null -eq $validCatConstList) { return } } else { $validCatConstList = (Get-IdoItConstant | Where-Object Type -in ('GlobalCategory','SpecificCategory','CustomCategory')).Name } $dynParamCategory = NewDynamicParameter -Name 'Category' -ParameterType 'System.String' -ValidateSet $validCatConstList -Mandatory $true $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $RuntimeParameterDictionary.Add('Category', $dynParamCategory) #endregion return $RuntimeParameterDictionary } begin { # Initialize the parameters $params = @{} } process { $Category = $PSBoundParameters['Category'] if ($InputObject) { $ObjId = $InputObject.objId $Category = $InputObject.Category if ($null -eq $ObjId) { Write-Error "InputObject does not have a valid Id property." return } # now convert each property in the InputObject to a hashtable entry $Data = @{} foreach ($property in $InputObject.PSObject.Properties) { if ($property.Name -in 'Id','ObjId','psid_custom','Category') { continue } # skip those if ($null -ne $InputObject.psid_custom) { # do we have to translage the property names? if ($InputObject.psid_custom.containsKey($property.Name)) { $Data[$InputObject.psid_custom[$property.Name]] = $property.Value # # convert to i-doit custom field name } else { $Data[$property.Name] = $property.Value } } else { $Data[$property.Name] = $property.Value } } } $params = @{ object = $ObjId # you wouldn't believe it, here object id must be passed as "object" (not objId!) category = $Category data = $Data } # Validate the properties (now that they are tranlated to i-doit names) $params.data = ValidateProperties -Category $params.category -Properties $params.data -ErrorAction Stop if ($null -eq $params.data.keys) { $errResponse = [PSCustomObject]@{ Success = $false Error = "No valid properties found for category '$($params.category)'." } Write-Output $errResponse Write-Error $errResponse.Error return } # if at the end there are no properties to update, we can return a success message # (this is the case if all properties are read-only or not valid for this category) if ($params.data.keys.Count -eq 0) { $errResponse = [PSCustomObject]@{ Success = $true Message = "No properties to update for category '$($params.category)'." } Write-Output $errResponse return } # if the category has multi_value=1, then we need to get an entry id, otherwise we can set the values directly $cat = $objCategoryList | Where-Object { $_.const -eq $params.category } if ($cat.multi_value -eq 1 -and $Entry -eq 0) { $errResponse = [PSCustomObject]@{ Success = $false Error = "Category '$($params.category)' is a multi-value category. Currently(?) entry id is mandatory here." } Write-Output $errResponse Write-Error $errResponse return } elseif ($cat.multi_value -eq 0 -and $Data.Entry -gt 0) { $errResponse = [PSCustomObject]@{ Success = $false Error = "Category '$($params.category)' is a single-value category. Please do not specify an entry id." } Write-Output $errResponse Write-Error $errResponse return } If ($PSCmdlet.ShouldProcess("Updating category on object $ObjId")) { $result = Invoke-IdoIt -Method "cmdb.category.save" -Params $params return $result } } } #EndRegion '.\Public\Set-IdoItCategory.ps1' 145 #Region '.\Public\Set-IdoItMappedObject.ps1' -1 function Set-IdoitMappedObject { <# .SYNOPSIS Set properties of an I-doit object based on a mapping. .DESCRIPTION This function updates properties of an I-doit object based on a provided mapping. Criterieas for update are: - I-doit attribute is not readonly - The mapping attribute "update" is set to true. - Idea for the future: The value in the input object is different from the current value in I-doit. - The value must be different from the current value in I-doit. Current limitations: - Multi-value categories are not supported yet. - Calculated properties are not supported in updates. - Scriptblock actions are not supported yet. .PARAMETER InputObject A PSObject containing the properties to be set in on the I-doit category values. The properties must match the properties defined in the mapping. The properties are defined in the mapping as PSProperty. .PARAMETER ObjId The ID of the I-doit object to be updated. .PARAMETER MappingName The name of the mapping to be used for the update. This is a name of a mapping registered with Register-IdoitCategoryMap. .PARAMETER PropertyMap A mapping object that defines how the properties of the input object map to the I-doit categories. For a detailed description of the mapping, see the documentation TODO: link to documentation. .PARAMETER IncludeProperty An array of properties to include in the update. This is in Addition to the updateable properties defined in the mapping. i.e. if a property is not marked as updateable in the mapping, it can be included here to be updated. .PARAMETER ExcludeProperty An array of properties to exclude from the update. This is in Addition to the updateable properties defined in the mapping. i.e. if a property is marked as updateable in the mapping, it can be excluded here to not be updated. Excluding a property has a higher priority than including a property. If a property is both included and excluded, it will be excluded. .EXAMPLE Set-IdoitMappedObject -InputObject $inputObject -ObjId 12345 -MappingName 'MyMapping' This example updates the I-doit object with ID 12345 using the mapping defined by 'MyMapping'. .EXAMPLE Set-IdoitMappedObject -InputObject $inputObject -ObjId 12345 -PropertyMap $propertyMap This example updates the I-doit object with ID 12345 using the properties defined in $inputObject .NOTES #> [CmdletBinding(SupportsShouldProcess=$true, DefaultParameterSetName = 'MappingName')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [Object] $InputObject, [Parameter(Mandatory = $true)] # [Alias('Id')] [int] $ObjId, [Parameter(Mandatory = $true, ParameterSetName = 'MappingName')] [ValidateNotNullOrEmpty()] [Alias('Name')] [string] $MappingName, [Parameter(Mandatory = $true, ParameterSetName = 'PropertyMap')] [ValidateNotNullOrEmpty()] $PropertyMap, [string[]] $IncludeProperty = @(), [string[]] $ExcludeProperty = @() ) begin { if ($PSCmdlet.ParameterSetName -eq 'MappingName') { if ($null -eq $Script:IdoitCategoryMaps -or -not $Script:IdoitCategoryMaps.ContainsKey($MappingName)) { Write-Error "No category map registered for name '$MappingName'. Use Register-IdoitCategoryMap to register a mapping." -ErrorAction Stop } $PropertyMap = $Script:IdoitCategoryMaps[$MappingName] } } process { $obj = Get-IdoItObject -ObjId $ObjId if ($null -eq $obj) { Write-Warning "Object with objId $ObjId not found. Please use New-IdoitObject or Get-IdoItObject to get the object to change" return } $objTypeCatList = Get-IdoItObjectTypeCategory -Type $obj.Objecttype if ($null -eq $objTypeCatList) { Throw "No categories found for object type $($obj.Objecttype)" return } $notfoundCatList = $PropertyMap.mapping.category | Where-Object { $_ -notin $objTypeCatList.const } if ($notfoundCatList) { Throw "Mapping categories $($notfoundCatList -join ', ') not found for object type $($obj.Objecttype)/$($obj.type_title)" return } # get those categories, which are used in the mapping $usedCatList = $objTypeCatList | Where-Object const -in $PropertyMap.mapping.category if ($null -eq $usedCatList) { Write-Warning "No categories found for object type $($obj.Objecttype)" return } # # multi_value categories are not supported yet # $multiValueCatList = $usedCatList | Where-Object { $_.multi_value -ne 0 } # foreach ($mv in $multiValueCatList) { # Write-Warning "Categories $($mv.const) is a multi value category. This is not supported yet." # $usedCatList = $usedCatList | Where-Object { $_.const -ne $mv.const } # } $srcObject = $InputObject $overallSucess = $true foreach ($propMap in $PropertyMap) { foreach ($thisMapping in $propMap.Mapping) { $thisCat = $usedCatList | Where-Object { $_.Const -eq $thisMapping.Category } if ($null -eq $thisCat) { Continue # unsupported category } $catValues = Get-IdoItCategory -ObjId $obj.Id -Category $thisMapping.Category if ($null -eq $catValues) { Write-Warning "No categories found for object type $($thisMapping.Category)($($obj.Objecttype))" continue } # if no action is defined, add the property. If the corresponding catvalue holds an array, the property is added as an array # TODO: Multivalue categories are not supported yet # create a list of property names to update # include those which are defined in the mapping as updateable # those which are defined by IncludeProperty parameter # exclude those which are defined by ExcludeProperty parameter $PSpropNameList = @($thisMapping.PropertyList | Where-Object { $_.Update -eq $true }).PSProperty + $IncludeProperty | Where-Object { $_ -notin $ExcludeProperty } foreach ($propListItem in ($thisMapping.PropertyList | Where-Object { $_.PSProperty -in $PSpropNameList -and [String]::IsNullOrEmpty($_.Action) })) { $attr, $field, $index = $propListItem.iAttribute -split '\.' if ($attr[0] -eq '!') { Write-Error "The iAttribute '$($propListItem.iAttribute)' is not supported for update. It must be or be a simple key." continue } # arrays are currently support under the condition that the attribute has a dialog or dialog_plus ui if ($catValues.$($attr) -is [System.Array]) { $catInfo = Get-IdoItCategoryInfo -Category $thisMapping.Category if ($catInfo.$attr.info.type -notin @('dialog', 'dialog_plus','multiselect')) { Write-Warning "The iAttribute '$($propListItem.iAttribute)' is not a dialog,dialog_plus or multiselect type. Arrays are not supported for this type." continue } $changedProperty = [string]::Format("{0}.{1}: {2}->{3}", $thisMapping.Category, $attr, ($catValues.$attr.$field -join ', '), ($srcObject.$($propListItem.PSProperty) -join ', ')) if ($PSCmdlet.ShouldProcess($changedProperty, "Update property $($obj.Title)")) { $result = Set-IdoItCategory -ObjId $obj.Id -Category $thisMapping.Category -Data @{ $attr = $srcObject.$($propListItem.PSProperty) } $overallSucess = $overallSucess -and $result.success } } else { # change only if the value is different; Case sensitive! if ($catValues.$attr.$field -cne $srcObject.$($propListItem.PSProperty)) { $changedProperty = [string]::Format("{0}.{1}. {2}->{3}", $thisMapping.Category, $propListItem.iAttribute, $catValues.$($propListItem.iAttribute), $srcObject.$($propListItem.PSProperty)) if ($PSCmdlet.ShouldProcess($changedProperty, "Update property $($obj.Title)")) { $result = Set-IdoItCategory -ObjId $obj.Id -Category $thisMapping.Category -Data @{ $attr = $srcObject.$($propListItem.PSProperty) } $overallSucess = $overallSucess -and $result.success } } } } } } Write-Output $overallSucess } end { } } #EndRegion '.\Public\Set-IdoItMappedObject.ps1' 182 #Region '.\Public\Show-IdoitObjectTree.ps1' -1 function Show-IdoitObjectTree { <# .SYNOPSIS Displays the full object tree for a given i-doit object ID or input object on the console. .DESCRIPTION This cmdlet retrieves and displays the object tree for a specified i-doit object ID or input object, including its categories and properties. It formats the output based on the specified style, which can be a table, JSON, or Spectre JSON format. If you have installed the module "PwshSpectreConsole", you can get a nice view of the results by using: Format-SpectreJson -Data (Show-IdoitObjectTree -ObjId 37) -Depth 5 .PARAMETER ObjId The ID of the i-doit object for which to retrieve the tree. This parameter is used when the input is an object ID. .PARAMETER InputObject The object already containing the full tree. .PARAMETER Style The style in which to display the object tree. Options are 'FormatTable', 'Json', or 'SpectreJson'. Default is 'FormatTable'. .PARAMETER ExcludeCategory An array of category constants to exclude from the results. Default is 'C__CATG__LOGBOOK'. .PARAMETER IncludeEmptyCategories A switch to include empty categories in the output. .EXAMPLE Show-IdoitObjectTree -ObjId 37 Displays the object tree for the i-doit object with ID 37 in a formatted table. .EXAMPLE $object = Get-IdoitObject -ObjId 37 Show-IdoitObjectTree -InputObject $object -Style Json Displays the object tree for the i-doit object with ID 37 in JSON format. .EXAMPLE Show-IdoitObjectTree -ObjId 37 -Style SpectreJson Displays the object tree for the i-doit object with ID 37 in Spectre JSON format, if the Spectre module is available. Falls back to JSON if not. .NOTES #> [CmdletBinding(DefaultParameterSetName = 'ObjId')] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ObjId')] [ValidateNotNullOrEmpty()] # [Alias('ObjID')] [int] $ObjId, [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'InputObject')] [ValidateNotNullOrEmpty()] [PSObject] $InputObject, [ValidateSet('FormatList', 'FormatTable', 'Json', 'SpectreJson')] [string] $Style = 'FormatTable', [string[]] $ExcludeCategory = 'C__CATG__LOGBOOK', [switch] $IncludeEmptyCategories ) begin { } process { if ($PSCmdlet.ParameterSetName -eq 'InputObject') { $fullObj = $InputObject } else { $splatGetIdoitObjectTree = @{ ObjId = $ObjId ExcludeCategory = $ExcludeCategory IncludeEmptyCategories = $IncludeEmptyCategories } $fullObj = Get-IdoitObjectTree @splatGetIdoitObjectTree if ($null -eq $fullObj) { Write-Error "Object with ID $ObjId not found or no categories available." return } } switch ($Style) { 'FormatList' { $fullObj | Select-Object -ExcludeProperty Categories | Format-List $fullObj.Categories | Foreach-Object { $_.Properties | Format-List -GroupBy Category } } 'FormatTable' { $fullObj | Select-Object -ExcludeProperty Categories | Format-Table -AutoSize -Wrap $fullObj.Categories | Foreach-Object { $_.Properties | Format-Table -GroupBy Category -AutoSize -Wrap } } 'Json' { $fullObj | ConvertTo-Json -Depth 5 } 'SpectreJson' { if (Get-Command -Name Format-SpectreJson -ErrorAction SilentlyContinue) { Format-SpectreJson -Data $fullObj -Depth 5 } else { Write-Warning "ConvertTo-SpectreJson function not found. Please ensure the Spectre module is imported." $fullObj | ConvertTo-Json -Depth 5 } } default { Write-Error "Unknown style: $Style" } } } end { } } #EndRegion '.\Public\Show-IdoitObjectTree.ps1' 115 #Region '.\Public\Start-IdoitApiTrace.ps1' -1 function Start-IdoitApiTrace { <# .SYNOPSIS Start the Idoit API trace. .DESCRIPTION This function starts the Idoit API trace by initializing a global variable to store the trace data. .PARAMETER None No parameters are required for this function. .EXAMPLE Start-IdoitApiTrace # Starts the Idoit API trace and initializes the global variable. .NOTES Any data already in the $Global:IdoItAPITrace variable will be lost. This function is intended for use in testing scenarios to capture API calls. #> [CmdletBinding(SupportsShouldProcess = $True)] [System.Diagnostics.CodeAnalysis.SuppressMessage('PSAVoidGlobalVars', '', Justification = 'Global variable is used outside this scope.')] param () # Suppress PSUseDeclaredVarsMoreThanAssignments for $Global:IdoItAPITrace # because it is intentionally assigned but not used in this scope. if ($PSCmdlet.ShouldProcess('IdoitApiTrace', 'Start')) { Write-Verbose -Message 'Starting Idoit API trace...' $Global:IdoItAPITrace = @() } } #EndRegion '.\Public\Start-IdoitApiTrace.ps1' 26 #Region '.\Public\Stop-IdoitApiTrace.ps1' -1 function Stop-IdoitApiTrace { <# .SYNOPSIS Stop the Idoit API trace. .DESCRIPTION This function stops the Idoit API trace by removing the global variable that stores the trace data. .PARAMETER None No parameters are required for this function. .EXAMPLE Stop-IdoitApiTrace # Stops the Idoit API trace and removes the global variable. .NOTES This function is intended for use in testing scenarios to stop capturing API calls. #> [CmdletBinding(SupportsShouldProcess = $True)] [System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidGlobalVars', '', Justification = 'Global variable is used outside this scope.')] param () if ($PSCmdlet.ShouldProcess('IdoitApiTrace', 'Stop')) { Write-Verbose -Message 'Stopping Idoit API trace...' Remove-Variable -Name 'IdoitApiTrace' -Scope Global -ErrorAction SilentlyContinue } } #EndRegion '.\Public\Stop-IdoitApiTrace.ps1' 23 |