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..."
            return
        }
        if (-not $OauthClientSecret) {
            Write-Warning "[$($MyInvocation.MyCommand.Name)] OauthClientSecret is required. Please use Set-DynatracePSConfig first. Exiting..."
            return
        }

        $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'

        $TokenData = $MyInvocation.MyCommand.Module.PrivateData.TokenData
    }

    process {
        Write-Debug "[$($MyInvocation.MyCommand.Name)] TokenData: $($TokenData | Out-String)"

        $Now = Get-Date

        if (($Now -lt $TokenData.token_expires) -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"
        } else {
            Write-Debug "[$($MyInvocation.MyCommand.Name)] Getting new token"
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Getting new token"
            try {
                $splatParameter = @{
                    Uri = $GetTokenURL
                    Method = 'POST'
                    Body = $body
                    ContentType = $ContentType
                }
                $GetTokenReturn = (Invoke-WebRequest @splatParameter).Content | ConvertFrom-Json

                Write-Debug "[$($MyInvocation.MyCommand.Name)] Adding results to module PrivateData"

                $TokenData = @{
                    '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)
                }
                $MyInvocation.MyCommand.Module.PrivateData.TokenData = $TokenData
            } catch {
                Write-Warning "[$($MyInvocation.MyCommand.Name)] Problem getting $GetTokenURL"
                $_
                return
            }
        }

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


Export-ModuleMember -Function Connect-DynatraceAccountManagement
#EndRegion - Connect-DynatraceAccountManagement.ps1
#Region - Get-DynatraceAlertingProfile.ps1
function Get-DynatraceAlertingProfile {
<#
    .SYNOPSIS
        Get alerting profile(s) available in your Dynatrace environment
 
    .DESCRIPTION
        Get alerting profile(s) available in your Dynatrace environment
 
    .PARAMETER id
        Optional, id of alerting profile to get details for
 
    .PARAMETER OutputAsJson
        Optional, output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceAlertingProfile
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/configuration-api/alerting-profiles-api/get-all
#>


    [CmdletBinding()]
    param(
        [string]$id,
        [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/config/v1/alertingProfiles"

        if ($id) {
            $uri = "$uri/$id"
        } else {
            $RestResponseProperty = 'values'
        }

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

    process {
        $splatParameters = @{
            Uri = $uri
            RestResponseProperty = $RestResponseProperty
        }
        $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-DynatraceAlertingProfile
#EndRegion - Get-DynatraceAlertingProfile.ps1
#Region - Get-DynatraceAuditLog.ps1
function Get-DynatraceAuditLog {
<#
    .SYNOPSIS
        Get audit logs
 
    .DESCRIPTION
        Get audit logs
 
    .PARAMETER From
        Start of the requested time frame. Defaults to now-2h
        There are several recognized formats available, see:
        https://www.dynatrace.com/support/help/shortlink/api-problems-v2-get-list#parameters
 
    .PARAMETER To
        End of the requested time frame. Defaults to current timestamp
        There are several recognized formats available, see:
        https://www.dynatrace.com/support/help/shortlink/api-problems-v2-get-list#parameters
 
    .PARAMETER OutputAsJson
        Optional, output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceAuditLog
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/audit-logs/get-log
#>


    [CmdletBinding()]
    param(
        [string]$From,
        [string]$To,
        [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/auditlogs"

        $GetParameter = @{}
        if ($from) {
            $GetParameter += @{from = $from}
        }
        if ($to) {
            $GetParameter += @{to = $to}
        }

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

    process {
        $splatParameters = @{
            Uri = $uri
            GetParameter = $GetParameter
            RestResponseProperty = 'auditLogs'
        }
        $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-DynatraceAuditLog
#EndRegion - Get-DynatraceAuditLog.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-DynatraceDatabase.ps1
function Get-DynatraceDatabase {
<#
    .SYNOPSIS
        Get Database services in Dynatrace environment
 
    .DESCRIPTION
        Get Database services in Dynatrace environment
 
    .PARAMETER Name
        Return databases that contain Name
 
    .EXAMPLE
        Get-DynatraceDatabase
 
    .EXAMPLE
        Get-DynatraceDatabase -Name ORT
 
        Return all database services that have ORT in the Name
 
    .NOTES
        https://api.dynatrace.com/spec/#/
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/entity-v2/entity-selector
#>


    [CmdletBinding()]
    param(
        [String]$Name
    )

    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"

        $entitySelector = "type(`"SERVICE`"),serviceType(`"DATABASE_SERVICE`")"

        if ($Name) {
            $entitySelector = "$entitySelector,entityName(`"$Name`")"
        }

        $GetParameter = @{
            entitySelector = $entitySelector
            pageSize = 200
        }

        $output = @()

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

    process {
        $splatParameters = @{
            Uri = $uri
            GetParameter = $GetParameter
            RestResponseProperty = 'entities'
        }

        $databases = Invoke-DynatraceAPIMethod @splatParameters

        # If Name parameter is used, get individual entity properties to provide
        # more details on the database services
        #
        if ($Name) {
            foreach ($database in $databases){
                $dets = Get-DynatraceEntityProperty -entityID $database.entityID
                $output += [PSCustomObject]@{
                    entityId = $database.entityID
                    Name = $dets.displayName
                    Host = $dets.properties.databaseHostNames -join ','
                    HostPort = $dets.properties.port
                    IPAddress = $dets.properties.ipAddress -join ','
                    Vendor = $dets.properties.databaseVendor
                }
            }
        } else {
            foreach ($database in $databases) {
                $output += [PSCustomObject]@{
                    entityID = $database.entityId
                    Name = $database.displayName
                }
            }
        }
        $output
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceDatabase
#EndRegion - Get-DynatraceDatabase.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 {
            $_
            return
        }
    }
    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 Type
        Specific entity type to get properties for
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceEntityType
 
    .EXAMPLE
        Get-DynatraceEntityType -Type SERVICE
 
    .NOTES
        https://api.dynatrace.com/spec/#/
#>


    [CmdletBinding()]
    param(
        [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/entityTypes"

        $GetParameter = @{
            pageSize = 200
        }

        if ($Type) {
            $uri = "https://$EnvironmentID.live.dynatrace.com/api/v2/entityTypes/$Type"
            $splatParameters = @{
                Uri = $uri
            }
        } else {
            $splatParameters = @{
                Uri = $uri
                GetParameter = $GetParameter
                RestResponseProperty = 'types'
            }
        }

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

        $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 {
            $_
            return
        }
    }
    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 {
            $_
            return
        }
        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 -OutputNotNested
 
    .EXAMPLE
        Get-DynatraceGroupPermission -GroupName 'Monitoring Viewer'
 
    .EXAMPLE
        Get-DynatraceGroup | %{Get-DynatraceGroupPermission -groupUuid $_.uuid}
 
    .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) {
                # caching management zones for lookup
                #
                $mz = Get-DynatraceManagementZone
                foreach ($permission in $result.permissions) {
                    if ($permission.scope -match ':') {
                        # if there is colon in permissions scope, it will be a string
                        # with environment name:management zone id
                        #
                        $splitPermissionScope = $permission.scope -split ':'
                        $mzName = ($mz | Where-Object{$_.ID -eq $splitPermissionScope[1]}).Name
                        $scope = "{0}:{1}" -f $splitPermissionScope[0],$mzName
                    } else {
                        $scope = $permission.scope;
                    }
                    $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 = $scope;
                        ScopeType = $permission.scopeType;
                        permissionCreatedAt = $permission.createdAt;
                        permissionUpdatedAt = $permission.updatedAt;
                    }
                }
            } else {
                $output = $result
            }
        } catch {
            $_
            return
        }
    }
    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 {
            $_
            return
        }
        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-DynatraceHostsInHostGroup.ps1
function Get-DynatraceHostsInHostGroup {
<#
    .SYNOPSIS
        Get hosts in host group
 
    .DESCRIPTION
        Get hosts in host group
 
    .PARAMETER Id
        Unique id of the host group
 
    .PARAMETER Name
        Name of the host group
 
    .EXAMPLE
        Get-DynatraceHostsInHostGroup -Name HOST_GROUP_NAME
 
    .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
    )

    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
        }
        $Output = @()
        $AllHosts = Get-DynatraceHost
    }

    process {
        $splatParm = @{
            entityID = $Id
        }
        $Properties = Get-DynatraceEntityProperty @splatParm
        foreach ($Host_ in $Properties.toRelationships.isInstanceOf) {
            $Output += [PSCustomObject]@{
                HostGroup = $Properties.displayName
                HostName = ($AllHosts | Where-Object{$_.entityID -eq $Host_.ID}).displayName
            }
        }
        $Output
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceHostsInHostGroup
#EndRegion - Get-DynatraceHostsInHostGroup.ps1
#Region - Get-DynatraceHTTPCheck.ps1
function Get-DynatraceHTTPCheck {
<#
    .SYNOPSIS
        Get HTTP checks
 
    .DESCRIPTION
        Get HTTP Checks
 
    .EXAMPLE
        Get-DynatraceHTTPCheck
 
    .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 'HTTP_CHECK'
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceHTTPCheck
#EndRegion - Get-DynatraceHTTPCheck.ps1
#Region - Get-DynatraceHTTPCheckProperty.ps1
function Get-DynatraceHTTPCheckProperty {
<#
    .SYNOPSIS
        Get properties for an http check
 
    .DESCRIPTION
        Get properties for an http check
 
    .PARAMETER Id
        Entity id of the http check
 
    .PARAMETER Name
        Name of the http check
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceHTTPCheckProperty -id entityid
 
    .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-DynatraceHTTPCheck | 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-DynatraceHTTPCheckProperty
#EndRegion - Get-DynatraceHTTPCheckProperty.ps1
#Region - Get-DynatraceHTTPCheckStatus.ps1
function Get-DynatraceHTTPCheckStatus {
<#
    .SYNOPSIS
        Get results of all HTTP check monitor executions
 
    .DESCRIPTION
        Get results of all HTTP check monitor executions
 
    .PARAMETER All
        Get all records, not just the latest ones
 
    .EXAMPLE
        Get-DynatraceHTTPCheckStatus
 
        Return the most recent failed execution for all http check synthetic monitors
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/synthetic-v2/synthetic-monitor-execution/get-http-monitor
#>


    [CmdletBinding()]
    param(
        [Switch]$All
    )

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

        $HTTPChecks = Get-DynatraceHTTPCheck
        $SyntheticLocations = Get-DynatraceSyntheticLocation
        $Output = @()
    }

    process {

        foreach ($HTTPCheck in $HTTPChecks) {
            $SuccessResults = Get-DynatraceHTTPCheckMonitorExecution -monitorID $HTTPCheck.entityID -resultType SUCCESS
            $FailedResults = Get-DynatraceHTTPCheckMonitorExecution -monitorID $HTTPCheck.entityID -resultType FAILED

            foreach ($location in $SuccessResults.locationsExecutionResults) {
                $Output += [PSCustomObject]@{
                    MonitorName = $HTTPCheck.displayName
                    Location = ($SyntheticLocations | Where-Object {$_.ID -eq $location.locationId}).Name
                    ResponseStatusCode = $location.requestResults.ResponseStatusCode
                    ResponseMessage = $location.requestResults.ResponseMessage
                    HealthStatus = $location.requestResults.healthStatus
                    StartTime = (ConvertTo-Datetime $location.requestResults.startTimestamp)
                    WaitingTime = $location.requestResults.waitingTime
                }
            }
            foreach ($location in $FailedResults.locationsExecutionResults) {
                $Output += [PSCustomObject]@{
                    MonitorName = $HTTPCheck.displayName
                    Location = ($SyntheticLocations | Where-Object {$_.ID -eq $location.locationId}).Name
                    ResponseStatusCode = $location.requestResults.ResponseStatusCode
                    ResponseMessage = $location.requestResults.ResponseMessage
                    HealthStatus = $location.requestResults.healthStatus
                    StartTime = (ConvertTo-Datetime $location.requestResults.startTimestamp)
                    WaitingTime = $location.requestResults.waitingTime
                }
            }
        }
        if ($All) {
            $Output
        } else {
            $Output | Group-Object MonitorName | Foreach-Object {$_.Group | Sort-Object StartTime | Select-Object -Last 1}
        }

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



Export-ModuleMember -Function Get-DynatraceHTTPCheckStatus
#EndRegion - Get-DynatraceHTTPCheckStatus.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-DynatraceManagementZoneRule.ps1
function Get-DynatraceManagementZoneRule {
<#
    .SYNOPSIS
        Get rules associated with a management zone in Dynatrace environment
 
    .DESCRIPTION
        Get rules associated with 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
 
    .EXAMPLE
        Get-DynatraceManagementZoneRule -id 1234554321
 
        Get management zone rules associated with management zone with id of 1234554321.
 
    .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
    )

    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"
        $output = @()
    }

    process {
        $return = Get-DynatraceManagementZoneProperty -id $id
        foreach ($rule in $return.Rules) {
            # using a for loop because the key is in one property
            # and the comparisons are in a different property
            #
            for ($i = 0; $i -lt ($rule.conditions).Count; $i++) {
                $condition = $rule.conditions[$i]
                $comparisonType = $condition.comparisonInfo.Type

                if ($condition.key.dynamicKey) {
                    $key = $condition.key.dynamicKey
                } elseif ($condition.key.attribute) {
                    $key = $condition.key.attribute
                } else {
                    $key = ''
                }
                if ($comparisonType -eq 'TAG') {
                    $comparisonType = "TAG:$($condition.comparisonInfo.value.key)"
                    $value = "$($condition.comparisonInfo.value.value)"
                } elseif ($comparisonType -eq 'SIMPLE_TECH') {
                    $comparisonType = "TECH:$($condition.comparisonInfo.value.type)"
                } else {
                    $comparisonType = ''
                    $value = $condition.comparisonInfo.value
                }
                $output += [PSCustomObject]@{
                    ManagementZoneName = $return.name
                    RuleType = $rule.Type
                    Enabled = $rule.enabled
                    Key = $key
                    ComparisonType = $comparisonType
                    Operator = $condition.comparisonInfo.operator
                    ComparisonValue = $value
                    Negate = $condition.comparisonInfo.negate
                }
            }
        }
        $output
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceManagementZoneRule
#EndRegion - Get-DynatraceManagementZoneRule.ps1
#Region - Get-DynatraceMetric.ps1
function Get-DynatraceMetric {
<#
    .SYNOPSIS
        Get metrics from Dynatrace environment
 
    .DESCRIPTION
        Get metrics in Dynatrace environment
 
    .PARAMETER metricSelector
        Select metrics for the query by their keys. Example: builtin:*
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/get-all-metrics#parameters
 
    .PARAMETER text
        Metric registry search term. Only show metrics that contain the term in their key, display name, or description.
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceMetric -metricSelector builtin:*
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/get-all-metrics
#>


    [CmdletBinding()]
    param(
        [String]$metricSelector,
        [String]$text,
        [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/metrics"

        # text and metricSelector are mutually exclusive, meaning
        # only one of these parameters may be used.
        if ($text) {
            $GetParameter = @{
                text = $text
            }
        } else {
            # if no metricSelector passed in, get all of them
            if (-not $metricSelector) {
                $metricSelector = '*'
            }
            $GetParameter = @{
                metricSelector = $metricSelector
            }
        }

        # Always set pageSize to a reasonable value?
        $GetParameter += @{
            pageSize = 200
        }

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

    process {
        $splatParameters = @{
            Uri = $uri
            GetParameter = $GetParameter
            RestResponseProperty = 'metrics'
        }
        $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-DynatraceMetric
#EndRegion - Get-DynatraceMetric.ps1
#Region - Get-DynatraceNotification.ps1
function Get-DynatraceNotification {
<#
    .SYNOPSIS
        Lists all notification configurations available in your environment
 
    .DESCRIPTION
        Lists all notification configurations available in your environment
 
    .PARAMETER id
        Optional, id of notification to get details for
 
    .PARAMETER OutputAsJson
        Optional, output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceNotification
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/configuration-api/notifications-api/get-all-notifications
#>


    [CmdletBinding()]
    param(
        [string]$id,
        [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/config/v1/notifications"

        if ($id) {
            $uri = "$uri/$id"
        } else {
            $RestResponseProperty = 'values'
        }

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

    process {
        $splatParameters = @{
            Uri = $uri
            RestResponseProperty = $RestResponseProperty
        }
        $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-DynatraceNotification
#EndRegion - Get-DynatraceNotification.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 {
            $_
            return
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatracePermission
#EndRegion - Get-DynatracePermission.ps1
#Region - Get-DynatraceProblem.ps1
function Get-DynatraceProblem {
<#
    .SYNOPSIS
        Gets problem(s) in Dynatrace environment
 
    .DESCRIPTION
        Gets problem(s) in Dynatrace environment
 
    .PARAMETER From
        Start of the requested time frame. Defaults to now-2h
        There are several recognized formats available, see:
        https://www.dynatrace.com/support/help/shortlink/api-problems-v2-get-list#parameters
 
    .PARAMETER To
        End of the requested time frame. Defaults to current timestamp
        There are several recognized formats available, see:
        https://www.dynatrace.com/support/help/shortlink/api-problems-v2-get-list#parameters
 
    .PARAMETER Status
        Specific status of problem to get. may be open or closed. defaults to both
 
    .PARAMETER Raw
        Output the raw json result, no formatting of results
 
    .EXAMPLE
        Get-DynatraceProblem
 
    .EXAMPLE
        Get-DynatraceProblem -From now-12h
 
        Get all problems in last 12 hours
 
    .EXAMPLE
        Get-DynatraceProblem -From now-12h -To now-11h
 
        Get all problems between 12 and 11 hours ago
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/problems-v2/
#>


    [CmdletBinding()]
    param(
        #[Parameter(Mandatory,ParameterSetName = 'id')]
        #[string]$Id,
        #[Parameter(Mandatory,ParameterSetName = 'name')]
        #[string]$Name
        [string]$From,
        [string]$To,
        [ValidateSet('open','closed')]
        [string]$Status,
        [switch]$Raw

    )

    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/problems"

        $problemSelector = @()
        if ($Status) {
            $problemSelector += "status(`"$($Status)`")"
        }

        $GetParameter = @{}
        if ($from) {
            $GetParameter += @{from = $from}
        }
        if ($to) {
            $GetParameter += @{to = $to}
        }

        # If we had problems selectors, join them together and add to get
        #
        if ($problemSelector) {
            $problemSelectorJoined = $problemSelector -join ','
            $GetParameter += @{
                problemSelector = $problemSelectorJoined
            }
        }

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

    process {
        $splatParameters = @{
            Uri = $uri
            GetParameter = $GetParameter
            RestResponseProperty = 'problems'
        }
        $problems = Invoke-DynatraceAPIMethod @splatParameters

        if ($Raw) {
            $output = $problems
        } else {
            $output = @()
            foreach ($problem in $problems) {
                $StartTime = ConvertTo-DateTime -TimeStamp $problem.StartTime
                if ($problem.EntTime -ne '-1') {
                    $EndTime = ConvertTo-DateTime -TimeStamp $problem.EndTime
                } else {
                    $EndTime = ''
                }
                $ManagementZones = $problem.managementZones.name -join ','
                $AffectedEntities = $problem.AffectedEntities.name -join ','
                $Tags = $problem.entityTags.stringRepresentation -join ','
                $item = [PSCustomObject]@{
                    ProblemID = $problem.displayID
                    Title = $problem.Title
                    Impact = $problem.impactLevel
                    Severity = $problem.severityLevel
                    Status = $problem.status
                    ManagementZones = $ManagementZones
                    AffectedEntities = $AffectedEntities
                    Tags = $tags
                    StartTime = $StartTime
                    EndTime = $EndTime
                }
                $output += $item
            }
        }

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



Export-ModuleMember -Function Get-DynatraceProblem
#EndRegion - Get-DynatraceProblem.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!"
            return
        }
        $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 {
            $_
            return
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceQuota
#EndRegion - Get-DynatraceQuota.ps1
#Region - Get-DynatraceSettingsObject.ps1
function Get-DynatraceSettingsObject {
<#
    .SYNOPSIS
        Get settings object(s)
 
    .DESCRIPTION
        Get settings object(s)
 
    .PARAMETER objectID
        Object ID string to get.
 
    .PARAMETER SchemaIDs
        Schema IDs to which the requested objects belong. Use Get-DynatraceSettingsSchema to get SchemaIDs
 
    .PARAMETER Scopes
        Scopes to get settings objects for. e.g. environment
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceSettingsobject -scopes 'environment'
 
    .EXAMPLE
        Get-DynatraceSettingsobject -schemaid 'builtin:management-zones' -OutputAsJson
 
        Get settings for the management zones schemaid and output as json
 
    .NOTES
        https://api.dynatrace.com/spec/#/
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/settings/objects/get-objects
#>


    [CmdletBinding()]
    param(
        [string]$objectID,
        [string[]]$SchemaIDs,
        [string[]]$scopes,
        [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/settings/objects"

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

    process {

        if ($SchemaIDs) {
            $GetParameter = @{schemaIds = $SchemaIDs -join ','}
            $RestResponseProperty = 'items'
        } elseif ($scopes) {
            $GetParameter = @{scopes = $scopes -join ','}
            $RestResponseProperty = 'items'
        } elseif ($objectID) {
            $uri = "$uri/$objectID"
            $GetParameter = $null
            $RestResponseProperty = $null
        } else {
            Write-Warning 'One of the following parameters must be used: -SchemaIds, -scopes, -objectID'
            Write-Warning 'Exiting...'
            return
        }

        $splatParameters += @{
            Uri = $uri
            GetParameter = $GetParameter
            RestResponseProperty = $RestResponseProperty
        }

        try {
            $output = Invoke-DynatraceAPIMethod @splatParameters
        } catch {
            $_
            return
        }

        if ($OutputAsJson) {
            $output | ConvertTo-Json -Depth 10
        } else {
            $output
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}
Export-ModuleMember -Function Get-DynatraceSettingsObject
#EndRegion - Get-DynatraceSettingsObject.ps1
#Region - Get-DynatraceSettingsSchema.ps1
function Get-DynatraceSettingsSchema {
<#
    .SYNOPSIS
        Get settings schemas in Dynatrace environment. Returns list if no schemaid value is passed in
 
    .PARAMETER SchemaID
        The ID of the required schema. Optional.
 
    .PARAMETER OutputAsJson
        Output the properties as a JSON string
 
    .EXAMPLE
        Get-DynatraceSettingsSchema
 
        Get list of Setting schemas
 
    .EXAMPLE
        Get-DynatraceSettingsSchema -SchemaID builtin:custom-metrics
 
        Get the builtin:custom-metrics schema id
 
    .NOTES
        https://api.dynatrace.com/spec/#/
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/settings/schemas/get-schema
#>


    [CmdletBinding()]
    param(
        [string]$SchemaID,
        [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/settings/schemas"

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

    process {
        if ($SchemaID) {
            $uri = "$uri/$SchemaID"
            $RestResponseProperty = $null
        } else {
            $RestResponseProperty = 'items'
        }

        $splatParameters = @{
            Uri = $uri
            RestResponseProperty = $RestResponseProperty
        }
        try {
            $output = Invoke-DynatraceAPIMethod @splatParameters
        } catch {
            $_
            return
        }

        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-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 {
            $_
            return
        }
        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-DynatraceSyntheticLocation.ps1
function Get-DynatraceSyntheticLocation {
<#
    .SYNOPSIS
        Lists all locations, public and private, and their parameters available for your environment
 
    .DESCRIPTION
        Lists all locations, public and private, and their parameters available for your environment
        Access token must have Read synthetic locations (syntheticLocations.read) scope.
 
    .PARAMETER Type
        Get only locations of the specified type. May be PUBLIC or PRIVATE. Optional.
 
    .EXAMPLE
        Get-DynatraceSyntheticLocation
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/synthetic-v2/synthetic-locations-v2/get-all-locations
#>


    [CmdletBinding()]
    param(
        [string]$Type
    )

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

        $EnvironmentID = (Get-DynatracePSConfig).EnvironmentID

        $locationsUri = "https://$EnvironmentID.live.dynatrace.com/api/v2/synthetic/locations"

        if ($Type) {
            $GetParameter = @{
                type = $Type.ToUpper()
            }
        }
        $output = @()
    }

    process {
        $splatParameters = @{
            Uri = $locationsUri
            RestResponseProperty = 'locations'
            GetParameter = $GetParameter
        }
        $results = Invoke-DynatraceAPIMethod @splatParameters
        foreach ($result in $results) {
            # Get the decimal value from the hex in the entityID
            #
            $id = [uint64]($result.entityID -replace 'SYNTHETIC_LOCATION-','0x')
            # Add it to the result
            #
            $result | Add-Member -MemberType NoteProperty -Name 'ID' -Value $id
            $result
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceSyntheticLocation
#EndRegion - Get-DynatraceSyntheticLocation.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 {
            $_
            return
        }
        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 {
            $_
            return
        }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}
Export-ModuleMember -Function Get-DynatraceUserGroup
#EndRegion - Get-DynatraceUserGroup.ps1
#Region - Get-DynatraceUserLastLogin.ps1
function Get-DynatraceUserLastLogin {
<#
    .SYNOPSIS
        List the users with last login time
 
    .DESCRIPTION
        List the users with last login time
 
    .EXAMPLE
        Get-DynatraceUserLastLogin
 
    .NOTES
        https://api.dynatrace.com/spec/#/User%20management/UsersController_getUsers
 
#>


    [CmdletBinding()]
    param()

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

    process {
        Get-DynatraceUser | Select-Object email,
            @{
                n='lastSuccessfulLoginUTC';
                e={
                    ($_.userLoginMetadata).lastSuccessfulLogin
                }
            }, @{
                n='lastSuccessfulLoginLocal';
                e={
                    $ParsedDateTime = Get-Date ($_.userLoginMetadata).lastSuccessfulLogin;
                    $ParsedDateTime.ToLocalTime()
                }
            }, @{
                n='successfulLoginCounter';
                e={
                    ($_.userLoginMetaData).successfulLoginCounter
                }
            }
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}



Export-ModuleMember -Function Get-DynatraceUserLastLogin
#EndRegion - Get-DynatraceUserLastLogin.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-Datetime.ps1
Function ConvertTo-Datetime {
<#
.SYNOPSIS
    Convert a timestamp value from dynatrace (which uses millisecond timestamps) to a datetime
 
.DESCRIPTION
    Convert a timestamp value from dynatrace (which uses millisecond timestamps) to a datetime
 
.PARAMETER TimeStamp
    Timestamp value
 
.NOTES
    https://community.dynatrace.com/t5/REST-API/Dynatrace-API-Timestamp/td-p/81309
 
#>

    param(
        [long]$TimeStamp
    )
    begin {
        [DateTime]$Epoch = Get-Date 1970-01-01
    }
    process {
        (($Epoch) + ([System.TimeSpan]::FromMilliseconds($TimeStamp))).ToLocalTime()
    }
}
#EndRegion - ConvertTo-Datetime.ps1
#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 {
<#
.SYNOPSIS
    Given a URI or uri query, return the query portion as a hash table. For internal use
 
.DESCRIPTION
    Extract query portion from a URI and return it as a hash table. For internal use
 
.PARAMETER Uri
    Uri to extract query from and convert to hash
 
.PARAMETER Query
    Query to convert to hash
#>

    [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 - Get-DynatraceHTTPCheckMonitorExecution.ps1
function Get-DynatraceHTTPCheckMonitorExecution {
<#
    .SYNOPSIS
        Get results of HTTP check monitor executions
 
    .DESCRIPTION
        Get results of HTTP check monitor executions
 
    .PARAMETER monitorID
        monitorID (entityID) identifier for which last execution result is returned
 
    .PARAMETER resultType
        SUCCESS or FAILED
 
    .EXAMPLE
        Get-DynatraceHTTPCheckMonitorExecution -monitorID HTTP_CHECK-0123456789ABCDEF -resultType SUCCESS
 
        Return the most recent SUCCESS execution for HTTP_CHECK-0123456789ABCDEF
 
    .NOTES
        https://www.dynatrace.com/support/help/dynatrace-api/environment-api/synthetic-v2/synthetic-monitor-execution/get-http-monitor
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$monitorID,
        [Parameter(Mandatory)][ValidateSet('SUCCESS','FAILED')]
        [string]$resultType
    )

    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/v2/synthetic/execution/$monitorID/"
    }

    process {
        $uri = "$($baseUri)$resultType"
        Invoke-DynatraceAPIMethod -Uri $uri
    }
    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
        Write-Debug "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}
#EndRegion - Get-DynatraceHTTPCheckMonitorExecution.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 {
                $splatParameters = @{
                    Uri = $Uri
                    Method = $Method
                    Headers = $headers
                }
                $Return = Invoke-WebRequest @splatParameters
                $output = $Return.Content | ConvertFrom-JSON
            } catch {
                Write-Warning "[$($MyInvocation.MyCommand.Name)] Problem with Invoke-WebRequest $uri"
                $_
                return
            }
            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..."
            return
        }

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

        # Create a hash table from the query in the URI, makes it easier to
        # manage in powershell
        #
        $uriQuery = ConvertTo-ParameterHash -Uri $Uri

        # Generate the Get parameter hashtable that we will use
        #
        $internalGetParameter = Join-Hashtable $uriQuery, $GetParameter
        [Uri]$LeftPartOfURI = $Uri.GetLeftPart('Path')

        # Concat the url and query together to form the URI address we are going to use
        #
        [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
            }
            # If -body parm is used, we add it to the splat parameters
            #
            if ($body) {
                Write-Debug "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] body: $($body | Out-String)"
                $splatParameters += @{
                    Body = $body
                }
            }
            # if contenttype is defined, add it to the parameters
            #
            if ($ContentType) {
                $splatParameters += @{
                    ContentType = $ContentType
                }
            }

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

            # Invoke-WebRequest (IWR) to Dynatrace. We use IWR instead of Invoke-RestMethod (IRM) because of reasons:
            # 1) IWR is standard from PS version 3 and up. IRM is not
            # 2) IRM doesn't do good job of returning headers and status codes consistently. IWR does.
            #
            # https://www.truesec.com/hub/blog/invoke-webrequest-or-invoke-restmethod
            #
            $Response = Invoke-WebRequest @splatParameters
            $RestResponse = $Response.Content | ConvertFrom-JSON
            $ResponseHeaders = $Response.Headers
            $StatusCode = $Response.StatusCode

            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"
            $_
            return
        }
        Write-Verbose "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] Executed RestMethod"

        # If there are bad status codes, this will break and cause function to exit
        #
        Test-ServerResponse -InputObject $RestResponse -StatusCode $StatusCode
    }

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

            # Set next page key to get
            #
            $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, as there are no more pages
                    #
                    break
                } else {
                    # Output results from this loop, and continue on the recursion
                    #
                    $result
                }

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

                Write-Verbose "[$($MyInvocation.MyCommand.Name) $LevelOfRecursion] Calling Invoke-DynatraceApiMethod"

                # Call this same function again (recursion)
                #
                $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.
 
.PARAMETER Hashtable
    Hash tables to merge together
 
.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
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU/RWdExf+1en1+Jwh8Su0j0Ds
# A3qgggMnMIIDIzCCAgugAwIBAgIQfxlXoOWZRbhMi6xrw92ZDzANBgkqhkiG9w0B
# 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
# BgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBQm8aNSBXP+suRuo99JPePIR29uOzAN
# BgkqhkiG9w0BAQEFAASCAQCrYtlypImYP06sq/VsswyeDhiz2mD8bzToCvhPDILI
# PAP7JE8vb05vJIbvu1KzdNnc5aIKgsWdV3WcrkB/NRDlCLvRG9QQ3tCRnkfBodfa
# 2uxb40S/9ixUKOKelgasm4boq8pxfOGM95MX9amVvDLC0JYZDbJ69WkoZH0yPAKP
# lPXEaCYI9TOJF1PmQjTcrBspp9XnojUzb0G36DOPWAWI3b6o490XuvDscghbxAmr
# dU3DvPAxN+dGqgJWbWn+Bz8D3F3ejwHU1hIWh1Ak5W/sTsRfv7LOvQiFOmIE6B6I
# 1C615/AYLZ7stryyc5wNTJuDluYljjwTrY1c3IhOW+nw
# SIG # End signature block