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\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 '.\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

    $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\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.
 
        .PARAMETER Id
        The object id of the object for which you want to retrieve category properties and values.
        Alias: ObjId
 
        .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).
 
        .EXAMPLE
        PS> Get-IdoItCategory -Id 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('ObjId')]
        [int] $Id,

        # dynamic parameter
        # [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        # [Alias('Const')]
        # [string] $Category,

        [Parameter(Position = 2, ParameterSetName = "Id")]
        [int] $Status = 2
    )
    DynamicParam {
        #region Category: if user has entered an Id, try to get defined categories for this object
        if ($Id -gt 0) {
            $obj = Get-IdoItObject -Id $Id -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 = $Id
            category = $PSBoundParameters['Category']
            status = $Status
        }
        $result = Invoke-Idoit -Method 'cmdb.category.read' -Params $params
        if ($null -ne $result) {
            foreach ($item in $result) {
                $item.PSObject.TypeNames.Insert(0, 'Idoit.Category')
                $item
            }
        }
    }

    end {

    }
}
#EndRegion '.\Public\Get-IdoItCategory.ps1' 86
#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
    #>

        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
        $result.PSObject.Typenames.Add('Idoit.CategoryInfo')
        # TODO: when reading by Id, the category is not returned => results in a warning
        if ([string]::IsNullOrEmpty($Category)) {
            Write-Error "No category found for $Category"
            return $null
        }
        $result | Add-Member -MemberType NoteProperty -Name Category -Value $Category -Force
        return $result
    }
#EndRegion '.\Public\Get-IdoitCategoryInfo.ps1' 62
#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-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 -Id 540
 
      .PARAMETER Id
       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)]
        [int] $Id
    )

    process {
        $apiResult = Invoke-Idoit -Method 'cmdb.object.read' -Params @{ id = $Id }
        if ($null -ne $apiResult) {
            $apiResult.PSObject.TypeNames.Insert(0, 'Idoit.Object')
        }
        else {
            Write-Error -Message "Object not found Id=$Id"
        }
        $apiResult
    }
}

#EndRegion '.\Public\Get-IdoitObject.ps1' 35
#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 Id
        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 -Id 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 (
        [Int[]] $Id,

        [Alias('Title')]
        [string[]] $Const,

        [Switch] $Enabled,

        [int] $Skip,
        [Int] $Limit
    )

    #checkCmdbConnection

    Process {
        $params= @{}
        $filter = @{}

        foreach ($PSBoundParameter in $PSBoundParameters.Keys) {
            switch ($PSBoundParameter) {
                "Id" {
                        $filter.Add("ids", @($Id))
                    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 { $_.PSObject.TypeNames.Add('Idoit.ObjectType') }
        Return $result
    }
}
#EndRegion '.\Public\Get-IdoItObjectType.ps1' 96
#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 Type
    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' }
    ...
 
    .NOTES
    #>

    [CmdletBinding()]
    [OutputType([System.Object[]])]
    Param (
        [Parameter (Mandatory = $True, ValueFromPipeline = $True)]
        [Alias('TypeId','Id')]
        $Type
    )

    Process {
        $params = @{}
        $params.Add("type", $Type)

        $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' 54
#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)"
        Throw $errMsg
    } else {
        If ( $body.Id -ne $apiResult.id) {
            Throw "Request id mismatch. Expected value was $RequestID but it is $($apiResult.id)"
        }
        $apiResult.result
    }
}
#EndRegion '.\Public\Invoke-IdoIt.ps1' 113
#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 {
                        $_.PSObject.TypeNames.Insert(0, 'IdoIt.ConditionalSearchResult')
                        $_
                    }
                    Write-Output $result
                }
                'Query' {
                    $result = Invoke-IdoIt -Endpoint 'idoit.search' -Params @{ query = $Query }
                    $result = $result | ForEach-Object {
                        $_.PSObject.TypeNames.Insert(0, 'IdoIt.QuerySearchResult')
                        $_
                    }
                    Write-Output $result
                }
            }
        } catch {
            Throw "Failed to execute the search: $_"
        }
    }
}
#EndRegion '.\Public\Search-IdoitObject.ps1' 95
#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