DynatracePS.psm1

### --- PUBLIC FUNCTIONS --- ###
#Region - Connect-DynatraceAccountManagement.ps1
function Connect-DynatraceAccountManagement {
<#
    .SYNOPSIS
        Connect to the Dynatrace Account Management API for SaaS. internal use
 
    .DESCRIPTION
        Connect to the Dynatrace Account Management API for SaaS. internal use
 
    .PARAMETER OauthClientSecret
        Client secret generated from the Manage account API OAuth Clients page
 
    .PARAMETER AccountUuid
        Account UUID from Dynatrace
 
    .PARAMETER GetNewToken
        Force getting a fresh token
 
    .EXAMPLE
        Connect-DynatraceAccountManagement
 
    .NOTES
        https://account.dynatrace.com/my/enterprise-api?
    #>


    [CmdletBinding()]
    param(
        [String]$OauthClientSecret,
        [String]$AccountUuid,
        [Switch]$GetNewtoken
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $config = Get-DynatracePSConfig
        $AccountUuid = $config.AccountUuid
        $OauthClientSecret = $config.OauthClientSecret

        if (-not $AccountUuid) {
            Write-Warning "[$($MyInvocation.MyCommand.Name)] AccountUuid is required. Please use Set-DynatracePSConfig first. Exiting..."
            break
        }
        if (-not $OauthClientSecret) {
            Write-Warning "[$($MyInvocation.MyCommand.Name)] OauthClientSecret is required. Please use Set-DynatracePSConfig first. Exiting..."
            break
        }

        $GetTokenURL = 'https://sso.dynatrace.com/sso/oauth2/token'
        $client_id = (($OauthClientSecret -split '\.') | Select-Object -First 2) -join '.'
        $scope = 'account-idm-read account-idm-write'
        $resource = "urn:dtaccount:$AccountUuid"
        $Body = @{
            grant_type = 'client_credentials'
            client_id = $client_id
            client_secret = $OauthClientSecret
            scope = $scope
            resource = $resource
        }
        $ContentType = 'application/x-www-form-urlencoded'
    }

    process {

        $ExistingTokenExpiration = $MyInvocation.MyCommand.Module.PrivateData.token_expires
        $Now = Get-Date
        if (($Now -lt $ExistingTokenExpiration) -and (-not $GetNewtoken)) {
            Write-Debug "[$($MyInvocation.MyCommand.Name)] Existing token should still be good, using it"
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Existing token should still be good, using it"
            $GetTokenReturn = $MyInvocation.MyCommand.Module.PrivateData |
                Select-Object scope,token_type,expires_in,access_token,resource
            Write-Debug "[$($MyInvocation.MyCommand.Name)] Expires_in: $($GetTokenReturn.expires_in)"
        } else {
            Write-Debug "[$($MyInvocation.MyCommand.Name)] Getting new token"
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Getting new token"
            try {
                $GetTokenReturn = Invoke-RestMethod -Uri $GetTokenURL -Method POST -Body $body -ContentType $ContentType
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Adding results to module PrivateData"
                Write-Debug "[$($MyInvocation.MyCommand.Name)] Adding results to module PrivateData"
                $MyInvocation.MyCommand.Module.PrivateData = @{
                    'scope' = $GetTokenReturn.scope
                    'token_type' = $GetTokenReturn.token_type
                    'expires_in' = $GetTokenReturn.expires_in
                    'access_token' = $GetTokenReturn.access_token
                    'resource' = $GetTokenReturn.resource
                    'token_expires' = (Get-Date).AddSeconds($GetTokenReturn.expires_in)
                }
                Write-Debug "[$($MyInvocation.MyCommand.Name)] Expires_in: $($GetTokenReturn.expires_in)"
            } catch {
                Write-Warning "[$($MyInvocation.MyCommand.Name)] Problem with invoke-restmethod $GetTokenURL"
                $_
                break
            }
        }

        # return everything to the pipeline
        $GetTokenReturn
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}


Export-ModuleMember -Function Connect-DynatraceAccountManagement
#EndRegion - Connect-DynatraceAccountManagement.ps1
#Region - Get-DynatraceContainer.ps1
function Get-DynatraceContainer {
<#
    .SYNOPSIS
        Gets containers in Dynatrace environment
 
    .DESCRIPTION
        Gets containers in Dynatrace environment
 
    .EXAMPLE
        Get-DynatraceContainer
 
    .NOTES
        https://api.dynatrace.com/spec/#/
#>


    [CmdletBinding()]
    param()

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $uri = "https://$EnvironmentID.live.dynatrace.com/api/v2/entities"

        $GetParameter = @{
            entitySelector = 'type("CONTAINER_GROUP_INSTANCE")'
            pageSize = 500
        }

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
    }

    process {
        $splatParameters = @{
            Uri = $uri
            GetParameter = $GetParameter
            RestResponseProperty = 'entities'
        }
        Invoke-DynatraceAPIMethod @splatParameters
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceContainer
#EndRegion - Get-DynatraceContainer.ps1
#Region - Get-DynatraceEffectivePermission.ps1
function Get-DynatraceEffectivePermission {
<#
    .SYNOPSIS
        Gets effective permissions for a user or group
 
    .DESCRIPTION
        Gets effective permissions for a user or group
 
    .PARAMETER levelType
        The type of the policy level. Valid values: account or environment
 
    .PARAMETER levelId
        The ID of the policy level. for account, use the UUID of the account. For environment, use the ID of the environment.
 
    .PARAMETER entityType
        The entity type. Valid values: user or group
 
    .PARAMETER entityId
        The entity id
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceEffectivePermission -levelType account -levelID <accountUuid>
 
    .EXAMPLE
        Get-DynatraceEffectivePermission -levelType environment -levelID <environment id>
 
    .NOTES
        https://api.dynatrace.com/spec/#/Policy%20management/PolicyController_getEffectivePermissions
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet('account','environment')]
        [String]$levelType,
        [Parameter(Mandatory)]
        [String]$levelID,
        [String]$entityType,
        [String]$entityId,
        [switch]$explain,
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $RestPath = "/iam/v1/resolution/$levelType/$levelID/effectivepermissions"
        $UrlParameters = "?entityType=$entityType&entityID=$entityID&explain=$explain"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] RestPath: $RestPath"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] URLParameters: $URLParameters"
        $path = "$RestPath$URLParameters"
    }

    process {
        try {
            $splatParameters = @{
                RestPath =$path
                OutputAsJson = $OutputAsJson
            }
            Invoke-DynatraceAccountManagementAPIMethod @splatParameters
        } catch {
            $_
            break
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceEffectivePermission
#EndRegion - Get-DynatraceEffectivePermission.ps1
#Region - Get-DynatraceEntity.ps1
function Get-DynatraceEntity {
<#
    .SYNOPSIS
        Get entities in Dynatrace environment
 
    .DESCRIPTION
        Get entities in Dynatrace environment
 
    .PARAMETER Type
        The type of entity to get
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceEntity -Type HOST_GROUP
 
    .NOTES
        https://api.dynatrace.com/spec/#/
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [String]$Type,
        [Switch]$outputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $uri = "https://$EnvironmentID.live.dynatrace.com/api/v2/entities"

        $GetParameter = @{
            entitySelector = "type(`"$Type`")"
            pageSize = 200
        }

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
    }

    process {
        $splatParameters = @{
            Uri = $uri
            GetParameter = $GetParameter
            RestResponseProperty = 'entities'
        }
        $output = Invoke-DynatraceAPIMethod @splatParameters
        if ($OutputAsJson) {
            $output | ConvertTo-Json -Depth 6
        } else {
            $output
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceEntity
#EndRegion - Get-DynatraceEntity.ps1
#Region - Get-DynatraceEntityProperty.ps1
function Get-DynatraceEntityProperty {
<#
    .SYNOPSIS
        Get properties for a specified entity in Dynatrace environment
 
    .DESCRIPTION
        Get properties for a specified entity in Dynatrace environment
 
    .PARAMETER entityID
        The entity ID
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceEntityProperty -entityID HOST-9FADEDA85A24F460
 
    .NOTES
        https://api.dynatrace.com/spec/#/
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [String]$entityID,
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $uri = "https://$EnvironmentID.live.dynatrace.com/api/v2/entities/$entityID"

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
    }

    process {
        $splatParameters = @{
            Uri = $uri
        }
        $output = Invoke-DynatraceAPIMethod @splatParameters

        if ($OutputAsJson) {
            $output | ConvertTo-Json -Depth 6
        } else {
            $output
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceEntityProperty
#EndRegion - Get-DynatraceEntityProperty.ps1
#Region - Get-DynatraceEntityType.ps1
function Get-DynatraceEntityType {
<#
    .SYNOPSIS
        Get list of entity types in Dynatrace environment
 
    .DESCRIPTION
        Get list of entity types in Dynatrace environment
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceEntityType
 
    .NOTES
        https://api.dynatrace.com/spec/#/
#>


    [CmdletBinding()]
    param(
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $uri = "https://$EnvironmentID.live.dynatrace.com/api/v2/entityTypes"

        $GetParameter = @{
            pageSize = 200
        }

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
    }
    process {
        $splatParameters = @{
            Uri = $uri
            GetParameter = $GetParameter
            RestResponseProperty = 'types'
        }
        $output = Invoke-DynatraceAPIMethod @splatParameters
        if ($OutputAsJson) {
            $output | ConvertTo-Json -Depth 6
        } else {
            $output
        }

    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceEntityType
#EndRegion - Get-DynatraceEntityType.ps1
#Region - Get-DynatraceEnvironment.ps1
function Get-DynatraceEnvironment {
<#
    .SYNOPSIS
        Lists all environments and management zones of a Dynatrace account
 
    .DESCRIPTION
        Lists all environments and management zones of a Dynatrace account
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceEnvironment
 
    .NOTES
        https://api.dynatrace.com/spec/#/Environment%20management/EnvironmentResourcesController_getEnvironmentResources
#>


    [CmdletBinding()]
    param(
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $AccountUuid = (Get-DynatracePSConfig).AccountUuid
        $RestPath = "/env/v1/accounts/$($AccountUuid)/environments"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $RestPath"
    }

    process {
        try {
            $splatParameters = @{
                RestPath =$RestPath
                OutputAsJson = $OutputAsJson
            }
            Invoke-DynatraceAccountManagementAPIMethod @splatParameters
        } catch {
            $_
            break
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceEnvironment
#EndRegion - Get-DynatraceEnvironment.ps1
#Region - Get-DynatraceGroup.ps1
function Get-DynatraceGroup {
<#
    .SYNOPSIS
        List the groups on a Dynatrace account
 
    .DESCRIPTION
        List the groups on a Dynatrace account
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceGroup
 
    .NOTES
        https://api.dynatrace.com/spec/#/Group%20management/GroupsController_getGroups
#>


    [CmdletBinding()]
    param(
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $AccountUuid = (Get-DynatracePSConfig).AccountUuid
        $RestPath = "/iam/v1/accounts/$($AccountUuid)/groups"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $RestPath"
    }

    process {
        try {
            $splatParameters = @{
                RestPath =$RestPath
                OutputAsJson = $OutputAsJson
            }
            $return = Invoke-DynatraceAccountManagementAPIMethod @splatParameters
        } catch {
            $_
            break
        }
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Count of results: $($return.count)"
        if ($OutputAsJson) {
            $return
        } else {
            $return.items
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceGroup
#EndRegion - Get-DynatraceGroup.ps1
#Region - Get-DynatraceGroupPermission.ps1
function Get-DynatraceGroupPermission {
<#
    .SYNOPSIS
        List all permissions for a single user group
 
    .DESCRIPTION
        List all permissions for a single user group
 
    .PARAMETER GroupUuid
        The UUID of a user group
 
    .PARAMETER GroupName
        The name of a group
 
    .PARAMETER OutputNotNested
        Set to make it so output is not nested, helpful for exporting to excel
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceGroupPermission -groupUuid e5e9b12d-daf8-40d0-a6b5-7094667dd142
 
    .EXAMPLE
        Get-DynatraceGroupPermission -GroupName 'Monitoring Viewer'
 
    .NOTES
        https://api.dynatrace.com/spec/#/Group%20management/GroupsController_getUsersForGroup
 
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ParameterSetName = 'Uuid')]
        [String]$groupUuid,
        [Parameter(Mandatory,ParameterSetName = 'Name')]
        [String]$GroupName,
        [switch]$OutputNotNested,
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $AccountUuid = (Get-DynatracePSConfig).AccountUuid

        if ($GroupName) {
            $groupUuid = (Get-DynatraceGroup | Where-Object{$_.Name -eq $GroupName}).Uuid
        }
        $RestPath = "/iam/v1/accounts/$AccountUuid/groups/$groupUuid/permissions"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $RestPath"
        $output = @()
    }

    process {
        try {
            $splatParameters = @{
                RestPath =$RestPath
                OutputAsJson = $OutputAsJson
            }
            $result = Invoke-DynatraceAccountManagementAPIMethod @splatParameters

            if ($OutputNotNested) {
                foreach ($permission in $result.permissions) {
                    $output += [pscustomobject]@{
                        uuid = $result.uuid;
                        name = $result.name;
                        owner = $result.owner;
                        description = $result.description;
                        hidden = $result.hidden;
                        createdAt = $result.createdAt;
                        updatedAt = $result.updatedAt;
                        permissionName = $permission.permissionName;
                        Scope = $permission.scope;
                        ScopeType = $permission.scopeType;
                        permissionCreatedAt = $permission.createdAt;
                        permissionUpdatedAt = $permission.updatedAt;
                    }
                }
            } else {
                $output = $result
            }
        } catch {
            $_
            break
        }
    }
    end {
        $output
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceGroupPermission
#EndRegion - Get-DynatraceGroupPermission.ps1
#Region - Get-DynatraceGroupUser.ps1
function Get-DynatraceGroupUser {
<#
    .SYNOPSIS
        List all members of a group on a Dynatrace account
 
    .DESCRIPTION
        List all members of a group on a Dynatrace account
 
    .PARAMETER GroupUuid
        The UUID of the user group
 
    .PARAMETER GroupName
        The Name of the group
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceGroupUser -groupUuid e5e9b12d-daf8-40d0-a6b5-7094667dd142
 
    .EXAMPLE
        Get-DynatraceGroupUser -GroupName 'Monitoring Viewer'
 
    .NOTES
        https://api.dynatrace.com/spec/#/Group%20management/GroupsController_getUsersForGroup
 
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ParameterSetName = 'Uuid')]
        [String]$groupUuid,
        [Parameter(Mandatory,ParameterSetName = 'Name')]
        [String]$GroupName,
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $AccountUuid = (Get-DynatracePSConfig).AccountUuid

        if ($GroupName) {
            $groupUuid = (Get-DynatraceGroup | Where-Object{$_.Name -eq $GroupName}).Uuid
        }
        $RestPath = "/iam/v1/accounts/$AccountUuid/groups/$groupUuid/users"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $RestPath"
    }

    process {
        try {
            $splatParameters = @{
                RestPath =$RestPath
                OutputAsJson = $OutputAsJson
            }
            $return = Invoke-DynatraceAccountManagementAPIMethod @splatParameters
        } catch {
            $_
            break
        }
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Count of results: $($return.count)"
        if ($OutputAsJson) {
            $return
        } else {
            $return.items
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceGroupUser
#EndRegion - Get-DynatraceGroupUser.ps1
#Region - Get-DynatraceHost.ps1
function Get-DynatraceHost {
<#
    .SYNOPSIS
        Gets hosts in Dynatrace environment
 
    .DESCRIPTION
        Gets hosts in Dynatrace environment
 
    .EXAMPLE
        Get-DynatraceHost
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/entity-v2
#>


    [CmdletBinding()]
    param()

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"
    }

    process {
        Get-DynatraceEntity -Type 'HOST'
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceHost
#EndRegion - Get-DynatraceHost.ps1
#Region - Get-DynatraceHostGroup.ps1
function Get-DynatraceHostGroup {
<#
    .SYNOPSIS
        Get host groups in Dynatrace environment
 
    .DESCRIPTION
        Get host groups in Dynatrace environment
 
    .EXAMPLE
        Get-DynatraceHostGroup
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/entity-v2
#>


    [CmdletBinding()]
    param()

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"
    }

    process {
        Get-DynatraceEntity -Type 'HOST_GROUP'
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceHostGroup
#EndRegion - Get-DynatraceHostGroup.ps1
#Region - Get-DynatraceHostGroupProperty.ps1
function Get-DynatraceHostGroupProperty {
<#
    .SYNOPSIS
        Get properties for a host group
 
    .DESCRIPTION
        Get properties for a host group
 
    .PARAMETER Id
        Unique id of the host group
 
    .PARAMETER Name
        Name of the host group
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceHostGroupProperty -id hostgroupid
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/entity-v2/get-entity
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ParameterSetName = 'id')]
        [string]$Id,
        [Parameter(Mandatory,ParameterSetName = 'name')]
        [string]$Name,
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        if ($Name) {
            $Id = (Get-DynatraceHostGroup | Where-Object {$_.displayName -eq $Name}).entityID
        }
    }

    process {
        $splatParm = @{
            entityID = $Id
            OutputAsJson = $OutputAsJson
        }
        Get-DynatraceEntityProperty @splatParm
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceHostGroupProperty
#EndRegion - Get-DynatraceHostGroupProperty.ps1
#Region - Get-DynatraceHostProperty.ps1
function Get-DynatraceHostProperty {
<#
    .SYNOPSIS
        Get properties for a host
 
    .DESCRIPTION
        Get properties for a host
 
    .PARAMETER Id
        Unique id of the host
 
    .PARAMETER Name
        Name of the host
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceHostProperty -id hostid
 
    .EXAMPLE
        Get-DynatraceHostProperty -Name hostname -OutputAsJson
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/entity-v2/get-entity
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ParameterSetName = 'id')]
        [string]$Id,
        [Parameter(Mandatory,ParameterSetName = 'name')]
        [string]$Name,
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        if ($Name) {
            $Id = (Get-DynatraceHost | Where-Object {$_.displayName -eq $Name}).entityId
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Got $Name entityID value of $Id"
        }
    }

    process {
        $splatParm = @{
            entityID = $Id
            OutputAsJson = $OutputAsJson
        }
        Get-DynatraceEntityProperty @splatParm
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceHostProperty
#EndRegion - Get-DynatraceHostProperty.ps1
#Region - Get-DynatraceManagementZone.ps1
function Get-DynatraceManagementZone {
<#
    .SYNOPSIS
        Get list of management zones in Dynatrace environment
 
    .DESCRIPTION
        Get list of management zones in Dynatrace environment
 
    .EXAMPLE
        Get-DynatraceManagementZone
 
    .NOTES
        https://api.dynatrace.com/spec/#/
#>


    [CmdletBinding()]
    param()

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $uri = "https://$EnvironmentID.live.dynatrace.com/api/config/v1/managementZones"

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
    }

    process {
        $splatParameters = @{
            Uri = $uri
            RestResponseProperty = 'values'
        }
        Invoke-DynatraceAPIMethod @splatParameters
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceManagementZone
#EndRegion - Get-DynatraceManagementZone.ps1
#Region - Get-DynatraceManagementZoneProperty.ps1
function Get-DynatraceManagementZoneProperty {
<#
    .SYNOPSIS
        Get properties of a management zone in Dynatrace environment
 
    .DESCRIPTION
        Get properties of a management zone in Dynatrace environment
 
    .PARAMETER Id
        The unique id of the management zone. Required if not using name
 
    .PARAMETER Name
        The name of the management zone. Required if not using id
 
    .PARAMETER OutputAsJson
        Output the management zone properties as Json
 
    .EXAMPLE
        Get-DynatraceManagementZoneProperty -id 1234554321
 
        Get management zone properties associated with management zone with id of 1234554321.
 
    .EXAMPLE
        Get-DynatraceManagementZoneProperty -Name PROD_WINDOWS -OutputAsJson
 
        Get management zone properties for the management zone named PROD_WINDOWS and output them as json
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/configuration-api/management-zones-api/get-mz
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ParameterSetName = 'id')]
        [string]$id,
        [Parameter(Mandatory,ParameterSetName = 'name')]
        [string]$Name,
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        if ($Name) {
            $id = (Get-DynatraceManagementZone | Where-Object {$_.Name -eq $Name}).id
        }

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID
        $uri = "https://$EnvironmentID.live.dynatrace.com/api/config/v1/managementZones/$id"

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
    }

    process {
        $splatParameters = @{
            Uri = $uri
        }
        $output = Invoke-DynatraceAPIMethod @splatParameters

        if ($OutputAsJson) {
            $output | ConvertTo-Json -Depth 6
        } else {
            $output
        }

    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceManagementZoneProperty
#EndRegion - Get-DynatraceManagementZoneProperty.ps1
#Region - Get-DynatracePermission.ps1
function Get-DynatracePermission {
<#
    .SYNOPSIS
        Lists all available permissions
 
    .DESCRIPTION
        Lists all available permissions
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatracePermission
 
    .NOTES
        https://api.dynatrace.com/spec/#/Reference%20data/ReferenceDataController_getPermissions
#>


    [CmdletBinding()]
    param(
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $RestPath = "/ref/v1/account/permissions"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $RestPath"
    }

    process {
        try {
            $splatParameters = @{
                RestPath =$RestPath
                OutputAsJson = $OutputAsJson
            }
            Invoke-DynatraceAccountManagementAPIMethod @splatParameters
        } catch {
            $_
            break
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatracePermission
#EndRegion - Get-DynatracePermission.ps1
#Region - Get-DynatraceProcess.ps1
function Get-DynatraceProcess {
<#
    .SYNOPSIS
        Get list of monitored processes from Dynatrace environment
 
    .DESCRIPTION
        Get list of monitored processes from Dynatrace environment
 
    .EXAMPLE
        Get-DynatraceProcess
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/entity-v2
#>


    [CmdletBinding()]
    param()

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"
    }

    process {
        Get-DynatraceEntity -Type 'PROCESS_GROUP_INSTANCE'
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceProcess
#EndRegion - Get-DynatraceProcess.ps1
#Region - Get-DynatraceProcessGroup.ps1
function Get-DynatraceProcessGroup {
<#
    .SYNOPSIS
        Get process groups in Dynatrace environment
 
    .DESCRIPTION
        Get process groups in Dynatrace environment
 
    .EXAMPLE
        Get-DynatraceProcessGroup
 
    .NOTES
        https://www.dynatrace.com/support/help/how-to-use-dynatrace/process-groups
 
#>


    [CmdletBinding()]
    param()

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"
    }

    process {
        Get-DynatraceEntity -Type 'PROCESS_GROUP'
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceProcessGroup
#EndRegion - Get-DynatraceProcessGroup.ps1
#Region - Get-DynatraceProcessGroupProperty.ps1
function Get-DynatraceProcessGroupProperty {
<#
    .SYNOPSIS
        Get process group properties from Dynatrace environment
 
    .DESCRIPTION
        Get process group properties from Dynatrace environment
 
    .PARAMETER Id
        Unique id of the process group
 
    .PARAMETER Name
        Name of the process group
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceProcessGroupProperty -Id <process-group-id>
 
    .EXAMPLE
        Get-DynatraceProcessGroupProperty -Name <process-group-name> -OutputAsJson
 
    .NOTES
        https://www.dynatrace.com/support/help/how-to-use-dynatrace/process-groups
 
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ParameterSetName = 'id')]
        [string]$Id,
        [Parameter(Mandatory,ParameterSetName = 'name')]
        [string]$Name,
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        if ($Name) {
            $entityIds = (Get-DynatraceProcessGroup | Where-Object {$_.displayName -eq $Name}).entityID
        } else {
            $entityIds = $Id
        }
    }

    process {
        foreach ($entityId in $entityIDs) {
            $splatParm = @{
                entityID = $entityId
                OutputAsJson = $OutputAsJson
            }
            Get-DynatraceEntityProperty @splatParm
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceProcessGroupProperty
#EndRegion - Get-DynatraceProcessGroupProperty.ps1
#Region - Get-DynatraceProcessProperty.ps1
function Get-DynatraceProcessProperty {
<#
    .SYNOPSIS
        Get a process's properties from Dynatrace environment
 
    .DESCRIPTION
        Get a process's properties from Dynatrace environment
 
    .PARAMETER Id
        Unique id of the process
 
    .PARAMETER Name
        Name of the process
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceProcessProperty -Id <process-id>
 
    .EXAMPLE
        Get-DynatraceProcessProperty -Name <process-name> -OutputAsJson
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/entity-v2/get-entity
 
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ParameterSetName = 'id')]
        [string]$Id,
        [Parameter(Mandatory,ParameterSetName = 'name')]
        [string]$Name,
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        if ($Name) {
            $entityIds = (Get-DynatraceProcess | Where-Object {$_.displayName -eq $Name}).entityID
        } else {
            $entityIds = $Id
        }
    }

    process {
        foreach ($entityId in $entityIDs) {
            $splatParm = @{
                entityID = $entityId
                OutputAsJson = $OutputAsJson
            }
            Get-DynatraceEntityProperty @splatParm
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceProcessProperty
#EndRegion - Get-DynatraceProcessProperty.ps1
#Region - Get-DynatracePSConfig.ps1
function Get-DynatracePSConfig {
<#
.SYNOPSIS
    Get default configurations for DynatracePS from config.json file
 
.DESCRIPTION
    Get default configurations for DynatracePS from config.json file
 
.EXAMPLE
    Get-DynatracePSConfig
#>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    Param ()

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"
        $config = "$([Environment]::GetFolderPath('ApplicationData'))\DynatracePS\config.json"
        $OauthXmlFile = "$([Environment]::GetFolderPath('ApplicationData'))\DynatracePS\OauthSecret.xml"
        $AccessXmlFile = "$([Environment]::GetFolderPath('ApplicationData'))\DynatracePS\AccessToken.xml"
    }

    process {
        $Output = [PSCustomObject]@()
        if (Test-Path $config) {
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Getting config from [$config]"
            if (Test-Path $OauthXmlFile) {
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Getting Oauth Secret from [$OauthXmlFile]"
                $OauthCredential = [PSCredential](Import-Clixml $OauthXmlFile)
                $OauthClientSecret = $OauthCredential.GetNetworkCredential().Password
            } else {
                $OauthClientSecret = ''
            }
            if (Test-Path $AccessXmlFile) {
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Getting Access Token from [$AccessXmlFile]"
                $AccessCredential = [PSCredential](Import-Clixml $AccessXmlFile)
                $AccessToken = $AccessCredential.GetNetworkCredential().Password
            } else {
                $AccessToken = ''
            }
            $Output = (Get-Content -Path "$config" -ErrorAction Stop | ConvertFrom-Json)
            $Output | Add-Member -MemberType NoteProperty -Name OauthClientSecret -Value $OauthClientSecret
            $Output | Add-Member -MemberType NoteProperty -Name AccessToken -Value $AccessToken
        } else {
            Write-Warning "[$($MyInvocation.MyCommand.Name)] No config found at [$config]"
            Write-Warning "[$($MyInvocation.MyCommand.Name)] Use Set-DynatracePSConfig first!"
            break
        }
        $Output
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function complete"
    }
} # end function

Export-ModuleMember -Function Get-DynatracePSConfig
#EndRegion - Get-DynatracePSConfig.ps1
#Region - Get-DynatraceQuota.ps1
function Get-DynatraceQuota {
<#
    .SYNOPSIS
        Gets the host units quota of a Dynatrace account
 
    .DESCRIPTION
        Gets the host units quota of a Dynatrace account
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceQuote
 
    .NOTES
        https://api.dynatrace.com/spec/#/Quota%20management/QuotaController_getQuota
#>


    [CmdletBinding()]
    param(
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $AccountUuid = (Get-DynatracePSConfig).AccountUuid
        $RestPath = "/env/v1/accounts/$($AccountUuid)/quotas/host-monitoring"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $RestPath"
    }

    process {
        try {
            $splatParameters = @{
                RestPath =$RestPath
                OutputAsJson = $OutputAsJson
            }
            Invoke-DynatraceAccountManagementAPIMethod @splatParameters
        } catch {
            $_
            break
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceQuota
#EndRegion - Get-DynatraceQuota.ps1
#Region - Get-DynatraceSettingsSchema.ps1
function Get-DynatraceSettingsSchema {
<#
    .SYNOPSIS
        Get list of settings schemas in Dynatrace environment
 
    .DESCRIPTION
        Get list of settings schemas in Dynatrace environment
 
    .EXAMPLE
        Get-DynatraceSettingsSchema
 
    .NOTES
        https://api.dynatrace.com/spec/#/
#>


    [CmdletBinding()]
    param()

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $uri = "https://$EnvironmentID.live.dynatrace.com/api/v2/settings/schemas"

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
    }

    process {
        $splatParameters = @{
            Uri = $uri
            RestResponseProperty = 'items'
        }
        Invoke-DynatraceAPIMethod @splatParameters
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceSettingsSchema
#EndRegion - Get-DynatraceSettingsSchema.ps1
#Region - Get-DynatraceSubscription.ps1
function Get-DynatraceSubscription {
<#
    .SYNOPSIS
        Lists all Dynatrace platform subscriptions of an account
 
    .DESCRIPTION
        Lists all Dynatrace platform subscriptions of an account
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceSubscription
 
    .NOTES
        https://api.dynatrace.com/spec/#/Dynatrace%20platform%20subscription/LimaClaController_getClaSubscriptions
#>


    [CmdletBinding()]
    param(
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $AccountUuid = (Get-DynatracePSConfig).AccountUuid
        $RestPath = "/sub/v1/accounts/$($AccountUuid)/subscriptions"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $RestPath"
    }

    process {
        try {
            $splatParameters = @{
                RestPath =$RestPath
                OutputAsJson = $OutputAsJson
            }
            $return = Invoke-DynatraceAccountManagementAPIMethod @splatParameters
        } catch {
            $_
            break
        }
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Count of results: $($return.totalCount)"
        if ($OutputAsJson) {
            $return | ConvertTo-Json -Depth 6
        } else {
            $return.records
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceSubscription
#EndRegion - Get-DynatraceSubscription.ps1
#Region - Get-DynatraceTag.ps1
function Get-DynatraceTag {
<#
    .SYNOPSIS
        Get tags in Dynatrace environment
 
    .DESCRIPTION
        Get tags in Dynatrace environment
 
    .PARAMETER entitySelector
        The entity selector string to get the tags for. Example: 'type("HOST")'
 
    .EXAMPLE
        Get-DynatraceTag -entitySelector 'type("HOST")'
 
        Get all the tags for HOST entities
 
    .EXAMPLE
        Get-DynatraceTag -entitySelector 'type("HOST_GROUP")'
 
        Get all the tags for HOST_GROUP entities
 
    .EXAMPLE
        Get-DynatraceTag -entitySelector 'entityId("123456789")'
 
        Get the tags associated with entity with ID of 123456789
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/custom-tags/get-tags
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$entitySelector
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $uri = "https://$EnvironmentID.live.dynatrace.com/api/v2/tags"

        $GetParameter = @{
            entitySelector = $entitySelector
        }

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
    }

    process {
        $splatParameters = @{
            Uri = $uri
            GetParameter = $GetParameter
            #RestResponseProperty = 'tags'
        }
        Invoke-DynatraceAPIMethod @splatParameters
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceTag
#EndRegion - Get-DynatraceTag.ps1
#Region - Get-DynatraceUser.ps1
function Get-DynatraceUser {
<#
    .SYNOPSIS
        List the users of a Dynatrace account
 
    .DESCRIPTION
        List the users of a Dynatrace account
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceUser
 
    .NOTES
        https://api.dynatrace.com/spec/#/User%20management/UsersController_getUsers
 
#>


    [CmdletBinding()]
    param(
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $AccountUuid = (Get-DynatracePSConfig).AccountUuid
        $RestPath = "/iam/v1/accounts/$($AccountUuid)/users"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $RestPath"
    }

    process {
        try {
            $splatParameters = @{
                RestPath =$RestPath
                OutputAsJson = $OutputAsJson
            }
            $return = Invoke-DynatraceAccountManagementAPIMethod @splatParameters
        } catch {
            $_
            break
        }
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Count of results: $($return.count)"
        if ($OutputAsJson) {
            $return
        } else {
            $return.items
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceUser
#EndRegion - Get-DynatraceUser.ps1
#Region - Get-DynatraceUserGroup.ps1
function Get-DynatraceUserGroup {
<#
    .SYNOPSIS
        List the groups for a user
 
    .DESCRIPTION
        List the groups for a user
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceUserGroup -Email name@domain.com
 
    .NOTES
        https://api.dynatrace.com/spec/#/User%20management/UsersController_getUserGroups
 
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [String]$Email,
        [switch]$OutputAsJson
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $AccountUuid = (Get-DynatracePSConfig).AccountUuid
        $RestPath = "/iam/v1/accounts/$($AccountUuid)/users/$Email"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $RestPath"
    }

    process {
        try {
            $splatParameters = @{
                RestPath =$RestPath
                OutputAsJson = $OutputAsJson
            }
            Invoke-DynatraceAccountManagementAPIMethod @splatParameters
        } catch {
            $_
            break
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}
Export-ModuleMember -Function Get-DynatraceUserGroup
#EndRegion - Get-DynatraceUserGroup.ps1
#Region - New-DynatraceManagementZone.ps1
function New-DynatraceManagementZone {
<#
    .SYNOPSIS
        Create new Dynatrace Management Zone
 
    .DESCRIPTION
        Create new Dynatrace Management Zone
 
    .PARAMETER Name
        Name of the management zone
 
    .PARAMETER Description
        Description for the management zone
 
    .PARAMETER Rules
        One or more management zone rule objects
        See https://www.dynatrace.com/support/help/dynatrace-api/configuration-api/management-zones-api/post-mz#definition--MzRule
 
    .EXAMPLE
        $Rule = New-DynatraceMzRuleHostGroup -HostGroupName 'TEST_HG'
        New-DynatraceManagementZone -Name 'Test MZ' -Rules $Rule
 
        Create a new management zone named `Test MZ`, with a rule that maps TEST_HG host group to the new management zone
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/configuration-api/management-zones-api/post-mz
#>


    [CmdletBinding(SupportsShouldProcess)]
    param(
        [string]$Name,
        [string]$Description,
        [PSCustomObject]$Rules
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $uri = "https://$EnvironmentID.live.dynatrace.com/api/config/v1/managementZones"
        $uriValidator = "$uri/validator"

        $Headers = @{
            'Content-Type' = 'application/json'
        }

        $Body = [ordered]@{
            name = $Name
            rules = @($Rules)
        }
        $JsonBody = $Body | ConvertTo-Json -Depth 10

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
    }

    process {

        # First call is to validator URL -- this confirms the payload is valid
        $splatParameters = @{
            Uri = $uriValidator
            Method = 'POST'
            Body = $JsonBody
            Headers = $Headers
        }
        Invoke-DynatraceAPIMethod @splatParameters

        # Second call is to actually create
        $splatParameters.Uri = $uri
        if ($PSCmdlet.ShouldContinue('Would you like to continue?',"Creating new management zone $Name")) {
            Invoke-DynatraceAPIMethod @splatParameters
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}
Export-ModuleMember -Function New-DynatraceManagementZone
#EndRegion - New-DynatraceManagementZone.ps1
#Region - New-DynatraceMzRuleHostGroup.ps1
Function New-DynatraceMzRuleHostGroup {
<#
    .SYNOPSIS
        Output a management zone rule object for a host group
 
    .DESCRIPTION
        Output a management zone rule object for mapping host group to a management zone
 
    .PARAMETER HostGroupID
        Entity ID of the host group for the rule
 
    .PARAMETER HostGroupName
        Host group name, will be used to look up the entity ID of the hose group
 
    .NOTES
        This page contains the format of what the rule object should look like:
        https://www.dynatrace.com/support/help/dynatrace-api/configuration-api/management-zones-api/post-mz#example
#>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    Param (
        [string]$HostGroupID,
        [string]$HostGroupName
    )
    begin {
        $output = @{}
        if ($HostGroupName) {
            $HostGroupID = (Get-DynatraceHostGroup | Where-Object {$_.DisplayName -eq $HostGroupName}).entityID
        }
        $conditions = @()
    }
    process {

        $conditions += (
            [ordered]@{
                key = [ordered]@{
                    attribute = 'HOST_GROUP_ID'
                    type = 'STATIC'
                }
                comparisonInfo = [ordered]@{
                    type = 'ENTITY_ID'
                    operator = 'EQUALS'
                    value = $HostGroupID
                    negate = $false
                }
            }
        )

        $output = [ordered]@{
            type = 'HOST'
            enabled = $true
            propagationTypes = @()
            conditions = $conditions
        }
    } # end process
    end {
        $output
    }
}
Export-ModuleMember -Function New-DynatraceMzRuleHostGroup
#EndRegion - New-DynatraceMzRuleHostGroup.ps1
#Region - Remove-DynatraceManagementZone.ps1
function Remove-DynatraceManagementZone {
<#
    .SYNOPSIS
        Remove the specified Management Zone
 
    .DESCRIPTION
        Remove the specified Management Zone
 
    .PARAMETER ID
        Entity ID of the management zone to remove. Required if not using Name
 
    .PARAMETER Name
        Name of the management zone to remove. Required if not using ID
 
    .EXAMPLE
        Remove-DynatraceManagementZone -Name 'Test MZ'
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/configuration-api/management-zones-api/del-mz
#>


    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory,ParameterSetName = 'id')]
        [string]$id,
        [Parameter(Mandatory,ParameterSetName = 'name')]
        [string]$Name
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        if ($Name) {
            $id = (Get-DynatraceManagementZone | Where-Object {$_.Name -eq $Name}).id
        } else {
            $Name = (Get-DynatraceManagementZone | Where-Object {$_.id -eq $id}).Name
        }

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $uri = "https://$EnvironmentID.live.dynatrace.com/api/config/v1/managementZones/$id"

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] $Uri"
    }

    process {

        # First call is to validator URL -- this confirms the payload is valid
        $splatParameters = @{
            Uri = $uri
            Method = 'DELETE'
        }
        Write-Debug "[$($MyInvocation.MyCommand.Name)] splatParameters: $($splatParameters | Out-String)"

        if ($PSCmdlet.ShouldContinue('Would you like to continue?',"Remove management zone $Name")) {
            Invoke-DynatraceAPIMethod @splatParameters
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}
Export-ModuleMember -Function Remove-DynatraceManagementZone
#EndRegion - Remove-DynatraceManagementZone.ps1
#Region - Rename-DynatraceManagementZone.ps1
function Rename-DynatraceManagementZone {
<#
    .SYNOPSIS
        Rename a Management zone
 
    .DESCRIPTION
        Rename a Management zone
 
    .PARAMETER Id
        The unique id of the management zone to rename. Required if not using name
 
    .PARAMETER Name
        The name of the management zone to rename. Required if not using id
 
    .PARAMETER NewName
        The new name of the management zone.
 
    .EXAMPLE
        Rename-DynatraceManagementZone -id 1234554321 -NewName 'MANAGEMENT_ZONE_1'
 
    .EXAMPLE
        Rename-DynatraceManagementZone -Name 'MANAGEMENT_ZONE' -NewName 'MANAGEMENT_ZONE_1'
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/configuration-api/management-zones-api/get-mz
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ParameterSetName = 'id')]
        [string]$id,
        [Parameter(Mandatory,ParameterSetName = 'name')]
        [string]$Name,
        [Parameter(Mandatory)]
        [string]$NewName
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID
        $baseUri = "https://$EnvironmentID.live.dynatrace.com/api/config/v1/managementZones"

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] baseUri: $baseUri"
    }

    process {

        # If name was used, we need the mz id
        #
        if ($Name) {
            $id = (Get-DynatraceManagementZone | Where-Object {$_.Name -eq $Name}).id
        }

        $ExistingMZ = Get-DynatraceManagementZoneProperty -id $id
        $ExistingMZ.Name = $NewName
        $body = $ExistingMZ | ConvertTo-Json -Depth 5

        $uri = "$baseUri/$id"
        $validatorUri = "$uri/validator"

        # Validate the payload
        $splatParameters = @{
            Uri = $validatorUri
            Body = $body
            Method = 'Post'
            ContentType = 'application/json'
        }
        Invoke-DynatraceAPIMethod @splatParameters

        # it passed, do the actual post
        $splatParameters = @{
            Uri = $Uri
            Body = $body
            Method = 'Put'
            ContentType = 'application/json'
        }
        Invoke-DynatraceAPIMethod @splatParameters
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}
Export-ModuleMember -Function Rename-DynatraceManagementZone
#EndRegion - Rename-DynatraceManagementZone.ps1
#Region - Set-DynatracePSConfig.ps1
function Set-DynatracePSConfig {
<#
.SYNOPSIS
    Set the Dynatrace SaaS Environment ID to use when connecting
 
.DESCRIPTION
    Set the Dynatrace SaaS Environment ID to use when connecting
    Saves the information to DynatracePS/config.json file in user profile
 
.PARAMETER EnvironmentID
    Dynatrace SaaS Environment ID. This is the left most part of the hostname name: envid.live.dynatrace.com
 
.PARAMETER AccountUuid
    Dynatrace Account ID. You can find the UUID on the Account > Account management API page, during creation of an OAuth client
 
.PARAMETER OAuthClientSecret
    The client_secret value that is created in the Account management API page. Used for connecting to Dynatrace Account Management API
 
.PARAMETER AccessToken
    The access token or personal access token for Dynatrace API
 
.EXAMPLE
    Set-DynatracePSConfig -EnvironmentID 'envid' -AccountUuid 'dynatrace-account-id'
 
.NOTES
    https://www.dynatrace.com/support/help/dynatrace-api/basics/dynatrace-api-authentication#tabgroup--generate-token--personal-access-token
    https://www.dynatrace.com/support/help/get-started/access-tokens
#>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification='This function is trivial enough that we do not need ShouldProcess')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '',
        Justification='There is no other easy way to do this. at least i can not come up with it at 1am')]
    Param (
        [String]$EnvironmentID,
        [String]$AccountUuid,
        [String]$OAuthClientSecret,
        [String]$AccessToken
    )
    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"

        $cliXmlFile = "$([Environment]::GetFolderPath('ApplicationData'))\DynatracePS\OauthSecret.xml"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Oauth secret will be stored in $($OauthXmlFile)"

        $AccessXmlFile = "$([Environment]::GetFolderPath('ApplicationData'))\DynatracePS\AccessToken.xml"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Access token will be stored in $($AccessXmlFile)"

        $configPath = "$([Environment]::GetFolderPath('ApplicationData'))\DynatracePS\config.json"
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Configuration will be stored in $($configPath)"

        if (-not (Test-Path $configPath)) {
            # If the config file doesn't exist, create it
            $null = New-Item -Path $configPath -ItemType File -Force
        }
    }

    process {
        $ExistingConfig = Get-DynatracePSConfig

        # If no environment ID passed in, and there is existing value, use existing
        if ((-not $EnvironmentID) -and ($ExistingConfig.EnvironmentID)) {
            $EnvironmentID = $ExistingConfig.EnvironmentID
        }
        if ((-not $AccountUuid) -and ($ExistingConfig.AccountUuid)) {
            $AccountUuid = $ExistingConfig.AccountUuid
        }
        $config = [ordered]@{
            EnvironmentID = $EnvironmentID
            AccountUuid = $AccountUuid
        }
        $config | ConvertTo-Json | Set-Content -Path "$configPath"

        if ($OAuthClientSecret) {
            $username = 'DynatraceAccountManagement'
            $pass = ConvertTo-SecureString $OAuthClientSecret -AsPlainText -Force
            [PSCredential]$credential = New-Object System.Management.Automation.PSCredential(
                $UserName, $pass
            )
            $credential | Export-Clixml $cliXmlFile
        }

        if ($AccessToken) {
            $username = 'DynatraceAccess'
            $pass = ConvertTo-SecureString $AccessToken -AsPlainText -Force
            [PSCredential]$credential = New-Object System.Management.Automation.PSCredential(
                $UserName, $pass
            )
            $credential | Export-Clixml $AccessXmlFile
        }
    }

    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
    }
} # end function
Export-ModuleMember -Function Set-DynatracePSConfig
#EndRegion - Set-DynatracePSConfig.ps1
#Region - Update-DynatraceManagementZone.ps1
function Update-DynatraceManagementZone {
<#
    .SYNOPSIS
        Update the specified Management Zone
 
    .DESCRIPTION
        Update the specified Management Zone
 
    .PARAMETER ID
        Entity ID of the management zone to update. Required if not using Name
 
    .PARAMETER Name
        Name of the management zone to update. Required if not using ID
 
    .PARAMETER Description
        Updated description for the management zone
 
    .PARAMETER Rules
        One or more management zone rule objects
        See https://www.dynatrace.com/support/help/dynatrace-api/configuration-api/management-zones-api/post-mz#definition--MzRule
 
    .EXAMPLE
        $Rule = New-DynatraceMzRuleHostGroup -HostGroupName 'TEST_HG'
        New-DynatraceManagementZone -Name 'Test MZ' -Rules $Rule
 
        Create a new management zone named `Test MZ`, with a rule that maps TEST_HG host group to the new management zone
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/configuration-api/management-zones-api/put-mz
#>


    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory,ParameterSetName = 'id')]
        [string]$id,
        [Parameter(Mandatory,ParameterSetName = 'name')]
        [string]$Name,
        [string]$Description,
        [PSCustomObject]$Rules
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $uri = "https://$EnvironmentID.live.dynatrace.com/api/config/v1/managementZones"
        $uriValidator = "$uri/validator"

        $Headers = @{
            'Content-Type' = 'application/json'
        }

        if ($Name) {
            $id = (Get-DynatraceManagementZone | Where-Object {$_.Name -eq $Name}).id
        } else {
            $Name = (Get-DynatraceManagementZone | Where-Object {$_.id -eq $id}).Name
        }

        $Body = [ordered]@{
            id = $id
            name = $Name
            rules = @($Rules)
        }
        $JsonBody = $Body | ConvertTo-Json -Depth 10

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
    }

    process {

        # First call is to validator URL -- this confirms the payload is valid
        $splatParameters = @{
            Uri = $uriValidator
            Method = 'POST'
            Body = $JsonBody
            Headers = $Headers
        }
        Invoke-DynatraceAPIMethod @splatParameters

        # Second call is to actually update
        $splatParameters.Uri = $uri
        $splatParameters.Method = 'PUT'

        if ($PSCmdlet.ShouldContinue('Would you like to continue?',"Update management zone $Name")) {
            Invoke-DynatraceAPIMethod @splatParameters
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}
Export-ModuleMember -Function Update-DynatraceManagementZone
#EndRegion - Update-DynatraceManagementZone.ps1
### --- PRIVATE FUNCTIONS --- ###
#Region - ConvertTo-GetParameter.ps1
function ConvertTo-GetParameter {
    <#
    .SYNOPSIS
    Generate the GET parameter string for an URL from a hashtable
    #>

    [CmdletBinding()]
    param (
        [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true )]
        [hashtable]$InputObject
    )

    process {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Making HTTP get parameter string out of a hashtable"
        Write-Verbose ($InputObject | Out-String)
        [string]$parameters = "?"
        # If the key is name nextPageKey is in the input object, it is *the only*
        # key that should be included in the parameters
        #
        if ('nextPageKey' -in $InputObject.Keys) {
            $value = $InputObject['nextPageKey']
            $parameters += "nextPageKey=$($value)&"
        } else {
            foreach ($key in $InputObject.Keys) {
                $value = $InputObject[$key]
                $parameters += "$key=$($value)&"
            }
        }
        $parameters -replace ".$"
    }
}
#EndRegion - ConvertTo-GetParameter.ps1
#Region - ConvertTo-ParameterHash.ps1
function ConvertTo-ParameterHash {
    [CmdletBinding( DefaultParameterSetName = 'ByString' )]
    param (
        # URI from which to use the query
        [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'ByUri' )]
        [Uri]$Uri,

        # Query string
        [Parameter( Position = 0, Mandatory, ParameterSetName = 'ByString' )]
        [String]$Query
    )

    process {
        $GetParameter = @{}

        if ($Uri) {
            $Query = $Uri.Query
        }

        if ($Query -match "^\?.+") {
            $Query.TrimStart("?").Split("&") | ForEach-Object {
                $key, $value = $_.Split("=")
                $GetParameter.Add($key, $value)
            }
        }

        Write-Output $GetParameter
    }
}
#EndRegion - ConvertTo-ParameterHash.ps1
#Region - Invoke-DynatraceAccountManagementAPIMethod.ps1
function Invoke-DynatraceAccountManagementAPIMethod {
    <#
        .SYNOPSIS
            Invoke a method on in the Dynatrace Account Management API
 
        .DESCRIPTION
            Invoke a method on in the Dynatrace Account Management API
 
        .PARAMETER RestPath
            The rest path to append to base url
 
        .EXAMPLE
            Invoke-DynatraceAccountManagementAPIMethod -RestPath '/ref/v1/account/permissions'
 
        .NOTES
            https://api.dynatrace.com/spec/#/
 
        #>


        [CmdletBinding()]
        param(
            [Parameter(Mandatory)]
            [String]$RestPath,
            [Microsoft.PowerShell.Commands.WebRequestMethod]$Method = 'GET',
            [switch]$OutputAsJson
        )

        begin {
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
            Write-Debug "[$($MyInvocation.MyCommand.Name)] Function started"

            $Connection = Connect-DynatraceAccountManagement
            $access_token = $Connection.access_token
            $headers = @{
                Authorization = "Bearer $access_token"
            }
            $Uri = "https://api.dynatrace.com$RestPath"
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] $Uri"
        }

        process {
            try {
                $output = Invoke-RestMethod -Uri $Uri -Method $Method -Headers $headers
            } catch {
                Write-Warning "[$($MyInvocation.MyCommand.Name)] Problem with Invoke-RestMethod $uri"
                $_
                break
            }
            if ($OutputAsJson) {
                $output | ConvertTo-Json -Depth 6
            } else {
                $output
            }
        }
        end {
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
            Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
        }
    }



#EndRegion - Invoke-DynatraceAccountManagementAPIMethod.ps1
#Region - Invoke-DynatraceAPIMethod.ps1
function Invoke-DynatraceAPIMethod {
<#
.SYNOPSIS
    Invoke a method in the Dynatrace API. This is a service function to be called by other functions.
 
.DESCRIPTION
    Invoke a method in the Dynatrace API. This is a service function to be called by other functions.
 
.PARAMETER Uri
    Uri to use for Invoke-RestMethod
 
.PARAMETER Method
    Defaults to GET
 
.PARAMETER Headers
    Headers to use. Will be joined with authorization header.
 
.PARAMETER GetParameter
    Get parameters to include
 
.PARAMETER RestResponseProperty
    Property of the rest response to return as results.
 
.PARAMETER LevelOfRecursion
    Internal parameter used to help determine how far into the matrix we are
 
.EXAMPLE
    Invoke-DynatraceAPIMethod -Uri https://environmentid.live.dynatrace.com/api/v2/entityTypes -RestResponseProperty types
 
.NOTES
    https://api.dynatrace.com/spec/#/
 
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [Uri]$Uri,
        [Microsoft.PowerShell.Commands.WebRequestMethod]$Method = 'GET',
        [Hashtable]$Headers,
        [Hashtable]$GetParameter = @{},
        [string]$RestResponseProperty,
        [System.Object]$Body,
        [string]$ContentType,
        [int]$LevelOfRecursion = 1
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] Function started"
        Write-Debug "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] Function started. PSBoundParameters: $($PSBoundParameters | Out-String)"

#region Headers
        $config = Get-DynatracePSConfig
        $access_token = $config.accesstoken

        if (-not $access_token) {
            Write-Warning "[$($MyInvocation.MyCommand.Name) $Level] Must first configure an access token with Set-DynatracePSConfig -AccessToken <token>. Exiting..."
            break
        }

        $_headers = @{
            Authorization = "Api-Token $access_token"
        }
        if ($Headers) {
            $_headers += $Headers
        }
#endregion Headers

        $uriQuery = ConvertTo-ParameterHash -Uri $Uri

        $internalGetParameter = Join-Hashtable $uriQuery, $GetParameter
        [Uri]$LeftPartOfURI = $Uri.GetLeftPart('Path')
        [Uri]$FinalURI = "{0}{1}" -f $LeftPartOfURI,(ConvertTo-GetParameter $internalGetParameter)

        Write-Verbose "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] FinalURI: [$FinalUri]"

        try {
            $splatParameters = @{
                Uri = $FinalURI
                Method = $Method
                Headers = $_headers
                ResponseHeadersVariable = 'ResponseHeaders'
                StatusCodeVariable = 'StatusCode'
            }
            if ($body) {
                Write-Debug "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] body: $($body | Out-String)"
                $splatParameters += @{
                    Body = $body
                }
            }
            if ($ContentType) {
                $splatParameters += @{
                    ContentType = $ContentType
                }
            }

            Write-Debug "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] splatParameters: $($splatParameters | Out-String)"

            $RestResponse = Invoke-RestMethod @splatParameters
            Write-Debug "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] RestResponse: $($RestResponse | Out-String)"
            Write-Debug "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] ResponseHeaders: $($ResponseHeaders | Out-String)"
            Write-Debug "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] StatusCode: $($StatusCode | Out-String)"

        } catch {
            Write-Warning "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] Problem with Invoke-RestMethod $uri"
            $_
            break
        }
        Write-Verbose "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] Executed RestMethod"

        Test-ServerResponse -InputObject $RestResponse -StatusCode $StatusCode
    }

    process {
        if ($RestResponse) {
            Write-Verbose "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] nextPageKey: $($RestResponse.nextPageKey)"

            $NextPageKey = $RestResponse.nextPageKey

            if ($RestResponseProperty) {
                $result = ($RestResponse).$RestResponseProperty
            } else {
                $result = $RestResponse
            }

            if (-not $PSBoundParameters["GetParameter"]) {
                $PSBoundParameters["GetParameter"] = $internalGetParameter
            }

            do {

                if (-not $NextPageKey) {
                    # if there is no page key, then quit
                    break
                } else {
                    # Output results from this loop
                    $result
                }

                $PSBoundParameters["GetParameter"]['nextPageKey'] = $NextPageKey
                $PSBoundParameters["LevelOfRecursion"] = $LevelOfRecursion + 1

                Write-Verbose "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] Calling Invoke-DynatraceApiMethod"
                $result = Invoke-DynatraceApiMethod @PSBoundParameters
            } while (-not $NextPageKey)

            $result
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}
#EndRegion - Invoke-DynatraceAPIMethod.ps1
#Region - Join-Hashtable.ps1
function Join-Hashtable {
    <#
    .SYNOPSIS
        Combines multiple hashtables into a single table.
    .DESCRIPTION
        Combines multiple hashtables into a single table.
        On multiple identic keys, the last wins.
    .EXAMPLE
        PS C:\> Join-Hashtable -Hashtable $Hash1, $Hash2
        Merges the hashtables contained in $Hash1 and $Hash2 into a single hashtable.
#>

    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    Param (
        # The tables to merge.
        [Parameter( Mandatory, ValueFromPipeline )]
        [AllowNull()]
        [System.Collections.IDictionary[]]
        $Hashtable
    )
    begin {
        $table = @{ }
    }

    process {
        foreach ($item in $Hashtable) {
            foreach ($key in $item.Keys) {
                $table[$key] = $item[$key]
            }
        }
    }

    end {
        $table
    }
}
#EndRegion - Join-Hashtable.ps1
#Region - Test-ServerResponse.ps1
function Test-ServerResponse {
    [CmdletBinding()]
    <#
        .SYNOPSIS
            Evaluate the response of the API call
        .NOTES
            Thanks to Lipkau:
            https://github.com/AtlassianPS/JiraPS/blob/master/JiraPS/Private/Test-ServerResponse.ps1
    #>

    param (
        [Parameter( ValueFromPipeline )]
        [PSObject]$InputObject,
        [string]$StatusCode
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Checking response for error"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Checking response for error"
    }

    process {
        if ($StatusCode -in (201,204)) {
            Write-Debug "[$($MyInvocation.MyCommand.Name)] Response code $StatusCode : Success"
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Response code $StatusCode : Success"
        } elseif ($StatusCode -gt 204) {
            Write-Debug "[$($MyInvocation.MyCommand.Name)] Error code found, throwing error"
            throw ("Code {0}: {1}" -f $StatusCode,($InputObject.message -join ','))
        } else {
            Write-Debug "[$($MyInvocation.MyCommand.Name)] Code: $StatusCode"
        }
    }

    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Done checking response for error"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Done checking response for error"
    }
}
#EndRegion - Test-ServerResponse.ps1

# SIG # Begin signature block
# MIIFjQYJKoZIhvcNAQcCoIIFfjCCBXoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU4m+FiyDQgL/ESvqUi4EgOiZt
# xQGgggMnMIIDIzCCAgugAwIBAgIQfxlXoOWZRbhMi6xrw92ZDzANBgkqhkiG9w0B
# AQsFADAbMRkwFwYDVQQDDBBzZWxmLnNpZ25lZC5jZXJ0MB4XDTIyMDQzMDAyNDYw
# MFoXDTIzMDQzMDAzMDYwMFowGzEZMBcGA1UEAwwQc2VsZi5zaWduZWQuY2VydDCC
# ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALUk2x84obgu2AqpvrBQ47LK
# T01imCRSOYU3wIVEyd0H0WE/gZkc/Aji68mojxlKcdLKGRNiEFDsXbWX2fWM6KC2
# PVS/txe1fgCpz5eeq9CyHqTuUz8m3XDRMtAX91R8xyiLQSFWgrfDPJWRBWHv3sNv
# ZD4c00hle+YHLhuw76oc2z22ikMhCND8GfVlSWxoIiI2hcNN5oCkqiNjxYs3fWD5
# sUcYTDWj62AL00Zml+FI6CvRO2XSdKVRMvAqN2vskwwO6ayMASYrEcJ4WTcQj1dp
# WjCZdgqMrBGVxvc6wg6YWVDzlHRCg4AxVZjt12LGkqYwkjROHN2wzE/4/0svU00C
# AwEAAaNjMGEwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsG
# A1UdEQQUMBKCEHNlbGYuc2lnbmVkLmNlcnQwHQYDVR0OBBYEFMYfeufU5fggCTcR
# +V0v1lQC6bOkMA0GCSqGSIb3DQEBCwUAA4IBAQAsI+0UFKIeVPZ36nodt9JqhyQK
# yC1SsuvIwGew0QsdeDaM2mLwbZfmKumAPq27tOIhozaydme4vnbKyDv4MShR6MLk
# UwquAJ70k+mMrivol7SL+I208iO0itt5Kxxd+cNKl2G1tRIfGvE9qoZ02WIjvNQH
# BazJHjldlW/QXkoy0Ip/02mR3KvnGGRiipU8DjLi/lUbAUJAVO3zZ99iiXg2w5Be
# 2gOt7n7Csx10Fe2KJfKrbXVYShcim6wMbrHtvYBrtagFaAT2RXzZyQfCoGYtP2Vw
# w8fHDmJ8yFy3fRCHL53A7FsJ499gJDbj0afH0AE7uDShNoc2F8WoHaLLe7UfMYIB
# 0DCCAcwCAQEwLzAbMRkwFwYDVQQDDBBzZWxmLnNpZ25lZC5jZXJ0AhB/GVeg5ZlF
# uEyLrGvD3ZkPMAkGBSsOAwIaBQCgeDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAA
# MBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgor
# BgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBSoYtCHXhXA1iIyCY9VnHj6XW8jaTAN
# BgkqhkiG9w0BAQEFAASCAQBM/MCGXbkzfQwl2pceZNw+sq62F7Qx+UtLiQGGKAbu
# yHofwb6aNfMfNxwEyirbXDNO60dgqhhfDKHo6yhNN0/qV32JA8F/tWwP0IhCNORm
# NCyZxTRcInNnEoNR1AFnhE/K4DVFYRvf4iQCh2AFFhlcbYcLM62yjdSFO17F3UqQ
# Dc16TMxNvhuUWCoQh8oOshkXZybUIYpLQndzoaIFDz0IqMxHfKTUh23t20tk2DEo
# Z+R002eXFjkoGoaOk7ugj6Z2lzH9L6FxyYQJ/X655nVW0idDuQ6LvLTWmYFjr0CV
# YNTzXsOqOqPGwOf3udSLP8LurqHoSbisNJrdnnoKYMdN
# SIG # End signature block