VenafiPS.psm1

#Region './Enum/TppCertificateStage.ps1' -1

enum TppCertificateStage {
    CheckStore = 100
    CreateConfigureStore = 200
    CreateKey = 300
    CreateCSR = 400
    PostCSR = 500
    ApproveRequest = 600
    RetrieveCertificate = 700
    InstallCertificate = 800
    CheckConfiguration = 900
    ConfigureApplication = 1000
    RestartApplication = 1100
    EndProcessing = 1200
    Revocation = 1400
    UpdateTrustStore = 1500
    EndTrustStoreProcessing = 1600
}
#EndRegion './Enum/TppCertificateStage.ps1' 18
#Region './Enum/TppEventSeverity.ps1' -1

enum TppEventSeverity {
    Emergency = 1
    Alert = 2
    Critical = 3
    Error = 4
    Warning = 5
    Notice = 6
    Info = 7
    Debug = 8
}
#EndRegion './Enum/TppEventSeverity.ps1' 11
#Region './Enum/TppIdentityType.ps1' -1

enum TppIdentityType {
    User = 1
    SecurityGroups = 2
    DistributionGroups = 8
}
#EndRegion './Enum/TppIdentityType.ps1' 6
#Region './Enum/TppManagementType.ps1' -1

enum TppManagementType {
    Unassigned
    Monitoring
    Enrollment
    Provisioning
}
#EndRegion './Enum/TppManagementType.ps1' 7
#Region './Enum/TppMetadataResult.ps1' -1

enum TppMetadataResult {
    Success = 0
    InvalidConfigObject = 1
    InvalidDN = 2
    InvalidName = 3
    InvalidItem = 4
    InvalidClass = 5
    InvalidMetadataObject = 6
    InvalidRights = 7
    ItemIsNull = 8
    ItemAlreadyExists = 9
    ItemTypeUnknown = 10
    ConfigCreateFailed = 11
    ConfigWriteFailed = 12
    ConfigDeleteFailed = 13
    MetadataInUse = 14
    NoAllowedValues = 15
    AllowedValueDoesNotExist = 16
    ValueNotInAllowedList = 17
    ItemNotValidForClass = 18
    NameTooLong = 19
    TooManyContainers = 20
    ConfigDnNotContainer = 21
    InvalidPolicyState = 22
    ConfigLockFailed = 23
    ConfigReadFailed = 24
    RemoteError = 25
}
#EndRegion './Enum/TppMetadataResult.ps1' 29
#Region './Enum/TppSecretStoreResult.ps1' -1

enum TppSecretStoreResult {
    Success = 0
    InvalidCallingAssembly = 1
    CreateDatabaseError = 2
    UseDatabaseError = 3
    CreateTableError = 4
    CreateIndexError = 5
    ConnectionError = 6
    TransactionError = 7
    InvalidVaultID = 8
    InvalidParams = 9
    InsufficientPermissions = 10
    CryptoFailure = 11
    DeleteSecretFailed = 12
    AddSecretFailed = 13
    RetrieveSecretFailed = 14
    RetrieveSecretTypeFailed = 15
    GetNextVaultIDFailed = 16
    DisassociateFailed = 17
    OwnerLookupFailed = 18
    AssociateDataFailed = 19
    LookupFailed = 20
    InvalidKey = 21
    QueryError = 22
    SecurityGroupNotImplemented = 23
    RemoteError = 24
    VaultIdAlreadyExists = 25
    OutOfMemory = 26
    AssociationAlreadyExists = 27
    VaultIdExceedsIntegerCapacity = 28
    AssociationLookupFailed = 29
    AddRevocationStateFailed = 30
    CertificateMigrationStateNotFound = 31
    TodoManagerError = 32
}
#EndRegion './Enum/TppSecretStoreResult.ps1' 36
#Region './Enum/TppWorkflowResult.ps1' -1

enum TppWorkflowResult {
    Success = 1
    GenericFailure = 2
    TicketDoesNotExist = 3
    InsufficientPrivileges = 4
    BadArguments = 5
    ObjectDoesNotExist = 6
    RemoteError = 7
    WorkflowObjectDoesNotExist = 8
}
#EndRegion './Enum/TppWorkflowResult.ps1' 11
#Region './Classes/TppPermission.ps1' -1

class TppPermission {

    [bool] $IsAssociateAllowed
    [bool] $IsCreateAllowed
    [bool] $IsDeleteAllowed
    [bool] $IsManagePermissionsAllowed
    [bool] $IsPolicyWriteAllowed
    [bool] $IsPrivateKeyReadAllowed
    [bool] $IsPrivateKeyWriteAllowed
    [bool] $IsReadAllowed
    [bool] $IsRenameAllowed
    [bool] $IsRevokeAllowed
    [bool] $IsViewAllowed
    [bool] $IsWriteAllowed

    TppPermission () {
        $this.IsAssociateAllowed = $false
        $this.IsCreateAllowed = $false
        $this.IsDeleteAllowed = $false
        $this.IsManagePermissionsAllowed = $false
        $this.IsPolicyWriteAllowed = $false
        $this.IsPrivateKeyReadAllowed = $false
        $this.IsPrivateKeyWriteAllowed = $false
        $this.IsReadAllowed = $false
        $this.IsRenameAllowed = $false
        $this.IsRevokeAllowed = $false
        $this.IsViewAllowed = $false
        $this.IsWriteAllowed = $false
    }

    TppPermission ([pscustomobject] $InputObject) {
        $this.IsAssociateAllowed = $InputObject.IsAssociateAllowed
        $this.IsCreateAllowed = $InputObject.IsCreateAllowed
        $this.IsDeleteAllowed = $InputObject.IsDeleteAllowed
        $this.IsManagePermissionsAllowed = $InputObject.IsManagePermissionsAllowed
        $this.IsPolicyWriteAllowed = $InputObject.IsPolicyWriteAllowed
        $this.IsPrivateKeyReadAllowed = $InputObject.IsPrivateKeyReadAllowed
        $this.IsPrivateKeyWriteAllowed = $InputObject.IsPrivateKeyWriteAllowed
        $this.IsReadAllowed = $InputObject.IsReadAllowed
        $this.IsRenameAllowed = $InputObject.IsRenameAllowed
        $this.IsRevokeAllowed = $InputObject.IsRevokeAllowed
        $this.IsViewAllowed = $InputObject.IsViewAllowed
        $this.IsWriteAllowed = $InputObject.IsWriteAllowed
    }

    [HashTable] ToHashtable() {

        $hash = @{}
        $propNames = $this | Get-Member | Where-Object { $_.MemberType -eq 'Property' } | Select-Object -ExpandProperty Name

        foreach ($prop in $propNames) {
            if ($this.GetType().GetProperty($prop)) {
                $hash.Add($prop, $this.$prop)
            }
        }

        return $hash
    }
}

#EndRegion './Classes/TppPermission.ps1' 61
#Region './Private/ConvertTo-PlaintextString.ps1' -1

function ConvertTo-PlaintextString {
    <#
    .SYNOPSIS
    Convert a SecureString or PSCredential to a plaintext string/password.
    .EXAMPLE
    ConvertTo-PlaintextString -InputObject $mySecureString
    #>

    [Cmdletbinding()]
    [OutputType([string])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)]
        [psobject] $InputObject
    )
    process {
        if ( $InputObject -is [string] ) {
            $InputObject
        }
        elseif ($InputObject -is [securestring]) {
            # use this workaround to support both v5 and v7
            (New-Object System.Management.Automation.PSCredential(0, $InputObject)).GetNetworkCredential().password
        }
        elseif ($InputObject -is [pscredential]) {
            $InputObject.GetNetworkCredential().Password
        }
        else {
            throw 'Unsupported type for -InputObject. Provide either a String, SecureString, or PSCredential.'
        }
    }
}
#EndRegion './Private/ConvertTo-PlaintextString.ps1' 31
#Region './Private/ConvertTo-UtcIso8601.ps1' -1

<#
.SYNOPSIS
Convert datetime to UTC ISO 8601 format

.DESCRIPTION
Convert datetime to UTC ISO 8601 format

.PARAMETER InputObject
DateTime object

.INPUTS
InputObject

.OUTPUTS
System.String

.EXAMPLE
(get-date) | ConvertTo-UtcIso8601

#>

function ConvertTo-UtcIso8601 {

    [CmdletBinding()]

    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [DateTime] $InputObject
    )

    begin {
    }

    process {
        $InputObject.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ")
    }
}
#EndRegion './Private/ConvertTo-UtcIso8601.ps1' 38
#Region './Private/ConvertTo-VcTeam.ps1' -1

function ConvertTo-VcTeam {
    <#
    .SYNOPSIS
    Convert vaas team to standard format

    .DESCRIPTION
    Convert vaas team to standard format

    .PARAMETER InputObject
    Team object

    .INPUTS
    InputObject

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    $teamObj | ConvertTo-VcTeam

    #>


    [CmdletBinding()]

    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [AllowNull()]
        [PSCustomObject[]] $InputObject
    )

    begin {
    }

    process {
        $InputObject | Select-Object -Property `
        @{
            n = 'teamId'
            e = { $_.id }
        }, * -ExcludeProperty id
    }
}
#EndRegion './Private/ConvertTo-VcTeam.ps1' 42
#Region './Private/ConvertTo-VdcFullPath.ps1' -1

function ConvertTo-VdcFullPath {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [string] $Path
    )

    begin {

    }

    process {
        if ( $Path.ToLower() -notlike '\ved*') {
            "\VED\Policy\$Path"
        }
        else {
            $Path
        }
    }

    end {

    }
}
#EndRegion './Private/ConvertTo-VdcFullPath.ps1' 25
#Region './Private/ConvertTo-VdcIdentity.ps1' -1

function ConvertTo-VdcIdentity {
    <#
    .SYNOPSIS
    Convert identity to standard format

    .DESCRIPTION
    Convert identity to standard format

    .PARAMETER InputObject
    Identity object

    .INPUTS
    InputObject

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    $identityObj | ConvertTo-VdcIdentity

    #>


    [CmdletBinding()]

    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [AllowNull()]
        [PSCustomObject[]] $InputObject
    )

    begin {
    }

    process {
        $InputObject | Select-Object -Property `
        @{
            n = 'Name'
            e = { $_.Name }
        },
        @{
            n = 'ID'
            e = { $_.PrefixedUniversal }
        },
        @{
            n = 'Path'
            e = { $_.FullName }
        },
        @{
            n = 'FullName'
            e = { $_.PrefixedName }
        },
        @{
            n = 'IsGroup'
            e = { $_.Type -ne 1 }
        }, * -ExcludeProperty PrefixedUniversal, FullName, Prefix, PrefixedName, Type, Universal, IsGroup, Name
    }
}
#EndRegion './Private/ConvertTo-VdcIdentity.ps1' 58
#Region './Private/ConvertTo-VdcObject.ps1' -1

function ConvertTo-VdcObject {

    [CmdletBinding(DefaultParameterSetName = 'Path')]

    param (
        [Parameter(Mandatory, ParameterSetName = 'ByObject', ValueFromPipeline)]
        [pscustomobject] $InputObject,

        [Parameter(Mandatory, ParameterSetName = 'Path', ValueFromPipeline)]
        [Parameter(Mandatory, ParameterSetName = 'All')]
        [string] $Path,

        [Parameter(Mandatory, ParameterSetName = 'Guid', ValueFromPipeline)]
        [Parameter(Mandatory, ParameterSetName = 'All')]
        [guid] $Guid,

        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'Guid')]
        [Parameter(Mandatory, ParameterSetName = 'All')]
        [string] $TypeName
    )

    begin {
    }

    process {
        switch ($PSCmdlet.ParameterSetName) {

            'ByObject' {
                $thisPath = $InputObject.Path
                $thisGuid = $InputObject.Guid
                $thisTypeName = $InputObject.TypeName
            }

            'Path' {
                $thisPath = $Path
                $info = $Path | ConvertTo-VdcGuid -IncludeType
                $thisGuid = $info.Guid
                $thisTypeName = $info.TypeName
            }

            'Guid' {
                $thisGuid = $Guid
                $info = ConvertTo-VdcPath -Guid $Guid -IncludeType
                $thisPath = $info.Path
                $thisTypeName = $info.TypeName
            }

            Default {
                $thisPath = $Path
                $thisGuid = $Guid
                $thisTypeName = $TypeName
            }
        }

        $out = [pscustomobject]@{
            Path     = $thisPath.Replace('\\', '\')
            TypeName = $thisTypeName
            Guid     = $thisGuid
            Name     = $thisPath.Split('\')[-1]
        }

        $out | Add-Member @{'ParentPath' = $out.Path.Substring(0, $out.Path.LastIndexOf("\$($out.Name)")) }
        $out | Add-Member -MemberType ScriptMethod -Name ToString -Value { $out.Path } -Force
        $out
    }
}
#EndRegion './Private/ConvertTo-VdcObject.ps1' 68
#Region './Private/Find-VcObject.ps1' -1

function Find-VcObject {
    <#
    .SYNOPSIS
    Find different objects on TLSPC

    .DESCRIPTION
    Find objects of type ActivityLog, Machine, MachineIdentity, CertificateRequest, CertificateInstance on TLSPC.
    Supports -First for page size; the max page size is 1000.
    To find certificate objects, use Find-VcCertificate.

    .PARAMETER Type
    Type of object to retrieve, either Certificate, ActivityLog, Machine, MachineIdentity, CertificateRequest, or CertificateInstance.

    .PARAMETER Name
    Case sensitive name to search for.
    The field to be searched is different for each object type.

    .PARAMETER Filter
    Array or multidimensional array of fields and values to filter on.
    Each array should be of the format @('operator', @(field, comparison operator, value), @(field2, comparison operator2, value2)).
    Nested filters are supported.
    For a complete list of comparison operators, see https://docs.venafi.cloud/api/about-api-search-operators/.

    .PARAMETER Order
    Array of fields to order on.
    For each item in the array, you can provide a field name by itself; this will default to ascending.
    You can also provide a hashtable with the field name as the key and either asc or desc as the value.

    .PARAMETER First
    Only retrieve this many records

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Find-VcObject -Type CertificateInstance

    Get all records

    .EXAMPLE
    Find-VcObject -Type CertificateInstance -First 50

    Get first 50 records

    .EXAMPLE
    Find-VcObject -Type ActivityLog -Filter @('activityType', 'eq', 'Notifications') -First 10

    Retrieve 10 records matching the field name

    .EXAMPLE
    Find-VcObject -Type Certificate -Name searchme

    Get all certificates where the name has 'searchme' in it

    .EXAMPLE
    Find-VcObject -type Certificate -filter @('selfSigned','eq','True')

    Get all self signed certificates

    .EXAMPLE
    Find-VcObject -Type ActivityLog -Filter @('activityType', 'eq', 'Notifications') -First 10 -Order @{'activityDate'='desc'}

    Retrieve the most recent 10 records matching the field name

    .EXAMPLE
    Find-VcObject -Filter @('and', @('activityDate', 'gt', (get-date).AddMonths(-1)), @('or', @('userId', 'eq', 'ab0feb46-8df7-47e7-8da9-f47ab314f26a'), @('userId', 'eq', '933c28de-6352-46f3-bc12-bd96077e8eae')))

    Advanced filtering of results. This filter will find log entries by 1 of 2 people within the last month.

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Find-VcObject/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Find-VcObject.ps1
    #>


    [CmdletBinding(DefaultParameterSetName = 'All')]

    param (

        [Parameter(Mandatory)]
        [ValidateSet('Certificate', 'ActivityLog', 'Machine', 'MachineIdentity', 'CertificateRequest', 'CertificateInstance')]
        [string] $Type,

        [Parameter(ParameterSetName = 'All')]
        [string] $Name,

        [Parameter(Mandatory, ParameterSetName = 'Filter')]
        [System.Collections.Generic.List[object]] $Filter,
        # [System.Collections.ArrayList] $Filter,

        [Parameter(ParameterSetName = 'All')]
        [Parameter(ParameterSetName = 'Filter')]
        [psobject[]] $Order,

        [parameter(Mandatory, ParameterSetName = 'SavedSearch')]
        [string] $SavedSearchName,

        [Parameter()]
        [int] $First,

        [Parameter()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

    $objectData = @{
        'Certificate'         = @{
            'uriroot'  = 'outagedetection/v1'
            'urileaf'  = 'certificatesearch'
            'property' = 'certificates'
            'filter'   = @{
                Property        = @{'n' = 'certificateId'; 'e' = { $_.Id } }
                ExcludeProperty = 'id'
            }
            'name'     = 'certificateName'
        }
        'CertificateRequest'  = @{
            'uriroot'  = 'outagedetection/v1'
            'urileaf'  = 'certificaterequestssearch'
            'property' = 'certificaterequests'
            'filter'   = @{
                Property        = @{'n' = 'certificateRequestId'; 'e' = { $_.Id } }
                ExcludeProperty = 'id'
            }
            'name'     = 'subjectDN'
        }
        'CertificateInstance' = @{
            'uriroot'  = 'outagedetection/v1'
            'urileaf'  = 'certificateinstancesearch'
            'property' = 'instances'
            'filter'   = ''
            'name'     = 'hostname'
        }
        'Machine'             = @{
            'uriroot'  = 'v1'
            'urileaf'  = 'machinesearch'
            'property' = 'machines'
            'filter'   = @{
                Property        = @{'n' = 'machineId'; 'e' = { $_.Id } }
                ExcludeProperty = 'id'
            }
            'name'     = 'machineName'
        }
        'MachineIdentity'     = @{
            'uriroot'  = 'v1'
            'urileaf'  = 'machineidentitysearch'
            'property' = 'machineidentities'
            'filter'   = @{
                Property        = @{'n' = 'machineIdentityId'; 'e' = { $_.Id } }
                ExcludeProperty = 'id'
            }
            'name'     = 'certificateName'
        }
        'ActivityLog'         = @{
            'uriroot'  = 'v1'
            'urileaf'  = 'activitylogsearch'
            'property' = 'activityLogEntries'
            'filter'   = @{
                Property        = @{'n' = 'activityLogId'; 'e' = { $_.Id } }
                ExcludeProperty = 'id'
            }
            'name'     = 'activityName'
        }
    }

    $queryParams = @{
        Filter = $Filter
        Order  = $Order
        First  = $First
    }

    if ($Name) {
        $queryParams.Filter = @($objectData.$Type.name, 'FIND', $Name)
    }

    $body = New-VcSearchQuery @queryParams

    if ( $PSBoundParameters.ContainsKey('SavedSearchName') ) {
        # get saved search data and update payload
        $thisSavedSearch = Invoke-VenafiRestMethod -UriRoot 'outagedetection/v1' -UriLeaf 'savedsearches' | Select-Object -ExpandProperty savedSearchInfo | Where-Object { $_.name -eq $SavedSearchName }
        if ( $thisSavedSearch ) {
            $body.expression = $thisSavedSearch.searchDetails.expression
            $body.ordering = $thisSavedSearch.searchDetails.ordering
        }
        else {
            throw "The saved search name $SavedSearchName could not be found"
        }
    }

    $params = @{
        Method = 'Post'
        Body   = $body
        Header = @{'Accept' = 'application/json' }
    }

    $params.UriRoot = $objectData.$Type.uriroot
    $params.UriLeaf = $objectData.$Type.urileaf

    $params.UriLeaf += '?ownershipTree=true'
    $allObjects = [System.Collections.Generic.List[object]]::new()

    do {

        $response = Invoke-VenafiRestMethod @params

        if ( -not $response ) { continue }

        $thisObjects = $response | Select-Object -ExpandProperty $objectData.$Type.property

        if ( -not $thisObjects ) { break }

        $allObjects.AddRange(@($thisObjects))

        if ( $thisObjects.Count -lt $params.Body.paging.pageSize ) { break }

        if ( $First ) {
            # break out if we have all the objects we asked for
            if ( $allObjects.Count -eq $First ) {
                break
            }
            else {
                $params.Body.paging.pageSize = [System.Math]::Min($First - $allObjects.Count, 1000)
            }
        }

        $params.body.paging.pageNumber += 1

    } while ( $true )

    if ( $objectData.$Type.filter ) {
        $allObjects | Select-Object -Property $objectData.$Type.filter.Property, * -ExcludeProperty $objectData.$Type.filter.ExcludeProperty
    }
    else {
        $allObjects
    }
}
#EndRegion './Private/Find-VcObject.ps1' 244
#Region './Private/Get-EnumValues.ps1' -1

function Get-EnumValues {

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')]

    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [string] $EnumName
    )

    process {
        [enum]::GetValues([type]$EnumName).ForEach{
            @{$_ = $_.value__}
        }
    }
}
#EndRegion './Private/Get-EnumValues.ps1' 18
#Region './Private/Get-VcData.ps1' -1

function Get-VcData {

    <#
    .SYNOPSIS
        Helper function to get data from Venafi Cloud
    #>



    [CmdletBinding(DefaultParameterSetName = 'ID')]
    param (
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ID', Position = '0')]
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Name', Position = '0')]
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Object', Position = '0')]
        [AllowEmptyString()]
        [string] $InputObject,

        [parameter(Mandatory)]
        [ValidateSet('Application', 'VSatellite', 'Certificate', 'IssuingTemplate', 'Team', 'Machine', 'Tag', 'MachinePlugin', 'CaPlugin', 'TppPlugin')]
        [string] $Type,

        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Name')]
        [switch] $Name,

        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Object')]
        [switch] $Object,

        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'First')]
        [switch] $First,

        [parameter()]
        [switch] $FailOnMultiple,

        [parameter()]
        [switch] $FailOnNotFound
    )

    begin {

    }

    process {

        # if we already have a guid and are just looking for the ID, return it
        if ( $PSCmdlet.ParameterSetName -eq 'ID' -and (Test-IsGuid($InputObject)) ) {
            return $InputObject
        }

        switch ($Type) {
            'Application' {

                # if ( -not $script:vcApplication ) {
                # $script:vcApplication = Get-VcApplication -All | Sort-Object -Property name
                # }

                # $allObject = $script:vcApplication
                # $thisObject = $script:vcApplication | Where-Object { $InputObject -in $_.name, $_.applicationId }

                $allObject = Get-VcApplication -All | Sort-Object -Property name
                $thisObject = $allObject | Where-Object { $InputObject -in $_.name, $_.applicationId }
            }

            'VSatellite' {

                # if ( -not $script:vcVSatellite ) {
                # $script:vcVSatellite = Get-VcSatellite -All | Sort-Object -Property name
                # }

                # $allObject = $script:vcVSatellite
                # $thisObject = $script:vcVSatellite | Where-Object { $InputObject -in $_.name, $_.vsatelliteId }

                $allObject = Get-VcSatellite -All | Where-Object { $_.edgeStatus -eq 'ACTIVE' } | Sort-Object -Property name
                $thisObject = $allObject | Where-Object { $InputObject -in $_.name, $_.vsatelliteId }
            }

            'IssuingTemplate' {
                # if ( -not $script:vcIssuingTemplate ) {
                # $script:vcIssuingTemplate = Get-VcIssuingTemplate -All | Sort-Object -Property name
                # }
                # $allObject = $script:vcIssuingTemplate
                # $thisObject = $script:vcIssuingTemplate | Where-Object { $InputObject -in $_.name, $_.issuingTemplateId }

                $allObject = Get-VcIssuingTemplate -All | Sort-Object -Property name
                $thisObject = $allObject | Where-Object { $InputObject -in $_.name, $_.issuingTemplateId }
            }

            'Team' {
                # if ( -not $script:vcTeam ) {
                # $script:vcTeam = Get-VcTeam -All | Sort-Object -Property name
                # }
                # $allObject = $script:vcTeam
                # $thisObject = $script:vcTeam | Where-Object { $InputObject -in $_.name, $_.teamId }

                $allObject = Get-VcTeam -All | Sort-Object -Property name
                $thisObject = $allObject | Where-Object { $InputObject -in $_.name, $_.teamId }
            }

            { $_ -match 'Plugin$' } {
                # for machine, ca, tpp, etc plugins

                # if ( -not $script:vcMachineType ) {
                # $script:vcMachineType = Invoke-VenafiRestMethod -UriLeaf 'machinetypes' |
                # Select-Object -ExpandProperty machineTypes |
                # Select-Object -Property @{'n' = 'machineTypeId'; 'e' = { $_.Id } }, * -ExcludeProperty id |
                # Sort-Object -Property machineType
                # }
                # $allObject = $script:vcMachineType
                # $thisObject = $script:vcMachineType | Where-Object { $InputObject -in $_.machineType, $_.machineTypeId }

                $pluginType = $_.Replace('Plugin', '').ToUpper()
                
                $allObject = Invoke-VenafiRestMethod -UriLeaf "plugins?pluginType=$pluginType" |
                Select-Object -ExpandProperty plugins |
                Select-Object -Property @{'n' = ('{0}Id' -f $Type); 'e' = { $_.Id } }, * -ExcludeProperty id

                $thisObject = $allObject | Where-Object { $InputObject -in $_.name, $_.('{0}Id' -f $Type) }
            }

            'Certificate' {
                $thisObject = Find-VcCertificate -Name $InputObject
            }

            'Machine' {
                $thisObject = Find-VcMachine -Name $InputObject
            }

            'Tag' {
                try {

                    if ( $InputObject.Contains(':') ) {
                        $tagName, $tagValue = $InputObject.Split(':')
                    }
                    else {
                        $tagName = $InputObject
                    }
                    $tag = Invoke-VenafiRestMethod -UriLeaf "tags/$tagName" |
                    Select-Object -Property @{'n' = 'tagId'; 'e' = { $_.Id } }, @{'n' = 'values'; 'e' = { $null } }, * -ExcludeProperty id

                    if ( $tag ) {
                        $values = Invoke-VenafiRestMethod -UriLeaf "tags/$($tag.name)/values"

                        if ( $values.values ) {
                            $tag.values = ($values.values | Select-Object id, @{'n' = 'name'; 'e' = { $_.value } })

                            # make sure the value, if provided, is valid
                            if ( $tagValue -and $tagValue -notin $tag.values.name ) {
                                $tag = $null
                            }
                        }
                        else {
                            if ( $tagValue ) {
                                # the tag name exists, but the value does not
                                $tag = $null
                            }
                        }
                    }

                    $thisObject = $tag
                }
                catch {
                    $thisObject = $null
                }
            }
        }

        if ( $FailOnMultiple -and @($thisObject).Count -gt 1 ) {
            throw "Multiple $Type found"
        }

        if ( $FailOnNotFound -and -not $thisObject ) {
            throw "'$InputObject' of type $Type not found"
        }

        switch ($PSCmdlet.ParameterSetName) {
            'ID' {
                if ( $thisObject ) {
                    $thisObject."$("$Type")id"
                }
                else {
                    return $null
                }

            }

            'Name' {
                switch ($Type) {
                    'Tag' { $InputObject }
                }
            }

            'Object' {
                $thisObject
            }

            'First' {
                $allObject | Select-Object -First 1
            }
        }
    }

    end {

    }
}
#EndRegion './Private/Get-VcData.ps1' 204
#Region './Private/Initialize-PSSodium.ps1' -1

function Initialize-PSSodium {
    [CmdletBinding()]
    param (

    )

    # Check if the module is already loaded
    if ( Get-Module PSSodium ) {
        return
    }

    # Check if the module is installed
    if ( -not (Get-Module PSSodium -ListAvailable) ) {
        throw 'The PSSodium module is not installed. Run `Install-Module -Name PSSodium` to install.'
    }

    try {
        Import-Module PSSodium -Force
    }
    catch {
        throw "Sodium encryption could not be loaded. Ensure you are running PowerShell v7+ and if on Windows, install the latest Visual C++ Runtime. $_"
    }
}
#EndRegion './Private/Initialize-PSSodium.ps1' 24
#Region './Private/Invoke-VenafiParallel.ps1' -1

function Invoke-VenafiParallel {

    <#
    .SYNOPSIS
    Helper function to execute a scriptblock in parallel

    .DESCRIPTION
    Execute a scriptblock in parallel.
    For PS v5, the ThreadJob module is required.

    .PARAMETER InputObject
    List of items to iterate over

    .PARAMETER ScriptBlock
    Scriptblock to execute against the list of items

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.

    .PARAMETER ProgressTitle
    Message displayed on the progress bar

    .PARAMETER VenafiSession
    Authentication for the function.

    .EXAMPLE
    Invoke-VenafiParallel -InputObject $myObjects -ScriptBlock { Do-Something $PSItem }

    Execute in parallel. Reference each item in the scriptblock as $PSItem or $_.

    .EXAMPLE
    Invoke-VenafiParallel -InputObject $myObjects -ScriptBlock { Do-Something $PSItem } -ThrottleLimit 5

    Only run 5 threads at a time instead of the default of 100.

    .EXAMPLE
    $ProgressPreference = 'SilentlyContinue'
    Invoke-VenafiParallel -InputObject $myObjects -ScriptBlock { Do-Something $PSItem }

    Execute in parallel with no progress bar.

    .NOTES
    In your ScriptBlock:
    - Use either $PSItem or $_ to reference the current input object
    - Remember hashtables are reference types so be sure to clone if 'using' from parent

    #>



    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [AllowNull()]
        [psobject] $InputObject,

        [Parameter(Mandatory)]
        [scriptblock] $ScriptBlock,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [string] $ProgressTitle = 'Performing action',

        [Parameter()]
        [psobject] $VenafiSession

    )

    begin {

        if (-not $InputObject) { return }

        # if ThreadJob module is not available, throttle to 1 so multithreading isn't used
        if ( $PSVersionTable.PSVersion.Major -eq 5 ) {
            if ( -not $script:ThreadJobAvailable ) {
                $ThrottleLimit = 1
            }
        }

        if ( $script:VenafiSession ) {
            $VenafiSession = $script:VenafiSession
        }
        elseif ($script:VenafiSessionNested) {
            $VenafiSession = $script:VenafiSessionNested
        }
        elseif ( $env:VDC_TOKEN ) {
            $VenafiSession = $env:VDC_TOKEN
        }
        elseif ( $env:VC_KEY ) {
            $VenafiSession = $env:VC_KEY
        }
    }

    process {
    }

    end {

        if (-not $InputObject) { return }

        # throttle to 1 = no parallel
        if ( $ThrottleLimit -le 1 ) {
            # ensure no $using: vars given not threaded and not needed
            $InputObject | ForEach-Object -Process ([ScriptBlock]::Create(($ScriptBlock.ToString() -ireplace [regex]::Escape('$using:'), '$')))
            return
        }

        # parallel processing from here down
        $starterSb = {

            # need to import module until https://github.com/PowerShell/PowerShell/issues/12240 is complete
            # import via path instead of just module name to support non-standard paths, eg. development work

            # ParallelImportPath is set during module import
            Import-Module $using:script:ParallelImportPath -Force

            # bring in the venafi session from the calling ps session
            $script:VenafiSession = $using:VenafiSession

            # bring in verbose preference from calling ps session
            $VerbosePreference = $using:VerbosePreference
        }


        if ( $PSVersionTable.PSVersion.Major -eq 5 ) {
            # Start-ThreadJob does not have any child jobs for some reason so there will be no progress and just exist
            $sb = ([ScriptBlock]::Create($starterSb.ToString() + '$using:InputObject | % { ' + $ScriptBlock.ToString() + '}'))
            return Start-ThreadJob -ScriptBlock $sb -ThrottleLimit $ThrottleLimit | Receive-Job -Wait -AutoRemoveJob
        }
        else {
            $sb = ([ScriptBlock]::Create($starterSb.ToString() + $ScriptBlock.ToString()))

            # no progress, arguably faster
            if ( $ProgressPreference -ne 'Continue' ) {
                return $InputObject | Foreach-Object -AsJob -ThrottleLimit $ThrottleLimit -Parallel $sb | Receive-Job -Wait -AutoRemoveJob
            }
        }

        Write-Progress -Activity $ProgressTitle -Status "Initializing..."

        $job = $InputObject | Foreach-Object -AsJob -ThrottleLimit $ThrottleLimit -Parallel $sb

        if ( -not $job.ChildJobs ) {
            return
        }

        do {

            # let threads run
            Start-Sleep -Seconds 0.1

            $completedJobsCount = $job.ChildJobs.Where({ $_.State -notin 'NotStarted', 'Running' }).Count

            # get latest job info
            $job | Receive-Job

            [int] $percent = ($completedJobsCount / $job.ChildJobs.Count) * 100
            Write-Progress -Activity $ProgressTitle -Status "$percent% complete" -PercentComplete $percent

        } while ($completedJobsCount -lt $job.ChildJobs.Count)

        Write-Progress -Completed -Activity 'done'
    }
}
#EndRegion './Private/Invoke-VenafiParallel.ps1' 168
#Region './Private/New-HttpQueryString.ps1' -1

<#
.SYNOPSIS
    Generate http query string
.LINK
    https://powershellmagazine.com/2019/06/14/pstip-a-better-way-to-generate-http-query-strings-in-powershell/
#>

function New-HttpQueryString {
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'No state is actually changing')]

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]
        $Uri,

        [Parameter(Mandatory = $true)]
        [Hashtable]
        $QueryParameter
    )
    # Add System.Web
    Add-Type -AssemblyName System.Web

    # Create a http name value collection from an empty string
    $nvCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)

    foreach ($key in $QueryParameter.Keys) {
        $nvCollection.Add($key, $QueryParameter.$key)
    }

    # Build the uri
    $uriRequest = [System.UriBuilder]$uri
    $uriRequest.Query = $nvCollection.ToString()

    return $uriRequest.Uri.OriginalString
}
#EndRegion './Private/New-HttpQueryString.ps1' 37
#Region './Private/New-VcSearchQuery.ps1' -1

function New-VcSearchQuery {
    <#
    .SYNOPSIS
        Build body for various vaas api calls

    .DESCRIPTION
        Build body for various api calls, typically for searching, eg. certificates, logs.

    .PARAMETER Filter
        Array or multidimensional array of fields and values to filter on.
        Each array should be of the format @('operator', @(field, comparison operator, value), @(field2, comparison operator2, value2)).
        Nested filters are supported.
        For a complete list of comparison operators, see https://docs.venafi.cloud/api/about-api-search-operators/.

    .PARAMETER Order
        Array or multidimensional array of fields to Order on.
        Each array should be of the format @(field, asc/desc).
        If just the field name is provided, ascending will be used.

    .EXAMPLE
    New-VcSearchQuery -Filter @('authenticationType', 'eq', 'NONE')
    Filter log results

    .OUTPUTS
    Hashtable
    #>


    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'No state is actually changing')]

    [CmdletBinding()]
    [OutputType([Hashtable])]

    param(

        [parameter()]
        [System.Collections.Generic.List[object]] $Filter,

        [parameter()]
        [psobject[]] $Order,

        [Parameter()]
        [int] $First
    )

    begin {

        $operators = 'EQ', 'FIND', 'GT', 'GTE', 'IN', 'LT', 'LTE', 'MATCH'

        $query = @{
            'expression' = @{}
            'ordering'   = @{}
            'paging'     = @{}
        }

        $vaasPageSizeLimit = 1000

        if ( $First -le 0 ) {
            $First = $vaasPageSizeLimit
        }

        $query.paging.Add('pageSize', [Math]::Min($First, $vaasPageSizeLimit))

        $query.paging.Add('pageNumber', 0)

        function New-VaasExpression {
            [CmdletBinding()]
            param (
                [parameter()]
                [System.Collections.Generic.List[object]] $Filter
            )

            $loopFilter = $Filter
            $operator = $null

            # first item may be the operator or a filter
            # if so, pull it off the list and process the rest
            if ($Filter[0] -is 'String' -and $Filter[0] -in 'AND', 'OR') {
                $operator = $Filter[0].ToUpper()
                $loopFilter = $Filter | Select-Object -Skip 1
                $loopFilter = @(, $loopFilter)
            }

            $operands = foreach ($thisItem in $loopFilter) {
                if ( $thisItem.count -eq 3 ) {

                    # handle nested expressions
                    if ( $thisItem[2] -is 'Object[]' -and $thisItem[2][1] -in $operators ) {
                        New-VaasExpression -Filter $thisItem
                    }

                    # vaas fields are case sensitive, get the proper case if we're aware of the field
                    $thisField = $thisItem[0]
                    $thisFieldCased = $vaasFields | Where-Object { $_.ToLower() -eq $thisField.ToLower() }

                    $newOperand = @{
                        'field'    = if ($thisFieldCased) { $thisFieldCased } else { $thisField }
                        'operator' = $thisItem[1].ToUpper()
                    }

                    # handle different value types
                    # note the use of property 'values' for an array, eg. when the operator is find, in, match
                    switch ($thisItem[2].GetType().Name) {
                        'DateTime' {
                            $newOperand.Add('value', ($thisItem[2] | ConvertTo-UtcIso8601))
                        }

                        'String' {
                            $newValue = $thisItem[2]
                            # these values should be upper case, fix in case not provided that way
                            if ( $newOperand.field.ToLower() -in $vaasValuesToUpper.ToLower() ) {
                                $newValue = $thisItem[2].ToUpper()
                            }
                            $newOperand.Add('value', $newValue)
                        }

                        Default {
                            # we have a list
                            $newOperand.Add('values', $thisItem[2])
                        }
                    }

                    $newOperand
                }
                else {
                    New-VaasExpression -Filter $thisItem
                }

            }
            if ( $operator ) {
                @{
                    'operator' = $operator
                    'operands' = @($operands)
                }
            }
            else {
                $operands
            }
        }
    }

    process {

        if ( $Filter ) {
            $thisFilter = $Filter
            # if we have a basic filter of field, operator, value, force it to be a 1 item array intead of 3 items
            if ( $Filter.Count -eq 3 -and -not ($Filter | Where-Object { $_ -isnot [string] })) {
                $thisFilter = @(, $Filter)
            }
            $query.expression = New-VaasExpression -Filter $thisFilter
        }


        if ( $Order ) {
            $query.ordering.Add('orders', @())

            # @($Order) | ForEach-Object {
            foreach ($thisOrder in $Order) {
                # $thisOrder = $_
                switch ($thisOrder.GetType().Name) {
                    'String' {
                        $thisOrderCased = $vaasFields | Where-Object { $_.ToLower() -eq $thisOrder.ToLower() }

                        $query.ordering.orders += @{
                            'field'     = if ($thisOrderCased) { $thisOrderCased } else { $thisOrder }
                            'direction' = 'ASC'
                        }
                    }

                    'HashTable' {
                        if ( $thisOrder.Values[0] -notin 'asc', 'desc' ) {
                            throw ('Invalid order direction, {0}. Provide either ''asc'' or ''desc''' -f $thisOrder.Values[0])
                        }

                        $thisOrderCased = $vaasFields | Where-Object { $_.ToLower() -eq $thisOrder.Keys[0].ToLower() }

                        $query.ordering.orders += @{
                            'field'     = if ($thisOrderCased) { $thisOrderCased } else { $thisOrder.Keys[0] }
                            'direction' = $thisOrder.Values[0].ToUpper()
                        }
                    }

                    Default {
                        throw 'Invalid format for Order'
                    }
                }
            }
        }
        $query
    }
}
#EndRegion './Private/New-VcSearchQuery.ps1' 191
#Region './Private/Select-VenBatch.ps1' -1

function Select-VenBatch {
    <#
    .SYNOPSIS
    Batches pipeline input.

    .DESCRIPTION
    Batches up pipeline input into consistently sized List[T]s of objects. Used to ensure that processing occurs in specific sized batches.
    Useful for not recieving API timouts due to sending more objects than can be processed in the connection timeout period.

    .PARAMETER InputObject
    The pipeline input objects binds to this parameter one by one.
    Do not use it directly.

    .PARAMETER BatchSize
    The size of the batches to separate the pipeline input into.

    .PARAMETER BatchType
    Type of object to group things into. Defaults to a Powershell custom object

    Valid Values: "pscustomobject", "string", "int", "guid"

    .PARAMETER TotalCount
    The total number of items in the pipeline. Used to calculate progress.
    If you do not provide this value or ProgressPreference is set to 'SilentlyContinue', no progress will be displayed.

    .OUTPUTS
    System.Collections.Generic.List[T]

    .EXAMPLE
    1..6000 | Select-VenBatch -batchsize 1000 -BatchType string

    #>


    [CmdletBinding(PositionalBinding = $false)]
    param (
        [Parameter(ValueFromPipeline)]
        $InputObject,

        [Parameter(Mandatory)]
        [int] $BatchSize,

        [Parameter(Mandatory, Position = 0)]
        [ValidateSet("pscustomobject", "string", "int", "guid")]
        [string] $BatchType = "pscustomobject",

        [Parameter()]
        [int] $TotalCount,

        [Parameter()]
        [string] $Activity = 'Processing batches'

    )

    Begin {
        switch ($BatchType) {
            'string' {
                $Batch = [System.Collections.Generic.Queue[string]]::new($BatchSize)
            }
            'int' {
                $Batch = [System.Collections.Generic.Queue[int]]::new($BatchSize)
            }
            'guid' {
                $Batch = [System.Collections.Generic.Queue[guid]]::new($BatchSize)
            }
            'pscustomobject' {
                $Batch = [System.Collections.Generic.Queue[pscustomobject]]::new($BatchSize)
            }
        }

        $count = 0
        If ($TotalCount) {
            $progressParams = @{
                Activity        = $Activity
                Status          = 'Initializing'
                PercentComplete = -1
            }
            Write-Progress @progressParams
        }
    }

    Process {

        $count++

        $Batch.Enqueue($_)

        if ($Batch.Count -eq $BatchSize) {

            If ($TotalCount) {
                $progressParams.Status = 'Batch {0}, items {1}-{2}' -f ($count / $BatchSize), ($count - $BatchSize + 1), $count
                [int] $percent = ($count / $TotalCount) * 100
                $progressParams.PercentComplete = $percent
                Write-Progress @progressParams
            }
            'Processing batch {0}, items {1}-{2}' -f ($count / $BatchSize), ($count - $BatchSize + 1), $count | Write-Verbose

            , ($Batch)
            $Batch.Clear() # start next batch
        }
    }

    end {
        # process any remaining items, eg. we didn't have a full batch
        if ( $Batch.Count ) {
            $batchNum = [math]::Ceiling($count / $BatchSize)

            If ($TotalCount) {
                $progressParams.Status = 'Batch {0}, items {1}-{2}' -f $batchNum, ((($batchNum - 1) * $BatchSize) + 1), $count
                [int] $percent = ($count / $TotalCount) * 100
                $progressParams.PercentComplete = $percent
                Write-Progress @progressParams
            }
            'Processing batch {0}, items {1}-{2}' -f $batchNum, ((($batchNum - 1) * $BatchSize) + 1), $count | Write-Verbose

            , ($Batch)
            $Batch.Clear()
            Remove-Variable Batch

            If ($TotalCount) {
                Write-Progress -Activity 'Completed' -Completed
            }
        }
    }
}
#EndRegion './Private/Select-VenBatch.ps1' 125
#Region './Private/Split-CertificateData.ps1' -1

function Split-CertificateData {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [String] $CertificateData
    )

    begin {
        $allCerts = [System.Collections.Generic.List[hashtable]]::new()
    }

    process {
        $pemLines = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($CertificateData)).Split("`n")

        $certPem = @()

        for ($i = 0; $i -lt $pemLines.Count; $i++) {
            if ($pemLines[$i].Contains('-----BEGIN')) {
                $iStart = $i
                continue
            }
            if ($pemLines[$i].Contains('-----END')) {
                $thisPemLines = $pemLines[$iStart..$i]
                if ( $pemLines[$i].Contains('KEY-----')) {
                    $keyPem = ($thisPemLines -join "`n") -replace "`r"
                }
                else {
                    $certPem += ($thisPemLines -join "`n") -replace "`r"
                }
                continue
            }
        }

        $allCerts.Add(
            @{
                CertPem  = $certPem[0]
                KeyPem   = $keyPem
                ChainPem = $certPem[1..($certPem.Count - 1)]
            }
        )
    }

    end {
        $allCerts
    }
}
#EndRegion './Private/Split-CertificateData.ps1' 48
#Region './Private/Test-IsGuid.ps1' -1

function Test-IsGuid {
    <#
    .SYNOPSIS
    Validates a given input string and checks string is a valid GUID
    .DESCRIPTION
    Validates a given input string and checks string is a valid GUID by using the .NET method Guid.TryParse
    .EXAMPLE
    Test-Guid -InputObject "3363e9e1-00d8-45a1-9c0c-b93ee03f8c13"
    .NOTES
    Uses .NET method [guid]::TryParse()
    #>

    [Cmdletbinding()]
    [OutputType([bool])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)]
        [AllowEmptyString()]
        [string] $InputObject
    )
    process {
        return [guid]::TryParse($InputObject, $([ref][guid]::Empty))
    }
}
#EndRegion './Private/Test-IsGuid.ps1' 24
#Region './Private/Test-TppDnPath.ps1' -1

function Test-TppDnPath {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [string] $Path,

        [Parameter()]
        [bool] $AllowRoot = $true
    )

    process {
        if ( $AllowRoot ) {
            $Path -imatch '^[\\|\\\\]+ved([\\|\\\\]+.+)*$'
        } else {
            $Path -imatch '^[\\|\\\\]+ved([\\|\\\\]+.+)+$'
        }
    }
}
#EndRegion './Private/Test-TppDnPath.ps1' 21
#Region './Private/Test-VdcIdentityFormat.ps1' -1

function Test-VdcIdentityFormat {

    <#
    .SYNOPSIS
        Validate identity formats
    .DESCRIPTION
        Validate the format for identities prefixed name, prefixed universal, and local.
        As these formats are often interchangeable with api calls, you can validate multiple formats at once.
        By default, prefixed name and prefixed universal will be validated.
    .PARAMETER Identity
        The identity string to be validated
    .PARAMETER Format
        Format to validate, Name, Universal, and/or Local.
    .EXAMPLE
        Test-VdcIdentityFormat -ID "AD+BigSur:cypher"

        Test the identity against all formats. This would succeed for Name, but fail on Universal
    .EXAMPLE
        Test-VdcIdentityFormat -ID "AD+BigSur:cypher" -Format Name

        Test the identity against a specific format.
    #>


    [CmdletBinding()]
    [OutputType([System.Boolean])]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string] $ID,

        [Parameter()]
        [ValidateSet('Name', 'Universal', 'Domain', 'Local')]
        [string] $Format
    )

    begin {
        $domainProviderRegex = '(?im)^(AD|LDAP)\+.+:'
        $localProviderRegex = '(?im)^local:'
        $nameRegex = '.+$'
        $universalRegex = '\{?([0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12})\}?$'
    }

    process {
        switch ( $Format ) {

            'Name' {
                $ID -match ($domainProviderRegex + $nameRegex) -or $ID -match ($localProviderRegex + $nameRegex)
            }

            'Universal' {
                $ID -match ($domainProviderRegex + $universalRegex) -or $ID -match ($localProviderRegex + $universalRegex)
            }

            'Domain' {
                $ID -match ($domainProviderRegex + $universalRegex) -or $ID -match ($domainProviderRegex + $nameRegex)
            }

            'Local' {
                $ID -match ($localProviderRegex + $universalRegex) -or $ID -match ($localProviderRegex + $nameRegex)
            }

            default {
                $ID -match ($domainProviderRegex + $universalRegex) -or $ID -match ($domainProviderRegex + $nameRegex) -or $ID -match ($localProviderRegex + $universalRegex) -or $ID -match ($localProviderRegex + $nameRegex)
            }
        }
    }
}
#EndRegion './Private/Test-VdcIdentityFormat.ps1' 68
#Region './Private/Test-VenafiSession.ps1' -1

function Test-VenafiSession {
    <#
    .SYNOPSIS
    Validate authentication session/key/token

    .DESCRIPTION
    Validate authentication session from New-VenafiSession, a TLSPC key, or TLSPDC token.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token or TLSPC key can also provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .PARAMETER Platform
    Platform, either TLSPDC or Vaas, to validate VenafiSession against.

    .PARAMETER PassThru
    Provide the determined platform from VenafiSession

    .OUTPUTS
    String - if PassThru provided

    .EXAMPLE
    Test-VenafiSession -VenafiSession $VenafiSession
    Test a session

    .EXAMPLE
    Test-VenafiSession -VenafiSession $VenafiSession -PassThru
    Test a session and return the platform type found

    .EXAMPLE
    Test-VenafiSession -VenafiSession $key
    Test a TLSPC key

    .EXAMPLE
    Test-VenafiSession -VenafiSession $VenafiSession -Platform TLSPDC
    Test session ensuring the platform is TLSPDC

    #>


    [CmdletBinding(DefaultParameterSetName = 'All')]

    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [AllowNull()]
        [Alias('Key', 'AccessToken')]
        [psobject] $VenafiSession,

        [Parameter(Mandatory, ParameterSetName = 'Platform')]
        [string] $Platform
    )

    process {

        if ( (Get-PSCallStack).Count -gt 3 -and -not $VenafiSession ) {
            # nested function, no need to continue testing session since it was already done
            return
        }

        if ( -not $VenafiSession ) {
            if ( $env:VDC_TOKEN ) {
                $VenafiSession = $env:VDC_TOKEN
            }
            elseif ( $env:VC_KEY ) {
                $VenafiSession = $env:VC_KEY
            }
            elseif ( $script:VenafiSession ) {
                $VenafiSession = $script:VenafiSession
            }
            else {
                throw 'Please run New-VenafiSession or provide a TLSPC key or TLSPDC token.'
            }
        }

        switch ($VenafiSession.GetType().Name) {
            'PSCustomObject' {

                if ( -not $VenafiSession.Key -and -not $VenafiSession.Token ) {
                    throw "You must first connect to either TLSPC or a TLSPDC server with New-VenafiSession"
                }

                # make sure the auth type and url we have match
                # this keeps folks from calling a vaas function with a token and vice versa
                if ( $Platform -and $Platform -ne $VenafiSession.Platform ) {
                    throw "This function is only accessible for $Platform"
                }

                if ( $Platform -eq 'VDC' ) {
                    if ( $VenafiSession.Token.Expires -and $VenafiSession.Token.Expires -lt (Get-Date).ToUniversalTime() ) {
                        throw 'TLSPDC token has expired. Execute New-VenafiSession and rerun your command.'
                    }
                }
    
                # don't perform .Validate as we do above since this
                # isn't the class, it's a converted pscustomobject
                # for Invoke-VenafiParallel usage
                break
            }

            'String' {

                if ( Test-IsGuid($VenafiSession) ) {

                    Write-Verbose 'Session is VC key'

                    if ( $Platform -and $Platform -notmatch '^VC$' ) {
                        throw "This function or parameter set is only accessible for $Platform"
                    }
                }
                else {

                    # TLSPDC access token
                    Write-Verbose 'Session is VDC token'
                    if ( $Platform -and $Platform -notmatch '^VDC' ) {
                        throw "This function or parameter set is only accessible for $Platform"
                    }
                    # get server from environment variable
                    if ( -not $env:VDC_SERVER ) {
                        throw 'TLSPDC token provided, but VDC_SERVER environment variable was not found'
                    }
                }
            }

            Default {
                throw "Unknown session '$VenafiSession'. Please run New-VenafiSession or provide a TLSPC key or TLSPDC access token."
            }
        }

        # at entry function call, not nested, set the temp variables
        # if ( $script:VenafiSessionNested ) {
        # # append/update existing
        # ($script:VenafiSessionNested).$($VenafiSession.Platform) = $VenafiSession
        # }
        # else {
        # $script:VenafiSessionNested = @{
        # $($VenafiSession.Platform) = $VenafiSession
        # }
        # }
        $script:VenafiSessionNested = $VenafiSession
        $script:PlatformNested = $Platform
    }
}
#EndRegion './Private/Test-VenafiSession.ps1' 144
#Region './Private/Write-VerboseWithSecret.ps1' -1

<#
.SYNOPSIS
Remove sensitive information when writing verbose info

.DESCRIPTION
Remove sensitive information when writing verbose info

.PARAMETER InputObject
JSON string or other object

.PARAMETER SecretName
Name of secret(s) to hide their values. Default value is 'Password', 'AccessToken', 'RefreshToken', 'access_token', 'refresh_token', 'Authorization', 'KeystorePassword', 'tppl-api-key', 'CertficateData'

.INPUTS
InputObject

.OUTPUTS
None

.EXAMPLE
@{'password'='foobar'} | Write-VerboseWithSecret
Hide password value from hashtable

.EXAMPLE
$jsonString | Write-VerboseWithSecret
Hide value(s) from JSON string

#>

function Write-VerboseWithSecret {

    [CmdletBinding()]

    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [AllowNull()]
        [AllowEmptyString()]
        [psobject] $InputObject,

        [Parameter()]
        [string[]] $PropertyName = @('AccessToken', 'Password', 'RefreshToken', 'access_token', 'refresh_token', 'Authorization', 'KeystorePassword', 'tppl-api-key', 'CertificateData', 'certificate')
    )

    begin {
    }

    process {

        if ( -not $InputObject -or [System.Management.Automation.ActionPreference]::SilentlyContinue -eq $VerbosePreference ) {
            return
        }

        Write-Debug ('Write-VerboseWithSecret input type: {0}' -f $InputObject.GetType().FullName)

        # default to json string
        $processMe = $InputObject
        if ($InputObject.GetType().FullName -ne 'System.String') {
            # if hashtable or other object, convert to json first
            $processMe = $InputObject | ConvertTo-Json -Depth 5 -Compress
        }

        foreach ($prop in $PropertyName) {

            # look for values in json string, eg. "Body": "{"Password":"MyPass"}" or {\"Password\":\"MyPass\"}
            if ( $processMe -match "\\?""$prop\\?"":\\?""(.*?)\\?""" ) {
                $processMe = $processMe.replace($matches[1], '***hidden***')
            }
        }

        Write-Verbose $processMe

    }
}
#EndRegion './Private/Write-VerboseWithSecret.ps1' 73
#Region './Public/Add-VcCertificateAssociation.ps1' -1

function Add-VcCertificateAssociation {
    <#
    .SYNOPSIS
    Associate certificates with applications

    .DESCRIPTION
    Associate one or more certificates with one or more applications.
    The associated applications can either replace or be added to existing.
    By default, applications will be replaced.

    .PARAMETER Certificate
    Certificate ID or name to be associated.
    If a name is provided and multiple certificates are found, they will all be associated.
    Tab completion can be used for a list of certificate names to choose from.
    Type 3 or more characters for tab completion to work.

    .PARAMETER Application
    One or more application IDs or names.
    Tab completion can be used for a list of application names.

    .PARAMETER NoOverwrite
    Append to existing applications as opposed to overwriting

    .PARAMETER PassThru
    Return the newly updated certificate object(s)

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    Certificate

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Add-VcCertificateAssociation -Certificate '7ac56ec0-2017-11ee-9417-a17dd25b82f9' -Application '96fc9310-67ec-11eb-a8a7-794fe75a8e6f'

    Associate a certificate to an application

    .EXAMPLE
    Add-VcCertificateAssociation -Certificate '7ac56ec0-2017-11ee-9417-a17dd25b82f9' -Application '96fc9310-67ec-11eb-a8a7-794fe75a8e6f', 'a05013bd-921d-440c-bc22-c9ead5c8d548'

    Associate a certificate to multiple applications

    .EXAMPLE
    Find-VcCertificate -First 5 | Add-VcCertificateAssociation -Application 'My Awesome App'

    Associate multiple certificates to 1 application by name

    .EXAMPLE
    Add-VcCertificateAssociation -Certificate 'www.barron.com' -Application '96fc9310-67ec-11eb-a8a7-794fe75a8e6f' -NoOverwrite

    Associate a certificate, by name, to another application, keeping the existing
    #>


    [CmdletBinding()]
    [Alias('Set-VaasCertificateAssignment')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('certificateID')]
        [string] $Certificate,

        [Parameter(Mandatory)]
        [Alias('ApplicationID')]
        [string[]] $Application,

        [Parameter()]
        [switch] $NoOverwrite,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [Alias('Key')]
        [psobject] $VenafiSession
    )

    begin {

        Write-Warning 'This function will soon be deprecated. Use Set-VcCertificate instead.'

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        $apps = $Application | Get-VcData -Type 'Application'

        $params = @{
            Method        = 'Patch'
            UriRoot       = 'outagedetection/v1'
            UriLeaf       = 'applications/certificates'
            Body          = @{
                action                 = 'REPLACE'
                targetedApplicationIds = @($apps)
            }
        }

        if ( $NoOverwrite ) {
            $params.Body.action = 'ADD'
        }

        $allCerts = [System.Collections.Generic.List[string]]::new()
    }

    process {
        if ( Test-IsGuid($Certificate) ) {
            $allCerts.Add($Certificate)
        }
        else {
            # search by name
            $certIDs = Get-VcData -InputObject $Certificate -Type 'Certificate'
            foreach ($certID in @($certIDs)) {
                $allCerts.Add($certID)
            }
        }
    }

    end {
        $params.Body.certificateIds = $allCerts

        $response = Invoke-VenafiRestMethod @params

        if ( $PassThru ) {
            $response.certificates
        }

    }
}
#EndRegion './Public/Add-VcCertificateAssociation.ps1' 132
#Region './Public/Add-VcTeamMember.ps1' -1

function Add-VcTeamMember {
    <#
    .SYNOPSIS
    Add members to a team

    .DESCRIPTION
    Add members to a TLSPC team

    .PARAMETER ID
    Team ID to add to

    .PARAMETER Member
    1 or more members to add to the team.
    This is the unique guid obtained from Get-VcIdentity.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Add-VcTeamMember -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Member @('ca7ff555-88d2-4bfc-9efa-2630ac44c1f3', 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f4')

    Add members to a TLSPC team

    .EXAMPLE
    Add-VcTeamMember -ID 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}' -Member 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e7}'

    Add members to a TLSPDC team

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html#/Teams/addMember
    #>


    [CmdletBinding()]
    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('PrefixedUniversal', 'Guid')]
        [string] $ID,

        [Parameter(Mandatory)]
        [string[]] $Member,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        $params.Method = 'Post'
        $params.UriLeaf = "teams/$ID/members"
        $params.Body = @{
            'members' = @($Member)
        }

        $null = Invoke-VenafiRestMethod @params
    }
}
#EndRegion './Public/Add-VcTeamMember.ps1' 67
#Region './Public/Add-VcTeamOwner.ps1' -1

function Add-VcTeamOwner {
    <#
    .SYNOPSIS
    Add owners to a team

    .DESCRIPTION
    Add owners to a TLSPC team

    .PARAMETER Team
    Team ID or name

    .PARAMETER Owner
    1 or more owners to add to the team
    This is the unique guid obtained from Get-VcIdentity.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    Team

    .EXAMPLE
    Add-VcTeamOwner -Team 'DevOps' -Owner @('ca7ff555-88d2-4bfc-9efa-2630ac44c1f3', 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f4')

    Add owners

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html#/Teams/addOwner
    #>


    [CmdletBinding()]
    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('ID')]
        [string] $Team,

        [Parameter(Mandatory)]
        [string[]] $Owner,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        $params = @{
            Method = 'Post'
            Body   = @{
                'owners' = @($Owner)
            }
        }
    }

    process {

        $params.UriLeaf = 'teams/{0}/owners' -f (Get-VcData -InputObject $Team -Type 'Team')

        $null = Invoke-VenafiRestMethod @params
    }
}
#EndRegion './Public/Add-VcTeamOwner.ps1' 65
#Region './Public/Add-VdcAdaptableHash.ps1' -1

function Add-VdcAdaptableHash {
    <#
    .SYNOPSIS
    Adds or updates the hash value for an adaptable script

    .DESCRIPTION
    TLSPDC stores a base64 encoded hash of the file contents of an adaptable script in the Secret Store. This is referenced by
    the Attribute 'PowerShell Script Hash Vault Id' on the DN of the adaptable script. This script retrieves the hash (if
    present) from the Secret Store and compares it to the hash of the file in one of the scripts directories. It then adds
    a new or updated hash if required. When updating an existing hash, it removes the old one from the Secret Store.

    .PARAMETER Path
    Required. Path to the object to add or update the hash.
    Note: For an adaptable app or an onboard discovery, 'Path' must always be a policy folder as this is where
    the hash is saved.

    .PARAMETER Keyname
    The name of the Secret Encryption Key (SEK) to used when encrypting this item. Default is "Software:Default"

    .PARAMETER FilePath
    Required. The full path to the adaptable script file. This should normally be in a
    '<drive>:\Program Files\Venafi\Scripts\<subdir>' directory for TLSPDC to recognize the script.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    None

    .OUTPUTS
    None

    .EXAMPLE
    Add-VdcAdaptableHash -Path $Path -FilePath 'C:\Program Files\Venafi\Scripts\AdaptableApp\AppDriver.ps1'

    Update the hash on an adaptable app object.

    Note: For an adaptable app or an onboard discovery, 'Path' must always be a policy folder as this is where
    the hash is saved.

    .EXAMPLE
    Add-VdcAdaptableHash -Path $Path -FilePath 'C:\Program Files\Venafi\Scripts\AdaptableLog\Generic-LogDriver.ps1'

    Update the hash on an adaptable log object.

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Add-VdcAdaptableHash/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Add-VdcAdaptableHash.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Secretstore-add.php

    .LINK
    https://docs.venafi.com/Docs/currentSDK/TopNav/Content/SDK/WebSDK/r-SDK-POST-Secretstore-ownerdelete.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Secretstore-retrieve.php
    #>


    [CmdletBinding(SupportsShouldProcess)]
    [Alias('Add-TppAdaptableHash')]

    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                } else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('DN')]
        [String] $Path,

        [Parameter()]
        [string] $Keyname = "Software:Default",

        [Parameter(Mandatory)]
        [Alias('File')]
        [string] $FilePath,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{
            Method        = 'Post'
        }

        $TypeName = (Get-VdcObject -Path $Path).TypeName

        if ( $TypeName -eq 'Policy' ) {
            $retrieveVaultID = ( Get-VdcAttribute -Path $Path -Class 'Adaptable App' -Attribute 'PowerShell Script Hash Vault Id' ).'PowerShell Script Hash Vault Id'
        } else {
            $retrieveVaultID = ( Get-VdcAttribute -Path $Path -Attribute 'PowerShell Script Hash Vault Id' ).'PowerShell Script Hash Vault Id'
        }

        $bytes = [Text.Encoding]::UTF32.GetBytes([IO.File]::ReadAllText($FilePath))
        $hash = Get-FileHash -InputStream ([System.IO.MemoryStream]::New($bytes))
        $base64data = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($hash.hash.ToLower()))

    }

    process {
        if ( -not $PSCmdlet.ShouldProcess($Path) ) {
            continue
        }

        if ( $retrieveVaultID ) {
            $paramsretrieve = $params.Clone()
            $paramsretrieve.UriLeaf = 'SecretStore/retrieve'
            $paramsretrieve.Body = @{
                VaultID = $retrieveVaultID
            }

            $retrieveResponse = Invoke-VenafiRestMethod @paramsretrieve

            if ( $retrieveResponse.Result -ne [TppSecretStoreResult]::Success ) {
                Write-Error ("Error retrieving VaultID: {0}" -f [enum]::GetName([TppSecretStoreResult], $retrieveResponse.Result)) -ErrorAction Stop
            }

            if($null -ne $retrieveResponse.Base64Data) {
                $retrieveBase64 = $retrieveResponse.Base64Data
            }
        }

        if ( $base64data -eq $retrieveBase64 ){
            Write-Verbose "PowerShell Script Hash Vault Id unchanged for $($Path)."
            continue
        } else {
            $paramsadd = $params.Clone()
            $paramsadd.UriLeaf = 'SecretStore/Add'
            $paramsadd.Body = @{
                VaultType = '128'
                Keyname = $Keyname
                Base64Data = $Base64Data
                Namespace = 'Config'
                Owner = $Path
            }

            $addresponse = Invoke-VenafiRestMethod @paramsadd

            if ( $addresponse.Result -ne [TppSecretStoreResult]::Success ) {
                Write-Error ("Error adding VaultID: {0}" -f [enum]::GetName([TppSecretStoreResult], $addResponse.Result)) -ErrorAction Stop
            }

            if ( $TypeName -eq 'Policy' ) {
                Set-VdcAttribute -Path $Path -PolicyClass 'Adaptable App' -Attribute @{ 'PowerShell Script Hash Vault Id' = [string]$addresponse.VaultID } -Lock -ErrorAction Stop
            } else {
                Set-VdcAttribute -Path $Path -Attribute @{ 'PowerShell Script Hash Vault Id' = [string]$addresponse.VaultID } -ErrorAction Stop
            }
            Write-Verbose "PowerShell Script Hash Vault Id for $($Path) set to $($addresponse.VaultID)."
        }

        if (( $retrieveBase64 ) -and ( $addresponse.VaultID )) {
            $paramsdelete = $params.Clone()
            $paramsdelete.UriLeaf = 'SecretStore/OwnerDelete'
            $paramsdelete.Body = @{
                Namespace = 'Config'
                Owner = $Path
                VaultID = $retrieveVaultID
            }

            $deleteResponse = Invoke-VenafiRestMethod @paramsdelete

            if ( $deleteResponse.Result -ne [TppSecretStoreResult]::Success ) {
                Write-Error ("Error removing VaultID: {0}" -f [enum]::GetName([TppSecretStoreResult], $deleteResponse.Result)) -ErrorAction Stop
            }
        }
    }
}
#EndRegion './Public/Add-VdcAdaptableHash.ps1' 181
#Region './Public/Add-VdcCertificateAssociation.ps1' -1

function Add-VdcCertificateAssociation {
    <#
    .SYNOPSIS
    Add certificate association

    .DESCRIPTION
    Associates one or more Application objects to an existing certificate.
    Optionally, you can push the certificate once the association is complete.

    .PARAMETER InputObject
    TppObject which represents a certificate

    .PARAMETER CertificatePath
    Path to the certificate. Required if InputObject not provided.

    .PARAMETER ApplicationPath
    List of application object paths to associate

    .PARAMETER PushCertificate
    Push the certificate after associating it to the Application objects.
    This will only be successful if the certificate management type is Provisioning and is not disabled, in error, or a push is already in process.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    None

    .EXAMPLE
    Add-VdcCertificateAssociation -CertificatePath '\ved\policy\my cert' -ApplicationPath '\ved\policy\my capi'
    Add a single application object association

    .EXAMPLE
    Add-VdcCertificateAssociation -Path '\ved\policy\my cert' -ApplicationPath '\ved\policy\my capi' -PushCertificate
    Add the association and push the certificate

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Add-VdcCertificateAssociation/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Add-VdcCertificateAssociation.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/API_Reference/r-SDK-POST-Certificates-Associate.php

    .NOTES
    You must have:
    - Write permission to the Certificate object.
    - Write or Associate and Delete permission to Application objects that are associated with the certificate

    #>


    [CmdletBinding(SupportsShouldProcess)]
    [Alias('Add-TppCertificateAssociation')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                } else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('DN', 'CertificateDN', 'Path')]
        [String] $CertificatePath,

        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                } else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [String[]] $ApplicationPath,

        [Parameter()]
        [Alias('ProvisionCertificate')]
        [switch] $PushCertificate,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{
            Method     = 'Post'
            UriLeaf    = 'Certificates/Associate'
            Body       = @{
                CertificateDN = ''
                ApplicationDN = ''
            }
        }

        if ( $PSBoundParameters.ContainsKey('PushCertificate') ) {
            $params.Body.Add('PushToNew', 'true')
        }
    }

    process {

        $params.Body.CertificateDN = $CertificatePath
        $params.Body.ApplicationDN = @($ApplicationPath)

        if ( $PSCmdlet.ShouldProcess($CertificatePath, 'Add association') ) {
            $null = Invoke-VenafiRestMethod @params
        }
    }
}
#EndRegion './Public/Add-VdcCertificateAssociation.ps1' 121
#Region './Public/Add-VdcEngineFolder.ps1' -1

function Add-VdcEngineFolder {
    <#
    .SYNOPSIS
    Add policy folder assignments to a TLSPDC processing engine
    .DESCRIPTION
    Add one or more policy folder assignments to a TLSPDC processing engine.
    .PARAMETER EnginePath
    The full DN path to a TLSPDC processing engine.
    .PARAMETER EngineObject
    TPPObject belonging to the 'Venafi Platform' class.
    .PARAMETER FolderPath
    The full DN path to one or more policy folders (string array).
    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided, but this requires an environment variable VDC_SERVER to be set.
    .INPUTS
    EnginePath or EngineObject, FolderPath[]
    .OUTPUTS
    None
    .EXAMPLE
    Add-VdcEngineFolder -EnginePath '\VED\Engines\MYVENAFI01' -FolderPath '\VED\Policy\Certificates\Web Team'
    Add processing engine MYVENAFI01 to the policy folders '\VED\Policy\Certificates\Web Team'.
    .EXAMPLE
    Add-VdcEngineFolder -EnginePath '\VED\Engines\MYVENAFI01' -FolderPath @('\VED\Policy\Certificates\Web Team','\VED\Policy\Certificates\Database Team')
    Add processing engine MYVENAFI01 to the policy folders '\VED\Policy\Certificates\Web Team' and '\VED\Policy\Certificates\Database Team'.
    .EXAMPLE
    $EngineObjects | Add-VdcEngineFolder -FolderPath @('\VED\Policy\Certificates\Web Team','\VED\Policy\Certificates\Database Team') -Confirm:$false
    Add one or more processing engines via the pipeline to multiple policy folders. Suppress the confirmation prompt.
    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Add-VdcEngineFolder/
    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Add-VdcEngineFolder.ps1
    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-ProcessingEngines-Engine-eguid.php
    #>


    [CmdletBinding(SupportsShouldProcess)]
    [Alias('Add-TppEngineFolder')]

    param (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateScript( {
            if ( $_ | Test-TppDnPath ) { $true }
            else { throw "'$_' is not a valid DN path" }
        })]
        [Alias('EngineDN', 'Engine', 'Path')]
        [String] $EnginePath,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
            if ( $_ | Test-TppDnPath ) { $true }
            else { throw "'$_' is not a valid DN path" }
        })]
        [Alias('FolderDN', 'Folder')]
        [String[]] $FolderPath,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{
            Method        = 'Post'
            Body       = @{
                FolderGuids = ''
            }
        }
    }

    process {
        if ( -not ($PSBoundParameters.ContainsKey('EngineObject')) ) {
            $EngineObject = Get-VdcObject -Path $EnginePath
            if ($EngineObject.TypeName -ne 'Venafi Platform') {
                throw ("DN/Path '$($EngineObject.Path)' is not a processing engine")
            }
        }

        $FolderGuids = [System.Collections.Generic.List[string]]::new()
        $params.UriLeaf = "ProcessingEngines/Engine/{$($EngineObject.Guid)}"

        foreach ($path in $FolderPath) {
            try {
                $folder = Get-VdcObject -Path $path
            }
            catch {
                Write-Warning ("TLSPDC object '$($path)' does not exist")
                Continue
            }
            if ($folder.TypeName -ne 'Policy') {
                Write-Warning ("TLSPDC object '$($folder.Path)' is not a policy folder")
                Continue
            }
            $lastFolder = $folder.Path
            $FolderGuids += "{$($folder.guid)}"
        }

        $params.Body.FolderGuids = @($FolderGuids)

        if ($FolderGuids.Count -gt 1) {
            $shouldProcessAction = "Add $($FolderGuids.Count) policy folders"
        }
        else {
            $shouldProcessAction = "Add $($lastFolder)"
        }

        if ($PSCmdlet.ShouldProcess($EngineObject.Name, $shouldProcessAction)) {
            $response = Invoke-VenafiRestMethod @params

            if ($response.AddedCount -ne $FolderGuids.Count) {
                $errorMessage = "Added $($response.AddedCount) folder(s), but requested $($FolderGuids.Count)"
                if ($response.Errors) {
                    $errorMessage += ": $($response.Errors)"
                }
                Write-Warning ($errorMessage)
            }
            else {
                Write-Verbose ("Added $($response.AddedCount) folder(s) to $($EngineObject.Name)")
            }
        }
    }
}
#EndRegion './Public/Add-VdcEngineFolder.ps1' 126
#Region './Public/Add-VdcTeamMember.ps1' -1

function Add-VdcTeamMember {
    <#
    .SYNOPSIS
    Add members to a team

    .DESCRIPTION
    Add members to a TLSPDC team

    .PARAMETER ID
    Team ID from Find-VdcIdentity or Get-VdcTeam.

    .PARAMETER Member
    1 or more members to add to the team.
    The identity ID property from Find-VdcIdentity or Get-VdcIdentity.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .EXAMPLE
    Add-VdcTeamMember -ID 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}' -Member 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e7}'

    Add members to a TLSPDC team

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-PUT-Teams-AddTeamMembers.php
    #>


    [CmdletBinding()]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('PrefixedUniversal', 'Guid')]
        [string] $ID,

        [Parameter(Mandatory)]
        [string[]] $Member,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        $teamName = Get-VdcIdentity -ID $ID | Select-Object -ExpandProperty FullName
        $members = foreach ($thisMember in $Member) {
            if ( $thisMember.StartsWith('local') ) {
                $memberIdentity = Get-VdcIdentity -ID $thisMember
                @{
                    'PrefixedName'      = $memberIdentity.FullName
                    'PrefixedUniversal' = $memberIdentity.ID
                }
            }
            else {
                @{'PrefixedUniversal' = $thisMember }
            }
        }

        $params = @{
            Method  = 'Put'
            UriLeaf = 'Teams/AddTeamMembers'
            Body    = @{
                'Team'    = @{'PrefixedName' = $teamName }
                'Members' = @($members)
            }
        }

        $null = Invoke-VenafiRestMethod @params
    }
}
#EndRegion './Public/Add-VdcTeamMember.ps1' 81
#Region './Public/Add-VdcTeamOwner.ps1' -1

function Add-VdcTeamOwner {
    <#
    .SYNOPSIS
    Add owners to a team

    .DESCRIPTION
    Add owners to a TLSPDC team

    .PARAMETER ID
    Team ID, this is the ID property from Find-VdcIdentity or Get-VdcTeam.

    .PARAMETER Owner
    1 or more owners to add to the team
    This is the identity ID property from Find-VdcIdentity or Get-VdcIdentity.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .EXAMPLE
    Add-VdcTeamOwner -ID 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}' -Owner 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e7}'

    Add owners

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-PUT-Teams-AddTeamOwners.php
    #>


    [CmdletBinding()]
    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('PrefixedUniversal', 'Guid')]
        [string] $ID,

        [Parameter(Mandatory)]
        [string[]] $Owner,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        $teamName = Get-VdcIdentity -ID $ID | Select-Object -ExpandProperty FullName
        $owners = foreach ($thisOwner in $Owner) {
            if ( $thisOwner.StartsWith('local') ) {
                $ownerIdentity = Get-VdcIdentity -ID $thisOwner
                @{
                    'PrefixedName'      = $ownerIdentity.FullName
                    'PrefixedUniversal' = $ownerIdentity.ID
                }
            }
            else {
                @{'PrefixedUniversal' = $thisOwner }
            }
        }

        $params = @{
            Method  = 'Put'
            UriLeaf = 'Teams/AddTeamOwners'
            Body    = @{
                'Team'   = @{'PrefixedName' = $teamName }
                'Owners' = @($owners)
            }
        }

        $null = Invoke-VenafiRestMethod @params
    }
}
#EndRegion './Public/Add-VdcTeamOwner.ps1' 80
#Region './Public/Convert-VdcObject.ps1' -1

function Convert-VdcObject {
    <#
    .SYNOPSIS
    Change the class/object type of an existing object

    .DESCRIPTION
    Change the class/object type of an existing object.
    Please note, changing the class does NOT change any attributes and must be done separately.
    Using -PassThru will allow you to pass the input to other functions including Set-VdcAttribute; see the examples.

    .PARAMETER Path
    Path to the object

    .PARAMETER Class
    New class/type

    .PARAMETER PassThru
    Return a TppObject representing the newly converted object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    TppObject, if -PassThru provided

    .EXAMPLE
    Convert-VdcObject -Path '\ved\policy\' -Class 'X509 Device Certificate'
    Convert an object to a different type

    .EXAMPLE
    Convert-VdcObject -Path '\ved\policy\device\app' -Class 'CAPI' -PassThru | Set-VdcAttribute -Attribute @{'Driver Name'='appcapi'}
    Convert an object to a different type, return the updated object and update attributes

    .EXAMPLE
    Find-VdcObject -Class Basic | Convert-VdcObject -Class 'capi' -PassThru | Set-VdcAttribute -Attribute @{'Driver Name'='appcapi'}
    Convert multiple objects to a different type, return the updated objects and update attributes

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Convert-VdcObject.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-mutateobject.php

    #>


    [CmdletBinding(SupportsShouldProcess)]
    [Alias('Convert-TppObject')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid path"
                }
            })]
        [String] $Path,

        [Parameter(Mandatory)]
        [String] $Class,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{
            Method        = 'Post'
            UriLeaf       = 'config/MutateObject'
            Body          = @{
                Class = $Class
            }
        }
    }

    process {

        $params.Body.ObjectDN = $Path

        if ( $PSCmdlet.ShouldProcess($Path, "Convert to type $Class") ) {

            $response = Invoke-VenafiRestMethod @params

            if ( $response.Result -eq 1 ) {
                if ( $PassThru ) {
                    ConvertTo-VdcObject -Path $Path
                }
            }
            else {
                Write-Error $response.Error
            }
        }
    }
}
#EndRegion './Public/Convert-VdcObject.ps1' 110
#Region './Public/ConvertTo-VdcGuid.ps1' -1

function ConvertTo-VdcGuid {
    <#
    .SYNOPSIS
    Convert DN path to GUID

    .DESCRIPTION
    Convert DN path to GUID

    .PARAMETER Path
    DN path representing an object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    Guid

    .EXAMPLE
    ConvertTo-VdcGuid -Path '\ved\policy\convertme'

    #>


    [CmdletBinding()]
    [Alias('ConvertTo-TppGuid')]
    [OutputType([System.Guid])]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('DN')]
        [String] $Path,

        [Parameter()]
        [switch] $IncludeType,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{
            Method  = 'Post'
            UriLeaf = 'config/DnToGuid'
            Body    = @{
                ObjectDN = ''
            }
        }
    }

    process {

        $params.Body.ObjectDN = $Path

        $response = Invoke-VenafiRestMethod @params

        switch ($response.Result) {
            1 {
                # success
                if ( $IncludeType ) {
                    [PSCustomObject] @{
                        Guid     = [Guid] $response.Guid
                        TypeName = $response.ClassName
                    }
                }
                else {
                    [Guid] $response.Guid
                }
            }

            7 {
                throw [System.UnauthorizedAccessException]::new($response.Error)
            }

            400 {
                throw [System.Management.Automation.ItemNotFoundException]::new($response.Error)
            }

            Default {
                throw $response.Error
            }
        }
    }
}
#EndRegion './Public/ConvertTo-VdcGuid.ps1' 100
#Region './Public/ConvertTo-VdcPath.ps1' -1

function ConvertTo-VdcPath {
    <#
    .SYNOPSIS
    Convert GUID to Path

    .DESCRIPTION
    Convert GUID to Path

    .PARAMETER Guid
    Guid type, [guid] 'xyxyxyxy-xyxy-xyxy-xyxy-xyxyxyxyxyxy'

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Guid

    .OUTPUTS
    String representing the Path

    .EXAMPLE
    ConvertTo-VdcPath -Guid [guid]'xyxyxyxy-xyxy-xyxy-xyxy-xyxyxyxyxyxy'

    #>


    [CmdletBinding()]
    [Alias('ConvertTo-TppPath')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Guid] $Guid,

        [Parameter()]
        [switch] $IncludeType,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{

            Method     = 'Post'
            UriLeaf    = 'config/GuidToDN'
            Body       = @{
                ObjectGUID = ''
            }
        }
    }

    process {

        $params.Body.ObjectGUID = "{$Guid}"

        $response = Invoke-VenafiRestMethod @params

        if ( $response.Result -eq 1 ) {
            if ( $PSBoundParameters.ContainsKey('IncludeType') ) {
                [PSCustomObject] @{
                    Path     = $response.ObjectDN
                    TypeName = $response.ClassName
                }
            } else {
                $response.ObjectDN
            }
        } else {
            throw $response.Error
        }
    }
}
#EndRegion './Public/ConvertTo-VdcPath.ps1' 78
#Region './Public/Export-VcCertificate.ps1' -1

function Export-VcCertificate {
    <#
    .SYNOPSIS
    Export certificate data from TLSPC

    .DESCRIPTION
    Export certificate data in PEM format. You can retrieve the certificate, chain, and key.
    You can also save the certificate and private key in PEM or PKCS12 format.

    .PARAMETER ID
    Certificate ID, also known as uuid. Use Find-VcCertificate or Get-VcCertificate to determine the ID.
    You can pipe those functions as well.

    .PARAMETER PrivateKeyPassword
    Password required to include the private key.
    You can either provide a String, SecureString, or PSCredential.
    Requires PowerShell v7.0+.

    .PARAMETER IncludeChain
    Include the certificate chain with the exported or saved PEM certificate data.

    .PARAMETER OutPath
    Folder path to save the certificate to. The name of the file will be determined automatically.
    For each certificate a directory will be created in this folder with the format Name-ID.
    In the case of PKCS12, the file will be saved to the root of the folder.

    .PARAMETER PKCS12
    Export the certificate and private key in PKCS12 format.
    Requires PowerShell v7.1+.

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    $certId | Export-VcCertificate

    Export certificate data

    .EXAMPLE
    $certId | Export-VcCertificate -PrivateKeyPassword 'myPassw0rd!'

    Export certificate and private key data

    .EXAMPLE
    $certId | Export-VcCertificate -PrivateKeyPassword 'myPassw0rd!' -PKCS12 -OutPath '~/temp'

    Export certificate and private key in PKCS12 format

    .EXAMPLE
    $cert | Export-VcCertificate -OutPath '~/temp'

    Get certificate data and save to a file

    .EXAMPLE
    $cert | Export-VcCertificate -IncludeChain

    Get certificate data with the certificate chain included.

    .NOTES
    This function requires the use of sodium encryption.
    PS v7.1+ is required.
    On Windows, the latest Visual C++ redist must be installed. See https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist.
    #>


    [CmdletBinding(DefaultParameterSetName = 'PEM')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('certificateId')]
        [string] $ID,

        [Parameter(ParameterSetName = 'PEM')]
        [Parameter(ParameterSetName = 'PKCS12', Mandatory)]
        [ValidateScript(
            {
                if ($PSVersionTable.PSVersion -lt [version]'7.0') {
                    throw 'Exporting private keys is only supported on PowerShell v7.0+'
                }

                if ( $_ -is [string] -or $_ -is [securestring] -or $_ -is [pscredential] ) {
                    $true
                }
                else {
                    throw 'Unsupported type. Provide either a String, SecureString, or PSCredential.'
                }
            }
        )]
        [psobject] $PrivateKeyPassword,

        [Parameter(ParameterSetName = 'PEM')]
        [switch] $IncludeChain,

        [Parameter(ParameterSetName = 'PEM')]
        [Parameter(ParameterSetName = 'PKCS12', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if (Test-Path $_ -PathType Container) {
                    $true
                }
                else {
                    Throw "Output path '$_' does not exist"
                }
            })]
        [String] $OutPath,

        [Parameter(ParameterSetName = 'PKCS12', Mandatory)]
        [ValidateScript(
            {
                if ($PSVersionTable.PSVersion -lt [version]'7.1') {
                    throw 'Exporting in PKCS#12 foramt is only supported on PowerShell v7.1+'
                }
                $true
            }
        )]
        [switch] $PKCS12,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        $allCerts = [System.Collections.Generic.List[string]]::new()

        # set up the scriptblocks to be executed via invoke-venafiparallel
        if ( $PrivateKeyPassword ) {

            $pkPassString = $PrivateKeyPassword | ConvertTo-PlaintextString

            $sb = {

                $id = $PSItem
                $pkPassString = $using:pkPassString

                $params = @{
                    Method       = 'Post'
                    Header       = @{ 'accept' = 'application/octet-stream' }
                    UriRoot      = 'outagedetection/v1'
                    UriLeaf      = 'certificates/{0}/keystore' -f $id
                    Body         = @{
                        'exportFormat'                = 'PEM'
                        'encryptedKeystorePassphrase' = ''
                        'certificateLabel'            = ''
                    }
                    FullResponse = $true
                }

                $out = [pscustomobject] @{
                    certificateId = $id
                    error         = ''
                }

                $thisCert = Get-VcCertificate -id $id

                if ( -not $thisCert ) {
                    $out.error = 'Certificate not found'
                    return $out
                }

                if ( -not $thisCert.dekHash ) {
                    $out.error = 'Private key not found'
                    return $out
                }

                # build the encrypted private key password
                Import-Module PSSodium -Force
                $publicKey = Invoke-VenafiRestMethod -UriLeaf "edgeencryptionkeys/$($thisCert.dekHash)" | Select-Object -ExpandProperty key
                $privateKeyPasswordEnc = ConvertTo-SodiumEncryptedString -Text $pkPassString -PublicKey $publicKey
                $params.Body.encryptedPrivateKeyPassphrase = $privateKeyPasswordEnc

                $innerResponse = Invoke-VenafiRestMethod @params

                if ($innerResponse.StatusCode -notin 200, 201, 202) {
                    $out.error = $innerResponse.StatusDescription
                    return $out
                }

                if ( -not $innerResponse.Content ) {
                    $out.error = 'No certificate data received'
                    return $out
                }

                $zipFile = '{0}.zip' -f (New-TemporaryFile)
                $unzipPath = Join-Path -Path (Split-Path -Path $zipFile -Parent) -ChildPath $id

                try {
                    # always save the zip file then decide to copy to the final destination or return contents
                    [IO.File]::WriteAllBytes($zipFile, $innerResponse.Content)

                    Write-Verbose ('Expanding {0} to {1}' -f $zipFile, $unzipPath)

                    Expand-Archive -Path $zipFile -DestinationPath $unzipPath
                    $unzipFiles = Get-ChildItem -Path $unzipPath

                    if ( $using:OutPath ) {

                        if ( $using:PKCS12 ) {
                            $keyFile = Get-ChildItem -Path $unzipPath -Filter '*.key'

                            if ( $keyFile.Count -ne 1 ) {
                                $out.error = 'Private key not found'
                                return $out
                            }

                            $keyPath = $keyFile.FullName
                            $crtPath = $keyPath.Replace('.key', '.crt')
                            $cert = [System.Security.Cryptography.X509Certificates.x509Certificate2]::CreateFromEncryptedPemFile($crtPath, $pkPassString, $keyPath)
                            # export content type of 3 is for pfx
                            $cert.Export(3, $pkPassString) | Set-Content -Path (Join-Path -Path $using:OutPath -ChildPath ('{0}.pfx' -f $keyFile.BaseName)) -AsByteStream
                        }
                        else {
                            # copy files to final desination
                            $dest = Join-Path -Path (Resolve-Path -Path $using:OutPath) -ChildPath ('{0}-{1}' -f $thisCert.certificateName, $thisCert.certificateId)
                            $null = New-Item -Path $dest -ItemType Directory -Force
                            $unzipFiles | Copy-Item -Destination $dest -Force
                            $out | Add-Member @{'outPath' = $dest }
                        }
                    }
                    else {
                        # pull in the contents so we can provide them
                        switch ($unzipFiles) {
                            { $_.Name.EndsWith('.key') } {
                                $out | Add-Member @{'KeyPem' = Get-Content -Path $_.FullName -Raw }
                            }
                            { $_.Name.EndsWith('root-last.pem') } {

                                $certPem = @()

                                $pemLines = Get-Content -Path $_.FullName
                                for ($i = 0; $i -lt $pemLines.Count; $i++) {
                                    if ($pemLines[$i].Contains('-----BEGIN')) {
                                        $iStart = $i
                                        continue
                                    }
                                    if ($pemLines[$i].Contains('-----END')) {
                                        $thisPemLines = $pemLines[$iStart..$i]
                                        $certPem += $thisPemLines -join "`n"
                                        continue
                                    }
                                }

                                $out | Add-Member @{'CertPem' = $certPem[0] }
                                if ( $using:IncludeChain ) {
                                    $out | Add-Member @{'ChainPem' = $certPem[1..($certPem.Count - 1)] }
                                }
                            }
                        }
                    }
                    $out
                }
                finally {
                    Remove-Item -Path $unzipPath -Recurse -Force -ErrorAction SilentlyContinue
                    Remove-Item -Path $zipFile -Force -ErrorAction SilentlyContinue
                }
            }
        }
        else {
            # no need to get the entire keystore if just getting cert/chain

            # cert/chain only, no private key. different api call, better performance.
            $sb = {
                $params = @{
                    UriRoot      = 'outagedetection/v1'
                    UriLeaf      = 'certificates/{0}/contents' -f $PSItem
                    Body         = @{
                        format = 'PEM'
                    }
                    FullResponse = $true
                }

                if ( $using:IncludeChain ) {
                    $params.Body.chainOrder = 'EE_FIRST'
                }
                else {
                    $params.Body.chainOrder = 'EE_ONLY'
                }

                $out = [pscustomobject] @{
                    certificateId = $PSItem
                    error         = if ($innerResponse.StatusCode -notin 200, 201, 202) { $innerResponse.StatusDescription }
                }

                $thisCert = Get-VcCertificate -id $PSItem

                if ( -not $thisCert ) {
                    $out.error = 'Certificate not found'
                    return $out
                }

                $innerResponse = Invoke-VenafiRestMethod @params
                $certificateData = $innerResponse.Content

                if ( $certificateData ) {
                    if ( $using:OutPath ) {
                        $dest = Join-Path -Path (Resolve-Path -Path $using:OutPath) -ChildPath ('{0}-{1}' -f $thisCert.certificateName, $thisCert.certificateId)
                        $null = New-Item -Path $dest -ItemType Directory -Force
                        $outFile = Join-Path -Path $dest -ChildPath ('{0}.{1}' -f $PSItem, $params.Body.format)
                        try {
                            $sw = [IO.StreamWriter]::new($outFile, $false, [Text.Encoding]::ASCII)
                            $sw.WriteLine($certificateData)
                            Write-Verbose "Saved $outFile"
                        }
                        finally {
                            if ($null -ne $sw) { $sw.Close() }
                        }

                        $out | Add-Member @{'outPath' = $dest }
                    }
                    else {
                        $out | Add-Member @{'certificateData' = $certificateData }
                    }
                }
                $out
            }
        }

        Initialize-PSSodium
    }

    process {
        $allCerts.Add($ID)
    }

    end {
        $invokeParams = @{
            InputObject   = $allCerts
            ScriptBlock   = $sb
            ThrottleLimit = $ThrottleLimit
            ProgressTitle = 'Exporting certificates'
        }
        Invoke-VenafiParallel @invokeParams
    }
}
#EndRegion './Public/Export-VcCertificate.ps1' 352
#Region './Public/Export-VdcCertificate.ps1' -1

function Export-VdcCertificate {
    <#
    .SYNOPSIS
    Export certificate data from TLSPDC

    .DESCRIPTION
    Export certificate data

    .PARAMETER Path
    Full path to the certificate

    .PARAMETER Base64
    Provide output in Base64 format. This is the default if no format is provided.

    .PARAMETER Pkcs7
    Provide output in PKCS #7 format

    .PARAMETER Pkcs8
    Provide output in PKCS #8 format

    .PARAMETER Der
    Provide output in DER format

    .PARAMETER Pkcs12
    Provide output in PKCS #12 format. Requires a value for PrivateKeyPassword.

    .PARAMETER Jks
    Provide output in JKS format. Requires a value for FriendlyName.

    .PARAMETER OutPath
    Folder path to save the certificate/key to. The name of the file will be determined automatically.

    .PARAMETER IncludeChain
    Include the certificate chain with the exported certificate.
    The end entity will be first and the root last.

    .PARAMETER FriendlyName
    Label or alias to use. Permitted with Base64 and PKCS #12 formats. Required when exporting JKS.

    .PARAMETER PrivateKeyPassword
    Password required to include the private key.
    You can either provide a String, SecureString, or PSCredential.
    You must adhere to the following rules:
    - Password is at least 12 characters.
    - Comprised of at least three of the following:
        - Uppercase alphabetic letters
        - Lowercase alphabetic letters
        - Numeric characters
        - Special characters

    .PARAMETER KeystorePassword
    Password required to retrieve the certificate in JKS format.
    You can either provide a String, SecureString, or PSCredential.
    You must adhere to the following rules:
    - Password is at least 12 characters.
    - Comprised of at least three of the following:
        - Uppercase alphabetic letters
        - Lowercase alphabetic letters
        - Numeric characters
        - Special characters

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Export-VdcCertificate -Path '\ved\policy\mycert.com'

    Get certificate data in Base64 format, the default

    .EXAMPLE
    $cert | Export-VdcCertificate -'PKCS7' -OutPath 'c:\temp'

    Get certificate data in a specific format and save to a file

    .EXAMPLE
    $cert | Export-VdcCertificate -'PKCS7' -IncludeChain

    Get one or more certificates with the certificate chain included

    .EXAMPLE
    $cert | Export-VdcCertificate -'PKCS12' -PrivateKeyPassword 'mySecretPassword!'

    Get one or more certificates with private key included

    .EXAMPLE
    $cert | Export-VdcCertificate -'PKCS8' -PrivateKeyPassword 'mySecretPassword!' -OutPath '~/temp'

    Save certificate info to a file. PKCS8 with private key will save 3 files, .pem (cert+key), .pem.cer (cert only), and .pem.key (key only)

    .EXAMPLE
    $cert | Export-VdcCertificate -Jks -FriendlyName 'MyFriendlyName' -KeystorePassword $cred.password

    Get certificates in JKS format.

    #>


    [CmdletBinding(DefaultParameterSetName = 'Base64')]

    param (

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Alias('id')]
        [string] $Path,

        [Parameter(ParameterSetName = 'Base64')]
        [switch] $Base64,

        [Parameter(Mandatory, ParameterSetName = 'Pkcs7')]
        [switch] $Pkcs7,

        [Parameter(Mandatory, ParameterSetName = 'Pkcs8')]
        [switch] $Pkcs8,

        [Parameter(Mandatory, ParameterSetName = 'Der')]
        [switch] $Der,

        [Parameter(Mandatory, ParameterSetName = 'Pkcs12')]
        [switch] $Pkcs12,

        [Parameter(Mandatory, ParameterSetName = 'Jks')]
        [switch] $Jks,

        [Parameter(ParameterSetName = 'Pkcs8')]
        [Parameter(Mandatory, ParameterSetName = 'Pkcs12')]
        [Parameter(ParameterSetName = 'Jks')]
        [ValidateScript(
            {
                if ( $_ -is [string] -or $_ -is [securestring] -or $_ -is [pscredential] ) {
                    $true
                }
                else {
                    throw 'Unsupported type. Provide either a String, SecureString, or PSCredential.'
                }
            }
        )]
        [Alias('SecurePassword')]
        [psobject] $PrivateKeyPassword,

        [Parameter(ParameterSetName = 'Base64')]
        [Parameter(ParameterSetName = 'Pkcs7')]
        [Parameter(ParameterSetName = 'Pkcs8')]
        [Parameter(ParameterSetName = 'Pkcs12')]
        [Parameter(ParameterSetName = 'Jks')]
        [switch] $IncludeChain,

        [Parameter(ParameterSetName = 'Base64')]
        [Parameter(ParameterSetName = 'Pkcs12')]
        [Parameter(Mandatory, ParameterSetName = 'Jks')]
        [string] $FriendlyName,

        [Parameter(Mandatory, ParameterSetName = 'Jks')]
        [ValidateScript(
            {
                if ( $_ -is [string] -or $_ -is [securestring] -or $_ -is [pscredential] ) {
                    $true
                }
                else {
                    throw 'Unsupported type. Provide either a String, SecureString, or PSCredential.'
                }
            }
        )]
        [psobject] $KeystorePassword,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if (Test-Path $_ -PathType Container) {
                    $true
                }
                else {
                    Throw "Output path '$_' does not exist"
                }
            })]
        [String] $OutPath,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $allCerts = [System.Collections.Generic.List[hashtable]]::new()

        $body = @{ Format = 'Base64' }
        switch ($PSCmdlet.ParameterSetName) {
            'Pkcs7' { $body.Format = 'PKCS #7' }
            'Pkcs8' { $body.Format = 'Base64 (PKCS#8)' }
            'Pkcs12' { $body.Format = 'PKCS #12' }
            'Der' { $body.Format = 'DER' }
            'Jks' { $body.Format = 'JKS' }
        }

        $body.IncludePrivateKey = $PSBoundParameters.ContainsKey('PrivateKeyPassword')

        if ( $body.IncludePrivateKey ) {
            $body.Password = $PrivateKeyPassword  | ConvertTo-PlaintextString
        }

        if ( $PSBoundParameters.ContainsKey('KeystorePassword') ) {
            $body.Format = 'JKS'
            $body.KeystorePassword = $KeystorePassword | ConvertTo-PlaintextString
        }

        if ( $PSBoundParameters.ContainsKey('FriendlyName') ) {
            $body.FriendlyName = $FriendlyName
        }

        if ($IncludeChain) {
            $body.IncludeChain = $true
        }

        $body | Write-VerboseWithSecret
    }

    process {
        $body.CertificateDN = ($Path | ConvertTo-VdcFullPath)

        $allCerts.Add($body.Clone())
    }

    end {
        Invoke-VenafiParallel -InputObject $allCerts -ScriptBlock {

            $thisBody = $PSItem

            try {
                $innerResponse = Invoke-VenafiRestMethod -Method 'Post' -UriLeaf 'certificates/retrieve' -Body $thisBody
            }
            catch {
                try {
                    # key not available, get just the cert
                    if ( $_.ToString() -like '*failed to lookup private key*') {

                        # we can't have a p12 without a private key
                        if ( $thisBody.Format -eq 'PKCS #12' ) {
                            throw $_
                        }

                        $thisBody.IncludePrivateKey = $false
                        $thisBody.Password = $null
                        $innerResponse = Invoke-VenafiRestMethod -Method 'Post' -UriLeaf 'certificates/retrieve' -Body $thisBody
                    }
                }
                catch {
                    return [pscustomobject]@{
                        'Path'            = $thisBody.CertificateDN
                        'Error'           = $_
                        'CertificateData' = $null
                    }
                }
            }

            $out = $innerResponse | Select-Object Filename, Format, @{
                n = 'Path'
                e = { $thisBody.CertificateDN }
            },
            @{
                n = 'Error'
                e = { $_.Status }
            }, CertificateData

            if ( $innerResponse.CertificateData ) {

                if ( $thisBody.Format -in 'Base64', 'Base64 (PKCS#8)' ) {
                    $splitData = Split-CertificateData -CertificateData $innerResponse.CertificateData
                }

                if ( $using:OutPath ) {

                    $out = $out | Select-Object -Property * -ExcludeProperty CertificateData

                    # write the file with the filename provided
                    $outFile = Join-Path -Path (Resolve-Path -Path $using:OutPath) -ChildPath ($innerResponse.FileName.Trim('"'))
                    $bytes = [Convert]::FromBase64String($innerResponse.CertificateData)
                    [IO.File]::WriteAllBytes($outFile, $bytes)

                    Write-Verbose "Saved $outFile"

                    $out | Add-Member @{'OutFile' = @($outFile) }

                    if ( $thisBody.Format -in 'Base64 (PKCS#8)' -and $thisBody.IncludePrivateKey) {
                        # outFile will be .pem with cert and key
                        # write out the individual files as well
                        try {
                            $crtFile = $outFile.Replace('.pem', '.crt')

                            $sw = [IO.StreamWriter]::new($crtFile, $false, [Text.Encoding]::ASCII)
                            $sw.WriteLine($splitData.CertPem)
                            Write-Verbose "Saved $crtFile"

                            $out.OutFile += $crtFile
                        }
                        finally {
                            if ($null -ne $sw) { $sw.Close() }
                        }

                        if ( $thisBody.IncludePrivateKey ) {
                            try {
                                $keyFile = $outFile.Replace('.pem', '.key')

                                $sw = [IO.StreamWriter]::new($keyFile, $false, [Text.Encoding]::ASCII)
                                $sw.WriteLine($splitData.KeyPem)
                                Write-Verbose "Saved $keyFile"

                                $out.OutFile += $keyFile
                            }
                            finally {
                                if ($null -ne $sw) { $sw.Close() }
                            }
                        }
                    }
                }
                else {
                    if ( $thisBody.Format -in 'Base64', 'Base64 (PKCS#8)' ) {

                        $out | Add-Member @{'CertPem' = $splitData.CertPem }

                        if ( $thisBody.IncludePrivateKey ) {
                            $out | Add-Member @{'KeyPem' = $splitData.KeyPem }
                        }

                        if ( $thisBody.IncludeChain -and $splitData.ChainPem ) {
                            $out | Add-Member @{'ChainPem' = $splitData.ChainPem }
                        }
                    }
                }
            }

            $out

        } -ThrottleLimit $ThrottleLimit -ProgressTitle 'Exporting certificates'
    }
}
#EndRegion './Public/Export-VdcCertificate.ps1' 353
#Region './Public/Export-VdcVaultObject.ps1' -1

function Export-VdcVaultObject {
    <#
    .SYNOPSIS
    Export an object from the vault

    .DESCRIPTION
    Export different object types from the vault.
    The currently supported types are certificate, key, and PKCS12.
    If the type is not supported, the base64 data will be returned as is.

    .PARAMETER ID
    ID of the vault object to export

    .PARAMETER OutPath
    Folder path to save the certificate/key to. The name of the file will be determined automatically.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject if unhandled type, otherwise saves the object to a file

    .EXAMPLE
    Export-VdcVaultObject -ID 12345 -OutPath 'c:\temp'

    Get vault object and save to a file

    #>


    [CmdletBinding()]

    param (

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Alias('VaultId')]
        [int] $ID,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if (Test-Path $_ -PathType Container) {
                    $true
                }
                else {
                    Throw "Output path '$_' does not exist"
                }
            })]
        [String] $OutPath,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {
        $response = Invoke-VenafiRestMethod -Method 'Post' -UriLeaf 'SecretStore/Retrieve' -Body @{ 'VaultID' = $ID }

        if ( $response.Result -ne 0 ) {
            Write-Error "Failed to retrieve vault object with a result code of $($response.Result). Look up this code at https://docs.venafi.com/Docs/currentSDK/TopNav/Content/SDK/WebSDK/r-SDK-SecretStore-ResultCodes.php."
            return
        }

        $ext = $null

        switch ( $response.VaultType ) {
            { $_ -in 2, 1073741826 } {
                # certificate
                $ext = 'cer'
            }

            { $_ -in 4, 1073741828 } {
                # PKCS12
                $ext = 'p12'
            }

            { $_ -in 256, 1073742080 } {
                # PKCS8
                $ext = 'key'
            }
        }

        if ( $ext ) {
            $outFile = Join-Path -Path (Resolve-Path -Path $OutPath) -ChildPath ('{0}.{1}' -f $ID, $ext)
            $bytes = [Convert]::FromBase64String($response.Base64Data)
            [IO.File]::WriteAllBytes($outFile, $bytes)

            Write-Verbose "Saved $outFile"
        }
        else {
            # unhandled type, send data as is
            Write-Verbose "Unhandled vault type $($response.VaultType), returning data as is"
            $response | Select-Object -Property *, @{'n' = 'VaultID'; 'e' = { $ID } } -ExcludeProperty Result
        }

    }
}
#EndRegion './Public/Export-VdcVaultObject.ps1' 106
#Region './Public/Find-VcCertificate.ps1' -1

function Find-VcCertificate {
    <#
    .SYNOPSIS
    Find certificates in TLSPC

    .DESCRIPTION
    Find certificates based on various attributes.

    .PARAMETER Name
    Search for certificates with the name matching part or all of the value

    .PARAMETER KeyLength
    Search by certificate key length

    .PARAMETER Serial
    Search by serial number

    .PARAMETER Fingerprint
    Search by fingerprint

    .PARAMETER IsSelfSigned
    Search for only self signed certificates

    .PARAMETER Status
    Search by one or more certificate statuses. Valid values include ACTIVE, RETIRED, and DELETED.

    .PARAMETER ExpireBefore
    Search for certificates expiring before a certain date.
    Use with -ExpireAfter for a defined start and end.

    .PARAMETER ExpireAfter
    Search for certificates expiring after a certain date.
    Use with -ExpireBefore for a defined start and end.

    .PARAMETER Version
    Search by version type. Valid values include CURRENT and OLD.

    .PARAMETER SanDns
    Search for certificates with SAN DNS matching part or all of the value

    .PARAMETER Application
    Application ID or name that this certificate is associated with

    .PARAMETER Tag
    One or more tags associated with the certificate.
    You can specify either just a tag name or name:value.

    .PARAMETER CN
    Search for certificates where the subject CN matches all of part of the value

    .PARAMETER Issuer
    Search by issuer name

    .PARAMETER Filter
    Array or multidimensional array of fields and values to filter on.
    Each array should be of the format @(field, comparison operator, value).
    To combine filters, use the format @('operator', @(field, comparison operator, value), @(field2, comparison operator2, value2)).
    Nested filters are supported.
    Field names and values are case sensitive, but VenafiPS will try and convert to the proper case if able.

    Available operators are:

    Operator | Name | Description and Usage
    -----------------------------------------------------------------------------------
    EQ Equal operator The search result is equal to the specified value. Valid for numeric or Boolean fields.
    FIND Find operator The search result is based on the value of all or part of one or more strings. You can also use Regular Expressions (regex).
    GT Greater than The search result has a higher numeric value than the specified value.
    GTE Greater than or equal to The search result is equal or has a higher numeric value than the specified value.
    IN In clause The search result matches one of the values in an array.
    LT Less Than The search result has a lower value than the specified value.
    LTE Less than or equal to The search result is equal or less than the specified value.
    MATCH Match operator The search result includes a string value from the supplied list. You can also use regex for your search.

    For more info comparison operators, see https://docs.venafi.cloud/api/about-api-search-operators/.

    .PARAMETER Order
    1 or more fields to order on.
    For each item in the array, you can provide a field name by itself; this will default to ascending.
    You can also provide a hashtable with the field name as the key and either asc or desc as the value.

    .PARAMETER SavedSearchName
    Find certificates based on a saved search, see https://docs.venafi.cloud/vaas/certificates/saving-certificate-filters

    .PARAMETER First
    Only retrieve this many records

    .PARAMETER ApplicationDetail
    Retrieve detailed application info.
    This will cause additional api calls to be made and take longer.

    .PARAMETER OwnerDetail
    Retrieve detailed user/team owner info.
    This will cause additional api calls to be made and take longer.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    None

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Find-VcCertificate

    Find all certificates

    .EXAMPLE
    Find-VcCertificate -First 500

    Find the first 500 certificates

    .EXAMPLE
    Find-VcCertificate -Name 'mycert.company.com'

    Find certificates matching all of part of the name

    .EXAMPLE
    Find-VcCertificate -Fingerprint '075C43428E70BCF941039F54B8ED78DE4FACA87F'

    Find certificates matching a single value

    .EXAMPLE
    Find-VcCertificate -ExpireAfter (get-date) -ExpireBefore (get-date).AddDays(30)

    Find certificates matching multiple values. In this case, find all certificates expiring in the next 30 days.

    .EXAMPLE
    Find-VcCertificate -ExpireAfter (get-date) -ExpireBefore (get-date).AddDays(30)| Invoke-VcCertificateAction -Renew

    Find all certificates expiring in the next 30 days and renew them

    .EXAMPLE
    Find-VcCertificate -Filter @('subjectDN', 'FIND', 'www.barron.com')

    Find via a filter instead of using built-in function properties

    .EXAMPLE
    Find-VcCertificate -ApplicatonDetail

    Include application details, not just the ID.
    This will make additional api calls and will increase the response time.

    .EXAMPLE
    Find-VcCertificate -OwnerDetail

    Include user/team owner details, not just the ID.
    This will make additional api calls and will increase the response time.
    #>


    [CmdletBinding(DefaultParameterSetName = 'All')]

    param (

        [Parameter(ParameterSetName = 'All')]
        [string] $Name,

        [Parameter(ParameterSetName = 'All')]
        [int32] $KeyLength,

        [Parameter(ParameterSetName = 'All')]
        [string] $Serial,

        [Parameter(ParameterSetName = 'All')]
        [string] $Fingerprint,

        [Parameter(ParameterSetName = 'All')]
        [switch] $IsSelfSigned,

        [Parameter(ParameterSetName = 'All')]
        [ValidateSet('ACTIVE', 'RETIRED', 'DELETED')]
        [string[]] $Status,

        [Parameter(ParameterSetName = 'All')]
        [datetime] $ExpireBefore,

        [Parameter(ParameterSetName = 'All')]
        [datetime] $ExpireAfter,

        [Parameter(ParameterSetName = 'All')]
        [ValidateSet('CURRENT', 'OLD')]
        [string] $Version,

        [Parameter(ParameterSetName = 'All')]
        [string] $SanDns,

        [Parameter(ParameterSetName = 'All')]
        [string] $Application,

        [Parameter(ParameterSetName = 'All')]
        [string[]] $Tag,

        [Parameter(ParameterSetName = 'All')]
        [string] $CN,

        [Parameter(ParameterSetName = 'All')]
        [string] $Issuer,

        [Parameter(Mandatory, ParameterSetName = 'Filter')]
        [System.Collections.ArrayList] $Filter,

        [Parameter(ParameterSetName = 'All')]
        [Parameter(ParameterSetName = 'Filter')]
        [psobject[]] $Order,

        [parameter(Mandatory, ParameterSetName = 'SavedSearch')]
        [string] $SavedSearchName,

        [Parameter()]
        [switch] $ApplicationDetail,

        [Parameter()]
        [Alias('IncludeVaasOwner')]
        [switch] $OwnerDetail,

        [Parameter()]
        [int] $First,

        [Parameter()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

    $apps = [System.Collections.Generic.List[object]]::new()
    $appOwners = [System.Collections.Generic.List[object]]::new()

    $params = @{
        Type  = 'Certificate'
        First = $First
    }

    if ( $Order ) { $params.Order = $Order }

    switch ($PSCmdlet.ParameterSetName) {
        'Filter' {
            $params.Filter = $Filter
        }

        'All' {
            $newFilter = [System.Collections.Generic.List[object]]::new()
            $newFilter.Add('AND')

            switch ($PSBoundParameters.Keys) {
                'Name' { $null = $newFilter.Add(@('certificateName', 'FIND', $Name)) }
                'KeyLength' { $null = $newFilter.Add(@('keyStrength', 'EQ', $KeyLength.ToString())) }
                'Serial' { $null = $newFilter.Add(@('serialNumber', 'EQ', $Serial)) }
                'Fingerprint' { $null = $newFilter.Add(@('fingerprint', 'EQ', $Fingerprint)) }
                'IsSelfSigned' { $null = $newFilter.Add(@('selfSigned', 'EQ', $IsSelfSigned.IsPresent.ToString())) }
                'Version' { $null = $newFilter.Add(@('versionType', 'EQ', $Version)) }
                'Status' {
                    $null = $newFilter.Add(@('certificateStatus', 'MATCH', $Status.ToUpper()))
                }
                'ExpireBefore' { $null = $newFilter.Add(@('validityEnd', 'LTE', $ExpireBefore)) }
                'ExpireAfter' { $null = $newFilter.Add(@('validityEnd', 'GTE', $ExpireAfter)) }
                'SanDns' { $null = $newFilter.Add(@('subjectAlternativeNameDns', 'FIND', $SanDns)) }
                'CN' { $null = $newFilter.Add(@('subjectCN', 'FIND', $CN)) }
                'Issuer' { $null = $newFilter.Add(@('issuerCN', 'FIND', $Issuer)) }
                'Application' {
                    $appId = Get-VcData -InputObject $Application -Type 'Application'
                    if ( -not $appId ) {
                        throw "Application '$Application' does not exist"
                    }
                    $newFilter.Add(@('applicationIds', 'MATCH', $appId ))
                }
                'Tag' {
                    $null = $newFilter.Add(@('tags', 'MATCH', $Tag))
                }
            }

            if ( $newFilter.Count -gt 1 ) { $params.Filter = $newFilter }
        }

        'SavedSearch' {
            $params.SavedSearchName = $SavedSearchName
        }
    }

    $response = Find-VcObject @params

    $response | Select-Object *,
    @{
        'n' = 'application'
        'e' = {
            if ( $ApplicationDetail ) {
                foreach ($thisAppId in $_.applicationIds) {
                    $thisApp = $apps | Where-Object { $_.applicationId -eq $thisAppId }
                    if ( -not $thisApp ) {
                        $thisApp = Get-VcApplication -ID $thisAppId | Select-Object -Property * -ExcludeProperty ownerIdsAndTypes, ownership
                        $apps.Add($thisApp)
                    }
                    $thisApp
                }
            }
            else {
                $_.applicationIds
            }
        }
    },
    @{
        'n' = 'owner'
        'e' = {
            if ( $OwnerDetail ) {

                # this scriptblock requires ?ownershipTree=true be part of the url
                foreach ( $thisOwner in $_.ownership.owningContainers.owningUsers ) {
                    $thisOwnerDetail = $appOwners | Where-Object { $_.id -eq $thisOwner }
                    if ( -not $thisOwnerDetail ) {
                        $thisOwnerDetail = Get-VcIdentity -ID $thisOwner | Select-Object firstName, lastName, emailAddress,
                        @{
                            'n' = 'status'
                            'e' = { $_.userStatus }
                        },
                        @{
                            'n' = 'role'
                            'e' = { $_.systemRoles }
                        },
                        @{
                            'n' = 'type'
                            'e' = { 'USER' }
                        },
                        @{
                            'n' = 'userId'
                            'e' = { $_.id }
                        }

                        $appOwners.Add($thisOwnerDetail)

                    }
                    $thisOwnerDetail
                }

                foreach ($thisOwner in $_.ownership.owningContainers.owningTeams) {
                    $thisOwnerDetail = $appOwners | Where-Object { $_.id -eq $thisOwner }
                    if ( -not $thisOwnerDetail ) {
                        $thisOwnerDetail = Get-VcTeam -ID $thisOwner | Select-Object name, role, members,
                        @{
                            'n' = 'type'
                            'e' = { 'TEAM' }
                        },
                        @{
                            'n' = 'teamId'
                            'e' = { $_.id }
                        }

                        $appOwners.Add($thisOwnerDetail)
                    }
                    $thisOwnerDetail
                }
            }
            else {
                $_.ownership.owningContainers | Select-Object owningUsers, owningTeams
            }
        }
    },
    @{
        'n' = 'instance'
        'e' = { $_.instances }
    },
    @{
        'n' = 'issuerCN'
        'e' = { $_.issuerCN[0] }
    },
    @{
        'n' = 'issuerOU'
        'e' = { $_.issuerOU[0] }
    } -ExcludeProperty applicationIds, instances, totalInstanceCount, ownership, issuerCN, issuerOU
}
#EndRegion './Public/Find-VcCertificate.ps1' 372
#Region './Public/Find-VcCertificateInstance.ps1' -1

function Find-VcCertificateInstance {
    <#
    .SYNOPSIS
    Find certificate requests

    .DESCRIPTION
    Find certificate requests

    .PARAMETER Filter
    Array or multidimensional array of fields and values to filter on.
    Each array should be of the format @('operator', @(field, comparison operator, value), @(field2, comparison operator2, value2)).
    Nested filters are supported.
    For a complete list of comparison operators, see https://docs.venafi.cloud/api/about-api-search-operators/.
    To see which fields you can search on, execute Find-VcCertificateInstance -First 1.

    .PARAMETER Order
    Array of fields to order on.
    For each item in the array, you can provide a field name by itself; this will default to ascending.
    You can also provide a hashtable with the field name as the key and either asc or desc as the value.

    .PARAMETER HostName
    Hostname to find via regex match

    .PARAMETER IpAddress
    Machine IP Address

    .PARAMETER Port
    Machine port

    .PARAMETER Status
    Instance status, either IN_USE or SUPERSEDED

    .PARAMETER First
    Only retrieve this many records

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .OUTPUTS

    #>


    [CmdletBinding(DefaultParameterSetName = 'All')]

    param (

        [Parameter(ParameterSetName = 'All')]
        [string] $HostName,

        [Parameter(ParameterSetName = 'All')]
        [ipaddress] $IpAddress,

        [Parameter(ParameterSetName = 'All')]
        [int] $Port,

        [Parameter(ParameterSetName = 'All')]
        [ValidateSet('IN_USE', 'SUPERSEDED')]
        [string] $Status,

        [Parameter(Mandatory, ParameterSetName = 'Filter')]
        [System.Collections.ArrayList] $Filter,

        [parameter()]
        [psobject[]] $Order,

        [Parameter()]
        [int] $First,

        [Parameter()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

    $params = @{
        Type = 'CertificateInstance'
        First = $First
    }

    if ( $Order ) { $params.Order = $Order }

    if ( $PSCmdlet.ParameterSetName -eq 'Filter' ) {
        $params.Filter = $Filter
    }
    else {
        $newFilter = [System.Collections.Generic.List[object]]::new()
        $newFilter.Add('AND')

        switch ($PSBoundParameters.Keys) {
            'HostName' { $null = $newFilter.Add(@('hostname', 'FIND', $HostName)) }
            'IpAddress' { $null = $newFilter.Add(@('ipAddress', 'EQ', $IpAddress.IPAddressToString)) }
            'Port' { $null = $newFilter.Add(@('port', 'EQ', $Port.ToString())) }
            'Status' { $null = $newFilter.Add(@('deploymentStatus', 'EQ', $Status.ToUpper())) }
        }

        if ( $newFilter.Count -gt 1 ) { $params.Filter = $newFilter }
    }

    Find-VcObject @params
}
#EndRegion './Public/Find-VcCertificateInstance.ps1' 103
#Region './Public/Find-VcCertificateRequest.ps1' -1

function Find-VcCertificateRequest {
    <#
    .SYNOPSIS
    Find certificate requests

    .DESCRIPTION
    Find certificate requests

    .PARAMETER Filter
    Array or multidimensional array of fields and values to filter on.
    Each array should be of the format @('operator', @(field, comparison operator, value), @(field2, comparison operator2, value2)).
    Nested filters are supported.
    For a complete list of comparison operators, see https://docs.venafi.cloud/api/about-api-search-operators/.

    .PARAMETER Order
    Array of fields to order on.
    For each item in the array, you can provide a field name by itself; this will default to ascending.
    You can also provide a hashtable with the field name as the key and either asc or desc as the value.

    .PARAMETER Status
    Request status, either ISSUED or FAILED

    .PARAMETER KeyLength
    Certificate key length

    .PARAMETER First
    Only retrieve this many records

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .OUTPUTS

    #>


    [CmdletBinding(DefaultParameterSetName = 'All')]

    param (

        [Parameter(ParameterSetName = 'All')]
        [ValidateSet('ISSUED', 'FAILED')]
        [string] $Status,

        [Parameter(ParameterSetName = 'All')]
        [int] $KeyLength,

        [Parameter(Mandatory, ParameterSetName = 'Filter')]
        [System.Collections.ArrayList] $Filter,

        [parameter()]
        [psobject[]] $Order,

        [Parameter()]
        [int] $First,

        [Parameter()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

    $params = @{
        Type = 'CertificateRequest'
        First = $First
    }

    if ( $Order ) { $params.Order = $Order }

    if ( $PSCmdlet.ParameterSetName -eq 'Filter' ) {
        $params.Filter = $Filter
    }
    else {
        $newFilter = [System.Collections.Generic.List[object]]::new()
        $newFilter.Add('AND')

        switch ($PSBoundParameters.Keys) {
            'Status' { $null = $newFilter.Add(@('status', 'EQ', $Status.ToUpper())) }
            'KeyLength' { $null = $newFilter.Add(@('keyLength', 'EQ', $KeyLength.ToString())) }
        }

        if ( $newFilter.Count -gt 1 ) { $params.Filter = $newFilter }
    }

    Find-VcObject @params

}
#EndRegion './Public/Find-VcCertificateRequest.ps1' 89
#Region './Public/Find-VcLog.ps1' -1

function Find-VcLog {
    <#
    .SYNOPSIS
    Find log entries on TLSPC

    .DESCRIPTION
    Find log entries

    .PARAMETER Filter
    Array or multidimensional array of fields and values to filter on.
    Each array should be of the format @('operator', @(field, comparison operator, value), @(field2, comparison operator2, value2)).
    Nested filters are supported.
    For a complete list of comparison operators, see https://docs.venafi.cloud/api/about-api-search-operators/.

    .PARAMETER Order
    Array of fields to order on.
    For each item in the array, you can provide a field name by itself; this will default to ascending.
    You can also provide a hashtable with the field name as the key and either asc or desc as the value.

    .PARAMETER Name
    Activity name to find via regex match

    .PARAMETER Type
    Activity type

    .PARAMETER Message
    Look anywhere in the message for the string provided

    .PARAMETER First
    Only retrieve this many records

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .OUTPUTS
    PSCustomObject
        activityLogId
        activityDate
        activityType
        activityName
        criticality
        message
        payload
        companyId

    .EXAMPLE
    Find-VcLog -First 10

    Get the most recent 10 log items

    .EXAMPLE
    Find-VcLog -Type 'Authentication'

    Filter log results by specific value

    .EXAMPLE
    Find-VcLog -Filter @('and', @('activityDate', 'gt', (get-date).AddMonths(-1)), @('or', @('message', 'find', 'greg@venafi.com'), @('message', 'find', 'bob@venafi.com')), @('activityType','eq','Authentication'))

    Advanced filtering of results.
    This filter will find authentication log entries by 1 of 2 people within the last month.

    .EXAMPLE
    Find-VcLog -Type 'Authentication' -Order 'activityDate'

    Filter log results and order them.
    By default, order will be ascending.

    .EXAMPLE
    Find-VcLog -Type 'Authentication' -Order @{'activityDate'='desc'}

    Filter log results and order them descending

    .EXAMPLE
    Find-VcLog -Type 'Authentication' -Order @{'activityDate'='desc'}, 'criticality'

    Filter log results and order them by multiple fields

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config#/Activity%20Logs/activitylogs_getByExpression

    #>


    [CmdletBinding(DefaultParameterSetName = 'All')]

    param (

        [Parameter(ParameterSetName = 'All')]
        [string] $Name,

        [Parameter(ParameterSetName = 'All')]
        [string] $Type,

        [Parameter(ParameterSetName = 'All')]
        [string] $Message,

        [Parameter(Mandatory, ParameterSetName = 'Filter')]
        [System.Collections.ArrayList] $Filter,

        [parameter()]
        [psobject[]] $Order,

        [Parameter()]
        [int] $First,

        [Parameter()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

    $params = @{
        Type  = 'ActivityLog'
        First = $First
    }

    if ( $Order ) { $params.Order = $Order }

    if ( $PSCmdlet.ParameterSetName -eq 'Filter' ) {
        $params.Filter = $Filter
    }
    else {
        $newFilter = [System.Collections.Generic.List[object]]::new()
        $newFilter.Add('AND')

        switch ($PSBoundParameters.Keys) {
            'Name' { $null = $newFilter.Add(@('activityName', 'FIND', $Name)) }
            'Type' { $null = $newFilter.Add(@('activityType', 'EQ', $Type)) }
            'Message' { $null = $newFilter.Add(@('message', 'FIND', $Message)) }
        }

        if ( $newFilter.Count -gt 1 ) { $params.Filter = $newFilter }
    }

    Find-VcObject @params
}
#EndRegion './Public/Find-VcLog.ps1' 138
#Region './Public/Find-VcMachine.ps1' -1

function Find-VcMachine {
    <#
    .SYNOPSIS
    Find machines

    .DESCRIPTION
    Find machines

    .PARAMETER Filter
    Array or multidimensional array of fields and values to filter on.
    Each array should be of the format @('operator', @(field, comparison operator, value), @(field2, comparison operator2, value2)).
    Nested filters are supported.
    For a complete list of comparison operators, see https://docs.venafi.cloud/api/about-api-search-operators/.

    .PARAMETER Order
    Array of fields to order on.
    For each item in the array, you can provide a field name by itself; this will default to ascending.
    You can also provide a hashtable with the field name as the key and either asc or desc as the value.

    .PARAMETER Name
    Machine name to find via regex match

    .PARAMETER MachineType
    Machine type. You can use tab-ahead autocompletion for a list.

    .PARAMETER Status
    Machine status, either DRAFT, VERIFIED, OR UNVERIFIED.

    .PARAMETER First
    Only retrieve this many records

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .OUTPUTS

    #>


    [CmdletBinding(DefaultParameterSetName = 'All')]

    param (

        [Parameter(ParameterSetName = 'All')]
        [string] $Name,

        [Parameter(ParameterSetName = 'All')]
        [Alias('Type')]
        [string] $MachineType,

        [Parameter(ParameterSetName = 'All')]
        [ValidateSet('DRAFT', 'VERIFIED', 'UNVERIFIED')]
        [string] $Status,

        [Parameter(Mandatory, ParameterSetName = 'Filter')]
        [System.Collections.ArrayList] $Filter,

        [Parameter()]
        [psobject[]] $Order,

        [Parameter()]
        [int] $First,

        [Parameter()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

    $params = @{
        Type = 'Machine'
        First = $First
    }

    if ( $Order ) { $params.Order = $Order }

    if ( $PSCmdlet.ParameterSetName -eq 'Filter' ) {
        $params.Filter = $Filter
    }
    else {
        $newFilter = [System.Collections.Generic.List[object]]::new()
        $newFilter.Add('AND')

        switch ($PSBoundParameters.Keys) {
            'Name' { $null = $newFilter.Add(@('machineName', 'FIND', $Name)) }
            'Type' { $null = $newFilter.Add(@('machineType', 'EQ', $Type)) }
            'Status' { $null = $newFilter.Add(@('status', 'EQ', $Status.ToUpper())) }
        }

        if ( $newFilter.Count -gt 1 ) { $params.Filter = $newFilter }
    }

    Find-VcObject @params

}
#EndRegion './Public/Find-VcMachine.ps1' 97
#Region './Public/Find-VcMachineIdentity.ps1' -1

function Find-VcMachineIdentity {
    <#
    .SYNOPSIS
    Find machine identities

    .DESCRIPTION
    Find machine identities

    .PARAMETER Status
    Search by one or more statuses. Valid values are DISCOVERED, VALIDATED, and INSTALLED

    .PARAMETER Filter
    Array or multidimensional array of fields and values to filter on.
    Each array should be of the format @('operator', @(field, comparison operator, value), @(field2, comparison operator2, value2)).
    Nested filters are supported.
    For a complete list of comparison operators, see https://docs.venafi.cloud/api/about-api-search-operators/.

    .PARAMETER Order
    Array of fields to order on.
    For each item in the array, you can provide a field name by itself; this will default to ascending.
    You can also provide a hashtable with the field name as the key and either asc or desc as the value.

    .PARAMETER First
    Only retrieve this many records

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .OUTPUTS
    pscustomobject
    #>


    [CmdletBinding(DefaultParameterSetName = 'All')]

    param (

        [Parameter(ParameterSetName = 'All')]
        [ValidateSet('DISCOVERED', 'VALIDATED', 'INSTALLED')]
        [string[]] $Status,

        [Parameter(Mandatory, ParameterSetName = 'Filter')]
        [System.Collections.ArrayList] $Filter,

        [parameter()]
        [psobject[]] $Order,

        [Parameter()]
        [int] $First,

        [Parameter()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

    $params = @{
        Type  = 'MachineIdentity'
        First = $First
    }

    if ( $Order ) { $params.Order = $Order }

    if ( $PSCmdlet.ParameterSetName -eq 'Filter' ) {
        $params.Filter = $Filter
    }
    else {
        $newFilter = [System.Collections.Generic.List[object]]::new()
        $newFilter.Add('AND')

        switch ($PSBoundParameters.Keys) {
            'Status' { $newFilter.Add(@('status', 'MATCH', $Status.ToUpper())) }

        }

        if ( $newFilter.Count -gt 1 ) { $params.Filter = $newFilter }
    }

    Find-VcObject @params
}
#EndRegion './Public/Find-VcMachineIdentity.ps1' 82
#Region './Public/Find-VdcCertificate.ps1' -1

function Find-VdcCertificate {
    <#
    .SYNOPSIS
    Find certificates in TLSPDC based on various attributes

    .DESCRIPTION
    Find certificates based on various attributes.
    Supports standard PS paging parameters First and Skip.
    If -First not provided, the default return is 1000 records.

    .PARAMETER Path
    Starting path to search from. If not provided, the default is \ved\policy.

    .PARAMETER Guid
    Guid which represents a starting path.

    .PARAMETER Recursive
    Search recursively starting from the search path.

    .PARAMETER Country
    Find certificates by Country attribute of Subject DN.

    .PARAMETER CommonName
    Find certificates by Common name attribute of Subject DN.

    .PARAMETER Issuer
    Find certificates by issuer. Use the CN, O, L, S, and C values from the certificate request.

    .PARAMETER KeyAlgorithm
    Find certificates by algorithm for the public key.

    .PARAMETER KeySize
    Find certificates by public key size.

    .PARAMETER KeySizeGreaterThan
    Find certificates with a key size greater than the specified value.

    .PARAMETER KeySizeLessThan
    Find certificates with a key size less than the specified value.

    .PARAMETER Locale
    Find certificates by Locality/City attribute of Subject Distinguished Name (DN).

    .PARAMETER Organization
    Find certificates by Organization attribute of Subject DN.

    .PARAMETER OrganizationUnit
    Find certificates by Organization Unit (OU).

    .PARAMETER State
    Find certificates by State/Province attribute of Subject DN.

    .PARAMETER SanDns
    Find certificates by Subject Alternate Name (SAN) Distinguished Name Server (DNS).

    .PARAMETER SanEmail
    Find certificates by SAN Email RFC822.

    .PARAMETER SanIP
    Find certificates by SAN IP Address.

    .PARAMETER SanUpn
    Find certificates by SAN User Principal Name (UPN) or OtherName.

    .PARAMETER SanUri
    Find certificates by SAN Uniform Resource Identifier (URI).

    .PARAMETER SerialNumber
    Find certificates by Serial number.

    .PARAMETER SignatureAlgorithm
    Find certificates by the algorithm used to sign the certificate (e.g. SHA1RSA).

    .PARAMETER Thumbprint
    Find certificates by one or more SHA-1 thumbprints.

    .PARAMETER IssueDate
    Find certificates by the date of issue.

    .PARAMETER ExpireDate
    Find certificates by expiration date.

    .PARAMETER ExpireAfter
    Find certificates that expire after a certain date.

    .PARAMETER ExpireBefore
    Find certificates that expire before a certain date.

    .PARAMETER Enabled
    Include only certificates that are enabled or disabled.

    .PARAMETER InError
    Only include certificates in an error state.

    .PARAMETER IsSelfSigned
    Only include self-signed certificates

    .PARAMETER IsWildcard
    Only include wilcard certificates

    .PARAMETER NetworkValidationEnabled
    Only include certificates with network validation enabled or disabled.

    .PARAMETER CreatedDate
    Find certificates that were created at an exact date and time.

    .PARAMETER CreatedAfter
    Find certificate created after this date and time.

    .PARAMETER CreatedBefore
    Find certificate created before this date and time.

    .PARAMETER CertificateType
    Find certificate by category of usage. Use CodeSigning, Device, Server, and/or User.

    .PARAMETER ManagementType
    Find certificates with a Management type of Unassigned, Monitoring, Enrollment, or Provisioning.

    .PARAMETER PendingWorkflow
    Only include certificates that have a pending workflow resolution (have an outstanding workflow ticket).

    .PARAMETER Stage
    Find certificates by one or more stages in the certificate lifecycle.

    .PARAMETER StageGreaterThan
    Find certificates with a stage greater than the specified stage (does not include specified stage).

    .PARAMETER StageLessThan
    Find certificates with a stage less than the specified stage (does not include specified stage).

    .PARAMETER ValidationEnabled
    Only include certificates with validation enabled or disabled.

    .PARAMETER ValidationState
    Find certificates with a validation state of Blank, Success, or Failure.

    .PARAMETER CountOnly
    Return the count of certificates found from the query as opposed to the certificates themselves

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    TppObject, Int when CountOnly provided

    .EXAMPLE
    Find-VdcCertificate

    Find first 1000 certificates

    .EXAMPLE
    Find-VdcCertificate -ExpireBefore [datetime]'2018-01-01'

    Find certificates expiring before a certain date

    .EXAMPLE
    Find-VdcCertificate -ExpireBefore "2018-01-01" -First 5

    Find 5 certificates expiring before a certain date

    .EXAMPLE
    Find-VdcCertificate -ExpireBefore "2018-01-01" -First 5 -Skip 2

    Find 5 certificates expiring before a certain date, starting at the 3rd certificate found.

    .EXAMPLE
    Find-VdcCertificate -Path '\VED\Policy\My Policy'

    Find certificates in a specific path

    .EXAMPLE
    Find-VdcCertificate -Issuer 'CN=Example Root CA, O=Venafi,Inc., L=Salt Lake City, S=Utah, C=US'

    Find certificates by issuer

    .EXAMPLE
    Find-VdcCertificate -Path '\VED\Policy\My Policy' -Recursive

    Find certificates in a specific path and all subfolders

    .EXAMPLE
    Find-VdcCertificate | Get-VdcCertificate

    Get detailed certificate info

    .EXAMPLE
    Find-VdcCertificate -ExpireBefore "2019-09-01" | Invoke-VdcCertificateAction -Renew

    Renew all certificates expiring before a certain date

    .EXAMPLE
    Find-VdcCertificate -First 500

    Find the first 500 certificates

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-Certificates.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-Certificates-guid.php

    .LINK
    https://msdn.microsoft.com/en-us/library/system.web.httputility(v=vs.110).aspx
    #>


    [CmdletBinding(SupportsPaging)]

    param (

        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('DN')]
        [String] $Path = '\ved\policy',

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [guid] $Guid,

        [Parameter()]
        [Switch] $Recursive,

        # [Parameter()]
        # [int] $Limit = 1000,

        # [Parameter()]
        # [int] $Offset,

        [Parameter()]
        [Alias('C')]
        [String] $Country,

        [Parameter()]
        [Alias('CN')]
        [String] $CommonName,

        [Parameter()]
        [String] $Issuer,

        [Parameter()]
        [String[]] $KeyAlgorithm,

        [Parameter()]
        [Int[]] $KeySize,

        [Parameter()]
        [Int] $KeySizeGreaterThan,

        [Parameter()]
        [Int] $KeySizeLessThan,

        [Parameter()]
        [Alias('L')]
        [String[]] $Locale,

        [Parameter()]
        [Alias('O')]
        [String[]] $Organization,

        [Parameter()]
        [Alias('OU')]
        [String[]] $OrganizationUnit,

        [Parameter()]
        [Alias('S')]
        [String[]] $State,

        [Parameter()]
        [String] $SanDns,

        [Parameter()]
        [String] $SanEmail,

        [Parameter()]
        [String] $SanIP,

        [Parameter()]
        [String] $SanUpn,

        [Parameter()]
        [String] $SanUri,

        [Parameter()]
        [String] $SerialNumber,

        [Parameter()]
        [String] $SignatureAlgorithm,

        [Parameter()]
        [String] $Thumbprint,

        [Parameter()]
        [Alias('ValidFrom')]
        [DateTime] $IssueDate,

        [Parameter()]
        [Alias('ValidFromGreater')]
        [DateTime] $IssueDateAfter,

        [Parameter()]
        [Alias('ValidFromLess')]
        [DateTime] $IssueDateBefore,

        [Parameter()]
        [Alias('ValidTo')]
        [DateTime] $ExpireDate,

        [Parameter()]
        [Alias('ValidToGreater')]
        [DateTime] $ExpireAfter,

        [Parameter()]
        [Alias('ValidToLess')]
        [DateTime] $ExpireBefore,

        [Parameter()]
        [Switch] $Enabled,

        [Parameter()]
        [switch] $InError,

        [Parameter()]
        [switch] $IsSelfSigned,

        [Parameter()]
        [switch] $IsWildcard,

        [Parameter()]
        [bool] $NetworkValidationEnabled,

        [Parameter()]
        [Alias('CreatedOn')]
        [datetime] $CreatedDate,

        [Parameter()]
        [Alias('CreatedOnGreater')]
        [datetime] $CreatedAfter,

        [Parameter()]
        [Alias('CreatedOnLess')]
        [datetime] $CreatedBefore,

        [Parameter()]
        [ValidateSet('CodeSigning', 'Device', 'Server', 'User')]
        [String[]] $CertificateType,

        [Parameter()]
        [TppManagementType[]] $ManagementType,

        [Parameter()]
        [Switch] $PendingWorkflow,

        [Parameter()]
        [TppCertificateStage[]] $Stage,

        [Parameter()]
        [Alias('StageGreater')]
        [TppCertificateStage] $StageGreaterThan,

        [Parameter()]
        [Alias('StageLess')]
        [TppCertificateStage] $StageLessThan,

        [Parameter()]
        [Switch] $ValidationEnabled,

        [Parameter()]
        [ValidateSet('Blank', 'Success', 'Failure')]
        [String[]] $ValidationState,

        [Parameter()]
        [Switch] $CountOnly,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{

            Method       = 'Get'
            UriLeaf      = 'certificates/'
            Body         = @{
                Limit  = 1000
                Offset = 0
            }
            FullResponse = $true
        }

        if ($PSCmdlet.PagingParameters.First -ne [uint64]::MaxValue -and $PSCmdlet.PagingParameters.First -le 1000) {
            $params.Body.Limit = $PSCmdlet.PagingParameters.First
        }

        if ($PSCmdlet.PagingParameters.Skip) {
            $params.Body.Offset = $PSCmdlet.PagingParameters.Skip
        }

        if ( $CountOnly.IsPresent ) {
            $params.Method = 'Head'
        }

        switch ($PSBoundParameters.Keys) {
            'CreatedDate' {
                $params.Body.Add( 'CreatedOn', ($CreatedDate | ConvertTo-UtcIso8601) )
            }
            'CreatedBefore' {
                $params.Body.Add( 'CreatedOnLess', ($CreatedBefore | ConvertTo-UtcIso8601) )
            }
            'CreatedAfter' {
                $params.Body.Add( 'CreatedOnGreater', ($CreatedAfter | ConvertTo-UtcIso8601) )
            }
            'CertificateType' {
                $params.Body.Add( 'CertificateType', $CertificateType -join ',' )
            }
            'Country' {
                $params.Body.Add( 'C', $Country )
            }
            'CommonName' {
                $params.Body.Add( 'CN', $CommonName )
            }
            'Issuer' {
                $params.Body.Add( 'Issuer', '"{0}"' -f $Issuer )
            }
            'KeyAlgorithm' {
                $params.Body.Add( 'KeyAlgorithm', $KeyAlgorithm -join ',' )
            }
            'KeySize' {
                $params.Body.Add( 'KeySize', $KeySize -join ',' )
            }
            'KeySizeGreaterThan' {
                $params.Body.Add( 'KeySizeGreater', $KeySizeGreaterThan )
            }
            'KeySizeLessThan' {
                $params.Body.Add( 'KeySizeLess', $KeySizeLessThan )
            }
            'Locale' {
                $params.Body.Add( 'L', $Locale -join ',' )
            }
            'Organization' {
                $params.Body.Add( 'O', $Organization -join ',' )
            }
            'OrganizationUnit' {
                $params.Body.Add( 'OU', $OrganizationUnit -join ',' )
            }
            'State' {
                $params.Body.Add( 'S', $State -join ',' )
            }
            'SanDns' {
                $params.Body.Add( 'SAN-DNS', $SanDns )
            }
            'SanEmail' {
                $params.Body.Add( 'SAN-Email', $SanEmail )
            }
            'SanIP' {
                $params.Body.Add( 'SAN-IP', $SanIP )
            }
            'SanUpn' {
                $params.Body.Add( 'SAN-UPN', $SanUpn )
            }
            'SanUri' {
                $params.Body.Add( 'SAN-URI', $SanUri )
            }
            'SerialNumber' {
                $params.Body.Add( 'Serial', $SerialNumber )
            }
            'SignatureAlgorithm' {
                $params.Body.Add( 'SignatureAlgorithm', $SignatureAlgorithm -join ',' )
            }
            'Thumbprint' {
                $params.Body.Add( 'Thumbprint', $Thumbprint )
            }
            'IssueDateAfter' {
                $params.Body.Add( 'ValidFromGreater', ($IssueDateAfter | ConvertTo-UtcIso8601) )
            }
            'IssueDateBefore' {
                $params.Body.Add( 'ValidFromLess', ($IssueDateBefore | ConvertTo-UtcIso8601) )
            }
            'IssueDate' {
                $params.Body.Add( 'ValidFrom', ($IssueDate | ConvertTo-UtcIso8601) )
            }
            'ExpireDate' {
                $params.Body.Add( 'ValidTo', ($ExpireDate | ConvertTo-UtcIso8601) )
            }
            'ExpireAfter' {
                $params.Body.Add( 'ValidToGreater', ($ExpireAfter | ConvertTo-UtcIso8601) )
            }
            'ExpireBefore' {
                $params.Body.Add( 'ValidToLess', ($ExpireBefore | ConvertTo-UtcIso8601) )
            }
            'Enabled' {
                $params.Body.Add( 'Disabled', [int] (-not $Enabled) )
            }
            'InError' {
                $params.Body.Add( 'InError', [int]$InError.IsPresent )
            }
            'IsSelfSigned' {
                $params.Body.Add( 'IsSelfSigned', [int] $IsSelfSigned.IsPresent )
            }
            'IsWildcard' {
                $params.Body.Add( 'IsWildcard', [int] $IsWildcard.IsPresent )
            }
            'NetworkValidationEnabled' {
                $params.Body.Add( 'NetworkValidationDisabled', [int] (-not $NetworkValidationEnabled) )
            }
            'ManagementType' {
                $params.Body.Add( 'ManagementType', $ManagementType -join ',' )
            }
            'PendingWorkflow' {
                $params.Body.Add( 'PendingWorkflow', '')
            }
            'Stage' {
                $params.Body.Add( 'Stage', ($Stage | ForEach-Object { [TppCertificateStage]::$_.value__ }) -join ',' )
            }
            'StageGreaterThan' {
                $params.Body.Add( 'StageGreater', [TppCertificateStage]::$StageGreaterThan.value__ )
            }
            'StageLessThan' {
                $params.Body.Add( 'StageLess', [TppCertificateStage]::$StageLessThan.value__ )
            }
            'ValidationEnabled' {
                $params.Body.Add( 'ValidationDisabled', [int] (-not $ValidationEnabled) )
            }
            'ValidationState' {
                $params.Body.Add( 'ValidationState', $ValidationState -join ',' )
            }
            # }
        }
    }

    process {

        if ( $PSBoundParameters.ContainsKey('Path') ) {
            $thisPath = $Path | ConvertTo-VdcFullPath
        }
        elseif ( $PSBoundParameters.ContainsKey('Guid') ) {
            # guid provided, get path
            $thisPath = $Guid | ConvertTo-VdcPath
        }

        if ( $thisPath ) {
            if ( $Recursive.IsPresent ) {
                $params.Body.ParentDnRecursive = $thisPath
            }
            else {
                $params.Body.ParentDn = $thisPath
            }
        }

        $response = Invoke-VenafiRestMethod @params

        $totalRecordCount = 0
        if ($PSVersionTable.PSVersion.Major -lt 6) {
            $totalRecordCount = [int]$response.Headers.'X-Record-Count'
        }
        else {
            $totalRecordCount = [int]($response.Headers.'X-Record-Count'[0])
        }

        if ( $CountOnly ) {
            return $totalRecordCount
        }

        Write-Verbose "Total number of records for this query: $totalRecordCount"

        $content = $response.content | ConvertFrom-Json
        $content.Certificates.ForEach{
            ConvertTo-VdcObject -Path $_.DN -Guid $_.Guid -TypeName $_.SchemaClass
        }

        if ($PSBoundParameters.ContainsKey('First')) {
            $totalRecordCount = $PSCmdlet.PagingParameters.First
        }

        $setPoint = $params.Body.Offset + $params.Body.Limit

        while ($totalRecordCount -gt $setPoint) {

            # up the offset so we get the next set of records
            $params.Body.Offset += $params.Body.Limit
            $setPoint = $params.Body.Offset + $params.Body.Limit

            $end = if ( $totalRecordCount -lt $setPoint ) {
                $totalRecordCount
            }
            else {
                $setPoint
            }

            Write-Verbose ('getting {0}-{1} of {2}' -f ($params.Body.Offset + 1), $end, $totalRecordCount)
            try {
                $response = Invoke-VenafiRestMethod @params -Verbose:$false
            }
            catch {
                $ProgressPreference = $oldProgressPreference
                throw $_
            }

            $content = $response.content | ConvertFrom-Json
            $content.Certificates.ForEach{
                ConvertTo-VdcObject -Path $_.DN -Guid $_.Guid -TypeName $_.SchemaClass
            }
        }
    }
}
#EndRegion './Public/Find-VdcCertificate.ps1' 612
#Region './Public/Find-VdcClient.ps1' -1

function Find-VdcClient {
    <#
    .SYNOPSIS
    Get information about registered Server Agents or Agentless clients

    .DESCRIPTION
    Get information about registered Server Agent or Agentless clients.

    .PARAMETER ClientType
    The client type.
    Allowed values include VenafiAgent, AgentJuniorMachine, AgentJuniorUser, Portal, Agentless, PreEnrollment, iOS, Android

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    None

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Find-VdcClient
    Find all clients

    .EXAMPLE
    Find-VdcClient -ClientType Portal
    Find clients with the specific type

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Find-VdcClient/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Find-VdcClient.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-ClientDetails.php

    #>


    [CmdletBinding()]
    [Alias('Find-TppClient')]

    param (
        [Parameter()]
        [ValidateSet('VenafiAgent', 'AgentJuniorMachine', 'AgentJuniorUser', 'Portal', 'Agentless', 'PreEnrollment', 'iOS', 'Android')]
        [String] $ClientType,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{

            Method        = 'Get'
            UriLeaf       = 'Client/Details/'
            Body          = @{ }
        }

        if ( $ClientType ) {
            $params.Body.ClientType = $ClientType
        }

        # if no filters provided, get all
        if ( $params.Body.Count -eq 0 ) {
            $params.Body.LastSeenOnLess = (Get-Date) | ConvertTo-UtcIso8601
        }
    }

    process {
        Invoke-VenafiRestMethod @params
    }
}
#EndRegion './Public/Find-VdcClient.ps1' 80
#Region './Public/Find-VdcEngine.ps1' -1

function Find-VdcEngine {
    <#
    .SYNOPSIS
    Find TLSPDC engines using an optional pattern

    .DESCRIPTION
    Find TLSPDC engines using an optional pattern.
    This function is an engine wrapper for Find-VdcObject.

    .PARAMETER Pattern
    Filter against engine names using asterisk (*) and/or question mark (?) wildcard characters.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided, but this requires an environment variable VDC_SERVER to be set.

    .INPUTS
    Pattern

    .OUTPUTS
    TppObject

    .EXAMPLE
    Find-VdcEngine -Pattern '*partialname*'

    Get engines whose name matches the supplied pattern

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Find-VdcEngine/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Find-VdcEngine.ps1
    #>


    [CmdletBinding()]
    [Alias('Find-TppEngine')]

    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [String] $Pattern,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {
        $params = @{
            Class         = 'Venafi Platform'
            Path          = '\VED\Engines'
            Pattern       = $Pattern
        }

        Find-VdcObject @params
    }
}
#EndRegion './Public/Find-VdcEngine.ps1' 61
#Region './Public/Find-VdcIdentity.ps1' -1

function Find-VdcIdentity {
    <#
    .SYNOPSIS
    Search for identity details

    .DESCRIPTION
    Returns information about individual identity, group identity, or distribution groups from a local or non-local provider such as Active Directory.
    You can specify individual identity types to search for or all

    .PARAMETER Name
    The individual identity, group identity, or distribution group name to search for

    .PARAMETER First
    First how many items are returned, the default is 500, but is limited by the provider.

    .PARAMETER IncludeUsers
    Include user identity type in search

    .PARAMETER IncludeSecurityGroups
    Include security group identity type in search

    .PARAMETER IncludeDistributionGroups
    Include distribution group identity type in search

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Name

    .OUTPUTS
    PSCustomObject with the following properties:
        Name
        ID
        Path

    .EXAMPLE
    Find-VdcIdentity -Name 'greg' -IncludeUsers
    Find only user identities with the name greg

    .EXAMPLE
    'greg', 'brownstein' | Find-VdcIdentity
    Find all identity types with the name greg and brownstein

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Identity-Browse.php
    #>


    [CmdletBinding()]
    [Alias('Find-TppIdentity')]

    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [String[]] $Name,

        [Parameter()]
        [Alias('Limit')]
        [int] $First = 500,

        [Parameter(ParameterSetName = 'Find')]
        [Switch] $IncludeUsers,

        [Parameter(ParameterSetName = 'Find')]
        [Switch] $IncludeSecurityGroups,

        [Parameter(ParameterSetName = 'Find')]
        [Switch] $IncludeDistributionGroups,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $identityType = 0
        # determine settings to use
        if ( $PSBoundParameters.ContainsKey('IncludeUsers') ) {
            $identityType += [TppIdentityType]::User
        }
        if ( $PSBoundParameters.ContainsKey('IncludeSecurityGroups') ) {
            $identityType += [TppIdentityType]::SecurityGroups
        }
        if ( $PSBoundParameters.ContainsKey('IncludeDistributionGroups') ) {
            $identityType += [TppIdentityType]::DistributionGroups
        }

        # if no types to include were provided, include all
        if ( $identityType -eq 0 ) {
            $identityType = [TppIdentityType]::User + [TppIdentityType]::SecurityGroups + [TppIdentityType]::DistributionGroups
        }

        $params = @{
            Method  = 'Post'
            UriLeaf = 'Identity/Browse'
            Body    = @{
                Filter       = 'placeholder'
                Limit        = $First
                IdentityType = $identityType
            }
        }
    }

    process {

        $response = $Name.ForEach{
            $params.Body.Filter = $_
            Invoke-VenafiRestMethod @params
        }

        $ids = $response.Identities

        if ( $ids ) {
            $ids | ConvertTo-VdcIdentity
        }
    }
}
#EndRegion './Public/Find-VdcIdentity.ps1' 122
#Region './Public/Find-VdcObject.ps1' -1

function Find-VdcObject {
    <#
    .SYNOPSIS
    Find objects by path, class, or pattern

    .DESCRIPTION
    Find objects by path, class, or pattern.

    .PARAMETER Path
    The path to start our search. The default is \ved\policy.

    .PARAMETER Class
    1 or more classes/types to search for

    .PARAMETER Pattern
    Filter against object paths.
    If the Attribute parameter is provided, this will filter against an object's attribute/custom field values instead of the path.

    Follow the below rules:
    - To list DNs that include an asterisk or question mark, prepend the character with two backslashes.
    - To list DNs with a wildcard character, append a question mark (?). For example, "test_?.mycompany.net" counts test_1.MyCompany.net and test_2.MyCompany.net but not test12.MyCompany.net.
    - To list DNs with similar names, prepend an asterisk. For example, *est.MyCompany.net, counts Test.MyCompany.net and West.MyCompany.net.
    You can also use both literals and wildcards in a pattern.

    .PARAMETER Attribute
    A list of attribute names to limit the search against. Only valid when searching by pattern.
    A custom field name can also be provided.

    .PARAMETER Recursive
    Searches the subordinates of the object specified in Path.

    .PARAMETER NoLookup
    Default functionality when finding by Attribute is to perform a lookup to see if they are custom fields or not.
    If they are, pass along the guid instead of name required by the api for custom fields.
    To override this behavior and use the attribute name as is, add -NoLookup.
    Useful if on the off chance you have a custom field with the same name as a built-in attribute.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    TppObject

    .EXAMPLE
    Find-VdcObject
    Get all objects recursively starting from \ved\policy

    .EXAMPLE
    Find-VdcObject -Path '\VED\Policy\certificates'
    Get all objects in the root of a specific folder

    .EXAMPLE
    Find-VdcObject -Path '\VED\Policy\My Folder' -Recursive
    Get all objects in a folder and subfolders

    .EXAMPLE
    Find-VdcObject -Path '\VED\Policy' -Pattern '*test*'
    Get items in a specific folder filtering the path

    .EXAMPLE
    Find-VdcObject -Class 'capi' -Path '\ved\policy\installations' -Recursive
    Get objects of a specific type

    .EXAMPLE
    Find-VdcObject -Class 'capi' -Pattern '*test*' -Path '\ved\policy\installations' -Recursive
    Get all objects of a specific type where the path is of a specific pattern

    .EXAMPLE
    Find-VdcObject -Class 'capi', 'iis6' -Pattern '*test*' -Path '\ved\policy\installations' -Recursive
    Get objects for multiple types

    .EXAMPLE
    Find-VdcObject -Pattern '*f5*'
    Find objects with the specific name. All objects under \ved\policy (the default) will be searched.

    .EXAMPLE
    Find-VdcObject -Attribute 'Description' -Pattern 'awesome'
    Find objects where the specific attribute matches the pattern

    .EXAMPLE
    Find-VdcObject -Attribute 'Environment' -Pattern 'Development'

    Find objects where a custom field value matches the pattern.
    By default, the attribute will be checked against the current list of custom fields.

    .EXAMPLE
    Find-VdcObject -Attribute 'Description' -Pattern 'duplicate' -NoLookup

    Bypass custom field lookup and force Attribute to be treated as a built-in attribute.
    Useful if there are conflicting custom field and built-in attribute names and you want to force the lookup against built-in.

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Find-VdcObject/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Find-VdcObject.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-find.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-findobjectsofclass.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-enumerate.php

    #>


    [CmdletBinding(DefaultParameterSetName = 'FindByPath')]
    [Alias('fto', 'Find-TppObject')]

    param (
        [Parameter(ParameterSetName = 'FindByPath', ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'FindByClass', ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'FindByPattern', ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('DN')]
        [String] $Path = '\ved\policy',

        [Parameter(Mandatory, ParameterSetName = 'FindByPattern')]
        [Parameter(ParameterSetName = 'FindByClass')]
        [Parameter(Mandatory, ParameterSetName = 'FindByAttribute')]
        [ValidateNotNull()]
        [AllowEmptyString()]
        [String] $Pattern,

        [Parameter(Mandatory, ParameterSetName = 'FindByClass')]
        [ValidateNotNullOrEmpty()]
        [Alias('TypeName')]
        [String[]] $Class,

        [Parameter(Mandatory, ParameterSetName = 'FindByAttribute')]
        [ValidateNotNullOrEmpty()]
        [Alias('AttributeName')]
        [String[]] $Attribute,

        [Parameter(ParameterSetName = 'FindByPath')]
        [Parameter(ParameterSetName = 'FindByClass')]
        [Parameter(ParameterSetName = 'FindByPattern')]
        [Alias('r')]
        [switch] $Recursive,

        [Parameter(ParameterSetName = 'FindByAttribute')]
        [switch] $NoLookup,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        $params = @{
            Method = 'Post'
            Body   = @{
                'ObjectDN' = $Path | ConvertTo-VdcFullPath
            }
        }

        Switch ($PsCmdlet.ParameterSetName) {

            'FindByAttribute' {

                $customField = $VenafiSessionNested.CustomField | Where-Object { $_.Label -eq $Attribute[0] -or $_.Guid -eq $Attribute[0] }

                # if attribute isn't a custom field or user doesn't want to perform a cf lookup for a conflicting attrib/cf name, perform standard find object
                if ( $NoLookup -or (-not $customField) ) {

                    $params.UriLeaf = 'config/find'
                    $params.Body = @{
                        AttributeNames = $Attribute
                    }
                }
                else {
                    # cf found
                    $params.UriLeaf = 'Metadata/Find'
                    $params.Body = @{
                        ItemGuid = $customField.Guid
                        Value    = $Pattern
                    }
                }
            }

            { $_ -in 'FindByPath', 'FindByPattern', 'FindByClass' } {
                # if a path wasn't provided, default to recursive enumeration of \ved\policy
                if ( -not $PSBoundParameters.ContainsKey('Path') ) {
                    $params.Body.Recursive = 'true'
                }
            }

            { $_ -in 'FindByPath', 'FindByPattern' } {
                $params.UriLeaf = 'config/enumerate'
            }

            'FindByClass' {
                $params.UriLeaf = 'config/FindObjectsOfClass'
            }

        }

        # pattern is not used by custom field lookup
        if ( $PSBoundParameters.ContainsKey('Pattern') -and ($params.UriLeaf -ne 'Metadata/Find') ) {
            $params.Body.Pattern = $Pattern
        }

        if ( $PSBoundParameters.ContainsKey('Recursive') ) {
            $params.Body.Recursive = 'true'
        }

        if ( $PSBoundParameters.ContainsKey('Class') ) {
            # the rest api doesn't have the ability to search for multiple classes and path at the same time
            # loop through classes to get around this
            $params.Body.Class = ''
            $objects = $Class.ForEach{
                $thisClass = $_
                $params.Body.Class = $thisClass

                $response = Invoke-VenafiRestMethod @params

                if ( $response.Result -eq 1 ) {
                    $response.Objects
                }
                else {
                    Write-Error ('Retrieval of class {0} failed with error {1}' -f $thisClass, $response.Error)
                    Continue
                }
            }
        }
        else {
            $response = Invoke-VenafiRestMethod @params

            # success for cf lookup is 0, all others are config calls and success is 1
            if ( $response.Result -in 0, 1 ) {
                $objects = $response.Objects
            }
            else {
                Write-Error $response.Error
            }
        }

        foreach ($object in $objects) {
            ConvertTo-VdcObject -Path $object.DN -Guid $object.Guid -TypeName $object.TypeName
        }
    }
}
#EndRegion './Public/Find-VdcObject.ps1' 255
#Region './Public/Find-VdcVaultId.ps1' -1

function Find-VdcVaultId {
    <#
    .SYNOPSIS
    Find vault IDs in the secret store

    .DESCRIPTION
    Find vault IDs in the secret store by their attributes and associated values

    .PARAMETER Attribute
    Name and value to search.
    See https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Secretstore-lookupbyassociation.php for more details.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Attribute

    .OUTPUTS
    String

    .EXAMPLE
    Find-VdcVaultId -Attribute @{'Serial'='0812E11D213DE8E07890BCC1234567'}
    Find a vault id

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Find-VdcVaultId/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Find-VdcVaultId.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Secretstore-lookupbyassociation.php

    #>


    [CmdletBinding()]
    [Alias('Find-TppVaultId')]

    param (

        [Parameter(Mandatory, ValueFromPipeline)]
        [hashtable] $Attribute,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{

            Method        = 'Post'
            UriLeaf       = 'SecretStore/LookupByAssociation'
            Body          = @{}
        }
    }

    process {

        $thisKey = "$($Attribute.Keys[0])"
        $thisValue = "$($Attribute.Values[0])"

        switch ($thisKey) {

            { $_ -in 'Certificate Type', 'Key Size', 'Parent ID', 'Template Major Version' } {
                $type = 'IntValue'
            }

            { $_ -in 'Create Date', 'Revocation Check Date', 'Revocation Date', 'ValidFrom', 'ValidTo' } {
                $type = 'ValueDate'
            }

            Default {
                $type = 'StringValue'
            }
        }

        $params.Body = @{
            'Name' = $thisKey
            $type  = $thisValue
        }

        $response = Invoke-VenafiRestMethod @params

        if ( $response.Result -eq 0 ) {
            $response.VaultIDs
        }
        else {
            throw ('Secret store search failed with error code {0}' -f $response.Result)
        }
    }

    end {

    }
}
#EndRegion './Public/Find-VdcVaultId.ps1' 102
#Region './Public/Get-VcApplication.ps1' -1

function Get-VcApplication {
    <#
    .SYNOPSIS
    Get application info

    .DESCRIPTION
    Get 1 or more applications.

    .PARAMETER Application
    Application ID or name

    .PARAMETER All
    Get all applications

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Get-VcApplication -Application 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'

    applicationId : 96fc9310-67ec-11eb-a8a7-794fe75a8e6f
    certificateIssuingTemplate : @{Name=MyTemplate; id=7fb6af20-b22e-11ea-9a24-930fb5d2b247}
    companyId : 09b24f81-b22b-11ea-91f3-ebd6dea5452e
    name : myapp
    description :
    ownerIdsAndTypes : {@{ownerId=0a2adae0-b22b-11ea-91f3-ebd6dea5452f; ownerType=TEAM}}
    fullyQualifiedDomainNames : {}
    ipRanges : {}
    ports : {}
    modificationDate : 6/8/2023 11:06:43 AM
    creationDate : 2/5/2021 2:59:00 PM
    ownership : @{owningUsers=System.Object[]}

    Get a single object by ID

    .EXAMPLE
    Get-VcApplication -Application 'My Awesome App'

    Get a single object by name. The name is case sensitive.

    .EXAMPLE
    Get-VcApplication -All

    Get all applications

    #>


    [CmdletBinding(DefaultParameterSetName = 'ID')]
    [Alias('Get-VaasApplication')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('applicationId', 'ID')]
        [string] $Application,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        $params = @{
            UriRoot = 'outagedetection/v1'
            UriLeaf = 'applications'
        }

        if ( $PSBoundParameters.ContainsKey('Application') ) {
            if ( Test-IsGuid($Application) ) {
                $params.UriLeaf += "/{0}" -f $Application
            }
            else {
                # search by name
                $params.UriLeaf += "/name/$Application"
            }
        }

        try {
            $response = Invoke-VenafiRestMethod @params
        }
        catch {
            if ( $_.Exception.Response.StatusCode.value__ -eq 404 ) {
                # not found, return nothing
                return
            }
            else {
                throw $_
            }
        }

        if ( $response.PSObject.Properties.Name -contains 'applications' ) {
            $applications = $response | Select-Object -ExpandProperty applications
        }
        else {
            $applications = $response
        }

        if ( $applications ) {
            $applications | Select-Object @{'n' = 'applicationId'; 'e' = { $_.Id } },
            @{
                'n' = 'issuingTemplate'
                'e' = {
                    $_.certificateIssuingTemplateAliasIdMap.psobject.Properties | Select-Object @{'n' = 'name'; 'e' = { $_.Name } }, @{'n' = 'issuingTemplateId'; 'e' = { $_.Value } }
                }
            }, * -ExcludeProperty Id, certificateIssuingTemplateAliasIdMap
        }
    }
}
#EndRegion './Public/Get-VcApplication.ps1' 121
#Region './Public/Get-VcCertificate.ps1' -1

function Get-VcCertificate {
    <#
    .SYNOPSIS
    Get certificate information

    .DESCRIPTION
    Get certificate information, either all available to the api key provided or by id or zone.

    .PARAMETER ID
    Certificate identifier, the ID or certificate name.

    .PARAMETER All
    Retrieve all certificates

    .PARAMETER IncludeVaasOwner
    Retrieve extended application owner info

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also be provided.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Get-VdcCertificate -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'

    Get certificate info for a specific cert

    .EXAMPLE
    Get-VdcCertificate -All

    Get certificate info for all certs
    #>


    [CmdletBinding(DefaultParameterSetName = 'Id')]

    param (

        [Parameter(ParameterSetName = 'Id', Mandatory, ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('certificateId')]
        [string] $ID,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [Switch] $All,

        [Parameter()]
        [switch] $IncludeVaasOwner,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        $appOwners = [System.Collections.Generic.List[object]]::new()

    }

    process {

        if ( $All ) {
            return (Find-VcCertificate -IncludeVaasOwner:$IncludeVaasOwner)
        }

        $params = @{
            UriRoot = 'outagedetection/v1'
            UriLeaf = "certificates/"
        }

        if ( Test-IsGuid($ID) ) {
            $params.UriLeaf += $ID
        }
        else {
            $findParams = @{
                Filter           = @('certificateName', 'eq', $ID)
                IncludeVaasOwner = $IncludeVaasOwner
            }
            return (Find-VcCertificate @findParams | Get-VcCertificate)
        }

        $params.UriLeaf += "?ownershipTree=true"

        $response = Invoke-VenafiRestMethod @params

        if ( $response.PSObject.Properties.Name -contains 'certificates' ) {
            $certs = $response | Select-Object -ExpandProperty certificates
        }
        else {
            $certs = $response
        }

        $certs | Select-Object @{
            'n' = 'certificateId'
            'e' = {
                $_.Id
            }
        },
        @{
            'n' = 'application'
            'e' = {
                if ( $_.applicationIds ) {
                    $_.applicationIds | Get-VcApplication | Select-Object -Property * -ExcludeProperty ownerIdsAndTypes, ownership
                }
            }
        },
        @{
            'n' = 'owner'
            'e' = {
                if ( $IncludeVaasOwner ) {

                    # this scriptblock requires ?ownershipTree=true be part of the url
                    foreach ( $thisOwner in $_.ownership.owningContainers.owningUsers ) {
                        $thisOwnerDetail = $appOwners | Where-Object { $_.id -eq $thisOwner }
                        if ( -not $thisOwnerDetail ) {
                            $thisOwnerDetail = Get-VcIdentity -ID $thisOwner | Select-Object firstName, lastName, emailAddress,
                            @{
                                'n' = 'status'
                                'e' = { $_.userStatus }
                            },
                            @{
                                'n' = 'role'
                                'e' = { $_.systemRoles }
                            },
                            @{
                                'n' = 'type'
                                'e' = { 'USER' }
                            },
                            @{
                                'n' = 'userId'
                                'e' = { $_.id }
                            }

                            $appOwners.Add($thisOwnerDetail)

                        }
                        $thisOwnerDetail
                    }

                    foreach ($thisOwner in $_.ownership.owningContainers.owningTeams) {
                        $thisOwnerDetail = $appOwners | Where-Object { $_.id -eq $thisOwner }
                        if ( -not $thisOwnerDetail ) {
                            $thisOwnerDetail = Get-VcTeam -ID $thisOwner | Select-Object name, role, members,
                            @{
                                'n' = 'type'
                                'e' = { 'TEAM' }
                            },
                            @{
                                'n' = 'teamId'
                                'e' = { $_.id }
                            }

                            $appOwners.Add($thisOwnerDetail)
                        }
                        $thisOwnerDetail
                    }
                }
                else {
                    $_.ownership.owningContainers | Select-Object owningUsers, owningTeams
                }
            }
        },
        @{
            'n' = 'instance'
            'e' = { $_.instances }
        },
        * -ExcludeProperty Id, applicationIds, instances, totalInstanceCount, ownership
    }
}
#EndRegion './Public/Get-VcCertificate.ps1' 176
#Region './Public/Get-VcConnector.ps1' -1

function Get-VcConnector {
    <#
    .SYNOPSIS
    Get connector info

    .DESCRIPTION
    Get details on 1 or all connectors associated with your tenant

    .PARAMETER Connector
    Connector ID or name

    .PARAMETER All
    Get all connectors

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    Connector

    .EXAMPLE
    Get-VcConnector -Connector 'My Connector'

    Get a single object by name. The name is case sensitive.

    .EXAMPLE
    Get-VcConnector -All

    Get all connectors

    #>


    [CmdletBinding(DefaultParameterSetName = 'ID')]
    [Alias('Get-VaasConnector')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('connectorId', 'ID')]
        [string] $Connector,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        $params = @{
            UriLeaf = 'plugins'
        }

        if ( $PSBoundParameters.ContainsKey('Connector') ) {
            if ( Test-IsGuid($Connector) ) {
                $params.UriLeaf += "/{0}" -f $Connector
            }
            else {
                # search by name
                return Get-VcConnector -All | Where-Object { $_.name -eq $Connector }
            }
        }
        else {
            # getting all by default excludes disabled connectors so let's include them
            $params.Body = @{'includeDisabled' = $true }
        }

        $response = Invoke-VenafiRestMethod @params

        if ( $response.PSObject.Properties.Name -contains 'plugins' ) {
            $connectors = $response | Select-Object -ExpandProperty 'plugins'
        }
        else {
            $connectors = $response
        }

        if ( $connectors ) {
            $connectors | Select-Object @{ 'n' = 'connectorId'; 'e' = { $_.Id } }, @{ 'n' = 'connectorType'; 'e' = { $_.pluginType } }, * -ExcludeProperty Id, pluginType
        }
    }
}
#EndRegion './Public/Get-VcConnector.ps1' 89
#Region './Public/Get-VcIssuingTemplate.ps1' -1

function Get-VcIssuingTemplate {
    <#
    .SYNOPSIS
    Get issuing template info

    .DESCRIPTION
    Get 1 or more issuing template details

    .PARAMETER IssuingTemplate
    Issuing template ID or name

    .PARAMETER All
    Get all issuing templates

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Get-VcIssuingTemplate -IssuingTemplate 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'

    issuingTemplateId : 0a19eaf2-b22b-11ea-a1eb-a37c69eabd4e
    companyId : 09b24f81-b22b-11ea-91f3-ebd6dea5452f
    certificateAuthority : BUILTIN
    name : Default
    certificateAuthorityAccountId : 0a19eaf0-b22b-11ea-a1eb-a37c69eabd4e
    certificateAuthorityProductOptionId : 0a19eaf1-b22b-11ea-a1eb-a37c69eabd4e
    product : @{certificateAuthority=BUILTIN; productName=Default Product; productTypes=System.Object[]; validityPeriod=P90D}
    priority : 0
    systemGenerated : True
    creationDate : 6/19/2020 8:47:30 AM
    modificationDate : 6/19/2020 8:47:30 AM
    status : AVAILABLE
    reason :
    referencingApplicationIds : {995d1fb0-67e9-11eb-a8a7-794fe75a8e6f}
    subjectCNRegexes : {.*}
    subjectORegexes : {.*}
    subjectOURegexes : {.*}
    subjectSTRegexes : {.*}
    subjectLRegexes : {.*}
    subjectCValues : {.*}
    sanRegexes : {.*}
    sanDnsNameRegexes : {.*}
    keyTypes : {@{keyType=RSA; keyLengths=System.Object[]}}
    keyReuse : False
    extendedKeyUsageValues : {}
    csrUploadAllowed : True
    keyGeneratedByVenafiAllowed : False
    resourceConsumerUserIds : {}
    resourceConsumerTeamIds : {}
    everyoneIsConsumer : True

    Get a single object by ID

    .EXAMPLE
    Get-VcIssuingTemplate -IssuingTemplate 'MyTemplate'

    Get a single object by name. The name is case sensitive.

    .EXAMPLE
    Get-VcIssuingTemplate -All

    Get all issuing templates

    #>


    [CmdletBinding(DefaultParameterSetName='ID')]
    [Alias('Get-VaasIssuingTemplate')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('issuingTemplateId', 'ID')]
        [string] $IssuingTemplate,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        $params = @{
            UriLeaf = 'certificateissuingtemplates'
        }

        if ( $PSBoundParameters.ContainsKey('IssuingTemplate') ) {
            if ( Test-IsGuid($IssuingTemplate) ) {
                $params.UriLeaf += "/{0}" -f $IssuingTemplate
            }
            else {
                # search by name
                return Get-VcIssuingTemplate -All | Where-Object { $_.name -eq $IssuingTemplate }
            }
        }

        try {
            $response = Invoke-VenafiRestMethod @params
        }
        catch {
            if ( $_.Exception.Response.StatusCode.value__ -eq 404 ) {
                # not found, return nothing
                return
            }
            else {
                throw $_
            }
        }

        if ( $response.PSObject.Properties.Name -contains 'certificateissuingtemplates' ) {
            $templates = $response | Select-Object -ExpandProperty certificateissuingtemplates
        }
        else {
            $templates = $response
        }

        if ( $templates ) {
            $templates | Select-Object -Property @{'n' = 'issuingTemplateId'; 'e' = { $_.id } }, * -ExcludeProperty id
        }
    }
}
#EndRegion './Public/Get-VcIssuingTemplate.ps1' 132
#Region './Public/Get-VcMachine.ps1' -1

function Get-VcMachine {
    <#
    .SYNOPSIS
    Get machine details

    .DESCRIPTION
    Get machine details for 1 or all.

    .PARAMETER Machine
    Machine ID or name

    .PARAMETER All
    Get all machines

    .PARAMETER IncludeConnectionDetail
    Getting all machines does not include connection details.
    Use -IncludeConnectionDetail to add this to the output, but note it will require an additional API call for each machine and can take some time.
    Execute with PowerShell v7+ to run in parallel and speed things up.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    Machine

    .EXAMPLE
    Get-VcMachine -Machine 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'

    machineId : cf7cfdc0-2b2a-11ee-9546-5136c4b21504
    companyId : cf7cfdc0-2b2a-11ee-9546-5136c4b21504
    machineTypeId : fc569b60-cf24-11ed-bdc6-77a4bac4cb50
    pluginId : ff645e14-bd1a-11ed-a009-ce063932f86d
    integrationId : cf7c8014-2b2a-11ee-9a03-fa8930555887
    machineName : MyCitrix
    status : VERIFIED
    machineType : Citrix ADC
    creationDate : 7/25/2023 4:35:36 PM
    modificationDate : 7/25/2023 4:35:36 PM
    machineIdentitiesCount : 0
    owningTeam : 59920180-a3e2-11ec-8dcd-3fcbf84c7db1
    ownership : @{owningTeams=System.Object[]}
    connectionDetails : @{hostnameOrAddress=1.2.3.4; password=uYroVBk/KtuuujEbfFC/06wtkIrOga7N96JdFSEQFhhn7KPUEWA=;
                             username=ZLQlnciWsVp+qIUJQ8nYcAuHh55FxKdFsWhHVp7LLU+0y8aDp1pw==}

    Get a single machine by ID

    .EXAMPLE
    Get-VcMachine -Machine 'MyCitrix'

    Get a single machine by name. The name is case sensitive.

    .EXAMPLE
    Get-VcMachine -All

    machineId : cf7cfdc0-2b2a-11ee-9546-5136c4b21504
    companyId : cf7cfdc0-2b2a-11ee-9546-5136c4b21504
    machineTypeId : fc569b60-cf24-11ed-bdc6-77a4bac4cb50
    pluginId : ff645e14-bd1a-11ed-a009-ce063932f86d
    integrationId : cf7c8014-2b2a-11ee-9a03-fa8930555887
    machineName : MyCitrix
    status : VERIFIED
    machineType : Citrix ADC
    creationDate : 7/25/2023 4:35:36 PM
    modificationDate : 7/25/2023 4:35:36 PM
    machineIdentitiesCount : 0
    owningTeam : 59920180-a3e2-11ec-8dcd-3fcbf84c7db1
    ownership : @{owningTeams=System.Object[]}

    Get all machines. Note the connection details are not included by default with -All.
    See -IncludeConnectionDetails if this is needed.

    .EXAMPLE
    Get-VcMachine -All -IncludeConnectionDetails

    machineId : cf7cfdc0-2b2a-11ee-9546-5136c4b21504
    companyId : cf7cfdc0-2b2a-11ee-9546-5136c4b21504
    machineTypeId : fc569b60-cf24-11ed-bdc6-77a4bac4cb50
    pluginId : ff645e14-bd1a-11ed-a009-ce063932f86d
    integrationId : cf7c8014-2b2a-11ee-9a03-fa8930555887
    machineName : MyCitrix
    status : VERIFIED
    machineType : Citrix ADC
    creationDate : 7/25/2023 4:35:36 PM
    modificationDate : 7/25/2023 4:35:36 PM
    machineIdentitiesCount : 0
    owningTeam : 59920180-a3e2-11ec-8dcd-3fcbf84c7db1
    ownership : @{owningTeams=System.Object[]}
    connectionDetails : @{hostnameOrAddress=1.2.3.4; password=uYroVBk/KtuuujEbfFC/06wtkIrOga7N96JdFSEQFhhn7KPUEWA=;
                             username=ZLQlnciWsVp+qIUJQ8nYcAuHh55FxKdFsWhHVp7LLU+0y8aDp1pw==}

    Get all machines and include the connection details.
    Getting connection details will require an additional API call for each machine and can take some time.
    Use PowerShell v7+ to perform this in parallel and speed things up.

    #>


    [CmdletBinding(DefaultParameterSetName = 'ID')]
    [Alias('Get-VaasMachine')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('machineId', 'ID')]
        [string] $Machine,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter(ParameterSetName = 'All')]
        [switch] $IncludeConnectionDetail,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        if ( $PSCmdlet.ParameterSetName -eq 'All' ) {

            $allMachines = Find-VcObject -Type Machine

            if ( $IncludeConnectionDetail ) {
                $params = @{
                    InputObject = $allMachines
                    ScriptBlock = { $PSItem | Get-VcMachine }
                }
                return Invoke-VenafiParallel @params
            }
            else {
                return $allMachines
            }
        }
        else {
            if ( Test-IsGuid($Machine) ) {
                try {
                    $response = Invoke-VenafiRestMethod -UriLeaf ('machines/{0}' -f $Machine)
                    $response | Select-Object @{ 'n' = 'machineId'; 'e' = { $_.Id } }, * -ExcludeProperty Id
                }
                catch {
                    if ( $_.Exception.Response.StatusCode.value__ -eq 404 ) {
                        # not found, return nothing
                        return
                    }
                    else {
                        throw $_
                    }
                }
            }
            else {
                # no lookup by name directly. search for it and then get details
                Find-VcObject -Type 'Machine' -Name $Machine | Get-VcMachine
            }
        }
    }
}
#EndRegion './Public/Get-VcMachine.ps1' 162
#Region './Public/Get-VcMachineIdentity.ps1' -1

function Get-VcMachineIdentity {
    <#
    .SYNOPSIS
    Get machine identities

    .DESCRIPTION
    Get 1 or all machine identities

    .PARAMETER ID
    Machine identity ID

    .PARAMETER All
    Get all machine identities

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Get-VcMachineIdentity -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'

    machineIdentityId : cc57e830-1a90-11ee-abe7-bda0c823b1ad
    companyId : cc57e830-1a90-11ee-abe7-bda0c823b1ad
    machineId : 5995ecf0-19ca-11ee-9386-3ba941243b67
    certificateId : cc535450-1a90-11ee-8774-3d248c9b48c5
    status : DISCOVERED
    creationDate : 7/4/2023 1:32:50 PM
    lastSeenOn : 7/4/2023 1:32:50 PM
    modificationDate : 7/4/2023 1:32:50 PM
    keystore : @{friendlyName=1.test.net; keystoreCapiStore=my; privateKeyIsExportable=False}
    binding : @{createBinding=False; port=40112; siteName=domain.io}

    Get a single machine identity by ID

    .EXAMPLE
    Get-VcMachineIdentity -All

    Get all machine identities

    #>


    [CmdletBinding(DefaultParameterSetName = 'ID')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('machineIdentityId')]
        [ValidateScript(
            {
                if ( Test-IsGuid($_) ) { $true } else { throw '-ID must be a uuid/guid' }
            }
        )]
        [string] $ID,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        if ( $PSCmdlet.ParameterSetName -eq 'All' ) {

            $params = @{
                InputObject = Find-VcObject -Type MachineIdentity
                ScriptBlock = {
                    $PSItem | Get-VcMachineIdentity
                }
            }
            Invoke-VenafiParallel @params
        }
        else {
            try {
                $response = Invoke-VenafiRestMethod -UriLeaf ('machineidentities/{0}' -f $ID)
            }
            catch {
                if ( $_.Exception.Response.StatusCode.value__ -eq 404 ) {
                    # not found, return nothing
                    return
                }
                else {
                    throw $_
                }
            }

            if ( $response ) {
                $response | Select-Object @{ 'n' = 'machineIdentityId'; 'e' = { $_.Id } },
                @{
                    'n'='certificateValidityEnd'
                    'e'={ Get-VcCertificate -CertificateID $_.certificateId | Select-Object -ExpandProperty validityEnd }
                }, * -ExcludeProperty Id
            }
        }
    }
}
#EndRegion './Public/Get-VcMachineIdentity.ps1' 106
#Region './Public/Get-VcSatellite.ps1' -1

function Get-VcSatellite {
    <#
    .SYNOPSIS
    Get VSatellite info

    .DESCRIPTION
    Get 1 or more VSatellites. Encyption key and algorithm will be included.

    .PARAMETER VSatellite
    VSatellite ID or name

    .PARAMETER All
    Get all VSatellites

    .PARAMETER IncludeWorkers
    Include VSatellite workers in the output

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Get-VcSatellite -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'

    vsatelliteId : e2d60b61-9a8c-4a3a-985c-92498bd1fc77
    encryptionKey : o4aFaJUTtCydprvb1jN15hIa5vJqFpQ1ZiY=
    encryptionKeyAlgorithm : ED25519
    companyId : e2d60b61-9a8c-4a3a-985c-92498bd1fc77
    productEntitlements : {ANY}
    environmentId : ea2c3f80-658b-11eb-9bbd-338917ba2e36
    pairingCodeId : e2d60b61-9a8c-4a3a-985c-92498bd1fc77
    name : VSatellite Hub 0001
    edgeType : HUB
    edgeStatus : ACTIVE
    clientId : e2d60b61-9a8c-4a3a-985c-92498bd1fc77
    modificationDate : 6/15/2023 11:48:40 AM
    address : 1.2.3.4
    deploymentDate : 6/15/2023 11:44:14 AM
    lastSeenOnDate : 8/13/2023 8:00:06 AM
    reconciliationFailed : False
    encryptionKeyId : mwU4oTesrjyGBln0pZ8FkRfhek0UtvighIw=
    encryptionKeyDeploymentDate : 6/15/2023 11:48:40 AM
    kubernetesVersion : v1.23.6+k3s1
    integrationServicesCount : 0

    Get a single object by ID

    .EXAMPLE
    Get-VcSatellite -ID 'My Awesome App'

    Get a single object by name. The name is case sensitive.

    .EXAMPLE
    Get-VcSatellite -All

    Get all VSatellites

    .EXAMPLE
    Get-VcSatellite -All -IncludeWorkers

    Get all VSatellites and include workers
    #>


    [CmdletBinding()]
    [Alias('Get-VaasSatellite')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName)]
        [Alias('vsatelliteId', 'ID')]
        [string] $VSatellite,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [switch] $IncludeWorkers,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
        $allKeys = Invoke-VenafiRestMethod -UriLeaf 'edgeencryptionkeys' | Select-Object -ExpandProperty encryptionKeys
    }

    process {
        if ( $PSCmdlet.ParameterSetName -eq 'All' ) {
            $response = Invoke-VenafiRestMethod -UriLeaf 'edgeinstances' | Select-Object -ExpandProperty edgeinstances
        }
        else {
            # if the value is a guid, we can look up the vsat directly otherwise get all and search by name
            if ( Test-IsGuid($VSatellite) ) {
                $guid = [guid] $VSatellite
                $response = Invoke-VenafiRestMethod -UriLeaf ('edgeinstances/{0}' -f $guid.ToString())
            }
            else {
                $response = Invoke-VenafiRestMethod -UriLeaf 'edgeinstances' | Select-Object -ExpandProperty edgeinstances | Where-Object { $_.name -eq $VSatellite }
            }
        }

        if ( -not $response ) { return }

        $out = $response | Select-Object @{'n' = 'vsatelliteId'; 'e' = { $_.Id } },
        @{
            'n' = 'encryptionKey'
            'e' = {
                $thisId = $_.encryptionKeyId
                                ($allKeys | Where-Object { $_.id -eq $thisId }).key
            }
        },
        @{
            'n' = 'encryptionKeyAlgorithm'
            'e' = {
                $thisId = $_.encryptionKeyId
                                ($allKeys | Where-Object { $_.id -eq $thisId }).KeyAlgorithm
            }
        }, * -ExcludeProperty Id

        if ( $IncludeWorkers ) {
            $out | Select-Object *, @{
                'n' = 'workers'
                'e' = {
                    Get-VcSatelliteWorker -VSatellite $_.vsatelliteId
                }
            }
        }
        else {
            $out
        }
    }
}
#EndRegion './Public/Get-VcSatellite.ps1' 138
#Region './Public/Get-VcSatelliteWorker.ps1' -1

function Get-VcSatelliteWorker {
    <#
    .SYNOPSIS
    Get VSatellite worker info

    .DESCRIPTION
    Get 1 or more VSatellite workers, the bridge between a vsatellite and ADCS

    .PARAMETER ID
    VSatellite worker ID

    .PARAMETER All
    Get all VSatellite workers

    .PARAMETER VSatellite
    Get workers associated with a specific VSatellite, specify either VSatellite ID or name

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID, VSatelliteID

    .EXAMPLE
    Get-VcSatelliteWorker -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'

    vsatelliteWorkerId : 5df78790-a155-11ef-a5a8-8f3513444123
    companyId : 09b24f81-b22b-11ea-91f3-123456789098
    host : 1.2.3.4
    port : 555
    pairingCode : a138fe58-ecb6-45a4-a9af-01dd4d5c74d1
    pairingPublicKey : FDww6Nml8IUFQZ56j9LRweEWoCQ1732wi/ZfZaQj+s0=
    status : DRAFT

    Get a single worker by ID

    .EXAMPLE
    Get-VcSatelliteWorker -All

    Get all VSatellite workers

    .EXAMPLE
    Get-VcSatelliteWorker -VSatellite 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f3'

    Get all workers associated with a specific VSatellite

    #>


    [CmdletBinding()]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName)]
        [Alias('vsatelliteWorkerId')]
        [guid] $ID,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter(Mandatory, ParameterSetName = 'VSatellite', ValueFromPipelineByPropertyName)]
        [Alias('vsatelliteId')]
        [string] $VSatellite,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        switch ($PSCmdlet.ParameterSetName) {
            'All' {
                $response = Invoke-VenafiRestMethod -UriLeaf 'edgeworkers' | Select-Object -ExpandProperty edgeWorkers
            }

            'ID' {
                $guid = [guid] $ID
                $response = Invoke-VenafiRestMethod -UriLeaf ('edgeworkers/{0}' -f $guid.ToString())
            }

            'VSatellite' {
                # limit workers retrieved by the vsat they are associated with
                
                # if the value is a guid, we can look up the vsat directly otherwise get all and search by name
                $vsatelliteId = if ( Test-IsGuid($VSatellite) ) {
                    $guid = [guid] $VSatellite
                    $guid.ToString()
                }
                else {
                    Invoke-VenafiRestMethod -UriLeaf 'edgeinstances' | Select-Object -ExpandProperty edgeInstances | Where-Object { $_.name -eq $VSatellite } | Select-Object -ExpandProperty id
                }

                $response = Invoke-VenafiRestMethod -UriLeaf 'edgeworkers' -Body @{'edgeInstanceId' = $vsatelliteID } | Select-Object -ExpandProperty edgeWorkers
            }
        }

        if ( -not $response ) { return }

        $response | Select-Object `
        @{'n' = 'vsatelliteWorkerId'; 'e' = { $_.Id } },
        @{'n' = 'vsatelliteId'; 'e' = { $_.edgeInstanceId } }, * -ExcludeProperty Id, edgeInstanceId
    }
}
#EndRegion './Public/Get-VcSatelliteWorker.ps1' 109
#Region './Public/Get-VcTag.ps1' -1

function Get-VcTag {
    <#
    .SYNOPSIS
    Get tags from TLSPC

    .DESCRIPTION
    Get 1 or all tags.
    Tag values will be provided.

    .PARAMETER Name
    Tag name

    .PARAMETER All
    Get all tags

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.key can also provided.

    .INPUTS
    Name

    .EXAMPLE
    Get-VcTag -Name 'MyTag'

    Get a single tag

    .EXAMPLE
    Get-VcTag -All

    Get all tags

    #>


    [CmdletBinding()]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName, Position = 0)]
        [string] $Name,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        if ( $PSCmdlet.ParameterSetName -eq 'All' ) {
            $values = Invoke-VenafiRestMethod -UriLeaf 'tags/values' | Select-Object -ExpandProperty values
            $response = Invoke-VenafiRestMethod -UriLeaf 'tags' | Select-Object -ExpandProperty tags
        }
        else {
            $response = Invoke-VenafiRestMethod -UriLeaf "tags/$Name"
            $values = Invoke-VenafiRestMethod -UriLeaf "tags/$Name/values" | Select-Object -ExpandProperty values
        }

        if ( $response ) {
            $response | Select-Object @{'n' = 'tagId'; 'e' = { $_.key } },
            @{
                'n' = 'value'
                'e' = {
                    $thisId = $_.id
                    , @(($values | Where-Object { $_.tagId -eq $thisId }).value)
                }
            }, * -ExcludeProperty id, name
        }
    }
}
#EndRegion './Public/Get-VcTag.ps1' 77
#Region './Public/Get-VcTeam.ps1' -1

function Get-VcTeam {
    <#
    .SYNOPSIS
    Get team info

    .DESCRIPTION
    Get info on teams including members and owners.
    Retrieve info on 1 or all.

    .PARAMETER Team
    Team name or guid.

    .PARAMETER All
    Get all teams

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Get-VcTeam -Team 'MyTeam'

    Get info for a team by name

    .EXAMPLE
    Get-VcTeam -Team 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'

    Get info for a team by id

    .EXAMPLE
    Get-VcTeam -All

    Get info for all teams

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html?urls.primaryName=account-service#/Teams/get_2

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html?urls.primaryName=account-service#/Teams/get_1
    #>


    [CmdletBinding()]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('teamID', 'owningTeam', 'owningTeams', 'owningTeamId', 'ownedTeams', 'ID')]
        [string[]] $Team,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [Alias('Key', 'AccessToken')]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        if ( $PSCmdlet.ParameterSetName -eq 'All' ) {
            $response = Invoke-VenafiRestMethod -UriLeaf 'teams'
        }
        else {

            if ( Test-IsGuid -InputObject $Team ) {
                $guid = [guid] $Team
                $response = Invoke-VenafiRestMethod -UriLeaf ('teams/{0}' -f $guid.ToString())
            }
            else {
                # assume team name
                $response = Invoke-VenafiRestMethod -UriLeaf 'teams' | Where-Object { $_.name -eq $Team }
            }
        }

        if ( $response.PSObject.Properties.Name -contains 'teams' ) {
            $response | Select-Object -ExpandProperty teams | ConvertTo-VcTeam
        }
        else {
            $response | ConvertTo-VcTeam
        }
    }
}
#EndRegion './Public/Get-VcTeam.ps1' 94
#Region './Public/Get-VcUser.ps1' -1

function Get-VcUser {
    <#
    .SYNOPSIS
    Get user details

    .DESCRIPTION
    Returns user information for TLSPC.

    .PARAMETER User
    Either be the user id (guid) or username which is the email address.

    .PARAMETER Me
    Returns details of the authenticated/current user

    .PARAMETER All
    Return a complete list of local users.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject
        username
        userId
        companyId
        firstname
        lastname
        emailAddress
        userType
        userAccountType
        userStatus
        systemRoles
        productRoles
        localLoginDisabled
        hasPassword
        firstLoginDate
        creationDate
        ownedTeams
        memberedTeams

    .EXAMPLE
    Get-VcUser -ID 9e9db8d6-234a-409c-8299-e3b81ce2f916

    Get user details from an id

    .EXAMPLE
    Get-VcUser -ID 'greg.brownstein@venafi.com'

    Get user details from a username

    .EXAMPLE
    Get-VcUser -Me

    Get user details for authenticated/current user

    .EXAMPLE
    Get-VcUser -All

    Get all users

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html?urls.primaryName=account-service#/Users/users_getByUsername
    #>


    [CmdletBinding(DefaultParameterSetName = 'Id')]
    [Alias('Get-VcIdentity')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = "Parameter is used")]

    param (
        [Parameter(Mandatory, ParameterSetName = 'Id', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('userId', 'owningUser', 'owningUsers', 'owningUserId', 'ID')]
        [String] $User,

        [Parameter(Mandatory, ParameterSetName = 'Me')]
        [Switch] $Me,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [Switch] $All,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        Switch ($PsCmdlet.ParameterSetName)    {
            'Id' {
                # can search by user id (guid) or username
                if ( Test-IsGuid($User) ) {
                    $guid = [guid] $User
                    $response = Invoke-VenafiRestMethod -UriLeaf ('users/{0}' -f $guid.ToString())
                }
                else {
                    $response = Invoke-VenafiRestMethod -UriLeaf "users/username/$User" | Select-Object -ExpandProperty users
                }
            }

            'Me' {
                $response = Invoke-VenafiRestMethod -UriLeaf 'useraccounts' | Select-Object -ExpandProperty user
            }

            'All' {
                $response = Invoke-VenafiRestMethod -UriLeaf 'users' | Select-Object -ExpandProperty users
            }
        }

        $response | Select-Object @{'n' = 'userId'; 'e' = { $_.id } }, * -ExcludeProperty id
    }
}
#EndRegion './Public/Get-VcUser.ps1' 120
#Region './Public/Get-VcWebhook.ps1' -1

function Get-VcWebhook {
    <#
    .SYNOPSIS
    Get webhook info

    .DESCRIPTION
    Get 1 or all webhooks

    .PARAMETER ID
    Webhook ID or name

    .PARAMETER All
    Get all webhooks

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Get-VcWebhook -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' | ConvertTo-Json

    {
        "webhookId": "a7ddd210-0a39-11ee-8763-134b935c90aa",
        "name": "ServiceNow-expiry,
        "properties": {
            "connectorKind": "WEBHOOK",
            "filter": {
                "filterType": "EXPIRATION",
                "applicationIds": []
            },
            "target": {
                "type": "generic",
                "connection": {
                    "secret": "MySecret",
                    "url": "https://instance.service-now.com/api/company/endpoint"
                }
            }
        }
    }

    Get a single object by ID

    .EXAMPLE
    Get-VcWebhook -ID 'My Webhook'

    Get a single object by name. The name is case sensitive.

    .EXAMPLE
    Get-VcWebhook -All

    Get all webhooks

    #>


    [CmdletBinding(DefaultParameterSetName = 'ID')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('webhookId')]
        [string] $ID,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        $params = @{
            UriLeaf = 'connectors'
        }

        if ( $PSBoundParameters.ContainsKey('ID') ) {
            if ( Test-IsGuid($ID) ) {
                $params.UriLeaf += "/{0}" -f $ID
            }
            else {
                # search by name
                return Get-VcWebhook -All | Where-Object { $_.name -eq $ID }
            }
        }

        $response = Invoke-VenafiRestMethod @params

        if ( $response.PSObject.Properties.Name -contains 'connectors' ) {
            $connectors = $response | Select-Object -ExpandProperty 'connectors'
        }
        else {
            $connectors = $response
        }

        if ( $connectors ) {
            $connectors | Select-Object @{ 'n' = 'webhookId'; 'e' = { $_.Id } }, * -ExcludeProperty Id
        }
    }
}
#EndRegion './Public/Get-VcWebhook.ps1' 108
#Region './Public/Get-VdcAttribute.ps1' -1

function Get-VdcAttribute {
    <#
    .SYNOPSIS
    Get object attributes as well as policy attributes

    .DESCRIPTION
    Retrieves object attributes as well as policy attributes.
    You can either retrieve all attributes or individual ones.
    Policy folders can have attributes as well as policies which apply to the resultant objects.
    For more info on policies and how they are different than attributes, see https://docs.venafi.com/Docs/current/TopNav/Content/Policies/c_policies_tpp.php.

    Attribute properties are directly added to the return object for ease of access.
    To retrieve attribute configuration, see the Attribute property of the return object which has properties
    Name, PolicyPath, Locked, Value, Overridden (when applicable), and CustomFieldGuid (when applicable).

    .PARAMETER Path
    Path to the object. If the root is excluded, \ved\policy will be prepended.

    .PARAMETER Attribute
    Only retrieve the value/values for this attribute.
    For custom fields, you can provide either the Guid or Label.

    .PARAMETER Class
    Get policy attributes instead of object attributes.
    Provide the class name to retrieve the value(s) for.
    If unsure of the class name, add the value through the TLSPDC UI and go to Support->Policy Attributes to find it.
    The Attribute property of the return object will contain the path where the policy was applied.

    .PARAMETER All
    Get all object attributes or policy attributes.
    This will perform 3 steps, get the object type, enumerate the attributes for the object type, and get all the values.
    Note, expect this to take longer than usual given the number of api calls.

    .PARAMETER NoLookup
    Default functionality is to perform lookup of attributes names to see if they are custom fields or not.
    If they are, pass along the guid instead of name required by the api for custom fields.
    To override this behavior and use the attribute name as is, add -NoLookup.
    Useful if, on the off chance, you have a custom field with the same name as a built-in attribute.
    Can also be used with -All and the output will contain guids instead of looked up names.

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can be provided directly.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\certificates\test.gdb.com' -Attribute 'State'

    Name : test.gdb.com
    Path : \VED\Policy\Certificates\test.gdb.com
    TypeName : X509 Server Certificate
    Guid : b7a7221b-e038-41d9-9d49-d7f45c1ca128
    Attribute : {@{Name=State; PolicyPath=\VED\Policy\Certificates; Locked=False; Value=UT; Overridden=False}}
    State : UT

    Retrieve a single attribute

    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\certificates\test.gdb.com' -Attribute 'State', 'Driver Name'

    Name : test.gdb.com
    Path : \VED\Policy\Certificates\test.gdb.com
    TypeName : X509 Server Certificate
    Guid : b7a7221b-e038-41d9-9d49-d7f45c1ca128
    Attribute : {@{Name=State; PolicyPath=\VED\Policy\Certificates; Locked=False; Value=UT; Overridden=False}, @{Name=Driver
                Name; PolicyPath=; Locked=False; Value=appx509certificate; Overridden=False}}
    State : UT
    Driver Name : appx509certificate

    Retrieve multiple attributes

    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\certificates\test.gdb.com' -Attribute 'ServiceNow Assignment Group'

    Name : test.gdb.com
    Path : \VED\Policy\Certificates\test.gdb.com
    TypeName : X509 Server Certificate
    Guid : b7a7221b-e038-41d9-9d49-d7f45c1ca128
    Attribute : {@{CustomFieldGuid={7f214dec-9878-495f-a96c-57291f0d42da}; Name=ServiceNow Assignment Group;
                                PolicyPath=; Locked=False; Value=Venafi Management; Overridden=False}}
    ServiceNow Assignment Group : Venafi Management

    Retrieve a custom field attribute.
    You can specify either the guid or custom field label name.

    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\mydevice\myapp' -Attribute 'Certificate' -NoLookup

    Name : myapp
    Path : \VED\Policy\mydevice\myapp
    TypeName : Adaptable App
    Guid : b7a7221b-e038-41d9-9d49-d7f45c1ca128
    Attribute : {@{Name=Certificate; PolicyPath=; Value=\VED\Policy\mycert; Locked=False; Overridden=False}}
    Certificate : \VED\Policy\mycert

    Retrieve an attribute value without custom value lookup

    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\certificates\test.gdb.com' -All

    Name : test.gdb.com
    Path : \VED\Policy\Certificates\test.gdb.com
    TypeName : X509 Server Certificate
    Guid : b7a7221b-e038-41d9-9d49-d7f45c1ca128
    Attribute : {@{CustomFieldGuid={7f214dec-9878-495f-a96c-57291f0d42da}; Name=ServiceNow
                                            Assignment Group; PolicyPath=; Locked=False; Value=Venafi Management;
                                            Overridden=False}…}
    ServiceNow Assignment Group : Venafi Management
    City : Salt Lake City
    Consumers : {\VED\Policy\Installations\Agentless\US Zone\mydevice\myapp}
    Contact : local:{b1c77034-c099-4a5c-9911-9e26007817da}
    Country : US
    Created By : WebAdmin
    Driver Name : appx509certificate
    ...

    Retrieve all attributes applicable to this object

    .EXAMPLE
    Get-VdcAttribute -Path 'Certificates' -Class 'X509 Certificate' -Attribute 'State'

    Name : Certificates
    Path : \VED\Policy\Certificates
    TypeName : Policy
    Guid : a91fc152-a9fb-4b49-a7ca-7014b14d73eb
    Attribute : {@{Name=State; PolicyPath=\VED\Policy\Certificates; Locked=False; Value=UT}}
    ClassName : X509 Certificate
    State : UT

    Retrieve a policy attribute value for the specified policy folder and class.
    \ved\policy will be prepended to the path.

    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\certificates' -Class 'X509 Certificate' -All

    Name : Certificates
    Path : \VED\Policy\Certificates
    TypeName : Policy
    Guid : a91fc152-a9fb-4b49-a7ca-7014b14d73eb
    Attribute : {@{CustomFieldGuid={7f214dec-9878-495f-a96c-57291f0d42da}; Name=ServiceNow
                                            Assignment Group; PolicyPath=; Locked=False; Value=}…}
    ClassName : X509 Certificate
    Approver : local:{b1c77034-c099-4a5c-9911-9e26007817da}
    Key Algorithm : RSA
    Key Bit Strength : 2048
    Managed By : Aperture
    Management Type : Enrollment
    Network Validation Disabled : 1
    Notification Disabled : 0
    ...

    Retrieve all policy attributes for the specified policy folder and class

    .EXAMPLE
    Find-VdcCertificate | Get-VdcAttribute -Attribute Contact,'Managed By','Want Renewal' -ThrottleLimit 50

    Name : mycert
    Path : \VED\Policy\mycert
    TypeName : X509 Server Certificate
    Guid : 1dc31664-a9f3-407c-8bf3-1e388e90a114
    Attribute : {@{Name=Contact; PolicyPath=\VED\Policy; Value=local:{ab2a2e32-b412-4466-b5b5-484478a99bf4}; Locked=False; Overridden=False}, @{Name=Managed By; PolicyPath=\VED\Policy;
                Value=Aperture; Locked=True; Overridden=False}, @{Name=Want Renewal; PolicyPath=\VED\Policy; Value=0; Locked=True; Overridden=False}}
    Contact : local:{ab2a2e32-b412-4466-b5b5-484478a99bf4}
    Managed By : Aperture
    Want Renewal : 0
    ...

    Retrieve specific attributes for all certificates. Throttle the number of threads to 50, the default is 100

    .LINK
    https://docs.venafi.com/Docs/currentSDK/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-findpolicy.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-readeffectivepolicy.php

    #>

    [CmdletBinding(DefaultParameterSetName = 'Attribute')]
    [Alias('Get-TppAttribute')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'Attribute', ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'All', ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('DN')]
        [String] $Path,

        [Parameter(Mandatory, ParameterSetName = 'Attribute')]
        [ValidateNotNullOrEmpty()]
        [String[]] $Attribute,

        [ValidateNotNullOrEmpty()]
        [Alias('ClassName', 'PolicyClass')]
        [string] $Class,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [switch] $NoLookup,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Write-Verbose $PSCmdlet.ParameterSetName

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $newAttribute = $Attribute
        if ( $All -and $Class ) {
            Write-Verbose "Getting attributes for class $Class"
            $newAttribute = Get-VdcClassAttribute -ClassName $Class | Select-Object -ExpandProperty Name -Unique
        }

        $allItems = [System.Collections.Generic.List[hashtable]]::new()
    }

    process {

        if ( $PSCmdlet.ParameterSetName -eq 'Attribute') {
            # small number of attributes so focus parallelism on the objects
            # this is done in the end block
            $allItems.Add(
                @{
                    Path      = $Path | ConvertTo-VdcFullPath
                    Attribute = $newAttribute
                }
            )

            return
        }

        # All parameter set below

        # with -All there is a large list of attributes so focus parallelism on them

        $newPath = $Path | ConvertTo-VdcFullPath
        $thisObject = Get-VdcObject -Path $newPath

        if ( $Class -and ($thisObject.TypeName -ne 'Policy') ) {
            Write-Error ('You are attempting to retrieve policy attributes, but {0} is not a policy path' -f $newPath)
            return
        }

        $return = [pscustomobject] @{
            Name      = $thisObject.Name
            Path      = $newPath
            TypeName  = $thisObject.TypeName
            Guid      = $thisObject.Guid
            Attribute = [pscustomobject] @{}
        }

        if ( $Class ) {
            $return | Add-Member @{ 'ClassName' = $Class }
        }
        else {
            # get list of attributes for this specific class
            $newAttribute = Get-VdcClassAttribute -ClassName $thisObject.TypeName | Select-Object -ExpandProperty Name -Unique
        }

        $allAttributes = Invoke-VenafiParallel -InputObject $newAttribute -ScriptBlock {

            $thisAttribute = $PSItem

            if ( $using:Class ) {

                $params = @{
                    Method  = 'Post'
                    Body    = @{
                        Class         = $using:Class
                        ObjectDN      = $using:newPath
                        AttributeName = $thisAttribute
                    }
                    UriLeaf = 'config/FindPolicy'
                }
            }
            else {
                $params = @{
                    Method  = 'Post'
                    Body    = @{
                        ObjectDN      = $using:newPath
                        AttributeName = $thisAttribute
                    }
                    UriLeaf = 'config/ReadEffectivePolicy'
                }
            }

            Write-Verbose "Processing attribute $thisAttribute"

            $customField = $null

            if ( -not $using:NoLookup ) {
                # parallel lookup
                $customField = $VenafiSession.CustomField | Where-Object { $_.Label -eq $thisAttribute -or $_.Guid -eq $thisAttribute }

                if ( -not $customField ) {
                    # sequential lookup
                    $customField = $VenafiSessionNested.CustomField | Where-Object { $_.Label -eq $thisAttribute -or $_.Guid -eq $thisAttribute }
                }

                if ( $customField ) {
                    $params.Body.AttributeName = $customField.Guid
                }
            }

            # disabled is a special kind of attribute which cannot be read with readeffectivepolicy
            if ( $params.Body.AttributeName -eq 'Disabled' ) {
                $oldUri = $params.UriLeaf
                $params.UriLeaf = 'Config/Read'
                $response = Invoke-VenafiRestMethod @params
                $params.UriLeaf = $oldUri
            }
            else {
                $response = Invoke-VenafiRestMethod @params
            }

            if ( $response.Error ) {
                if ( $response.Result -in 601, 112) {
                    Write-Error "'$thisAttribute' is not a valid attribute for $newPath. Are you looking for a policy attribute? If so, add -Class."
                    continue
                }
                elseif ( $response.Result -eq 102) {
                    # attribute is valid, but value not set
                    # we're ok with this one
                }
                else {
                    Write-Error $response.Error
                    continue
                }
            }

            $valueOut = $null

            if ( $response.Values ) {
                switch ($response.Values.GetType().Name) {
                    'Object[]' {
                        switch ($response.Values.Count) {
                            1 {
                                $valueOut = $response.Values[0]
                            }

                            Default {
                                $valueOut = $response.Values
                            }
                        }
                    }
                    Default {
                        $valueOut = $response.Values
                    }
                }
            }

            $newProp = [pscustomobject] @{}

            # only add attributes to the root of the response object if they have a value
            # always add them to .Attribute ($newProp)
            if ( $CustomField ) {
                $newProp | Add-Member @{
                    Name              = $customField.Label
                    'CustomFieldGuid' = $customField.Guid
                }

                if ( $valueOut ) {
                    $using:return | Add-Member @{ $customField.Label = $valueOut }
                }

            }
            else {

                if ( $valueOut ) {
                    $using:return | Add-Member @{ $thisAttribute = $valueOut } -ErrorAction SilentlyContinue
                }

                $newProp | Add-Member @{ Name = $thisAttribute }
            }

            $newProp | Add-Member @{
                Value      = $valueOut
                PolicyPath = $response.PolicyDN
                Locked     = $response.Locked
            }

            # overridden not available at policy level
            if ( -not $PSBoundParameters.ContainsKey('Class') ) {
                $newProp | Add-Member @{ 'Overridden' = $response.Overridden }
            }

            $newProp

        } -ThrottleLimit $ThrottleLimit -ProgressTitle 'Getting attributes'

        $return.Attribute = @($allAttributes)
        $return
    }

    end {

        # parallelism is focused on the objects, not attributes
        # used when -Attribute is provided, not -All
        Invoke-VenafiParallel -InputObject $allItems -ScriptBlock {

            $newPath = $PSItem.Path
            $thisObject = Get-VdcObject -Path $newPath

            if ( $using:Class -and ($thisObject.TypeName -ne 'Policy') ) {
                Write-Error ('You are attempting to retrieve policy attributes, but {0} is not a policy path' -f $newPath)
                return
            }

            $newAttribute = $PSItem.Attribute

            $return = [pscustomobject] @{
                Name      = $thisObject.Name
                Path      = $newPath
                TypeName  = $thisObject.TypeName
                Guid      = $thisObject.Guid
                Attribute = [pscustomobject] @{}
            }

            if ( $using:Class ) {
                $return | Add-Member @{ 'ClassName' = $using:Class }

                $params = @{
                    Method  = 'Post'
                    Body    = @{
                        Class    = $using:Class
                        ObjectDN = $newPath
                    }
                    UriLeaf = 'config/FindPolicy'
                }
            }
            else {
                $params = @{
                    Method  = 'Post'
                    Body    = @{
                        ObjectDN = $newPath
                    }
                    UriLeaf = 'config/ReadEffectivePolicy'
                }
            }

            $allAttributes = foreach ($thisAttribute in $newAttribute) {

                Write-Verbose "Processing attribute $thisAttribute"

                $params.Body.AttributeName = $thisAttribute
                $customField = $null

                if ( -not $using:NoLookup ) {
                    # parallel lookup
                    $customField = $VenafiSession.CustomField | Where-Object { $_.Label -eq $thisAttribute -or $_.Guid -eq $thisAttribute }

                    if ( -not $customField ) {
                        # sequential lookup
                        $customField = $VenafiSessionNested.CustomField | Where-Object { $_.Label -eq $thisAttribute -or $_.Guid -eq $thisAttribute }
                    }

                    if ( $customField ) {
                        $params.Body.AttributeName = $customField.Guid
                    }
                }

                # disabled is a special kind of attribute which cannot be read with readeffectivepolicy
                if ( $params.Body.AttributeName -eq 'Disabled' ) {
                    $oldUri = $params.UriLeaf
                    $params.UriLeaf = 'Config/Read'
                    $response = Invoke-VenafiRestMethod @params
                    $params.UriLeaf = $oldUri
                }
                else {
                    $response = Invoke-VenafiRestMethod @params
                }

                if ( $response.Error ) {
                    if ( $response.Result -in 601, 112) {
                        Write-Error "'$thisAttribute' is not a valid attribute for $newPath. Are you looking for a policy attribute? If so, add -Class."
                        continue
                    }
                    elseif ( $response.Result -eq 102) {
                        # attribute is valid, but value not set
                        # we're ok with this one
                    }
                    else {
                        Write-Error $response.Error
                        continue
                    }
                }

                $valueOut = $null

                if ( $response.Values ) {
                    switch ($response.Values.GetType().Name) {
                        'Object[]' {
                            switch ($response.Values.Count) {
                                1 {
                                    $valueOut = $response.Values[0]
                                }

                                Default {
                                    $valueOut = $response.Values
                                }
                            }
                        }
                        Default {
                            $valueOut = $response.Values
                        }
                    }
                }

                $newProp = [pscustomobject] @{}

                # only add attributes to the root of the response object if they have a value
                # always add them to .Attribute ($newProp)
                if ( $CustomField ) {
                    $newProp | Add-Member @{
                        Name              = $customField.Label
                        'CustomFieldGuid' = $customField.Guid
                    }

                    if ( $valueOut ) {
                        $return | Add-Member @{ $customField.Label = $valueOut }
                    }

                }
                else {

                    if ( $valueOut ) {
                        $return | Add-Member @{ $thisAttribute = $valueOut } -ErrorAction SilentlyContinue
                    }

                    $newProp | Add-Member @{ Name = $thisAttribute }
                }

                $newProp | Add-Member @{
                    Value      = $valueOut
                    PolicyPath = $response.PolicyDN
                    Locked     = $response.Locked
                }

                # overridden not available at policy level
                if ( -not $using:Class ) {
                    $newProp | Add-Member @{ 'Overridden' = $response.Overridden }
                }

                $newProp
            }

            $return.Attribute = @($allAttributes)
            $return

        } -ThrottleLimit $ThrottleLimit -ProgressTitle 'Getting attributes'
    }
}
#EndRegion './Public/Get-VdcAttribute.ps1' 572
#Region './Public/Get-VdcCertificate.ps1' -1

function Get-VdcCertificate {
    <#
    .SYNOPSIS
    Get certificate information

    .DESCRIPTION
    Get certificate information, either all available to the api key provided or by id or zone.

    .PARAMETER ID
    Certificate identifier by either path or guid.
    \ved\policy will be automatically applied if a full path isn't provided.

    .PARAMETER IncludePreviousVersions
    Returns details about previous (historical) versions of a certificate.
    This option will add a property named PreviousVersions to the returned object.

    .PARAMETER ExcludeExpired
    Omits expired versions of the previous (historical) versions of a certificate.
    Can only be used with the IncludePreviousVersions parameter.

    .PARAMETER ExcludeRevoked
    Omits revoked versions of the previous (historical) versions of a certificate.
    Can only be used with the IncludePreviousVersions parameter.

    .PARAMETER All
    Retrieve all certificates

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Get-VdcCertificate -ID '\ved\policy\mycert.com'

    Get certificate info for a specific cert

    .EXAMPLE
    Get-VdcCertificate -All

    Get certificate info for all certs

    .EXAMPLE
    Get-VdcCertificate -ID '\ved\policy\mycert.com' -IncludePreviousVersions

    Get certificate info for a specific cert, including historical versions of the certificate.

    .EXAMPLE
    Get-VdcCertificate -ID '\ved\policy\mycert.com' -IncludeTppPreviousVersions -ExcludeRevoked -ExcludeExpired

    Get certificate info for a specific cert, including historical versions of the certificate that are not revoked or expired.

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-Certificates-guid.php
    #>


    [CmdletBinding(DefaultParameterSetName = 'Id')]

    param (

        [Parameter(ParameterSetName = 'Id', Mandatory, ValueFromPipelineByPropertyName, Position = 0)]
        [Parameter(ParameterSetName = 'IdWithPrevious', Mandatory, ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('Guid', 'Path')]
        [string] $ID,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [Parameter(Mandatory, ParameterSetName = 'AllWithPrevious')]
        [Switch] $All,

        [Parameter(Mandatory, ParameterSetName = 'IdWithPrevious')]
        [Parameter(Mandatory, ParameterSetName = 'AllWithPrevious')]
        [switch] $IncludePreviousVersions,

        [Parameter(ParameterSetName = 'IdWithPrevious')]
        [Parameter(ParameterSetName = 'AllWithPrevious')]
        [switch] $ExcludeExpired,

        [Parameter(ParameterSetName = 'IdWithPrevious')]
        [Parameter(ParameterSetName = 'AllWithPrevious')]
        [switch] $ExcludeRevoked,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $certs = [System.Collections.Generic.List[string]]::new()

    }

    process {

        if ( $All ) {
            return (Find-VdcCertificate -Path '\ved' -Recursive | Get-VdcCertificate -IncludePreviousVersions:$IncludePreviousVersions -ExcludeExpired:$ExcludeExpired -ExcludeRevoked:$ExcludeRevoked)
        }

        if ( Test-IsGuid($ID) ) {
            $certs.Add($ID)
        }
        else {
            $certs.Add((ConvertTo-VdcFullPath -Path $ID))
        }
    }

    end {

        Invoke-VenafiParallel -InputObject $certs -ScriptBlock {

            if ( $PSItem -like '\ved*' ) {
                # a path was provided, get the guid
                $thisGuid = ConvertTo-VdcGuid -Path $PSItem
            } else {
                $thisGuid = ([guid] $PSItem).ToString()
            }

            $params = @{
                UriLeaf = [System.Web.HttpUtility]::UrlEncode("certificates/{$thisGuid}")
            }

            $response = Invoke-VenafiRestMethod @params

            if ( $IncludePreviousVersions ) {
                $params.UriLeaf = [System.Web.HttpUtility]::UrlEncode("certificates/{$thisGuid}/PreviousVersions")
                $params.Body = @{}

                if ( $ExcludeExpired.IsPresent ) {
                    $params.Body.ExcludeExpired = $ExcludeExpired
                }
                if ( $ExcludeRevoked.IsPresent ) {
                    $params.Body.ExcludeRevoked = $ExcludeRevoked
                }

                $previous = Invoke-VenafiRestMethod @params

                if ( $previous.PreviousVersions ) {
                    $previous.PreviousVersions.CertificateDetails | ForEach-Object {
                        $_.StoreAdded = [datetime]$_.StoreAdded
                        $_.ValidFrom = [datetime]$_.ValidFrom
                        $_.ValidTo = [datetime]$_.ValidTo
                    }
                }

                $response | Add-Member @{'PreviousVersions' = $previous.PreviousVersions }
            }

            # object transformations
            # put in try/catch in case datetime conversion fails
            try {
                $response.CertificateDetails.StoreAdded = [datetime]$response.CertificateDetails.StoreAdded
                $response.CertificateDetails.ValidFrom = [datetime]$response.CertificateDetails.ValidFrom
                $response.CertificateDetails.ValidTo = [datetime]$response.CertificateDetails.ValidTo
            }
            catch {

            }

            $selectProps = @{
                Property        =
                @{
                    n = 'Name'
                    e = { $_.Name }
                },
                @{
                    n = 'TypeName'
                    e = { $_.SchemaClass }
                },
                @{
                    n = 'Path'
                    e = { $_.DN }
                }, @{
                    n = 'Guid'
                    e = { [guid]$_.guid }
                }, @{
                    n = 'ParentPath'
                    e = { $_.ParentDN }
                }, @{
                    n = 'CreatedOn'
                    e = { [datetime]$_.CreatedOn }
                },
                '*'
                ExcludeProperty = 'DN', 'GUID', 'ParentDn', 'SchemaClass', 'Name', 'CreatedOn'
            }

            $response | Select-Object @selectProps

        } -ThrottleLimit $ThrottleLimit -ProgressTitle 'Getting certificates'
    }
}
#EndRegion './Public/Get-VdcCertificate.ps1' 208
#Region './Public/Get-VdcClassAttribute.ps1' -1

function Get-VdcClassAttribute {
    <#
    .SYNOPSIS
        List all attributes for a specified class
    .DESCRIPTION
        List all attributes for a specified class, helpful for validation or to pass to Get-VdcAttribute
    .EXAMPLE
        Get-VdcClassAttribute -ClassName 'X509 Server Certificate'
        Get all attributes for the specified class
    .INPUTS
        ClassName
    .OUTPUTS
        PSCustomObject
    #>


    [CmdletBinding()]
    [Alias('Get-TppClassAttribute')]

    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [string] $ClassName,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $allAttributes = [System.Collections.Generic.List[object]]::new()
    }

    process {

        Write-Verbose "Processing $ClassName"

        $params = @{
            VenafiSession = $VenafiSession
            Method        = 'Post'
            UriLeaf       = 'configschema/class'
            Body          = @{
                'Class' = $ClassName
            }
        }
        $classDetails = Invoke-VenafiRestMethod @params | Select-Object -ExpandProperty 'ClassDefinition'

        if ($ClassName -ne 'Top') {
            $recurseAttribs = $classDetails.SuperClassNames | Get-VdcClassAttribute
            foreach ($item in $recurseAttribs) {
                $allAttributes.Add($item)
            }
        }

        foreach ($item in ($classDetails.OptionalNames)) {
            $allAttributes.Add(
                [pscustomobject] @{
                    'Name'  = $item
                    'Class' = $classDetails.Name
                }
            )
        }
    }

    end {
        $allAttributes | Sort-Object -Property 'Name', 'Class' -Unique
    }
}
#EndRegion './Public/Get-VdcClassAttribute.ps1' 68
#Region './Public/Get-VdcCredential.ps1' -1

function Get-VdcCredential {

    <#
    .SYNOPSIS
    Get credential details

    .DESCRIPTION
    Get credential details.
    Object returned will depend on the credential type.

    .PARAMETER Path
    The full path to the credential object

    .PARAMETER IncludeDetail
    Include metadata associated with the credential in addition to the credential itself

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    Password/UsernamePassword Credential - PSCredential
    Certificate Credential - X509Certificate2
    with IncludeDetail - PSCustomObject

    .EXAMPLE
    Get-VdcCredential -Path '\VED\Policy\MySecureCred'
    Get a credential

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Get-VdcCredential/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Get-VdcCredential.ps1

    #>


    [CmdletBinding()]
    [Alias('Get-TppCredential')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Generating cred from api call response data')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [String] $Path,

        [Parameter()]
        [switch] $IncludeDetail,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{
            VenafiSession = $VenafiSession
            Method        = 'Post'
            UriLeaf       = 'Credentials/Retrieve'
            Body          = @{}
        }

    }

    process {

        $params.Body.CredentialPath = $Path
        $response = Invoke-VenafiRestMethod @params

        if ( -not $response ) {
            continue
        }

        switch ($response.Classname) {
            'Password Credential' {
                $pw = $response.Values | Where-Object { $_.Name -eq 'Password' } | Select-Object -ExpandProperty Value
                $cred = New-Object System.Management.Automation.PSCredential((Split-Path -Path $Path -Leaf), ($pw | ConvertTo-SecureString -AsPlainText -Force))
            }

            'Username Password Credential' {
                $un = $response.Values | Where-Object { $_.Name -eq 'Username' } | Select-Object -ExpandProperty Value
                $pw = $response.Values | Where-Object { $_.Name -eq 'Password' } | Select-Object -ExpandProperty Value
                $cred = New-Object System.Management.Automation.PSCredential($un, ($pw | ConvertTo-SecureString -AsPlainText -Force))
            }

            'Certificate Credential' {
                $cert = $response.Values | Where-Object { $_.Name -eq 'Certificate' } | Select-Object -ExpandProperty Value
                $pw = $response.Values | Where-Object { $_.Name -eq 'Password' } | Select-Object -ExpandProperty Value
                $cred = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new([system.convert]::FromBase64String($cert), $pw)
            }

            Default {
                throw "Credential type '$_' is not supported yet. Submit an enhancement request."
            }
        }

        if ( $IncludeDetail ) {

            $return = $response | Select-Object -Property @{
                'n' = 'Type'
                'e' = { $_.Classname }
            }, *,
            @{
                'n' = 'Credential'
                'e' = { $cred }
            },
            @{
                'n' = 'Contact'
                'e' = { Get-VdcIdentity -Id $_.Contact.PrefixedUniversal }
            } -ExcludeProperty Classname, FriendlyName, Values, Result, Contact

            if ( $response.Classname -eq 'Certificate Credential' ) {
                $return | Add-Member @{
                    'CertificateLinkPath' = ($response.Values | Where-Object { $_.Name -eq 'Certificate DN' }).Value
                    'Password'        = if ($pw) { ConvertTo-SecureString -String $pw -AsPlainText -Force }
                }
            }

            $return
        }
        else {
            $cred
        }
    }
}
#EndRegion './Public/Get-VdcCredential.ps1' 140
#Region './Public/Get-VdcCustomField.ps1' -1

function Get-VdcCustomField {
    <#
    .SYNOPSIS
    Get custom field details

    .DESCRIPTION
    Get details about custom fields

    .PARAMETER Class
    Class to get details on

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    None

    .OUTPUTS
    Query returns a PSCustomObject with the following properties:
        Items
            AllowedValues
            Classes
            ConfigAttribute
            DN
            DefaultValues
            Guid
            Label
            Mandatory
            Name
            Policyable
            RenderHidden
            RenderReadOnly
            Single
            Type
        Locked
        Result

    .EXAMPLE
    Get-VdcCustomField -Class 'X509 Certificate'
    Get custom fields for certificates

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Get-VdcCustomField/

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Metadata-GetItemsForClass.php

    .NOTES
    All custom fields are retrieved upon inital connect to the server and a property of VenafiSession
    #>


    [CmdletBinding()]
    [Alias('Get-TppCustomField')]

    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [string] $Class,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {
        $params = @{
            Method     = 'Post'
            UriLeaf    = 'Metadata/GetItemsForClass'
            Body       = @{
                'ConfigClass' = $Class
            }
        }

        $response = Invoke-VenafiRestMethod @params

        if ( $response.Result -eq [TppMetadataResult]::Success ) {
            [PSCustomObject] @{
                Items  = $response.Items
                Locked = $response.Locked
            }
        } else {
            throw $response.Error
        }
    }
}
#EndRegion './Public/Get-VdcCustomField.ps1' 91
#Region './Public/Get-VdcEngineFolder.ps1' -1

function Get-VdcEngineFolder {
    <#
    .SYNOPSIS
    Get TLSPDC folder/engine assignments

    .DESCRIPTION
    When the input is a policy folder, retrieves an array of assigned TLSPDC processing engines.
    When the input is a TLSPDC engine, retrieves an array of assigned policy folders.
    If there are no matching assignments, nothing will be returned.

    .PARAMETER ID
    The full DN path or Guid to a TLSPDC processing engine or policy folder.

    .PARAMETER All
    Get all engine/folder assignments

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided, but this requires an environment variable VDC_SERVER to be set.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Get-VdcEngineFolder -Path '\VED\Engines\MYVENSERVER'

    Get an array of policy folders assigned to the TLSPDC processing engine 'MYVENSERVER'.

    .EXAMPLE
    Get-VdcEngineFolder -Path '\VED\Policy\Certificates\Web Team'

    Get an array of TLSPDC processing engines assigned to the policy folder '\VED\Policy\Certificates\Web Team'.

    .EXAMPLE
    [guid]'866e1d59-d5d2-482a-b9e6-7bb657e0f416' | Get-VdcEngineFolder

    When the GUID is assigned to a TLSPDC processing engine, returns an array of assigned policy folders.
    When the GUID is assigned to a policy folder, returns an array of assigned TLSPDC processing engines.
    Otherwise nothing will be returned.

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Get-VdcEngineFolder/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Get-VdcEngineFolder.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-ProcessingEngines-Engine-eguid.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-ProcessingEngines-Folder-fguid.php
    #>

    [CmdletBinding(DefaultParameterSetName = 'ID')]

    param (
        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [Alias('EngineGuid', 'Guid', 'EnginePath', 'Path')]
        [String] $ID,

        [Parameter(ParameterSetName = 'All', Mandatory)]
        [switch] $All,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        if ( $PSCmdlet.ParameterSetName -eq 'All' ) {
            Invoke-VenafiRestMethod -UriLeaf 'ProcessingEngines/' | Select-Object -ExpandProperty Engines | Get-VdcEngineFolder
        } else {

            if ( [guid]::TryParse($ID, $([ref][guid]::Empty)) ) {
                $thisObject = Get-VdcObject -Guid $ID
            } else {
                $thisObject = Get-VdcObject -Path $ID
            }

            $thisObjectGuid = '{{{0}}}' -f $thisObject.Guid

            if ( $thisObject.TypeName -eq 'Venafi Platform' ) {

                # engine

                $response = Invoke-VenafiRestMethod -UriLeaf "ProcessingEngines/Engine/$thisObjectGuid" | Select-Object -ExpandProperty Folders

                $response | Where-Object { $_.FolderGuid -ne $thisObjectGuid } | Select-Object FolderName,
                @{ 'n' = 'FolderPath'; 'e' = { $_.FolderDN } },
                @{ 'n' = 'FolderGuid'; 'e' = { $_.FolderGuid.Trim('{}') } },
                @{ 'n' = 'EngineName'; 'e' = { $thisObject.Name } },
                @{ 'n' = 'EnginePath'; 'e' = { $thisObject.Path } },
                @{ 'n' = 'EngineGuid'; 'e' = { $thisObject.Guid } }

            } elseif ( $thisObject.TypeName -eq 'Policy' ) {

                # policy folder

                $response = Invoke-VenafiRestMethod -UriLeaf "ProcessingEngines/Folder/$thisObjectGuid" | Select-Object -ExpandProperty Engines

                $response | Select-Object EngineName,
                @{ 'n' = 'EnginePath'; 'e' = { $_.EngineDN } },
                @{ 'n' = 'EngineGuid'; 'e' = { $_.EngineGuid.Trim('{}') } },
                @{ 'n' = 'FolderName'; 'e' = { $thisObject.Name } },
                @{ 'n' = 'FolderPath'; 'e' = { $thisObject.Path } },
                @{ 'n' = 'FolderGuid'; 'e' = { $thisObject.Guid } }

            } else {
                throw ('Unsupported object type, {0}' -f $thisObject.TypeName)
            }
        }
    }
}
#EndRegion './Public/Get-VdcEngineFolder.ps1' 122
#Region './Public/Get-VdcIdentity.ps1' -1

function Get-VdcIdentity {
    <#
    .SYNOPSIS
    Get user and group details

    .DESCRIPTION
    Returns user/group information for TLSPDC
    This returns individual identity, group identity, or distribution groups from a local or non-local provider such as Active Directory.

    .PARAMETER ID
    Provide the guid or prefixed universal id. To search, use Find-VdcIdentity.

    .PARAMETER IncludeAssociated
    Include all associated identity groups and folders.

    .PARAMETER IncludeMembers
    Include all individual members if the ID is a group.

    .PARAMETER Me
    Returns the identity of the authenticated/current user

    .PARAMETER All
    Return a complete list of local users.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject
        Name
        ID
        Path
        FullName
        Associated (if -IncludeAssociated provided)
        Members (if -IncludeMembers provided)

    .EXAMPLE
    Get-VdcIdentity -ID 'AD+myprov:asdfgadsf9g87df98g7d9f8g7'

    Get identity details from an id

    .EXAMPLE
    Get-VdcIdentity -ID 'AD+myprov:asdfgadsf9g87df98g7d9f8g7' -IncludeMembers

    Get identity details including members if the identity is a group

    .EXAMPLE
    Get-VdcIdentity -ID 'AD+myprov:asdfgadsf9g87df98g7d9f8g7' -IncludeAssociated

    Get identity details including associated groups/folders

    .EXAMPLE
    Get-VdcIdentity -Me

    Get identity details for authenticated/current user

    .EXAMPLE
    Get-VdcIdentity -All

    Get all user and group info

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Identity-Validate.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-Identity-Self.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Identity-GetAssociatedEntries.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Identity-GetMembers.php
    #>


    [CmdletBinding(DefaultParameterSetName = 'Id')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = "Parameter is used")]
    [Alias('Get-TppIdentity')]

    param (
        [Parameter(Mandatory, ParameterSetName = 'Id', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('Guid', 'FullName')]
        [String] $ID,

        [Parameter(Mandatory, ParameterSetName = 'Me')]
        [Switch] $Me,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [Switch] $All,

        [Parameter(ParameterSetName = 'Id')]
        [Parameter(ParameterSetName = 'All')]
        [Switch] $IncludeAssociated,

        [Parameter(ParameterSetName = 'Id')]
        [Parameter(ParameterSetName = 'All')]
        [Switch] $IncludeMembers,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        Switch ($PsCmdlet.ParameterSetName)    {
            'Id' {

                $params = @{
                    Method  = 'Post'
                    UriLeaf = 'Identity/Validate'
                    Body    = @{'ID' = @{ } }
                }

                if ( Test-VdcIdentityFormat -ID $ID -Format 'Universal' ) {
                    $params.Body.ID.PrefixedUniversal = $ID
                }
                elseif ( Test-VdcIdentityFormat -ID $ID -Format 'Name' ) {
                    $params.Body.ID.PrefixedName = $ID
                }
                elseif ( [guid]::TryParse($ID, $([ref][guid]::Empty)) ) {
                    $guid = [guid] $ID
                    $params.Body.ID.PrefixedUniversal = 'local:{{{0}}}' -f $guid.ToString()
                }
                else {
                    Write-Error "'$ID' is not a valid identity"
                    return
                }

                $response = Invoke-VenafiRestMethod @params | Select-Object -ExpandProperty ID

                if ( $IncludeAssociated ) {
                    $assocParams = $params.Clone()
                    $assocParams.UriLeaf = 'Identity/GetAssociatedEntries'
                    $associated = Invoke-VenafiRestMethod @assocParams
                    $response | Add-Member @{ 'Associated' = $null }
                    $response.Associated = $associated.Identities | ConvertTo-VdcIdentity
                }

                if ( $IncludeMembers ) {
                    $response | Add-Member @{ 'Members' = $null }
                    if ( $response.IsGroup ) {
                        $assocParams = $params.Clone()
                        $assocParams.UriLeaf = 'Identity/GetMembers'
                        $assocParams.Body.ResolveNested = "1"
                        $members = Invoke-VenafiRestMethod @assocParams
                        $response.Members = $members.Identities | ConvertTo-VdcIdentity
                    }
                }

                $idOut = $response
            }

            'Me' {
                $response = Invoke-VenafiRestMethod -UriLeaf 'Identity/Self'

                $idOut = $response.Identities | Select-Object -First 1
            }

            'All' {
                # no built-in api for this, get group objects and then get details
                Find-VdcObject -Path '\VED\Identity' -Class 'User', 'Group' | Get-VdcIdentity -IncludeAssociated:$IncludeAssociated.IsPresent -IncludeMembers:$IncludeMembers.IsPresent
            }
        }

        if ( $idOut ) {
            $idOut | ConvertTo-VdcIdentity
        }
    }
}
#EndRegion './Public/Get-VdcIdentity.ps1' 180
#Region './Public/Get-VdcIdentityAttribute.ps1' -1

function Get-VdcIdentityAttribute {
    <#
    .SYNOPSIS
    Get attribute values for TLSPDC identity objects

    .DESCRIPTION
    Get attribute values for TLSPDC identity objects.

    .PARAMETER ID
    The id that represents the user or group. Use Find-VdcIdentity to get the id.

    .PARAMETER Attribute
    Retrieve identity attribute values for the users and groups.
    Common user attributes include Group Membership, Name, Internet Email Address, Given Name, and Surname.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject with the properties Identity and Attribute

    .EXAMPLE
    Get-VdcIdentityAttribute -IdentityId 'AD+blah:{1234567890olikujyhtgrfedwsqa}'

    Get basic attributes

    .EXAMPLE
    Get-VdcIdentityAttribute -IdentityId 'AD+blah:{1234567890olikujyhtgrfedwsqa}' -Attribute 'Surname'

    Get specific attribute for user

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Identity-Validate.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Identity-Readattribute.php

    #>


    [CmdletBinding()]
    [Alias('Get-TppIdentityAttribute')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('PrefixedUniversalId', 'Contact', 'IdentityId')]
        [string[]] $ID,

        [Parameter()]
        [string[]] $Attribute,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{
            Method     = 'Post'
            UriLeaf    = 'Identity/Validate'
            Body       = @{
                'ID' = @{
                    PrefixedUniversal = 'placeholder'
                }
            }
        }

        if ( $PSBoundParameters.ContainsKey('Attribute') ) {
            $params.UriLeaf = 'Identity/ReadAttribute'
            $params.Body.Add('AttributeName', 'placeholder')
        }

    }

    process {

        foreach ( $thisId in $ID ) {

            $params.Body.ID.PrefixedUniversal = $thisId

            if ( $PSBoundParameters.ContainsKey('Attribute') ) {

                $attribHash = @{ }
                foreach ( $thisAttribute in $Attribute ) {

                    $params.Body.AttributeName = $thisAttribute

                    $response = Invoke-VenafiRestMethod @params
                    if ( $response.Attributes ) {
                        $attribHash.$thisAttribute = $response.Attributes[0]
                    }
                }

                $attribsOut = [PSCustomObject] $attribHash

            } else {
                $response = Invoke-VenafiRestMethod @params
                $attribsOut = $response.Id
            }

            [PSCustomObject] @{
                ID         = $thisId
                Attributes = $attribsOut
            }
        }
    }
}
#EndRegion './Public/Get-VdcIdentityAttribute.ps1' 115
#Region './Public/Get-VdcObject.ps1' -1

function Get-VdcObject {
    <#
    .SYNOPSIS
    Get object information

    .DESCRIPTION
    Return object information by either path or guid. This will return a TppObject which can be used with many other functions.

    .PARAMETER Path
    The full path to the object.
    \ved\policy will be automatically applied if a full path isn't provided.

    .PARAMETER Guid
    Guid of the object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path, Guid

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Get-VdcObject -Path '\VED\Policy\My object'

    Get an object by path

    .EXAMPLE
    [guid]'dab22152-0a81-4fb8-a8da-8c5e3d07c3f1' | Get-VdcObject

    Get an object by guid

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Get-VdcObject/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Get-VdcObject.ps1

    #>


    [CmdletBinding()]
    [Alias('Get-TppObject', 'gvdo')]

    param (
        [Parameter(Mandatory, ParameterSetName = 'ByPath', ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [Alias('DN')]
        [String[]] $Path,

        [Parameter(Mandatory, ParameterSetName = 'ByGuid', ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [Alias('ObjectGuid')]
        [guid[]] $Guid,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        if ( $PSCmdLet.ParameterSetName -eq 'ByPath' ) {
            $Path | ConvertTo-VdcFullPath | ForEach-Object {
                ConvertTo-VdcObject -Path $_
            }
        }
        else {
            $Guid | ForEach-Object {
                ConvertTo-VdcObject -Guid $_
            }
        }
    }
}
#EndRegion './Public/Get-VdcObject.ps1' 82
#Region './Public/Get-VdcPermission.ps1' -1

function Get-VdcPermission {
    <#
    .SYNOPSIS
    Get permissions for TLSPDC objects

    .DESCRIPTION
    Get permissions for users and groups on any object.
    The effective permissions will be retrieved by default, but inherited/explicit permissions can optionally be retrieved.
    You can retrieve all permissions for an object or for a specific user/group.

    .PARAMETER InputObject
    TppObject representing an object in TLSPDC, eg. from Find-VdcObject or Get-VdcObject

    .PARAMETER Path
    Full path to an object

    .PARAMETER Guid
    Guid representing a unique object

    .PARAMETER IdentityId
    Specifying this optional parameter will only return objects that have permissions assigned to this id.
    You can use Find-VdcIdentity to search for identities.

    .PARAMETER Explicit
    Get explicit (direct) and implicit (inherited) permissions instead of effective.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    InputObject, Path, Guid, IdentityId

    .OUTPUTS
    PSCustomObject with the following properties:
        Path
        Guid
        Name
        TypeName
        IdentityId
        IdentityPath, may be null if the identity has been deleted
        IdentityName, may be null if the identity has been deleted
        EffectivePermissions (if Explicit switch is not used)
        ExplicitPermissions (if Explicit switch is used)
        ImplicitPermissions (if Explicit switch is used)

    .EXAMPLE
    Get-VdcPermission -Path '\VED\Policy\barron'

    Path : \ved\policy\barron
    Guid : 3ba630d8-acf0-4b52-9824-df549cb33b82
    Name : barron
    TypeName : Policy
    IdentityId : AD+domain:410aaf10ea816c4d823e9e05b1ad055d
    IdentityPath : CN=Greg Brownstein,OU=Users,OU=Enterprise Administration,DC=domain,DC=net
    IdentityName : greg
    EffectivePermissions : TppPermission

    Get all assigned effective permissions for users/groups on a specific policy folder

    .EXAMPLE
    Get-VdcObject -Path '\VED\Policy\My folder' | Get-VdcPermission

    Get all assigned effective permissions for users/groups on a specific policy folder by piping the object

    .EXAMPLE
    Get-VdcObject -Path '\VED\Policy\barron' | Get-VdcPermission -Explicit

    Path : \ved\policy\barron
    Guid : 3ba630d8-acf0-4b52-9824-df549cb33b82
    Name : barron
    TypeName : Policy
    IdentityId : AD+domain:410aaf10ea816c4d823e9e05b1ad055d
    IdentityPath : CN=Greg Brownstein,OU=Users,OU=Enterprise Administration,DC=domain,DC=net
    IdentityName : greg
    ExplicitPermissions : TppPermission
    ImplicitPermissions : TppPermission

    Get explicit and implicit permissions for users/groups on a specific policy folder

    .EXAMPLE
    Find-VdcObject -Path '\VED' -Recursive | Get-VdcPermission -IdentityId 'AD+myprov:jasdf87s9dfsdfhkashfg78f7'

    Find assigned permissions for a specific user across all objects

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Get-VdcPermission/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Get-VdcPermission.ps1

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Get-VdcIdentityAttribute.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-Permissions-object-guid.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-Permissions-object-guid-external.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-Permissions-object-guid-local.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-Permissions-object-guid-principal.php


    #>


    [CmdletBinding(DefaultParameterSetName = 'ByObject')]
    [Alias('Get-TppPermission')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ByObject', ValueFromPipeline)]
        [pscustomobject] $InputObject,

        [Parameter(Mandatory, ParameterSetName = 'ByPath', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('DN')]
        [String[]] $Path,

        [Parameter(Mandatory, ParameterSetName = 'ByGuid', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('ObjectGuid')]
        [guid[]] $Guid,

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateScript( {
                if ( $_ | Test-VdcIdentityFormat ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid Identity format. See https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-IdentityInformation.php."
                }
            })]
        [Alias('PrefixedUniversalId', 'ID')]
        [string[]] $IdentityId,

        [Parameter()]
        [Alias('ExplicitImplicit')]
        [switch] $Explicit,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{

            Method        = 'Get'
            UriLeaf       = 'placeholder'
        }
    }

    process {

        switch ( $PsCmdLet.ParameterSetName) {
            'ByObject' {
                $newInputObject = $InputObject
            }

            'ByPath' {
                $newInputObject = $Path
            }

            'ByGuid' {
                $newInputObject = $Guid
            }

            Default {
                throw ('Unknown parameterset {0}' -f $PsCmdLet.ParameterSetName)
            }
        }

        foreach ( $thisInputObject in $newInputObject ) {

            switch ( $PsCmdLet.ParameterSetName) {
                'ByObject' {
                    $thisTppObject = $thisInputObject
                }

                Default {
                    $thisTppObject = $thisInputObject | ConvertTo-VdcObject
                }
            }

            $uriBase = ('Permissions/Object/{{{0}}}' -f $thisTppObject.Guid )
            $params.UriLeaf = $uriBase

            try {
                # get list of identities permissioned to this object
                $identities = Invoke-VenafiRestMethod @params
            }
            catch {
                Write-Error ('Couldn''t obtain list of permissions for {0}. {1}' -f $thisTppObject.Path, $_ | Out-String)
                continue
            }

            # limit to specific identities provided
            if ( $PSBoundParameters.ContainsKey('IdentityId') ) {
                $identities = $identities | Where-Object { $_ -in $IdentityId }
            }

            foreach ( $thisId in $identities ) {

                Write-Verbose ('Path: {0}, Id: {1}' -f $thisTppObject.Path, $thisId)

                $params.UriLeaf = $uriBase

                if ( $thisId.StartsWith('local:') ) {
                    # format of local is local:universalId
                    $type, $id = $thisId.Split(':')
                    $params.UriLeaf += "/local/$id"
                }
                else {
                    # external source, eg. AD, LDAP
                    # format is type+name:universalId
                    $type, $name, $id = $thisId -Split { $_ -in '+', ':' }
                    $params.UriLeaf += "/$type/$name/$id"
                }

                if ( -not $Explicit.IsPresent ) {
                    $params.UriLeaf += '/Effective'
                }

                $thisReturnObject = [PSCustomObject] @{
                    Path         = $thisTppObject.Path
                    Guid         = $thisTppObject.Guid
                    Name         = $thisTppObject.Name
                    TypeName     = $thisTppObject.TypeName
                    IdentityId   = $thisId
                    IdentityPath = $null
                    IdentityName = $null
                }

                if ( $Explicit ) {
                    $thisReturnObject | Add-Member @{
                        ExplicitPermissions = $null
                        ImplicitPermissions = $null
                    }
                }
                else {
                    $thisReturnObject | Add-Member @{
                        EffectivePermissions = $null
                    }
                }

                try {

                    $response = Invoke-VenafiRestMethod @params

                    if ( $Explicit ) {
                        $thisReturnObject.ExplicitPermissions = [TppPermission] $response.ExplicitPermissions
                        $thisReturnObject.ImplicitPermissions = [TppPermission] $response.ImplicitPermissions
                    }
                    else {
                        $thisReturnObject.EffectivePermissions = [TppPermission] $response.EffectivePermissions
                    }

                    $attribParams = @{
                        IdentityId    = $thisReturnObject.IdentityId

                    }

                    $attribResponse = Get-VdcIdentityAttribute @attribParams -ErrorAction SilentlyContinue

                    if ( $attribResponse ) {
                        $thisReturnObject.IdentityPath = $attribResponse.Attributes.FullName
                        $thisReturnObject.IdentityName = $attribResponse.Attributes.Name
                    }
                }
                catch {
                    # handle edge case where permissions had been set, but the user account has been deleted
                    # this way we can return the permissions that are set, just not the identity attributes, eg. name
                    if ( $_ -like '*Unable to verify principal*' ) {
                        if ( $Explicit ) {
                            # this will only return explicit permissions, not effective
                            $notFoundParams = @{
                                Method  = 'Post'
                                UriLeaf = 'permissions/getpermissions'
                                Body    = @{
                                    ObjectDN  = $thisTppObject.Path
                                    Principal = $thisID
                                }
                            }

                            $notFoundResponse = invoke-venafirestmethod @notFoundParams

                            if ( $notFoundResponse.Permissions ) {
                                $thisReturnObject.ExplicitPermissions = [TppPermission]$notFoundResponse.Permissions
                            }
                        }
                    }
                    else {
                        Write-Error ('Unable to retrieve permissions. Path: {0}, Id: {1}, Error: {2}' -f $thisTppObject.Path, $thisId, $_)
                    }
                }

                $thisReturnObject
            }
        }
    }
}
#EndRegion './Public/Get-VdcPermission.ps1' 317
#Region './Public/Get-VdcSystemStatus.ps1' -1

function Get-VdcSystemStatus {
    <#
    .SYNOPSIS
    Get the TLSPDC system status

    .DESCRIPTION
    Returns service module statuses for Trust Protection Platform, Log Server, and Trust Protection Platform services that run on Microsoft Internet Information Services (IIS)

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    none

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Get-VdcSystemStatus
    Get the status

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Get-VdcSystemStatus/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Get-VdcSystemStatus.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-SystemStatus.php

    #>

    [CmdletBinding()]
    [Alias('Get-TppSystemStatus')]

    param (
        [Parameter()]
        [psobject] $VenafiSession
    )

    Write-Warning "Possible bug with Venafi TLSPDC API causing this to fail"

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

    $params = @{

        Method     = 'Get'
        UriLeaf    = 'SystemStatus/'
    }

    try {
        Invoke-VenafiRestMethod @params
    }
    catch {
        Throw ("Getting the system status failed with the following error: {0}. Ensure you have read rights to the engine root." -f $_)
    }
}
#EndRegion './Public/Get-VdcSystemStatus.ps1' 60
#Region './Public/Get-VdcTeam.ps1' -1

function Get-VdcTeam {
    <#
    .SYNOPSIS
    Get team info

    .DESCRIPTION
    Get info for a team including members and owners.

    .PARAMETER ID
    Team ID in local prefixed universal format. You can find the team/group ID with Find-VdcIdentity.

    .PARAMETER All
    Provide this switch to get all teams

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Get-VdcTeam -ID 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}'

    Get info for a TLSPDC team

    .EXAMPLE
    Find-VdcIdentity -Name MyTeamName | Get-VdcTeam

    Search for a team and then get details

    .EXAMPLE
    Get-VdcTeam -All

    Get info for all teams

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-Teams-prefix-universal.php
    #>


    [CmdletBinding(DefaultParameterSetName = 'ID')]
    [Alias('Get-TppTeam')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ID', ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('PrefixedUniversal', 'Guid', 'PrefixedName')]
        [string] $ID,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        if ( $PSCmdlet.ParameterSetName -eq 'All' ) {

            # no built-in api for this, get group objects and then get details
            Find-VdcObject -Path '\VED\Identity' -Class 'Group' | Where-Object { $_.Name -ne 'Everyone' } | Get-VdcTeam
        }
        else {

            # not only does -match set $matches, but -notmatch does as well
            if ( $ID -notmatch '(?im)^(local:)?\{?([0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12})\}?$' ) {
                Write-Error "'$ID' is not the proper format for a Team. Format should either be a guid or local:{guid}."
                return
            }

            $params = @{
                UriLeaf = ('Teams/local/{{{0}}}' -f $matches[2])
            }

            try {

                $response = Invoke-VenafiRestMethod @params

                $out = [pscustomobject] ($response.ID | ConvertTo-VdcIdentity)
                $out | Add-Member @{
                    Members = $response.Members | ConvertTo-VdcIdentity
                    Owners  = $response.Owners | ConvertTo-VdcIdentity
                }
                $out
            }
            catch {

                # handle known errors where the local group is not actually a team
                if ( $_.ErrorDetails.Message -like '*Failed to read the team identity;*' ) {
                    Write-Verbose "$ID looks to be a local group and not a Team. The server responded with $_"
                }
                else {
                    Write-Error "$ID : $_"
                }
            }
        }
    }
}
#EndRegion './Public/Get-VdcTeam.ps1' 109
#Region './Public/Get-VdcVersion.ps1' -1

function Get-VdcVersion {
    <#
    .SYNOPSIS
    Get the TLSPDC version

    .DESCRIPTION
    Returns the TLSPDC version

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    none

    .OUTPUTS
    Version

    .EXAMPLE
    Get-VdcVersion
    Get the version

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Get-VdcVersion/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Get-VdcVersion.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-SystemStatusVersion.php

    #>


    [CmdletBinding()]
    [Alias('Get-TppVersion')]
    [OutputType([System.Version])]

    param (
        [Parameter()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

    $params = @{

        Method        = 'Get'
        UriLeaf       = 'SystemStatus/Version'
    }

    try {
        [Version]((Invoke-VenafiRestMethod @params).Version)
    }
    catch {
        Throw ("Getting the version failed with the following error: {0}. This feature was introduced in v18.3." -f $_)
    }
}
#EndRegion './Public/Get-VdcVersion.ps1' 60
#Region './Public/Get-VdcWorkflowTicket.ps1' -1

function Get-VdcWorkflowTicket {
    <#
    .SYNOPSIS
    Get workflow ticket

    .DESCRIPTION
    Get details about workflow tickets associated with a certificate.

    .PARAMETER InputObject
    TppObject which represents a certificate object

    .PARAMETER Path
    Path to the certificate

    .PARAMETER Guid
    Certificate guid

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    InputObject, Path, or Guid

    .OUTPUTS
    PSCustomObject with the following properties:
        Guid: Workflow ticket Guid
        ApprovalExplanation: The explanation supplied by the approver.
        ApprovalFrom: The identity to be contacted for approving.
        ApprovalReason: The administrator-defined reason text.
        Approvers: An array of workflow approvers for the certificate.
        Blocking: The object that the ticket is associated with.
        Created: The date/time the ticket was created.
        IssuedDueTo: The workflow object that caused this ticket to be created (if any).
        Result: Integer result code indicating success 1 or failure. For more information, see Workflow result codes.
        Status: The status of the ticket.
        Updated: The date/time that the ticket was last updated.

    .EXAMPLE
    Get-VdcWorkflowTicket -Path '\VED\policy\myapp.company.com'
    Get ticket details for 1 certificate

    .EXAMPLE
    $certs | Get-VdcWorkflowTicket
    Get ticket details for multiple certificates

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Get-VdcWorkflowTicket/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Get-VdcWorkflowTicket.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Workflow-ticket-enumerate.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Workflow-ticket-details.php

    #>


    [CmdletBinding()]
    [Alias('Get-TppWorkflowTicket')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('DN', 'CertificateDN')]
        [String[]] $Path,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
        Write-Verbose ("Parameter set {0}" -f $PsCmdlet.ParameterSetName)
    }

    process {

        $ticketGuid = foreach ($thisDn in $Path) {

            $params = @{

                Method     = 'Post'
                UriLeaf    = 'Workflow/Ticket/Enumerate'
                Body       = @{
                    'ObjectDN' = $thisDn
                }
            }

            $response = Invoke-VenafiRestMethod @params

            if ( $response ) {
                Write-Verbose ("Found {0} workflow tickets for certificate {1}" -f $response.GUIDs.count, $thisDn)
                $response.GUIDs
            }
        }

        foreach ($thisGuid in $ticketGuid) {
            $params = @{

                Method     = 'Post'
                UriLeaf    = 'Workflow/Ticket/Details'
                Body       = @{
                    'GUID' = $thisGuid
                }
            }

            $response = Invoke-VenafiRestMethod @params

            if ( $response.Result -eq [TppWorkflowResult]::Success ) {
                $response | Add-Member @{
                    TicketGuid = [guid] $thisGuid
                }
                $response
            }
            else {
                throw ("Error getting ticket details, error is {0}" -f [enum]::GetName([TppWorkflowResult], $response.Result))
            }
        }
    }
}
#EndRegion './Public/Get-VdcWorkflowTicket.ps1' 135
#Region './Public/Import-VcCertificate.ps1' -1

function Import-VcCertificate {
    <#
    .SYNOPSIS
    Import one or more certificates

    .DESCRIPTION
    Import one or more certificates and their private keys. Currently PKCS #8 and PKCS #12 (.pfx or .p12) are supported.

    .PARAMETER Path
    Path to a certificate file. Provide either this or -Data.

    .PARAMETER Data
    Contents of a certificate/key to import. Provide either this or -Path.

    .PARAMETER Pkcs8
    Provided -Data is in PKCS #8 format

    .PARAMETER Pkcs12
    Provided -Data is in PKCS #12 format

    .PARAMETER PrivateKeyPassword
    Password the private key was encrypted with

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 10. Applicable to PS v7+ only.
    100 keystores will be imported at a time so it's less important to have a very high throttle limit.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .EXAMPLE
    Import-VcCertificate -CertificatePath c:\www.VenafiPS.com.pfx

    Import a certificate/key

    .EXAMPLE
    $p12 = Export-VdcCertificate -Path '\ved\policy\my.cert.com' -Pkcs12 -PrivateKeyPassword 'myPassw0rd!'
    $p12 | Import-VcCertificate -Pkcs12 -PrivateKeyPassword 'myPassw0rd!' -VenafiSession $vaas_key

    Export from TLSPDC and import into TLSPC.
    As $VenafiSession can only point to one platform at a time, in this case TLSPDC, the session needs to be overridden for the import.

    .EXAMPLE
    $p12 = Find-VdcCertificate -Path '\ved\policy\certs' -Recursive | Export-VdcCertificate -Pkcs12 -PrivateKeyPassword 'myPassw0rd!'
    $p12 | Import-VcCertificate -Pkcs12 -PrivateKeyPassword 'myPassw0rd!' -VenafiSession $vaas_key

    Bulk export from TLSPDC and import into TLSPC.
    As $VenafiSession can only point to one platform at a time, in this case TLSPDC, the session needs to be overridden for the import.

    .INPUTS
    Path, Data

    .LINK
    https://developer.venafi.com/tlsprotectcloud/reference/certificates_import

    .NOTES
    This function requires the use of sodium encryption.
    .net standard 2.0 or greater is required via PS Core (recommended) or supporting .net runtime.
    On Windows, the latest Visual C++ redist must be installed. See https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist.
    #>


    [CmdletBinding(DefaultParameterSetName = 'ByFile')]
    [Alias('Import-VaasCertificate')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'ByFile', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( -not (Test-Path -Path (Resolve-Path -Path $_) -PathType Leaf) ) {
                    throw "'$_' is not a valid file path"
                }

                if ([System.IO.Path]::GetExtension((Resolve-Path -Path $_)) -notin '.pfx', '.p12') {
                    throw "$_ is not a .p12 or .pfx file"
                }

                $true
            })]
        [Alias('FullName', 'CertificatePath')]
        [String] $Path,

        [Parameter(Mandatory, ParameterSetName = 'Pkcs12', ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'Pkcs8', ValueFromPipelineByPropertyName)]
        [AllowNull()]
        [AllowEmptyString()]
        [Alias('CertificateData')]
        [String] $Data,

        [Parameter(Mandatory, ParameterSetName = 'Pkcs12')]
        [switch] $Pkcs12,

        [Parameter(Mandatory, ParameterSetName = 'Pkcs8')]
        [switch] $Pkcs8,

        [Parameter(Mandatory)]
        [ValidateScript(
            {
                if ( $_ -is [string] -or $_ -is [securestring] -or $_ -is [pscredential] ) {
                    $true
                }
                else {
                    throw 'Unsupported type. Provide either a String, SecureString, or PSCredential.'
                }
            }
        )]
        [psobject] $PrivateKeyPassword,

        [Parameter()]
        [int32] $ThrottleLimit = 10,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        Initialize-PSSodium

        $vSat = Get-VcData -Type 'VSatellite' -First
        if ( -not $vSat ) { throw 'No active VSatellites were found' }

        $pkPassString = $PrivateKeyPassword | ConvertTo-PlaintextString

        $allCerts = [System.Collections.Generic.List[hashtable]]::new()

    }

    process {

        if ( $PSBoundParameters.ContainsKey('Path') ) {
            $thisCertPath = Resolve-Path -Path $Path

            switch ([System.IO.Path]::GetExtension($thisCertPath)) {
                { $_ -in '.pfx', '.p12' } { $format = 'Pkcs12' }
            }

            if ($PSVersionTable.PSVersion.Major -lt 6) {
                $cert = Get-Content $thisCertPath -Encoding Byte
            }
            else {
                $cert = Get-Content $thisCertPath -AsByteStream
            }

            $allCerts.Add(@{
                    'CertData' = [System.Convert]::ToBase64String($cert)
                    'Format'   = $format
                }
            )
        }
        else {
            # check if Data exists since we allow null/empty in case piping from another function and data is not there
            if ( $Data ) {

                $addMe = @{
                    'Format' = $PSCmdlet.ParameterSetName
                }

                switch ($PSCmdlet.ParameterSetName) {
                    'Pkcs12' {
                        $addMe.'CertData' = $Data -replace "`r|`n|-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----"
                    }

                    'Pkcs8' {
                        $splitData = Split-CertificateData -CertificateData $Data
                        $addMe.CertPem = $splitData.CertPem
                        if ( $splitData.KeyPem ) { $addMe.KeyPem = $splitData.KeyPem }
                    }
                }

                $allCerts.Add($addMe)
            }
        }

    }

    end {
        $importList = [System.Collections.Generic.List[hashtable]]::new()

        $dekEncryptedPassword = ConvertTo-SodiumEncryptedString -Text $pkPassString -PublicKey $vSat.encryptionKey

        # rebuild invoke params as the payload can contain multiple keys at once
        # max 100 keys at a time
        for ($i = 0; $i -lt $allCerts.Count; $i += 100) {

            $params = @{
                Method        = 'post'
                UriRoot       = 'outagedetection/v1'
                UriLeaf       = 'certificates/imports'
                Body          = @{
                    'edgeInstanceId'  = $vSat.vsatelliteId
                    'encryptionKeyId' = $vSat.encryptionKeyId
                }
                VenafiSession = $VenafiSession
            }

            $keystores = foreach ($thisCert in $allCerts[$i..($i + 99)]) {
                switch ($allCerts[$i].Format) {
                    'Pkcs12' {
                        @{
                            'pkcs12Keystore'       = $thisCert.CertData
                            'dekEncryptedPassword' = $dekEncryptedPassword
                        }
                    }

                    'Pkcs8' {
                        $thisKeystore = @{
                            'certificate'          = $thisCert.CertPem
                            'dekEncryptedPassword' = $dekEncryptedPassword
                        }
                        if ( $thisCert.KeyPem ) { $thisKeystore.passwordEncryptedPrivateKey = $thisCert.KeyPem }
                        $thisKeystore
                    }
                }
            }

            $params.Body.importInformation = @($keystores)
            $importList.Add($params)
        }

        $sb = {
            $params = $PSItem

            $requestResponse = Invoke-VenafiRestMethod @params
            do {
                Write-Verbose "checking job status for id $($requestResponse.id)"
                $jobResponse = invoke-VenafiRestMethod -UriRoot 'outagedetection/v1' -UriLeaf "certificates/imports/$($requestResponse.id)"
                Start-Sleep 2
            } until (
                $jobResponse.status -in 'COMPLETED', 'FAILED'
            )

            if ( $jobResponse.status -eq 'COMPLETED' ) {
                $jobResponse.results
            }
            else {
                # importing only 1 keycert that fails does not give us any results to return to the user :(
                throw 'Import failed'
            }
        }

        $invokeParams = @{
            InputObject   = $importList
            ScriptBlock   = $sb
            ThrottleLimit = $ThrottleLimit
            ProgressTitle = 'Importing certificates'
        }
        $invokeResponse = Invoke-VenafiParallel @invokeParams

        $invokeResponse | Select-Object -Property fingerprint, status, reason
    }
}
#EndRegion './Public/Import-VcCertificate.ps1' 257
#Region './Public/Import-VdcCertificate.ps1' -1

function Import-VdcCertificate {
    <#
    .SYNOPSIS
    Import one or more certificates

    .DESCRIPTION
    Import one or more certificates with or without private key.
    PowerShell v5 will execute sequentially and v7 will run in parallel.

    .PARAMETER Path
    Path to a certificate file. Provide either this or -Data.

    .PARAMETER Data
    Contents of a certificate to import. Provide either this or -Path.

    .PARAMETER PolicyPath
    Policy path to import the certificate to.
    \ved\policy is prepended if not provided.

    .PARAMETER EnrollmentAttribute
    A hashtable providing any CA attributes to store with the Certificate object, and then submit to the CA during enrollment

    .PARAMETER Name
    Optional name for the certificate object.
    If not provided, the certificate Common Name (CN) is used.
    The derived certificate object name references an existing object (of any class).
    If another certificate has the same CN, a dash (-) integer appends to the CertificateDN. For example, test.venafi.example - 3.
    If not provided and the CN is also missing, the name becomes the first Domain Name System (DNS) Subject Alternative Name (SAN).
    Finally, if none of the above are found, the serial number is used.

    .PARAMETER PrivateKey
    Private key data; requires a value for PrivateKeyPassword.
    For a PEM certificate, the private key is in either the RSA or PKCS#8 format.
    Do not provide for a PKCS#12 certificate as the private key is already included.

    .PARAMETER PrivateKeyPassword
    Password required if providing a private key.
    You can either provide a String, SecureString, or PSCredential.

    .PARAMETER Reconcile
    Controls certificate and corresponding private key replacement.
    By default, this function will import and replace the certificate regardless of whether a past, future, or same version of the certificate exists in Trust Protection Platform.
    By using this parameter, this function will import, but use newest. Only import the certificate when no Certificate object exists with a past, present, or current version of the imported certificate.
    If a match is found between the Certificate object and imported certificate, activate the certificate with the most current 'Valid From' date.
    Archive the unused certificate, even if it is the imported certificate, to the History tab.
    See https://docs.venafi.com/Docs/currentSDK/TopNav/Content/CA/c-CA-Import-ReconciliationRules-tpp.php for a flowchart of the reconciliation algorithm.

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER PassThru
    Return a TppObject representing the newly imported object.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .EXAMPLE
    Import-VdcCertificate -PolicyPath \ved\policy\mycerts -Path c:\www.VenafiPS.com.cer

    Import a certificate

    .EXAMPLE
    gci c:\certs | Import-VdcCertificate -PolicyPath \ved\policy\mycerts

    Import multiple certificates. On PS v7+, the certificates will be imported in parallel.

    .EXAMPLE
    Import-VdcCertificate -PolicyPath mycerts -Data $certData

    Import a certificate from data instead of a path

    .INPUTS
    Path, Data

    .OUTPUTS
    TppObject, if PassThru provided

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Certificates-Import.php
    #>


    [CmdletBinding(DefaultParameterSetName = 'ByFile')]
    [Alias('Import-TppCertificate')]

    param (

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String] $PolicyPath,

        [Parameter(Mandatory, ParameterSetName = 'ByFile', ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'ByFileWithPrivateKey', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-Path ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid path"
                }
            })]
        [Alias('FullName')]
        [String] $Path,

        [Parameter(Mandatory, ParameterSetName = 'ByData', ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'ByDataWithPrivateKey', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [String] $Data,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String] $Name,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [Hashtable] $EnrollmentAttribute,

        [Parameter(Mandatory, ParameterSetName = 'ByFileWithPrivateKey')]
        [Parameter(Mandatory, ParameterSetName = 'ByDataWithPrivateKey')]
        [ValidateNotNullOrEmpty()]
        [String] $PrivateKey,

        [Parameter(ParameterSetName = 'ByFile')]
        [Parameter(ParameterSetName = 'ByData')]
        [Parameter(Mandatory, ParameterSetName = 'ByFileWithPrivateKey')]
        [Parameter(Mandatory, ParameterSetName = 'ByDataWithPrivateKey')]
        [ValidateNotNullOrEmpty()]
        [ValidateScript(
            {
                if ( $_ -is [string] -or $_ -is [securestring] -or $_ -is [pscredential] ) {
                    $true
                }
                else {
                    throw 'Unsupported type. Provide either a String, SecureString, or PSCredential.'
                }
            }
        )]
        [psobject] $PrivateKeyPassword,

        [Parameter()]
        [switch] $Reconcile,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
        $allCerts = [System.Collections.Generic.List[hashtable]]::new()

        $params = @{

            Method  = 'Post'
            UriLeaf = 'certificates/import'
            Body    = @{
                PolicyDN = $PolicyPath | ConvertTo-VdcFullPath
            }
        }

        if ( $PSBoundParameters.ContainsKey('EnrollmentAttribute') ) {
            $updatedAttribute = @($EnrollmentAttribute.GetEnumerator() | ForEach-Object { @{'Name' = $_.name; 'Value' = $_.value } })
            $params.Body.CASpecificAttributes = $updatedAttribute
        }

        if ( $Reconcile ) {
            $params.Body.Reconcile = 'true'
        }

        if ( $PSBoundParameters.ContainsKey('Name') ) {
            $params.Body.ObjectName = $Name
        }

        if ( $PSBoundParameters.ContainsKey('PrivateKeyPassword') ) {
            $params.Body.PrivateKeyPassword = $PrivateKeyPassword | ConvertTo-PlaintextString
        }

        if ( $PSBoundParameters.ContainsKey('PrivateKey') ) {
            $params.Body.PrivateKeyData = $PrivateKey
        }
    }

    process {
        $allCerts.Add(
            @{
                InvokeParams = $params
                Data         = $Data
                Path         = $Path
            }
        )
    }

    end {
        Invoke-VenafiParallel -InputObject $allCerts -ScriptBlock {

            $certData = $PSItem.Data
            if ( $PSItem.Path ) {
                if ((([System.IO.Path]::GetExtension($PSItem.Path)) -in '.pfx', '.p12') -and $PSItem.InvokeParams.Body.PrivateKeyPassword ) {

                    # tpp won't accept a p12 and password so use this workaround to decrypt first
                    $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($PSItem.Path, $PSItem.InvokeParams.Body.PrivateKeyPassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
                    $certData = [System.Convert]::ToBase64String( $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12))

                }
                else {

                    if ($PSVersionTable.PSVersion.Major -lt 6) {
                        $cert = Get-Content $PSItem.Path -Encoding Byte
                    }
                    else {
                        $cert = Get-Content $PSItem.Path -AsByteStream
                    }
                    $certData = [System.Convert]::ToBase64String($cert)
                }

            }

            $params = $PSItem.InvokeParams
            $params.Body.CertificateData = $certData

            try {
                $response = Invoke-VenafiRestMethod @params

                if ( $using:PassThru ) {
                    Get-VdcObject -Guid $response.Guid.trim('{}')
                }
            }
            catch {
                Write-Error $_
            }
        } -ThrottleLimit $ThrottleLimit -ProgressTitle 'Importing certificates'
    }
}
#EndRegion './Public/Import-VdcCertificate.ps1' 246
#Region './Public/Invoke-VcCertificateAction.ps1' -1

function Invoke-VcCertificateAction {
    <#
    .SYNOPSIS
    Perform an action against one or more certificates

    .DESCRIPTION
    One stop shop for certificate actions.
    You can Retire, Recover, Renew, Validate, Provision, or Delete.

    .PARAMETER ID
    ID of the certificate

    .PARAMETER Retire
    Retire a certificate

    .PARAMETER Recover
    Recover a retired certificate

    .PARAMETER Renew
    Requests immediate renewal for an existing certificate.
    If more than 1 application is associated with the certificate, provide -AdditionalParameters @{'Application'='application id'} to specify the id.
    Use -AdditionalParameters to provide additional parameters to the renewal request, see https://developer.venafi.com/tlsprotectcloud/reference/certificaterequests_create.

    .PARAMETER Validate
    Initiates SSL/TLS network validation

    .PARAMETER Delete
    Delete a certificate.
    As only retired certificates can be deleted, this will be performed first.

    .PARAMETER Provision
    Provision a certificate to all associated machine identities.

    .PARAMETER BatchSize
    How many certificates to retire per retirement API call. Useful to prevent API call timeouts.
    Defaults to 1000.
    Not applicable to Renew or Provision.

    .PARAMETER AdditionalParameters
    Additional items specific to the action being taken, if needed.
    See the api documentation for appropriate items, many are in the links in this help.

    .PARAMETER Force
    Force the operation under certain circumstances.
    - During a renewal, force choosing the first CN in the case of multiple CNs as only 1 is supported.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .OUTPUTS
    When using retire and recover, PSCustomObject with the following properties:
        CertificateID - Certificate uuid
        Success - A value of true indicates that the action was successful

    .EXAMPLE
    Invoke-VcCertificateAction -ID '3699b03e-ff62-4772-960d-82e53c34bf60' -Retire

    Perform an action against 1 certificate

    .EXAMPLE
    Invoke-VcCertificateAction -ID '3699b03e-ff62-4772-960d-82e53c34bf60' -Renew -AdditionalParameters @{'Application'='10f71a12-daf3-4737-b589-6a9dd1cc5a97'}

    Perform an action against 1 certificate with additional parameters.
    In this case we are renewing a certificate, but the certificate has multiple applications associated with it.
    Only one certificate and application combination can be renewed at a time so provide the specific application to be renewed.

    .EXAMPLE
    Find-VcCertificate -Version CURRENT -Issuer i1 | Invoke-VcCertificateAction -Renew -AdditionalParameters @{'certificateIssuingTemplateId'='10f71a12-daf3-4737-b589-6a9dd1cc5a97'}

    Find all current certificates issued by i1 and renew them with a different issuer.

    .EXAMPLE
    Invoke-VcCertificateAction -ID '3699b03e-ff62-4772-960d-82e53c34bf60' -Renew -Force

    Renewals can only support 1 CN assigned to a certificate. To force this function to renew and automatically select the first CN, use -Force.

    .EXAMPLE
    Invoke-VcCertificateAction -ID '3699b03e-ff62-4772-960d-82e53c34bf60' -Delete

    Delete a certificate. As only retired certificates can be deleted, it will be retired first.

    .EXAMPLE
    Invoke-VcCertificateAction -ID '3699b03e-ff62-4772-960d-82e53c34bf60' -Delete -Confirm:$false

    Perform an action bypassing the confirmation prompt. Only applicable to Delete.

    .EXAMPLE
    Find-VcObject -Type Certificate -Filter @('certificateStatus','eq','retired') | Invoke-VcCertificateAction -Delete -BatchSize 100

    Search for all retired certificates and delete them using a non default batch size of 100

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html?configUrl=%2Fv3%2Fapi-docs%2Fswagger-config&urls.primaryName=outagedetection-service

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html?urls.primaryName=outagedetection-service#/Certificates/certificateretirement_deleteCertificates

    .NOTES
    If performing a renewal and subjectCN has more than 1 value, only the first will be submitted with the renewal.
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Params being used in paramset check, not by variable')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('certificateID')]
        [guid] $ID,

        [Parameter(Mandatory, ParameterSetName = 'Retire')]
        [switch] $Retire,

        [Parameter(Mandatory, ParameterSetName = 'Recover')]
        [switch] $Recover,

        [Parameter(Mandatory, ParameterSetName = 'Renew')]
        [switch] $Renew,

        [Parameter(Mandatory, ParameterSetName = 'Validate')]
        [switch] $Validate,

        [Parameter(Mandatory, ParameterSetName = 'Delete')]
        [switch] $Delete,

        [Parameter(Mandatory, ParameterSetName = 'Provision')]
        [Parameter(ParameterSetName = 'Renew')]
        [switch] $Provision,

        [Parameter(ParameterSetName = 'Retire')]
        [Parameter(ParameterSetName = 'Recover')]
        [Parameter(ParameterSetName = 'Validate')]
        [Parameter(ParameterSetName = 'Delete')]
        [ValidateRange(1, 10000)]
        [int] $BatchSize = 1000,

        [Parameter(ParameterSetName = 'Renew')]
        [switch] $Force,

        [Parameter()]
        [hashtable] $AdditionalParameters,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        $params = @{
            Method  = 'Post'
            UriRoot = 'outagedetection/v1'
        }

        $allCerts = [System.Collections.Generic.List[string]]::new()
        Write-Verbose $PSCmdlet.ParameterSetName
    }

    process {

        switch ($PSCmdlet.ParameterSetName) {
            'Provision' {
                # get all machine identities associated with certificate
                # since ID is a guid, ensure its converted to string otherwise Find will think it's another filter
                $mi = Find-VcMachineIdentity -Filter @('certificateId', 'eq', $ID.ToString()) | Select-Object -ExpandProperty machineIdentityId

                if ( -not $mi ) {
                    throw "No machine identities found for certificate ID $ID"
                }

                Write-Verbose ('Provisioning certificate ID {0} to machine identities {1}' -f $ID, ($mi -join ','))
                $mi | Invoke-VcWorkflow -Workflow 'Provision'
            }

            'Renew' {

                $out = [pscustomobject] @{
                    oldCertificateId = $ID
                    success          = $false
                    error            = $null
                }

                $thisCert = Get-VcCertificate -ID $ID

                # only current certs can be renewed
                if ( $thisCert.versionType -ne 'CURRENT' ) {
                    $out.error = 'Only certificates with a versionType of CURRENT can be renewed'
                    return $out
                }

                # multiple CN certs are supported by tlspc, but the request/renew api does not support it
                if ( $thisCert.subjectCN.count -gt 1 ) {
                    if ( -not $Force ) {
                        $out.error = 'The certificate you are trying to renew has more than 1 common name. You can either use -Force to automatically choose the first common name or utilize a different process to renew.'
                        return $out
                    }
                }

                switch (([array]$thisCert.application).count) {
                    1 {
                        $thisAppId = $thisCert.application.applicationId
                    }

                    0 {
                        throw 'To renew a certificate at least one application must be assigned'
                    }

                    Default {
                        # more than 1 application assigned
                        if ( $AdditionalParameters.Application ) {
                            $thisAppId = $AdditionalParameters.Application
                        }
                        else {
                            $out.error = 'Multiple applications associated, {0}. Only 1 application can be renewed at a time. Rerun Invoke-VcCertificateAction and add ''-AdditionalParameter @{{''Application''=''application id''}}'' and provide the actual id you would like to renew.' -f (($thisCert.application | ForEach-Object { '{0} ({1})' -f $_.name, $_.applicationId }) -join ',')
                            return $out
                        }
                    }
                }

                if ( -not $thisCert.certificateRequestId ) {
                    $out.error = 'An initial certificate request could not be found. This is required to renew a certificate.'
                    return $out
                }

                $thisCertRequest = Invoke-VenafiRestMethod -UriRoot 'outagedetection/v1' -UriLeaf "certificaterequests/$($thisCert.certificateRequestId)"

                $renewParams = @{
                    existingCertificateId        = $ID
                    certificateIssuingTemplateId = $thisCertRequest.certificateIssuingTemplateId
                    applicationId                = $thisAppId
                    isVaaSGenerated              = $true
                    validityPeriod               = $thisCertRequest.validityPeriod
                    certificateOwnerUserId       = $thisCertRequest.certificateOwnerUserId
                    csrAttributes                = @{}
                }

                switch ($thisCert.PSObject.Properties.Name) {
                    'subjectCN' { $renewParams.csrAttributes.commonName = $thisCert.subjectCN[0] }
                    'subjectO' { $renewParams.csrAttributes.organization = $thisCert.subjectO }
                    'subjectOU' { $renewParams.csrAttributes.organizationalUnits = $thisCert.subjectOU }
                    'subjectL' { $renewParams.csrAttributes.locality = $thisCert.subjectL }
                    'subjectST' { $renewParams.csrAttributes.state = $thisCert.subjectST }
                    'subjectC' { $renewParams.csrAttributes.country = $thisCert.subjectC }
                    'subjectAlternativeNamesByType' {
                        $renewParams.csrAttributes.subjectAlternativeNamesByType = @{
                            'dnsNames'                   = $thisCert.subjectAlternativeNamesByType.dNSName
                            'ipAddresses'                = $thisCert.subjectAlternativeNamesByType.iPAddress
                            'rfc822Names'                = $thisCert.subjectAlternativeNamesByType.rfc822Name
                            'uniformResourceIdentifiers' = $thisCert.subjectAlternativeNamesByType.uniformResourceIdentifier
                        }
                    }
                }

                if ( $AdditionalParameters ) {
                    foreach ($key in $AdditionalParameters.Keys) {
                        $renewParams[$key] = $AdditionalParameters[$key]
                    }
                }

                try {
                    $renewResponse = Invoke-VenafiRestMethod -Method 'Post' -UriRoot 'outagedetection/v1' -UriLeaf 'certificaterequests' -Body $renewParams -ErrorAction Stop
                    $newCertId = $renewResponse.certificateRequests.certificateIds[0]
                    $out | Add-Member @{
                        'renew'         = $renewResponse
                        'certificateID' = $newCertId
                    }   

                    if ( $Provision ) {
                        Write-Verbose "Renew was successful, now provisioning certificate ID $newCertId"

                        # wait a few seconds for machine identities to be reassociated with the new certificate
                        Start-Sleep -Seconds 5

                        $provisionResponse = Invoke-VcCertificateAction -ID $newCertId -Provision
                        $out | Add-Member @{'provision' = $provisionResponse }
                    }

                    $out.success = $true
                }
                catch {
                    $out.error = $_
                }

                return $out
            }

            Default {
                $allCerts.Add($ID)
            }
        }
    }

    end {

        if ( $allCerts.Count -eq 0 ) { return }

        switch ($PSCmdLet.ParameterSetName) {

            'Renew' {
                # handled in Process
            }

            'Retire' {
                $params.UriLeaf = "certificates/retirement"

                if ( $AdditionalParameters ) {
                    $params.Body += $AdditionalParameters
                }

                if ( $PSCmdlet.ShouldProcess('TLSPC', ('Retire {0} certificate(s) in batches of {1}' -f $allCerts.Count, $BatchSize) ) ) {
                    $allCerts | Select-VenBatch -Activity 'Retiring certificates' -BatchSize $BatchSize -BatchType 'string' -TotalCount $allCerts.Count | ForEach-Object {
                        $params.Body = @{"certificateIds" = $_ }

                        $response = Invoke-VenafiRestMethod @params

                        $processedIds = $response.certificates.id

                        foreach ($certId in $_) {
                            [pscustomobject] @{
                                CertificateID = $certId
                                Success       = ($certId -in $processedIds)
                            }
                        }
                    }
                }
            }

            'Recover' {
                $params.UriLeaf = "certificates/recovery"

                if ( $AdditionalParameters ) {
                    $params.Body += $AdditionalParameters
                }

                if ( $PSCmdlet.ShouldProcess('TLSPC', ('Recover {0} certificate(s) in batches of {1}' -f $allCerts.Count, $BatchSize) ) ) {
                    $allCerts | Select-VenBatch -Activity 'Recovering certificates' -BatchSize $BatchSize -BatchType 'string' -TotalCount $allCerts.Count | ForEach-Object {
                        $params.Body = @{"certificateIds" = $_ }

                        $response = Invoke-VenafiRestMethod @params

                        $processedIds = $response.certificates.id

                        foreach ($certId in $_) {
                            [pscustomobject] @{
                                CertificateID = $certId
                                Success       = ($certId -in $processedIds)
                            }
                        }
                    }
                }
            }

            'Validate' {
                $params.UriLeaf = "certificates/validation"

                if ( $PSCmdlet.ShouldProcess('TLSPC', ('Validate {0} certificate(s) in batches of {1}' -f $allCerts.Count, $BatchSize) ) ) {
                    $allCerts | Select-VenBatch -Activity 'Validating certificates' -BatchSize $BatchSize -BatchType 'string' -TotalCount $allCerts.Count | ForEach-Object {
                        $params.Body = @{"certificateIds" = $_ }

                        $null = Invoke-VenafiRestMethod @params
                    }
                }
            }

            'Delete' {

                $params.UriLeaf = "certificates/deletion"

                if ( $PSCmdlet.ShouldProcess('TLSPC', ('Delete {0} certificate(s) in batches of {1}' -f $allCerts.Count, $BatchSize) ) ) {
                    $null = $allCerts | Invoke-VcCertificateAction -Retire -BatchSize $BatchSize -Confirm:$false
                    $allCerts | Select-VenBatch -Activity 'Deleting certificates' -BatchSize $BatchSize -BatchType 'string' -TotalCount $allCerts.Count | ForEach-Object {
                        $params.Body = @{"certificateIds" = $_ }

                        $null = Invoke-VenafiRestMethod @params
                    }
                }
            }
        }

    }
}
#EndRegion './Public/Invoke-VcCertificateAction.ps1' 387
#Region './Public/Invoke-VcWorkflow.ps1' -1

function Invoke-VcWorkflow {
    <#
    .SYNOPSIS
    Start a machine or machine identity workflow

    .DESCRIPTION
    Start a workflow to either test machine credentials or provision or discover machine identities

    .PARAMETER ID
    Machine or machine identity id for the workflow to trigger.
    Workflows 'Test' and 'GetConfig' require the machine ID.
    Workflows 'Provision' and 'Discover' require the machine identity ID.

    .PARAMETER Workflow
    The name of the workflow to trigger.
    Valid values are 'Test', 'GetConfig', 'Provision', or 'Discover'.

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .EXAMPLE
    Invoke-VcWorkflow -ID '1345baf1-fc56-49b7-aa03-78e35bfe0a1a' -Workflow 'Provision'

    ID Success WorkflowName WorkflowID
    -- ------- ------------ ----------
    1345baf1-fc56-49b7-aa03-78e35bfe0a1a True Provision 345b9d33-8c8a-4d4b-9fea-124f3a72f957

    Trigger provisioning

    .EXAMPLE
    Invoke-VcWorkflow -ID '1345baf1-fc56-49b7-aa03-78e35bfe0a1a' -Workflow 'Test'

    ID : 1345baf1-fc56-49b7-aa03-78e35bfe0a1a
    Success : False
    WorkflowName : Test
    WorkflowID : 345b9d33-8c8a-4d4b-9fea-124f3a72f957
    Error : failed to connect to Citrix ADC: [ERROR] nitro-go: Failed to create resource of type login, name=login, err=failed: 401 Unauthorized ({ "errorcode": 354,
                       "message": "Invalid username or password", "severity": "ERROR" })

    Trigger test connection, but it failed

    .EXAMPLE
    Find-VcObject -Type MachineIdentity -Filter @('and', @('certificateValidityEnd', 'lt', (get-date).AddDays(30)), @('certificateValidityEnd', 'gt', (get-date))) | ForEach-Object {
        $renewResult = $_ | Invoke-VenafiCertificateAction -Renew
        # optionally add renew validation
        $_ | Invoke-VcWorkflow -Workflow 'Provision'
    }

    ID Success WorkflowName WorkflowID
    -- ------- ------------ ----------
    1345baf1-fc56-49b7-aa03-78e35bfe0a1a True Provision 345b9d33-8c8a-4d4b-9fea-124f3a72f957
    89fa4370-2026-11ee-8a18-ff9579bb988e True Provision 7598917c-7027-4927-be73-e592bcc4c567

    Renew and provision all machine identities with certificates expiring within 30 days

    .INPUTS
    ID

    .OUTPUTS
    pscustomobject
    #>



    [CmdletBinding()]
    [Alias('Invoke-VaasWorkflow')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('machineID', 'machineIdentityID')]
        [string] $ID,

        [Parameter()]
        [ValidateSet('Test', 'GetConfig', 'Provision', 'Discover')]
        [string] $Workflow = 'Test',

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
        $allIDs = [System.Collections.Generic.List[string]]::new()
    }

    process {
        $allIDs.Add($ID)
    }

    end {

        Invoke-VenafiParallel -InputObject $allIDs -ScriptBlock {

            $thisID = $PSItem
            $workflow = $using:Workflow
            $thisWebSocketID = (New-Guid).Guid

            try {

                $WS = New-Object System.Net.WebSockets.ClientWebSocket
                $CT = New-Object System.Threading.CancellationToken

                if ( $VenafiSession -is [PSCustomObject] ) {
                    $server = $VenafiSession.Server.Replace('https://', '')
                    $WS.Options.SetRequestHeader("tppl-api-key", $VenafiSession.Key.GetNetworkCredential().password)
                }
                else {
                    $VcRegions | ConvertTo-Json | Write-Verbose
                    if ( $env:VC_SERVER ) {
                        $server = $env:VC_SERVER
                    }
                    else {
                        # default to US region
                        $server = ($script:VcRegions).'us'
                    }
                    $server = $server.Replace('https://', '')
                    $WS.Options.SetRequestHeader("tppl-api-key", $VenafiSession)
                }
                $URL = 'wss://{0}/ws/notificationclients/{1}' -f $server, $thisWebSocketID

                #Get connected
                $Conn = $WS.ConnectAsync($URL, $CT)

                While ( !$Conn.IsCompleted ) {
                    Start-Sleep -Milliseconds 100
                }

                Write-Verbose "Connecting to $($URL)..."
                $Size = 2048
                $Array = [byte[]] @(, 0) * $Size

                #Send Starting Request
                $Command = [System.Text.Encoding]::UTF8.GetBytes("ACTION=Command")
                $Send = New-Object System.ArraySegment[byte] -ArgumentList @(, $Command)
                $Conn = $WS.SendAsync($Send, [System.Net.WebSockets.WebSocketMessageType]::Text, $true, $CT)

                While (!$Conn.IsCompleted) {
                    Start-Sleep -Milliseconds 100
                }

                #Start reading the received items
                $Recv = New-Object System.ArraySegment[byte] -ArgumentList @(, $Array)
                $Conn = $WS.ReceiveAsync($Recv, $CT)

                Write-Verbose 'Triggering workflow'

                $triggerParams = @{
                    UriLeaf = "machines/$thisID/workflows"
                    Method  = 'Post'
                    Body    = @{
                        'workflowInput' = @{
                            'wsClientId' = $thisWebSocketID
                        }
                        'workflowName'  = 'testConnection'
                    }

                }

                switch ($Workflow) {
                    'GetConfig' {
                        $triggerParams.Body.workflowName = 'getTargetConfiguration'
                    }

                    'Provision' {
                        $triggerParams.Body.workflowName = 'provisionCertificate'
                        $triggerParams.UriLeaf = "machineidentities/$thisID/workflows"
                    }

                    'Discover' {
                        $triggerParams.Body.workflowName = 'discoverCertificates'
                        $triggerParams.UriLeaf = "machines/$thisID/workflows"
                    }
                }

                $null = Invoke-VenafiRestMethod @triggerParams

                While (!$Conn.IsCompleted) {
                    Start-Sleep -Milliseconds 100
                }

                $response = ''
                $Recv.Array[0..($Conn.Result.Count - 1)] | ForEach-Object { $response += [char]$_ }

                Write-Verbose $response

                $responseObj = $response | ConvertFrom-Json

                $out = [pscustomobject]@{
                    ID           = $thisID
                    Success      = $true
                    WorkflowName = $Workflow
                    WorkflowID   = $thisWebSocketID
                }

                if ( $responseObj.data.result -ne $true ) {
                    $out.Success = $false
                    $out | Add-Member @{'Error' = $responseObj.data.result.message }
                }

                $out

            }
            finally {
                if ( $WS ) {
                    $WS.Dispose()
                }
            }
        } -ThrottleLimit $ThrottleLimit -ProgressTitle 'Invoking workflow'
    }
}
#EndRegion './Public/Invoke-VcWorkflow.ps1' 221
#Region './Public/Invoke-VdcCertificateAction.ps1' -1

function Invoke-VdcCertificateAction {
    <#
    .SYNOPSIS
    Perform an action against a certificate

    .DESCRIPTION
    One stop shop for basic certificate actions.
    You can Retire, Reset, Renew, Push, Validate, Revoke, or Delete.
    If using PowerShell v7+, this will be run in parallel.

    .PARAMETER Path
    Full path to the certificate

    .PARAMETER Disable
    Disable a certificate

    .PARAMETER Reset
    Reset the state of a certificate and its associated applications

    .PARAMETER Renew
    Requests immediate renewal for an existing certificate

    .PARAMETER Push
    Provisions the same certificate and private key to one or more devices or servers.
    The certificate must be associated with one or more Application objects.
    By default, this will provision the certificate to all associated applications.
    To specify a subset of applications, use the AdditionalParameter parameter as shown in the examples.

    .PARAMETER Validate
    Initiates SSL/TLS network validation

    .PARAMETER Revoke
    Sends a revocation request to the certificate CA

    .PARAMETER Delete
    Delete a certificate.

    .PARAMETER AdditionalParameter
    Additional items specific to the action being taken, if needed.
    See the examples for suggestions.

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    PSCustomObject with the following properties:
        CertificateID - Certificate path
        Success - A value of true indicates that the action was successful
        Error - Indicates any errors that occurred. Not returned when Success is true

    .EXAMPLE
    Invoke-VdcCertificateAction -Path '\VED\Policy\My folder\app.mycompany.com' -Revoke

    Perform an action

    .EXAMPLE
    Invoke-VdcCertificateAction -Path '\VED\Policy\My folder\app.mycompany.com' -Delete -Confirm:$false

    Perform an action bypassing the confirmation prompt. Only applicable to revoke, disable, and delete.

    .EXAMPLE
    Invoke-VdcCertificateAction -Path '\VED\Policy\My folder\app.mycompany.com' -Revoke -Confirm:$false | Invoke-VdcCertificateAction -Delete -Confirm:$false

    Chain multiple actions together

    .EXAMPLE
    Invoke-VdcCertificateAction -Path '\VED\Policy\My folder\app.mycompany.com' -Push -AdditionalParameter @{'PushToAll'=$false; 'ApplicationDN'=@('\VED\Policy\My folder\app.mycompany.com\app1','\VED\Policy\My folder\app.mycompany.com\app2')}

    Perform a push to a subset of associated applications overwriting the default of pushing to all.

    .EXAMPLE
    Invoke-VdcCertificateAction -Path '\VED\Policy\My folder\app.mycompany.com' -Revoke -AdditionalParameter @{'Comments'='Key compromised'; 'Reason'='3'}

    Perform a revoke sending additional parameters.

    Comments: The details about why the certificate is being revoked. Be sure the comment length does not exceed the limitation from the CA. When accepting a revocation request, they may handle data outside their limits differently. For Entrust CA or EntrustPKI CA, the maximum character limit is 250.

    Values for Reason can be:
        0: None
        1: User key compromised
        2: CA key compromised
        3: User changed affiliation
        4: Certificate superseded
        5: Original use no longer valid


    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Certificates-Reset.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Certificates-renew.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Certificates-Push.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Certificates-Validate.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Certificates-revoke.php

    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Params being used in paramset check, not by variable')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('CertificateID', 'id')]
        [string] $Path,

        [Parameter(Mandatory, ParameterSetName = 'Disable')]
        [Alias('Retire')]
        [switch] $Disable,

        [Parameter(Mandatory, ParameterSetName = 'Reset')]
        [switch] $Reset,

        [Parameter(Mandatory, ParameterSetName = 'Renew')]
        [switch] $Renew,

        [Parameter(Mandatory, ParameterSetName = 'Push')]
        [switch] $Push,

        [Parameter(Mandatory, ParameterSetName = 'Validate')]
        [switch] $Validate,

        [Parameter(Mandatory, ParameterSetName = 'Revoke')]
        [switch] $Revoke,

        [Parameter(Mandatory, ParameterSetName = 'Delete')]
        [switch] $Delete,

        [Parameter()]
        [hashtable] $AdditionalParameter,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $allCerts = [System.Collections.Generic.List[string]]::new()
    }

    process {

        $addThis = $true

        if ( $PsCmdlet.ParameterSetName -in 'Delete', 'Revoke', 'Disable' ) {
            $addThis = $PSCmdlet.ShouldProcess($Path, ('{0} certificate' -f $PsCmdlet.ParameterSetName))
        }

        if ( $addThis ) { $allCerts.Add($Path) }

    }

    end {

        if ( $allCerts.Count -eq 0 ) { return }
        $action = $PsCmdlet.ParameterSetName

        Invoke-VenafiParallel -InputObject $allCerts -ScriptBlock {

            $action = $using:action
            $thisCert = $PSItem

            $params = @{
                Method = 'Post'
            }

            $returnObject = [PSCustomObject]@{
                Path    = $PSItem
                Success = $true
                Error   = $null
            }

            # at times, we don't want to call an api in the process block
            $performInvoke = $true

            switch ($action) {
                'Disable' {
                    $performInvoke = $false

                    try {
                        Set-VdcAttribute -Path $thisCert -Attribute @{ 'Disabled' = '1' }
                    }
                    catch {
                        $returnObject.Success = $false
                        $returnObject.Error = $_
                    }
                }

                'Reset' {
                    $params.UriLeaf = 'Certificates/Reset'
                    $params.Body = @{CertificateDN = $thisCert }
                }

                'Renew' {
                    $params.UriLeaf = 'Certificates/Renew'
                    $params.Body = @{CertificateDN = $thisCert }
                }

                'Push' {
                    $params.UriLeaf = 'Certificates/Push'
                    $params.Body = @{
                        CertificateDN = $thisCert
                        'PushToAll'   = $true
                    }
                }

                'Validate' {
                    $params.UriLeaf = 'Certificates/Validate'
                    $params.Body = @{CertificateDNs = @($thisCert) }
                }

                'Revoke' {
                    $params.UriLeaf = 'Certificates/Revoke'
                    $params.Body = @{CertificateDN = $thisCert }
                }

                'Delete' {
                    $performInvoke = $false
                    Remove-VdcCertificate -Path $thisCert -Confirm:$false
                }
            }

            if ( $AdditionalParameter ) {
                $params.Body += $AdditionalParameter
            }

            if ( $performInvoke ) {
                try {
                    $null = Invoke-VenafiRestMethod @params -FullResponse
                }
                catch {
                    $returnObject.Success = $false
                    $returnObject.Error = $_
                }
            }

            # return path so another function can be called
            $returnObject

        } -ThrottleLimit $ThrottleLimit -ProgressTitle ('{0} certificates' -f $PsCmdlet.ParameterSetName)




    }
}
#EndRegion './Public/Invoke-VdcCertificateAction.ps1' 269
#Region './Public/Invoke-VenafiRestMethod.ps1' -1

function Invoke-VenafiRestMethod {
    <#
    .SYNOPSIS
    Ability to execute REST API calls which don't exist in a dedicated function yet

    .DESCRIPTION
    Ability to execute REST API calls which don't exist in a dedicated function yet

    .PARAMETER VenafiSession
    VenafiSession object from New-VenafiSession.
    For typical calls to New-VenafiSession, the object will be stored as a session object named $VenafiSession.
    Otherwise, if -PassThru was used, provide the resulting object.

    .PARAMETER Method
    API method, either get, post, patch, put or delete.

    .PARAMETER UriLeaf
    Path to the api endpoint excluding the base url and site, eg. certificates/import

    .PARAMETER Header
    Optional additional headers. The authorization header will be included automatically.

    .PARAMETER Body
    Optional body to pass to the endpoint

    .INPUTS
    None

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Invoke-VenafiRestMethod -Method Delete -UriLeaf 'Discovery/{1345311e-83c5-4945-9b4b-1da0a17c45c6}'
    Api call

    .EXAMPLE
    Invoke-VenafiRestMethod -Method Post -UriLeaf 'Certificates/Revoke' -Body @{'CertificateDN'='\ved\policy\mycert.com'}
    Api call with optional payload

    #>


    [CmdletBinding(DefaultParameterSetName = 'Session')]
    [Alias('Invoke-TppRestMethod')]

    param (
        [Parameter(ParameterSetName = 'Session')]
        [AllowNull()]
        [Alias('Key', 'AccessToken')]
        [psobject] $VenafiSession,

        [Parameter(Mandatory, ParameterSetName = 'URL')]
        [ValidateNotNullOrEmpty()]
        [Alias('ServerUrl')]
        [String] $Server,

        [Parameter(ParameterSetName = 'URL')]
        [Alias('UseDefaultCredentials')]
        [switch] $UseDefaultCredential,

        [Parameter(ParameterSetName = 'URL')]
        [X509Certificate] $Certificate,

        [Parameter()]
        [ValidateSet("Get", "Post", "Patch", "Put", "Delete", 'Head')]
        [String] $Method = 'Get',

        [Parameter()]
        [String] $UriRoot = 'vedsdk',

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String] $UriLeaf,

        [Parameter()]
        [hashtable] $Header,

        [Parameter()]
        [Hashtable] $Body,

        [Parameter()]
        [switch] $FullResponse,

        [Parameter()]
        [Int32] $TimeoutSec = 0,

        [Parameter()]
        [switch] $SkipCertificateCheck
    )

    $params = @{
        Method          = $Method
        ContentType     = 'application/json'
        UseBasicParsing = $true
        TimeoutSec      = $TimeoutSec
    }


    if ( $PSCmdLet.ParameterSetName -eq 'Session' ) {

        # if ( -not $VenafiSession ) {
        if ( $env:VDC_TOKEN ) {
            $VenafiSession = $env:VDC_TOKEN
            Write-Verbose 'Using TLSPDC token environment variable'
        }
        elseif ( $env:VC_KEY ) {
            $VenafiSession = $env:VC_KEY
            Write-Verbose 'Using TLSPC key environment variable'
        }
        elseif ( $PSBoundParameters.VenafiSession ) {
            Write-Verbose 'Using session provided'
        }
        elseif ($script:VenafiSessionNested) {
            $VenafiSession = $script:VenafiSessionNested
            Write-Verbose 'Using nested session'
        }
        elseif ( $script:VenafiSession ) {
            $VenafiSession = $script:VenafiSession
            Write-Verbose 'Using script session'
        }
        else {
            throw 'Please run New-VenafiSession or provide a TLSPC key or TLSPDC token.'
        }
        # }

        switch ($VenafiSession.GetType().Name) {
            'PSCustomObject' {
                $Server = $VenafiSession.Server
                $platform = $VenafiSession.Platform
                if ( $VenafiSession.Platform -eq 'VC' ) {
                    $auth = $VenafiSession.Key.GetNetworkCredential().password
                }
                else {
                    # TLSPDC
                    $auth = $VenafiSession.Token.AccessToken.GetNetworkCredential().password
                }
                $SkipCertificateCheck = $VenafiSession.SkipCertificateCheck
                $params.TimeoutSec = $VenafiSession.TimeoutSec
                break
            }

            'String' {
                $auth = $VenafiSession

                if ( Test-IsGuid($VenafiSession) ) {
                    if ( $env:VC_SERVER ) {
                        $Server = $env:VC_SERVER
                    }
                    else {
                        # default to US region
                        $Server = ($script:VcRegions).'us'
                    }
                    if ( $Server -notlike 'https://*') {
                        $Server = 'https://{0}' -f $Server
                    }
                    $platform = 'VC'
                }
                else {
                    # TLSPDC access token
                    # get server from environment variable
                    if ( -not $env:VDC_SERVER ) {
                        throw 'VDC_SERVER environment variable was not found'
                    }
                    $Server = $env:VDC_SERVER
                    if ( $Server -notlike 'https://*') {
                        $Server = 'https://{0}' -f $Server
                    }
                    $platform = 'VDC'
                }
            }

            Default {
                throw "Unknown session '$VenafiSession'. Please run New-VenafiSession or provide a TLSPC key or TLSPDC token."
            }
        }

        # set auth
        switch ($platform) {
            'VC' {
                $allHeaders = @{
                    "tppl-api-key" = $auth
                }
                if ( -not $PSBoundParameters.ContainsKey('UriRoot') ) {
                    $UriRoot = 'v1'
                }
            }

            'VDC' {
                $allHeaders = @{
                    'Authorization' = 'Bearer {0}' -f $auth
                }
            }

            Default {}
        }
    }

    $params.Uri = '{0}/{1}/{2}' -f $Server, $UriRoot, $UriLeaf

    # append any headers passed in
    if ( $Header ) { $allHeaders += $Header }
    # if there are any headers, add to the rest payload
    # in the case of inital authentication, eg, there won't be any
    if ( $allHeaders ) { $params.Headers = $allHeaders }

    if ( $UseDefaultCredential.IsPresent -and $Certificate ) {
        throw 'You cannot use UseDefaultCredential and Certificate parameters together'
    }

    if ( $UseDefaultCredential.IsPresent ) {
        $params.Add('UseDefaultCredentials', $true)
    }

    if ( $Body ) {
        switch ($Method.ToLower()) {
            'head' {
                # a head method requires the params be provided as a query string, not body
                # invoke-webrequest does not do this so we have to build the string manually
                $newUri = New-HttpQueryString -Uri $uri -QueryParameter $Body
                $params.Uri = $newUri
                $params.Body = $null
            }

            'get' {
                $params.Body = $Body
            }

            Default {
                $preJsonBody = $Body
                $params.Body = (ConvertTo-Json $Body -Depth 20 -Compress)
                # for special characters, we need to set the content type to utf-8
                $params.ContentType = "application/json; charset=utf-8"
            }
        }
    }

    if ( $preJsonBody ) {
        $paramsToWrite = $params.Clone()
        $paramsToWrite.Body = $preJsonBody
        $paramsToWrite | Write-VerboseWithSecret
    }
    else {
        $params | Write-VerboseWithSecret
    }

    # ConvertTo-Json, used in Write-VerboseWithSecret, has an issue with certificates
    # add this param after
    if ( $Certificate ) {
        $params.Add('Certificate', $Certificate)
    }

    if ( $SkipCertificateCheck -or $env:VENAFIPS_SKIP_CERT_CHECK -eq '1' ) {
        if ( $PSVersionTable.PSVersion.Major -lt 6 ) {
            if ( [System.Net.ServicePointManager]::CertificatePolicy.GetType().FullName -ne 'TrustAllCertsPolicy' ) {
                add-type @"
                using System.Net;
                using System.Security.Cryptography.X509Certificates;
                public class TrustAllCertsPolicy : ICertificatePolicy {
                    public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) {
                        return true;
                    }
                }
"@

                [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
            }
        }
        else {
            $params.Add('SkipCertificateCheck', $true)
        }
    }

    $oldProgressPreference = $ProgressPreference
    $ProgressPreference = 'SilentlyContinue'

    try {
        $verboseOutput = $($response = Invoke-WebRequest @params -ErrorAction Stop) 4>&1
        $verboseOutput.Message | Write-VerboseWithSecret
    }
    catch {

        # if trying with a slash below doesn't work, we want to provide the original error
        $originalError = $_

        $statusCode = [int]$originalError.Exception.Response.StatusCode
        Write-Verbose ('Response status code {0}' -f $statusCode)

        switch ($statusCode) {
            403 {

                $permMsg = ''

                # get scope details for tpp
                if ( $platform -ne 'VC' ) {
                    $callingFunction = @(Get-PSCallStack)[1].InvocationInfo.MyCommand.Name
                    $callingFunctionScope = ($script:functionConfig).$callingFunction.TppTokenScope
                    if ( $callingFunctionScope ) { $permMsg += "$callingFunction requires a token scope of '$callingFunctionScope'." }

                    $rejectedScope = Select-String -InputObject $originalError.ErrorDetails.Message -Pattern 'Grant rejected scope ([^.]+)'

                    if ( $rejectedScope.Matches.Groups.Count -gt 1 ) {
                        $permMsg += (" The current scope of {0} is insufficient." -f $rejectedScope.Matches.Groups[1].Value.Replace('\u0027', "'"))
                    }
                    $permMsg += ' Call New-VenafiSession with the correct scope.'
                }
                else {
                    $permMsg = $originalError.ErrorDetails.Message
                }


                throw $permMsg
            }

            409 {
                # 409 = item already exists. some functions use this for a 'force' option, eg. Set-VdcPermission
                # treat this as non error/exception if FullResponse provided
                if ( $FullResponse ) {
                    $response = [pscustomobject] @{
                        StatusCode = $statusCode
                        Error      =
                        try {
                            $originalError.ErrorDetails.Message | ConvertFrom-Json
                        }
                        catch {
                            $originalError.ErrorDetails.Message
                        }
                    }
                }
                else {
                    throw $originalError
                }
            }

            Default {
                throw $originalError
            }
        }

    }
    finally {
        $ProgressPreference = $oldProgressPreference
    }

    if ( $FullResponse ) {
        $response
    }
    else {
        if ( $response.Content ) {
            try {
                $response.Content | ConvertFrom-Json
            }
            catch {
                throw ('Invalid JSON response {0}' -f $response.Content)
            }
        }
    }
}
#EndRegion './Public/Invoke-VenafiRestMethod.ps1' 356
#Region './Public/Move-VdcObject.ps1' -1

function Move-VdcObject {
    <#
    .SYNOPSIS
    Move an object of any type

    .DESCRIPTION
    Move an object of any type from one policy to another.
    A rename can be done at the same time as the move by providing a full target path including the new object name.

    .PARAMETER SourcePath
    Full path to an existing object in TLSPDC

    .PARAMETER TargetPath
    New path. This can either be an existing policy and the existing object name will be kept or a full path including a new object name.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    SourcePath (Path)

    .OUTPUTS
    n/a

    .EXAMPLE
    Move-VdcObject -SourceDN '\VED\Policy\My Folder\mycert.company.com' -TargetDN '\VED\Policy\New Folder\mycert.company.com'
    Move object to a new Policy folder

    .EXAMPLE
    Find-VdcCertificate -Path '\ved\policy\certs' | Move-VdcObject -TargetDN '\VED\Policy\New Folder'
    Move all objects found in 1 folder to another

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Move-VdcObject/

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Test-VdcObject/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Move-VdcObject.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-renameobject.php

    #>


    [CmdletBinding(SupportsShouldProcess)]
    [Alias('Move-TppObject')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('SourceDN', 'Path')]
        [String] $SourcePath,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('TargetDN')]
        [String] $TargetPath,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        # determine if target is a policy or other object
        # if policy, we'll need to append the object name in the process block when moving
        try {
            $targetObject = Get-VdcObject -Path $TargetPath -ErrorAction SilentlyContinue
        }
        catch {
            # expected if target is a new object name and not policy
        }
        $targetIsPolicy = ($targetObject.TypeName -eq 'Policy')
    }

    process {

        $params = @{
            Method        = 'Post'
            UriLeaf       = 'config/RenameObject'
            Body          = @{
                ObjectDN    = $SourcePath
                NewObjectDN = $TargetPath
            }
        }

        # if target is a policy, append the object name from source
        if ( $targetIsPolicy ) {
            # get object name, issue 129
            $childPath = $SourcePath.Split('\')[-1]
            $params.Body.NewObjectDN = '{0}\{1}' -f $targetObject.Path, $childPath
        }

        if ( $PSCmdlet.ShouldProcess($SourcePath, ('Move to {0}' -f $params.Body.NewObjectDN)) ) {
            $response = Invoke-VenafiRestMethod @params

            if ( $response.Result -ne 1 ) {
                Write-Error $response.Error
            }
        }
    }
}
#EndRegion './Public/Move-VdcObject.ps1' 125
#Region './Public/New-VcApplication.ps1' -1

function New-VcApplication {
    <#
    .SYNOPSIS
    Create a new application

    .DESCRIPTION
    Create a new application with optional details

    .PARAMETER Name
    Application name

    .PARAMETER Owner
    List of user and/or team IDs or names to be owners

    .PARAMETER Description
    Application description

    .PARAMETER IssuingTemplate
    1 or more issuing template IDs or names to associate with the new application

    .PARAMETER Fqdn
    Fully qualified domain names to assign to the application

    .PARAMETER IPRange
    IP ranges to assign to the application

    .PARAMETER Port
    Ports to assign to the application.
    Required if either Fqdn or IPRange are specified.

    .PARAMETER PassThru
    Return newly created application object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .OUTPUTS
    PSCustomObject, if PassThru provided

    .EXAMPLE
    New-VcApplication -Name 'MyNewApp' -Owner '4ba1e64f-12ad-4a34-a0e2-bc4481a56f7d','greg@venafi.com'

    Create a new application

    .EXAMPLE
    New-VcApplication -Name 'MyNewApp' -Owner '4ba1e64f-12ad-4a34-a0e2-bc4481a56f7d' -CertificateIssuingTemplate @{'9c9618e8-6b4c-4a1c-8c11-902c9b2676d3'=$null} -Description 'this app is awesome' -Fqdn 'me.com' -IPRange '1.2.3.4/24' -Port '443','9443'

    Create a new application with optional details

    .EXAMPLE
    New-VcApplication -Name 'MyNewApp' -Owner '4ba1e64f-12ad-4a34-a0e2-bc4481a56f7d' -PassThru

    Create a new application and return the newly created application object

    #>


    [CmdletBinding(DefaultParameterSetName = 'NoTarget', SupportsShouldProcess)]
    [Alias('New-VaasApplication')]

    param (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string] $Name,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string[]] $Owner,

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [String] $Description,

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string[]] $IssuingTemplate,

        [Parameter(ParameterSetName = 'Fqdn', Mandatory, ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'FqdnIPRange', Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string[]] $Fqdn,

        [Parameter(ParameterSetName = 'IPRange', Mandatory, ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'FqdnIPRange', Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string[]] $IPRange,

        [Parameter(ParameterSetName = 'Fqdn', Mandatory, ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'IPRange', Mandatory, ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'FqdnIPRange', Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string[]] $Port,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        # determine if user or team and build the payload
        $ownerHash = foreach ($thisOwner in $Owner) {

            $team = Get-VcTeam -ID $thisOwner -ErrorAction SilentlyContinue
            if ( $team ) {
                @{ 'ownerId' = $team.teamId; 'ownerType' = 'TEAM' }
            }
            else {
                $user = Get-VcIdentity -ID $thisOwner -ErrorAction SilentlyContinue
                if ( $user ) {
                    @{ 'ownerId' = $user.userId; 'ownerType' = 'USER' }
                }
                else {
                    Write-Error "Owner $thisOwner not found"
                    Continue
                }
            }
        }

        $templateHash = @{}
        foreach ($thisTemplateID in $IssuingTemplate) {
            $thisTemplate = Get-VcIssuingTemplate -ID $thisTemplateID
            if ( $thisTemplate ) {
                $templateHash.Add($thisTemplate.name, $thisTemplate.issuingTemplateId)
            }
            else {
                throw ('Template ID {0} not found' -f $thisTemplateID)
            }
        }

        # if ( $PSBoundParameters.ContainsKey('IssuingTemplate') ) {
        # $IssuingTemplate.GetEnumerator() | ForEach-Object {
        # if ( $_.Value ) {
        # $templateHash.Add($_.Value, $_.Key)
        # }
        # else {
        # $thisTemplate = Get-VcIssuingTemplate -ID $_.Key -ErrorAction SilentlyContinue
        # if ( $thisTemplate ) {
        # $templateHash.Add($thisTemplate.Name, $_.Key)
        # }
        # else {
        # Write-Error ('Template ID {0} not found' -f $_.Key)
        # Continue
        # }
        # }
        # }
        # }
    }

    process {

        Write-Verbose $PSCmdlet.ParameterSetName

        if ( -not $ownerHash ) {
            return
        }

        $params = @{
            VenafiSession = $VenafiSession
            Method        = 'Post'
            UriRoot       = 'outagedetection/v1'
            UriLeaf       = 'applications'
            Body          = @{
                name             = $Name
                ownerIdsAndTypes = [array] $ownerHash
            }
            FullResponse  = $true
        }

        if ( $PSBoundParameters.ContainsKey('Description') ) {
            $params.Body.description = $Description
        }

        if ( $templateHash.Count -gt 0 ) {
            $params.Body.certificateIssuingTemplateAliasIdMap = $templateHash
        }

        if ( $PSBoundParameters.ContainsKey('Fqdn') ) {
            $params.Body.fullyQualifiedDomainNames = $Fqdn
        }

        if ( $PSBoundParameters.ContainsKey('IPRange') ) {
            $params.Body.ipRanges = $IPRange
        }

        if ( $PSBoundParameters.ContainsKey('Port') ) {
            $params.Body.ports = $Port
        }

        if ( $PSCmdlet.ShouldProcess($Name, 'Create application') ) {

            try {
                $response = Invoke-VenafiRestMethod @params
                switch ( $response.StatusCode ) {

                    201 {
                        if ( $PassThru ) {
                            $response.Content | ConvertFrom-Json |
                            Select-Object -ExpandProperty applications | Select-Object -Property @{'n' = 'applicationId'; 'e' = { $_.id } }, * -ExcludeProperty id
                        }
                    }

                    409 {
                        throw "$Name already exists"
                    }

                    default {
                        throw $response
                    }
                }
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($PSItem)
            }
        }
    }
}
#EndRegion './Public/New-VcApplication.ps1' 222
#Region './Public/New-VcCertificate.ps1' -1

function New-VcCertificate {
    <#
    .SYNOPSIS
    Create certificate request

    .DESCRIPTION
    Create certificate request from automated secure keypair details or CSR

    .PARAMETER Application
    Application name (wildcards supported) or id to associate this certificate.

    .PARAMETER IssuingTemplate
    Issuing template name (wildcards supported) or id to use.
    The template must be available with the selected Application.

    .PARAMETER Csr
    CSR in PKCS#10 format which conforms to the rules of the issuing template

    .PARAMETER CommonName
    Common name (CN)

    .PARAMETER Organization
    The Organization field for the certificate Subject DN

    .PARAMETER OrganizationalUnit
    One or more departments or divisions within the organization that is responsible for maintaining the certificate

    .PARAMETER City
    The City/Locality field for the certificate Subject DN

    .PARAMETER State
    The State field for the certificate Subject DN

    .PARAMETER Country
    The Country field for the certificate Subject DN

    .PARAMETER SanDns
    One or more subject alternative name dns entries

    .PARAMETER SanIP
    One or more subject alternative name ip address entries

    .PARAMETER SanUri
    One or more subject alternative name uri entries

    .PARAMETER SanEmail
    One or more subject alternative name email entries

    .PARAMETER ValidUntil
    Date at which the certificate becomes invalid.
    Days and hours are supported, not minutes.

    .PARAMETER PassThru
    Return the certificate request.
    If the certificate was successfully issued, it will be returned as the property 'certificate'.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided directly.

    .INPUTS
    CommonName

    .OUTPUTS
    pscustomobject, if PassThru is provided

    .EXAMPLE
    New-VcCertificate -Application 'MyApp' -IssuingTemplate 'MSCA - 1 year' -CommonName 'app.mycert.com'

    Create certificate

    .EXAMPLE
    New-VcCertificate -Application 'MyApp' -IssuingTemplate 'MSCA - 1 year' -CommonName 'app.mycert.com' -SanIP '1.2.3.4'

    Create certificate with optional SAN data

    .EXAMPLE
    New-VcCertificate -Application 'MyApp' -IssuingTemplate 'MSCA - 1 year' -CommonName 'app.mycert.com' -ValidUntil (Get-Date).AddMonths(6)

    Create certificate with specific validity

    .EXAMPLE
    New-VcCertificate -Application 'MyApp' -IssuingTemplate 'MSCA - 1 year' -CommonName 'app.mycert.com' -PassThru

    Create certificate and return the created object

    .EXAMPLE
    New-VcCertificate -Application 'MyApp' -IssuingTemplate 'MSCA - 1 year' -Csr "-----BEGIN CERTIFICATE REQUEST-----\nMIICYzCCAUsCAQAwHj....BoiNIqtVQxFsfT+\n-----END CERTIFICATE REQUEST-----\n"

    Create certificate with a CSR

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html?urls.primaryName=outagedetection-service#/Certificate%20Request/certificaterequests_create

    #>


    [CmdletBinding(DefaultParameterSetName = 'Ask', SupportsShouldProcess)]
    [Alias('New-VaasCertificate')]

    param (

        [Parameter(Mandatory)]
        [String] $Application,

        [Parameter(Mandatory)]
        [String] $IssuingTemplate,

        [Parameter(ParameterSetName = 'Csr', Mandatory)]
        [string] $Csr,

        [Parameter(ParameterSetName = 'Ask', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String] $CommonName,

        [Parameter(ParameterSetName = 'Ask')]
        [ValidateNotNullOrEmpty()]
        [String] $Organization,

        [Parameter(ParameterSetName = 'Ask')]
        [ValidateNotNullOrEmpty()]
        [String[]] $OrganizationalUnit,

        [Parameter(ParameterSetName = 'Ask')]
        [ValidateNotNullOrEmpty()]
        [String] $City,

        [Parameter(ParameterSetName = 'Ask')]
        [ValidateNotNullOrEmpty()]
        [String] $State,

        [Parameter(ParameterSetName = 'Ask')]
        [ValidateNotNullOrEmpty()]
        [String] $Country,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String[]] $SanDns,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String[]] $SanIP,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String[]] $SanUri,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String[]] $SanEmail,

        [Parameter()]
        [ValidateScript(
            {
                $span = $_ - (Get-Date)
                if ( $span.Days -ge 0 -or $span.Hours -ge 0 ) {
                    $true
                }
                else {
                    throw 'ValidUntil must be a date in the future'
                }
            }
        )]
        [DateTime] $ValidUntil = (Get-Date).AddDays(90),

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        # validation
        $allApps = Get-VcApplication -All

        $thisApp = $allApps | Where-Object { $_.Name -like $Application -or $_.applicationId -eq $Application }
        switch (@($thisApp).Count) {
            0 {
                throw ('Application not found. Valid applications are {0}.' -f ($allApps.name -join ', '))
            }

            1 {
                Write-Verbose ('Found application {0}, ID: {1}' -f $thisApp.name, $thisApp.applicationId)
                $thisAppID = $thisApp.applicationId
            }

            Default {
                throw ('More than 1 application found that matches {0}: {1}' -f $Application, ($thisApp.name -join ', '))
            }
        }

        $thisTemplate = $thisApp.issuingTemplate | Where-Object { $_.Name -like $IssuingTemplate -or $_.issuingTemplateId -eq $IssuingTemplate }
        switch (@($thisTemplate).Count) {
            0 {
                throw ('Issuing template not found or not valid for this application. Valid templates are {0}.' -f ($thisApp.certificateIssuingTemplate.name -join ', '))
            }

            1 {
                Write-Verbose ('Found template {0}, ID: {1}' -f $thisTemplate.name, $thisTemplate.id)
                $thisTemplateID = $thisTemplate.issuingTemplateId
            }

            Default {
                throw ('More than 1 issuing template found that matches {0}: {1}' -f $IssuingTemplate, ($thisTemplate.name -join ', '))
            }
        }

        $span = New-TimeSpan -Start (Get-Date) -End $ValidUntil
        $validity = 'P{0}DT{1}H' -f $span.Days, $span.Hours

        $params = @{

            Method  = 'Post'
            UriRoot = 'outagedetection/v1'
            UriLeaf = 'certificaterequests'
            Body    = @{
                isVaaSGenerated              = $true
                applicationId                = $thisAppID
                certificateIssuingTemplateId = $thisTemplateID
                validityPeriod               = $validity
            }
        }

        if ( $PSCmdlet.ParameterSetName -eq 'Ask' ) {
            $params.Body.csrAttributes = @{}
        }
        else {
            $params.Body.certificateSigningRequest = $Csr
        }

        if ( $PSBoundParameters.ContainsKey('Organization') ) {
            $params.Body.csrAttributes.organization = $Organization
        }

        if ( $PSBoundParameters.ContainsKey('OrganizationalUnit') ) {
            $params.Body.csrAttributes.organizationalUnits = @($OrganizationalUnit)
        }

        if ( $PSBoundParameters.ContainsKey('City') ) {
            $params.Body.csrAttributes.locality = $City
        }

        if ( $PSBoundParameters.ContainsKey('State') ) {
            $params.Body.csrAttributes.state = $State
        }

        if ( $PSBoundParameters.ContainsKey('Country') ) {
            $params.Body.csrAttributes.country = $Country
        }

        if ( $SanDns -or $SanEmail -or $SanIP -or $SanUri ) {
            $params.Body.csrAttributes.subjectAlternativeNamesByType = @{}
        }
        if ( $PSBoundParameters.ContainsKey('SanDns') ) {
            $params.Body.csrAttributes.subjectAlternativeNamesByType.dnsNames = @($SanDns)
        }

        if ( $PSBoundParameters.ContainsKey('SanEmail') ) {
            $params.Body.csrAttributes.subjectAlternativeNamesByType.rfc822Names = @($SanEmail)
        }

        if ( $PSBoundParameters.ContainsKey('SanIP') ) {
            $params.Body.csrAttributes.subjectAlternativeNamesByType.ipAddresses = @($SanIP)
        }

        if ( $PSBoundParameters.ContainsKey('SanUri') ) {
            $params.Body.csrAttributes.subjectAlternativeNamesByType.uniformResourceIdentifiers = @($SanUri)
        }
    }

    process {

        if ( $PSCmdlet.ParameterSetName -eq 'Ask' ) {
            $params.Body.csrAttributes.commonName = $CommonName
            $target = $CommonName
        }
        else {
            $target = 'CSR'
        }

        if ( $PSCmdlet.ShouldProcess("$target", 'New certificate request') ) {

            try {
                $response = Invoke-VenafiRestMethod @params

                if ( $PassThru ) {
                    $certRequest = $response | Select-Object -ExpandProperty certificateRequests

                    if ( $certRequest.certificateIds ) {
                        $actualCert = Get-VcCertificate -CertificateId $certRequest.certificateIds[0]
                        $certRequest | Add-Member @{ 'certificate' = $actualCert }
                    }

                    $certRequest | Select-Object @{'n' = 'certificateRequestId'; 'e' = { $_.id } }, *, @{'n' = 'certificateId'; 'e' = { $_.certificateIds } } -ExcludeProperty id, certificateIds
                }
            }
            catch {
                Write-Error $_
                continue
            }
        }
    }
}
#EndRegion './Public/New-VcCertificate.ps1' 308
#Region './Public/New-VcConnector.ps1' -1

function New-VcConnector {
    <#
    .SYNOPSIS
    Create a new connector

    .DESCRIPTION
    Create a new machine, CA, TPP, or credential connector

    .PARAMETER ManifestPath
    Path to an existing manifest.
    Manifest can either be directly from the simulator or a full manifest with deployment element.
    If the manifest is from the simulator, the DeploymentImage parameter is required.

    .PARAMETER DeploymentImage
    Path to the already uploaded docker image.
    This parameter is only to be used for a manifest directly from the simulator.

    .PARAMETER Maintainer
    Optional value to specify the organization, individual, email, location, or website responsible for maintaining the connector
    This parameter is only to be used for a manifest directly from the simulator.

    .PARAMETER PassThru
    Return newly created connector object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .OUTPUTS
    PSCustomObject, if PassThru provided

    .EXAMPLE
    New-VcConnector -ManifestPath '/tmp/manifest.json'

    Create a new connector from a full manifest

    .EXAMPLE
    New-VcConnector -ManifestPath '/tmp/manifest.json' -PassThru

    Create a new connector and return the newly created connector object

    .EXAMPLE
    New-VcConnector -ManifestPath '/tmp/manifest.json' -DeploymentImage 'docker.io/venafi/connector:latest@sha256:1234567890abcdef'

    Create a new connector from a manifest from the simulator

    .LINK
    https://developer.venafi.com/tlsprotectcloud/reference/post-v1-plugins

    #>


    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'FullManifest')]

    param (
        [Parameter(Mandatory, ParameterSetName = 'FromSimulator')]
        [Parameter(Mandatory, ParameterSetName = 'FullManifest')]
        [ValidateScript(
            {
                if ( -not ( Test-Path $_ ) ) {
                    throw "The manifest path $_ cannot be found"
                }
                $true
            }
        )]
        [string] $ManifestPath,

        [Parameter(Mandatory, ParameterSetName = 'FromSimulator')]
        [string] $DeploymentImage,

        [Parameter(ParameterSetName = 'FromSimulator')]
        [string] $Maintainer,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        $manifestObject = Get-Content -Path $ManifestPath -Raw | ConvertFrom-Json

        if ( $PSCmdlet.ParameterSetName -eq 'FromSimulator' ) {

            if ( $manifestObject.manifest -or !$manifestObject.name ) {
                throw 'This manifest is not from the simulator'
            }

            $manifestBody = @{
                pluginType = $manifestObject.pluginType
                manifest   = $manifestObject
            }
            $manifestBody.manifest | Add-Member @{'deployment' = @{
                    image             = $DeploymentImage
                    'executionTarget' = 'vsat'
                }
            }

            if ( $Maintainer ) {
                $manifestBody.maintainer = $Maintainer
            }
        }
        else {
            # full manifest with deployment details, validate we have the structure and data needed
            if ( !$manifestObject.manifest -or !$manifestObject.manifest.deployment ) {
                throw 'This is not the correct manifest structure. See https://developer.venafi.com/tlsprotectcloud/reference/post-v1-plugins.'
            }
            $manifestBody = $manifestObject
        }

        $params = @{
            Method  = 'Post'
            UriLeaf = 'plugins'
            Body    = $manifestBody
        }

        if ( $PSCmdlet.ShouldProcess($manifestBody.manifest.name, 'Create connector') ) {

            try {
                $response = Invoke-VenafiRestMethod @params
                if ( $PassThru ) {
                    $response.plugins | Select-Object @{ 'n' = 'connectorId'; 'e' = { $_.Id } }, @{ 'n' = 'connectorType'; 'e' = { $_.pluginType } }, * -ExcludeProperty Id, pluginType
                }
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($PSItem)
            }
        }
    }
}
#EndRegion './Public/New-VcConnector.ps1' 137
#Region './Public/New-VcMachine.ps1' -1

function New-VcMachine {

    <#
    .SYNOPSIS
    Create 1 or more machines

    .DESCRIPTION
    This creation function is to be used for 'simple' machine types, eg. F5 and Citrix, where hostname, credential and optionally port are used.
    Machine creation for types with additional functionality will have dedicated functions, eg. New-VcMachineIis.
    By default, the machine details will be verified by performing a test connection; this can be turned off with -NoVerify.
    Creation will occur in parallel and PowerShell v7+ is required.

    .PARAMETER Name
    Machine name

    .PARAMETER MachineType
    Machine type by either ID or name, eg. 'Citrix ADC'.
    Get a list of available types by running `Get-VcConnector -All` and looking for connectorType is MACHINE.

    .PARAMETER VSatellite
    ID or name of a vsatellite.
    If not provided, the first vsatellite found will be used.

    .PARAMETER Owner
    ID or name of a team to be the owner of the machine

    .PARAMETER Tag
    Optional list of tags to assign

    .PARAMETER Hostname
    IP or fqdn of the machine.
    If this is to be the same value as -Name, this parameter can be ommitted.

    .PARAMETER Credential
    Username/password to access the machine

    .PARAMETER Port
    Optional port. The default value will depend on the machine type.
    Eg. for Citrix ADC this is 443.

    .PARAMETER ConnectionDetail
    Full connection detail object to create a machine.
    This is typically for use with other machine creation functions, but here for flexibility.

    .PARAMETER DekID
    ID of the data encryption key

    .PARAMETER NoVerify
    By default a connection to the host will be attempted.
    Use this switch to turn off this behavior.
    Not recommended.

    .PARAMETER Status
    Set the machine status to either 'DRAFT', 'VERIFIED', or 'UNVERIFIED'.
    This optional field has been added for flexibility, but should not be needed under typical usage.
    The platform will handle changing the status to the appropriate value.
    Setting this to a value other than VERIFIED will affect the ability to initiate workflows.

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER PassThru
    Return newly created object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .EXAMPLE
    $params = @{
        Name = 'c1'
        MachineType = 'Citrix ADC'
        Owner = 'MyTeam'
        Hostname = 'c1.company.com'
        Credential = $cred
    }
    New-VcMachine @params

    machineId : cf7cfdc0-2b2a-11ee-9546-5136c4b21504
    testConnection : @{Success=True; Error=; WorkflowID=c39310ee-51fc-49f3-8b5b-e504e1bc43d2}
    companyId : 20b24f81-b22b-11ea-91f3-ebd6dea5453f
    name : c1
    machineType : Citrix ADC
    pluginId : ff645e14-bd1a-11ed-a009-ce063932f86d
    integrationId : cf7c8014-2b2a-11ee-9a03-fa8930555887
    edgeInstanceId : 0bc771e1-7abe-4339-9fcd-93fffe9cba7f
    creationDate : 7/25/2023 4:35:36 PM
    modificationDate : 7/25/2023 4:35:36 PM
    status : UNVERIFIED
    owningTeamId : 59920180-a3e2-11ec-8dcd-3fcbf84c7da7

    Create a new Citrix machine

    .EXAMPLE
    [pscustomobject] @{
        Name = 'c1.company.com'
        MachineType = 'Citrix ADC'
        Owner = 'MyTeam'
        Credential = $cred
    } | New-VcMachine

    Use pipeline data to create a machine.
    More than 1 machine can be sent thru the pipeline and they will be created in parallel.
    You could also import a csv and pipe it to this function as well.

    .NOTES
    To see a full list of tab-completion options, be sure to set the Tab option, Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete.

    This function requires the use of sodium encryption via PSSodium, https://github.com/TylerLeonhardt/PSSodium, to be installed.
    .net standard 2.0 or greater is required via PS Core (recommended) or supporting .net runtime.
    On Windows, the latest Visual C++ redist must be installed. See https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist.
    #>



    [CmdletBinding()]
    [Alias('New-VaasMachine')]

    param (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string] $Name,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string] $MachineType,

        [Parameter(ParameterSetName = 'BasicMachine', ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'AdvancedMachine', ValueFromPipelineByPropertyName)]
        [string] $VSatellite,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [String] $Owner,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string[]] $Tag,

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateSet('DRAFT', 'VERIFIED', 'UNVERIFIED')]
        [string] $Status,

        [Parameter(ParameterSetName = 'BasicMachine', ValueFromPipelineByPropertyName)]
        [string] $Hostname,

        [Parameter(Mandatory, ParameterSetName = 'BasicMachine', ValueFromPipelineByPropertyName)]
        [pscredential] $Credential,

        [Parameter(ParameterSetName = 'BasicMachine', ValueFromPipelineByPropertyName)]
        [string] $Port,

        [Parameter(Mandatory, ParameterSetName = 'AdvancedMachine', ValueFromPipelineByPropertyName)]
        [hashtable] $ConnectionDetail,

        [Parameter(Mandatory, ParameterSetName = 'AdvancedMachine', ValueFromPipelineByPropertyName)]
        [string] $DekID,

        [Parameter()]
        [switch] $NoVerify,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        Initialize-PSSodium

        $allMachines = [System.Collections.Generic.List[hashtable]]::new()
    }

    process {

        Write-Verbose $PSCmdlet.ParameterSetName

        $thisMachineType = Get-VcData -InputObject $MachineType -Type 'MachinePlugin' -Object
        if ( -not $thisMachineType ) {
            Write-Error "'$MachineType' is not a valid machine type id or name"
            return
        }

        if ( $PSCmdlet.ParameterSetName -eq 'BasicMachine' ) {
            if ( $thisMachineType.name -in 'Microsoft IIS', 'Common Keystore (PEM, JKS, PKCS#12)' ) {
                throw 'To create IIS or Common Keystore machines, please use the dedicated function.'
            }
        }

        $ownerId = Get-VcData -InputObject $Owner -Type 'Team'
        if ( -not $ownerId ) {
            Write-Error "'$Owner' is not a valid team id or name"
            return
        }

        if ( $PSCmdlet.ParameterSetName -eq 'AdvancedMachine' ) {
            $thisEdgeInstanceId = $VSatellite
            $thisDekId = $DekID
            $thisConnectionDetail = $ConnectionDetail
        }
        else {

            if ( $VSatellite ) {
                $vSat = Get-VcData -InputObject $VSatellite -Type 'VSatellite' -Object
                if ( -not $vSat ) {
                    Write-Error "'$VSatellite' is either not a valid VSatellite id or name or it is not active."
                    return
                }
            }
            else {
                $vSat = Get-VcData -Type 'VSatellite' -First
                if ( -not $vSat ) {
                    Write-Error "An active VSatellite could not be found"
                    return
                }
            }

            $thisEdgeInstanceId = $vSat.vsatelliteId
            $thisDekId = $vSat.encryptionKeyId

            $userEnc = ConvertTo-SodiumEncryptedString -text $Credential.UserName -PublicKey $vSat.encryptionKey
            $pwEnc = ConvertTo-SodiumEncryptedString -text $Credential.GetNetworkCredential().Password -PublicKey $vSat.encryptionKey

            $thisConnectionDetail = @{
                hostnameOrAddress = if ($Hostname) { $Hostname } else { $Name }
                username          = $userEnc
                password          = $pwEnc
            }

            if ( $Port ) {
                $thisConnectionDetail.port = $Port
            }
        }

        $bodyParams = @{
            name              = $Name
            edgeInstanceId    = $thisEdgeInstanceId
            dekId             = $thisDekId
            pluginId          = $thisMachineType.machinePluginId
            owningTeamId      = $ownerId
            connectionDetails = $thisConnectionDetail
        }

        if ( $Tag ) {
            $bodyParams.tags = $Tag
        }

        if ( $Status ) {
            $bodyParams.status = $Status
        }

        $allMachines.Add( $bodyParams )
    }

    end {

        $response = Invoke-VenafiParallel -InputObject $allMachines -ScriptBlock {
            $response = Invoke-VenafiRestMethod -Method 'Post' -UriLeaf 'machines' -Body $PSItem

            if ( $using:NoVerify ) {
                $response | Select-Object @{
                    'n' = 'machineId'
                    'e' = { $_.id }
                }, * -ExcludeProperty id
            }
            else {
                $workflowResponse = Invoke-VcWorkflow -ID $response.id -Workflow 'Test'
                $response | Select-Object @{
                    'n' = 'machineId'
                    'e' = { $_.id }
                },
                @{
                    'n' = 'testConnection'
                    'e' = { $workflowResponse | Select-Object Success, Error, WorkflowID }
                }, * -ExcludeProperty id
            }
        } -ThrottleLimit $ThrottleLimit -VenafiSession $VenafiSession

        if ( $PassThru ) {
            $response
        }
    }
}
#EndRegion './Public/New-VcMachine.ps1' 291
#Region './Public/New-VcMachineCommonKeystore.ps1' -1

function New-VcMachineCommonKeystore {
    <#
    .SYNOPSIS
    Create a new common keystore machine

    .DESCRIPTION
    Create a new common keystore, PEM/JKS/PKCS#12, machine.
    SSH and WinRM are both supported in addition to different authentication types.
    By default, the machine details will be verified by performing a test connection; this can be turned off with -NoVerify.
    Creation will occur in parallel and PowerShell v7+ is required.

    .PARAMETER Name
    Machine name

    .PARAMETER VSatellite
    ID or name of a VSatellite.
    If not provided, the first active VSatellite found will be used.

    .PARAMETER Owner
    ID or name of a team to be the owner of the machine

    .PARAMETER Tag
    Optional list of tags to assign

    .PARAMETER Status
    Set the machine status to either 'DRAFT', 'VERIFIED', or 'UNVERIFIED'.
    This optional field has been added for flexibility, but should not be needed under typical usage.
    The platform will handle changing the status to the appropriate value.
    Setting this to a value other than VERIFIED will affect the ability to initiate workflows.

    .PARAMETER SshPassword
    Connect to the target machine over SSH with username and password

    .PARAMETER SshKey
    Connect to the target machine over SSH with username and private key

    .PARAMETER WinrmBasic
    Connect to the target machine over WinRM with Basic authentication

    .PARAMETER WinrmKerberos
    Connect to the target machine over WinRM with Kerberos authentication

    .PARAMETER Hostname
    IP or fqdn of the machine.
    If this is to be the same value as -Name, this parameter can be ommitted.

    .PARAMETER Credential
    Username/password to access the machine.
    If using key-based authentication over SSH, set the password to the private key.

    .PARAMETER Port
    Optional SSH/WinRM port.
    The default for SSH is 22 and WinRM is 5985.

    .PARAMETER UseTls
    Connect with WinRM over HTTPS as opposed to the default of HTTP

    .PARAMETER SkipCertificateCheck
    If connecting with WinRM over HTTPS and you wish to bypass certificate validation

    .PARAMETER DomainName
    Machine domain name for WinRM

    .PARAMETER KeyDistributionCenter
    Address or hostname of the key distribution center for WinRM

    .PARAMETER SPN
    Service Principal Name, eg. WSMAN/server.company.com, for WinRM

    .PARAMETER NoVerify
    By default a connection to the host will be attempted.
    Use this switch to turn off this behavior.
    Not recommended.

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER PassThru
    Return newly created object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .OUTPUTS
    PSCustomObject, if PassThru provided

    .EXAMPLE
    New-VcMachineCommonKeystore -Name 'ck1' -Owner 'MyTeam' -Hostname 'ck1.company.com' -Credential $cred -SshPassword

    machineId : a9b60a70-2b28-11ee-b5b5-4b044579acad
    testConnection : @{Success=True; Error=; WorkflowID=c14e181b-82ea-423a-b0fd-c3fe9b218a64}
    companyId : 20b24f81-b22b-11ea-91f3-ebd6dea5453f
    name : ck1
    machineType : Common KeyStore (PEM, JKS, PKCS#12)
    pluginId : 0e565e41-dd31-11ec-841d-a7d91c5a907c
    integrationId : a9b5b842-2b28-11ee-9263-9e16d4b8a8c9
    edgeInstanceId : 0bc771e1-7abe-4339-9fcd-93fffe9cba7f
    creationDate : 7/25/2023 4:20:14 PM
    modificationDate : 7/25/2023 4:20:14 PM
    status : UNVERIFIED
    owningTeamId : 59920180-a3e2-11ec-8dcd-3fcbf84c7da7

    Create a new machine with SSH password authentication

    .NOTES
    This function requires the use of sodium encryption.
    .net standard 2.0 or greater is required via PS Core (recommended) or supporting .net runtime.
    On Windows, the latest Visual C++ redist must be installed. See https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist.
    #>


    [CmdletBinding(DefaultParameterSetName = 'SshPassword')]
    [Alias('New-VaasMachineCommonKeystore')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string] $Name,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $VSatellite,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [String] $Owner,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $Hostname,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [pscredential] $Credential,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string[]] $Tag,

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateSet('DRAFT', 'VERIFIED', 'UNVERIFIED')]
        [string] $Status,

        [Parameter(ParameterSetName = 'SshPassword', Mandatory, ValueFromPipelineByPropertyName)]
        [switch] $SshPassword,

        [Parameter(ParameterSetName = 'SshKey', Mandatory, ValueFromPipelineByPropertyName)]
        [switch] $SshKey,

        [Parameter(ParameterSetName = 'WinrmBasic', Mandatory, ValueFromPipelineByPropertyName)]
        [switch] $WinrmBasic,

        [Parameter(ParameterSetName = 'WinrmKerberos', Mandatory, ValueFromPipelineByPropertyName)]
        [switch] $WinrmKerberos,

        [Parameter(ValueFromPipelineByPropertyName)]
        [int] $Port,

        [Parameter(ParameterSetName = 'WinrmBasic', ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'WinrmKerberos', ValueFromPipelineByPropertyName)]
        [switch] $UseTls,

        [Parameter(ParameterSetName = 'WinrmBasic', ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'WinrmKerberos', ValueFromPipelineByPropertyName)]
        [switch] $SkipCertificateCheck,

        [Parameter(ParameterSetName = 'WinrmKerberos', Mandatory, ValueFromPipelineByPropertyName)]
        [string] $DomainName,

        [Parameter(ParameterSetName = 'WinrmKerberos', Mandatory, ValueFromPipelineByPropertyName)]
        [string] $KeyDistributionCenter,

        [Parameter(ParameterSetName = 'WinrmKerberos', Mandatory, ValueFromPipelineByPropertyName)]
        [string] $SPN,

        [Parameter()]
        [switch] $NoVerify,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        $allMachines = [System.Collections.Generic.List[pscustomobject]]::new()
        $machineTypeId = Get-VcData -InputObject 'Common KeyStore (PEM, JKS, PKCS#12)' -Type 'MachinePlugin'

        Initialize-PSSodium
    }

    process {

        # need vsat to get dek for encrypting username/password
        if ( $VSatellite ) {
            $vSat = Get-VcData -InputObject $VSatellite -Type 'VSatellite' -Object
            if ( -not $vSat ) {
                throw "'$VSatellite' is either not a valid VSatellite id or name or it is not active"
            }
        }
        else {
            $vSat = Get-VcData -Type 'VSatellite' -First
            if ( -not $vSat ) {
                throw "An active VSatellite could not be found"
            }
        }

        $userEnc = ConvertTo-SodiumEncryptedString -text $Credential.UserName -PublicKey $vSat.encryptionKey
        $pwEnc = ConvertTo-SodiumEncryptedString -text $Credential.GetNetworkCredential().Password -PublicKey $vSat.encryptionKey

        switch ($PSCmdlet.ParameterSetName) {
            'SshPassword' {
                $connectionDetails = @{
                    protocol    = 'ssh2'
                    protocolSsh = @{
                        hostnameOrAddress = if ($Hostname) { $Hostname } else { $Name }
                        sshAuthentication = 'password'
                        sshPassword       = @{
                            credentialType = 'local'
                            username       = $userEnc
                            password       = $pwEnc
                        }
                    }
                }
            }

            'SshKey' {
                $connectionDetails = @{
                    protocol    = 'ssh2'
                    protocolSsh = @{
                        hostnameOrAddress = if ($Hostname) { $Hostname } else { $Name }
                        sshAuthentication = "key"
                        sshPrivateKey     = @{
                            credentialType = 'local'
                            username       = $userEnc
                            privateKey     = $pwEnc
                        }
                    }
                }
            }

            'WinrmBasic' {
                $connectionDetails = @{
                    protocol      = 'winrm2'
                    protocolWinRm = @{
                        hostnameOrAddress  = if ($Hostname) { $Hostname } else { $Name }
                        authenticationType = "basic"
                        credentialType     = 'local'
                        username           = $userEnc
                        password           = $pwEnc
                    }
                }
            }

            'WinrmKerberos' {
                $connectionDetails = @{
                    protocol      = 'winrm2'
                    protocolWinRm = @{
                        hostnameOrAddress  = if ($Hostname) { $Hostname } else { $Name }
                        authenticationType = 'kerberos'
                        credentialType     = 'local'
                        username           = $userEnc
                        password           = $pwEnc
                        kerberos           = @{
                            domain                = "$DomainName"
                            keyDistributionCenter = "$KeyDistributionCenter"
                            servicePrincipalName  = "$SPN"
                        }
                    }
                }
            }
        }

        if ( $Port ) {
            $connectionDetails.protocolWinRm.port = $Port
        }

        if ( $UseTls ) {
            $connectionDetails.protocolWinRm.https = $true
            if ( $SkipCertificateCheck ) {
                $connectionDetails.protocolWinRm.skipTlsVerify = $true
            }
        }

        $params = @{
            Name             = $Name
            MachineType      = $machineTypeId
            VSatellite       = $vSat.vsatelliteId
            DekID            = $vSat.encryptionKeyId
            Owner            = $Owner
            ConnectionDetail = $connectionDetails
        }

        if ( $Tag ) {
            $params.Tag = $Tag
        }

        if ( $Status ) {
            $params.Status = $Status
        }

        $allMachines.Add( [pscustomobject]$params )
    }

    end {

        $newParams = @{

            NoVerify      = $NoVerify
            ThrottleLimit = $ThrottleLimit
            PassThru      = $true
        }

        $response = $allMachines | New-VcMachine @newParams

        if ( $PassThru ) {
            $response
        }
    }
}
#EndRegion './Public/New-VcMachineCommonKeystore.ps1' 327
#Region './Public/New-VcMachineIis.ps1' -1

function New-VcMachineIis {
    <#
    .SYNOPSIS
    Create a new IIS machine

    .DESCRIPTION
    Create a new IIS machine with either basic or kerberos authentication.
    By default, the machine details will be verified by performing a test connection; this can be turned off with -NoVerify.
    Creation will occur in parallel and PowerShell v7+ is required.

    .PARAMETER Name
    Machine name

    .PARAMETER VSatellite
    ID or name of a vsatellite.
    If not provided, the first vsatellite found will be used.

    .PARAMETER Owner
    ID or name of a team to be the owner of the machine

    .PARAMETER Tag
    Optional list of tags to assign

    .PARAMETER Hostname
    IP or fqdn of the machine.
    If this is to be the same value as -Name, this parameter can be ommitted.

    .PARAMETER Credential
    Username/password to access the machine

    .PARAMETER Port
    Optional WinRM port. The default is 5985.

    .PARAMETER UseTls
    Connect over HTTPS as opposed to the default of HTTP

    .PARAMETER SkipCertificateCheck
    If connecting over HTTPS and you wish to bypass certificate validation

    .PARAMETER DomainName
    Machine domain name

    .PARAMETER KeyDistributionCenter
    Address or hostname of the key distribution center

    .PARAMETER SPN
    Service Principal Name, eg. WSMAN/server.company.com

    .PARAMETER Status
    Set the machine status to either 'DRAFT', 'VERIFIED', or 'UNVERIFIED'.
    This optional field has been added for flexibility, but should not be needed under typical usage.
    The platform will handle changing the status to the appropriate value.
    Setting this to a value other than VERIFIED will affect the ability to initiate workflows.

    .PARAMETER NoVerify
    By default a connection to the host will be attempted.
    Use this switch to turn off this behavior.
    Not recommended.

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER PassThru
    Return newly created object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .EXAMPLE
    $params = @{
        Name = 'iis1'
        Owner = 'MyTeam'
        Hostname = 'iis1.company.com'
        Credential = $cred
        DomainName = 'company.com'
        KeyDistributionCenter = '1.2.3.4'
        SPN = 'WSMAN/iis1.company.com'
    }
    New-VcMachineIis @params

    machineId : 55e054d0-2b2a-11ee-9546-5136c4b21504
    testConnection : @{Success=True; Error=; WorkflowID=c39310ee-51fc-49f3-8b5b-e504e1bc43d2}
    companyId : 20b24f81-b22b-11ea-91f3-ebd6dea5453f
    name : iis1
    machineType : Microsoft IIS
    pluginId : be453281-d080-11ec-a07a-6d5bc1b54078
    integrationId : 55df8877-2b2a-11ee-9264-9e16d4b8a8c9
    edgeInstanceId : 0bc771e1-7abe-4339-9fcd-93fffe9cba7f
    creationDate : 7/25/2023 4:32:12 PM
    modificationDate : 7/25/2023 4:32:12 PM
    status : UNVERIFIED
    owningTeamId : 59920180-a3e2-11ec-8dcd-3fcbf84c7da7

    Create a new machine with Kerberos authentication

    .NOTES
    This function requires the use of sodium encryption.
    .net standard 2.0 or greater is required via PS Core (recommended) or supporting .net runtime.
    On Windows, the latest Visual C++ redist must be installed. See https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist.
    #>


    [CmdletBinding(DefaultParameterSetName = 'WinrmBasic')]
    [Alias('New-VaasMachineIis')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = 'IIS is not plural')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string] $Name,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $VSatellite,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [String] $Owner,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $Hostname,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [pscredential] $Credential,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string[]] $Tag,

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateSet('DRAFT', 'VERIFIED', 'UNVERIFIED')]
        [string] $Status,

        [Parameter(ValueFromPipelineByPropertyName)]
        [int] $Port,

        [Parameter(ValueFromPipelineByPropertyName)]
        [switch] $UseTls,

        [Parameter(ValueFromPipelineByPropertyName)]
        [switch] $SkipCertificateCheck,

        [Parameter(ParameterSetName = 'WinrmKerberos', Mandatory, ValueFromPipelineByPropertyName)]
        [string] $DomainName,

        [Parameter(ParameterSetName = 'WinrmKerberos', Mandatory, ValueFromPipelineByPropertyName)]
        [string] $KeyDistributionCenter,

        [Parameter(ParameterSetName = 'WinrmKerberos', Mandatory, ValueFromPipelineByPropertyName)]
        [string] $SPN,

        [Parameter()]
        [switch] $NoVerify,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        $allMachines = [System.Collections.Generic.List[pscustomobject]]::new()
        $machineTypeId = Get-VcData -InputObject 'Microsoft IIS' -Type 'MachinePlugin'

        Initialize-PSSodium
    }

    process {

        # need vsat to get dek for encrypting username/password
        if ( $VSatellite ) {
            $vSat = Get-VcData -InputObject $VSatellite -Type 'VSatellite' -Object
            if ( -not $vSat ) {
                throw "'$VSatellite' is either not a valid VSatellite id or name or it is not active"
            }
        }
        else {
            $vSat = Get-VcData -Type 'VSatellite' -First
            if ( -not $vSat ) {
                throw "An active VSatellite could not be found"
            }
        }

        $userEnc = ConvertTo-SodiumEncryptedString -text $Credential.UserName -PublicKey $vSat.encryptionKey
        $pwEnc = ConvertTo-SodiumEncryptedString -text $Credential.GetNetworkCredential().Password -PublicKey $vSat.encryptionKey

        switch ($PSCmdlet.ParameterSetName) {
            'WinrmBasic' {
                $connectionDetails = @{
                    hostnameOrAddress  = if ($Hostname) { $Hostname } else { $Name }
                    authenticationType = "basic"
                    credentialType     = 'local'
                    username           = $userEnc
                    password           = $pwEnc
                }
            }

            'WinrmKerberos' {
                $connectionDetails = @{
                    hostnameOrAddress  = if ($Hostname) { $Hostname } else { $Name }
                    authenticationType = 'kerberos'
                    credentialType     = 'local'
                    username           = $userEnc
                    password           = $pwEnc
                    kerberos           = @{
                        domain                = "$DomainName"
                        keyDistributionCenter = "$KeyDistributionCenter"
                        servicePrincipalName  = "$SPN"
                    }
                }
            }
        }

        if ( $Port ) {
            $connectionDetails.protocolWinRm.port = $Port
        }

        if ( $UseTls ) {
            $connectionDetails.protocolWinRm.https = $true
            if ( $SkipCertificateCheck ) {
                $connectionDetails.protocolWinRm.skipTlsVerify = $true
            }
        }

        $params = @{
            Name             = $Name
            MachineType      = $machineTypeId
            VSatellite       = $vSat.vsatelliteId
            DekID            = $vSat.encryptionKeyId
            Owner            = $Owner
            ConnectionDetail = $connectionDetails
        }

        if ( $Tag ) {
            $params.Tag = $Tag
        }

        if ( $Status ) {
            $params.Status = $Status
        }

        $allMachines.Add( [pscustomobject]$params )
    }

    end {

        $newParams = @{

            NoVerify      = $NoVerify
            ThrottleLimit = $ThrottleLimit
            PassThru      = $true
        }

        $response = $allMachines | New-VcMachine @newParams

        if ( $PassThru ) {
            $response
        }
    }
}
#EndRegion './Public/New-VcMachineIis.ps1' 269
#Region './Public/New-VcTeam.ps1' -1

function New-VcTeam {
    <#
    .SYNOPSIS
    Create a new team

    .DESCRIPTION
    Create a new TLSPC team

    .PARAMETER Name
    Team name

    .PARAMETER Owner
    1 or more owners for the team.
    Provide the unique guid obtained from Get-VcIdentity.

    .PARAMETER Member
    1 or more members for the team.
    Provide the unique guid obtained from Get-VcIdentity.

    .PARAMETER Role
    Team role, either 'System Admin', 'PKI Admin', 'Resource Owner' or 'Guest'

    .PARAMETER UserMatchingRule
    If SSO is enabled, build your team membership rules to organize your users into teams automatically.
    If more than 1 rule is configured, they must all be met for a user to meet the criteria.
    Each rule should be of the format @('claim name', 'operator', 'value')
    where operator can be equals, not_equals, contains, not_contains, starts_with, or ends_with.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .EXAMPLE
    New-VenafiTeam -Name 'My New Team' -Member 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Owner @('ca7ff555-88d2-4bfc-9efa-2630ac44c1f3', 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f4') -Role 'System Admin'

    Create a new team

    .EXAMPLE
    New-VenafiTeam -Name 'My New Team' -Member 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Owner @('ca7ff555-88d2-4bfc-9efa-2630ac44c1f3', 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f4') -Role 'System Admin' -UserMatchingRule @('MyClaim', 'CONTAINS', 'Group')

    Create a new team with user matching rule

    .EXAMPLE
    New-VenafiTeam -Name 'My New Team' -Member 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Owner @('ca7ff555-88d2-4bfc-9efa-2630ac44c1f3', 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f4') -Role 'System Admin' -PassThru

    id : a7d60730-a967-11ec-8832-4d051bf6d0b4
    name : My New Team
    systemRoles : {SYSTEM_ADMIN}
    productRoles :
    role : SYSTEM_ADMIN
    members : {443de910-a6cc-11ec-ad22-018e33741844}
    owners : {0a2adae0-b22b-11ea-91f3-ebd6dea5452e}
    companyId : 0bc771e1-7abe-4339-9fcd-93fffe9cba7f
    userMatchingRules : {}
    modificationDate : 3/21/2022 6:38:40 PM

    Create a new team returning the new team

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html#/Teams/create_1

    .LINK
    https://docs.venafi.cloud/vcs-platform/creating-new-teams/
    #>


    [CmdletBinding()]

    param (

        [Parameter(Mandatory)]
        [string] $Name,

        [Parameter(Mandatory)]
        [string[]] $Owner,

        [Parameter(Mandatory)]
        [string[]] $Member,

        [Parameter(Mandatory)]
        [ValidateSet('System Admin', 'PKI Admin', 'Resource Owner', 'Guest')]
        [string] $Role,

        [Parameter()]
        [System.Collections.Generic.List[array]] $UserMatchingRule,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        $params = @{
            Method  = 'Post'
            UriLeaf = 'teams'
        }

        $rules = foreach ($rule in $UserMatchingRule) {

            if ( $rule.Count -ne 3 ) {
                throw 'Each rule must contain a claim name, operator, and value'
            }

            if ( $rule[1].ToUpper() -notin 'EQUALS', 'NOT_EQUALS', 'CONTAINS', 'NOT_CONTAINS', 'STARTS_WITH', 'ENDS_WITH') {
                throw 'Valid values for operator are EQUALS, NOT_EQUALS, CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH'
            }

            @{
                'claimName' = $rule[0]
                'operator'  = $rule[1].ToUpper()
                'value'     = $rule[2]
            }
        }

        $params.Body = @{
            'name'              = $Name
            'role'              = $Role.Replace(' ', '_').ToUpper()
            'members'           = @($Member)
            'owners'            = @($Owner)
            'userMatchingRules' = @($rules)
        }

        $response = Invoke-VenafiRestMethod @params

        if ( $PassThru ) {
            $response | Get-VcTeam
        }
    }
}
#EndRegion './Public/New-VcTeam.ps1' 137
#Region './Public/New-VcWebhook.ps1' -1

function New-VcWebhook {
    <#
    .SYNOPSIS
    Create a new webhook

    .DESCRIPTION
    Create a new webhook

    .PARAMETER Name
    Webhook name

    .PARAMETER Url
    Endpoint to be called when the event type/name is triggered.
    This should be the full url and will be validated during webhook creation.

    .PARAMETER EventType
    One or more event types to trigger on.
    You can retrieve a list of possible values from the Event Log and filtering on Event Type.

    .PARAMETER EventName
    One or more event names to trigger on.

    .PARAMETER Secret
    Secret value used to calculate signature which will be sent to the endpoint in the header

    .PARAMETER CriticalOnly
    Only subscribe to log messages deemed as critical

    .PARAMETER PassThru
    Return newly created webhook object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .OUTPUTS
    PSCustomObject, if PassThru provided

    .EXAMPLE
    New-VcWebhook -Name 'MyWebhook' -Url 'https://my.com/endpoint' -EventType 'Authentication'

    Create a new webhook for one event type

    .EXAMPLE
    New-VcWebhook -Name 'MyWebhook' -Url 'https://my.com/endpoint' -EventType 'Authentication', 'Certificates', 'Applications'

    Create a new webhook with multiple event types

    .EXAMPLE
    New-VcWebhook -Name 'MyWebhook' -Url 'https://my.com/endpoint' -EventName 'Certificate Validation Started'

    Create a new webhook with event names as opposed to event types.
    This will result in fewer messages received as opposed to subscribing at the event type level.

    .EXAMPLE
    New-VcWebhook -Name 'MyWebhook' -Url 'https://my.com/endpoint' -EventType 'Certificates' -CriticalOnly

    Subscribe to critical messages only for a specific event type

    .EXAMPLE
    New-VcWebhook -Name 'MyWebhook' -Url 'https://my.com/endpoint' -EventType 'Authentication' -Secret 'MySecret'

    Create a new webhook with optional secret

    .EXAMPLE
    New-VcWebhook -Name 'MyWebhook' -Url 'https://my.com/endpoint' -EventType 'Authentication' -PassThru

    Create a new webhook returning the newly created object

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html?urls.primaryName=connectors-service#/Connectors/connectors_create

    #>


    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'EventName')]

    param (
        [Parameter(Mandatory)]
        [string] $Name,

        [Parameter(Mandatory)]
        [string] $Url,

        [Parameter(Mandatory, ParameterSetName = 'EventType')]
        [String[]] $EventType,

        [Parameter(Mandatory, ParameterSetName = 'EventName')]
        [String[]] $EventName,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [Alias('token')]
        [string] $Secret,

        [Parameter()]
        [switch] $CriticalOnly,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        # validate inputs
        $at = Invoke-VenafiRestMethod -UriLeaf 'activitytypes'

        if ( $PSBoundParameters.ContainsKey('EventType') ) {
            $compare = compare-object -ReferenceObject $EventType -DifferenceObject $at.readablename | Where-Object { $_.SideIndicator -eq '<=' }
            if ( $compare ) {
                throw ('{0} are not valid event types. Valid values include {1}.' -f ($compare.InputObject -join ', '), ($at.readablename -join ', '))
            }
        }
        else {
            $compare = compare-object -ReferenceObject $EventName -DifferenceObject $at.values.readablename | Where-Object { $_.SideIndicator -eq '<=' }
            if ( $compare ) {
                throw ('{0} are not valid event names. Valid values include {1}.' -f ($compare.InputObject -join ', '), ($at.values.readablename -join ', '))
            }
        }
    }

    process {

        $params = @{
            VenafiSession = $VenafiSession
            Method        = 'Post'
            UriRoot       = 'v1'
            UriLeaf       = 'connectors'
            Body          = @{
                name       = $Name
                properties = @{
                    'connectorKind' = 'WEBHOOK'
                    'target'        = @{
                        'type'       = 'generic'
                        'connection' = @{
                            'url' = $Url
                        }
                    }
                    'filter'        = @{
                        criticality = [int]$CriticalOnly.IsPresent
                    }
                }
            }
            FullResponse  = $true
        }

        # either event type or name will be provided
        if ( $PSBoundParameters.ContainsKey('EventType') ) {
            $params.Body.properties.filter.activityTypes = @($EventType)
        }
        else {
            $params.Body.properties.filter.activities = @($EventName)
        }

        if ( $PSBoundParameters.ContainsKey('Secret') ) {
            $params.Body.properties.target.connection.secret = $Secret
        }

        if ( $PSCmdlet.ShouldProcess($Name, 'Create webhook') ) {

            try {
                $response = Invoke-VenafiRestMethod @params
                switch ( $response.StatusCode ) {

                    201 {
                        if ( $PassThru ) {
                            $response.Content | ConvertFrom-Json | Select-Object -Property @{'n' = 'webhookId'; 'e' = { $_.id } }, * -ExcludeProperty id
                        }
                    }

                    409 {
                        throw "Webhook '$Name' already exists"
                    }

                    default {
                        throw $response
                    }
                }
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($PSItem)
            }
        }
    }
}
#EndRegion './Public/New-VcWebhook.ps1' 190
#Region './Public/New-VdcCapiApplication.ps1' -1

function New-VdcCapiApplication {
    <#
    .SYNOPSIS
    Create a new CAPI application

    .DESCRIPTION
    Create a new CAPI application

    .PARAMETER Path
    Full path, including name, to the application to be created. The application must be created under a device.
    Alternatively, provide the path to the device and provide ApplicationName.

    .PARAMETER ApplicationName
    1 or more application names to create. Path property must be a path to a device.

    .PARAMETER CertificatePath
    Path to the certificate to associate to the new application

    .PARAMETER CredentialPath
    Path to the associated credential which has rights to access the connected device

    .PARAMETER FriendlyName
    The Friendly Name that helps to uniquely identify the certificate after it has been installed in the Windows CAPI store

    .PARAMETER Descripion
    Application description

    .PARAMETER WinRmPort
    WinRM port to connect to application on

    .PARAMETER Disable
    Set processing to disabled. It is enabled by default.

    .PARAMETER WebSiteName
    The unique name of the IIS web site

    .PARAMETER BindingIp
    The IP address to bind the certificate to the IIS web site. If not specified, the Internet Information Services (IIS) Manager console shows 'All Unassigned'.

    .PARAMETER BindingPort
    The TCP port 1 to 65535 to bind the certificate to the IIS web site

    .PARAMETER BindingHostName
    The hostname to bind the certificate to the IIS web site. Specifying this value will make it so the certificate is only accessible to clients using Server Name Indication (SNI)

    .PARAMETER CreateBinding
    Specify that Trust Protection Platform should create an IIS web site binding if the one specified doesn’t already exist.

    .PARAMETER PushCertificate
    Push the certificate to the application. CertificatePath must be provided.

    .PARAMETER SkipExistenceCheck
    By default, the paths for the new application, certifcate, and credential will be validated for existence.
    Specify this switch to bypass this check.

    .PARAMETER PassThru
    Return a TppObject representing the newly created capi app.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    TppObject, if PassThru provided

    .EXAMPLE
    New-VdcCapiApplication -Path '\ved\policy\mydevice\capi' -CertificatePath $cert.Path -CredentialPath $cred.Path
    Create a new application

    .EXAMPLE
    New-VdcCapiApplication -Path '\ved\policy\mydevice\capi' -CertificatePath $cert.Path -CredentialPath $cred.Path -WebSiteName 'mysite' -BindingIp '1.2.3.4'
    Create a new application and update IIS

    .EXAMPLE
    New-VdcCapiApplication -Path '\ved\policy\mydevice\capi' -CertificatePath $cert.Path -CredentialPath $cred.Path -WebSiteName 'mysite' -BindingIp '1.2.3.4' -PushCertificate
    Create a new application, update IIS, and push the certificate to the new app

    .EXAMPLE
    New-VdcCapiApplication -Path '\ved\policy\mydevice\capi' -CertificatePath $cert.Path -CredentialPath $cred.Path -PassThru
    Create a new application and return a TppObject for the newly created app

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/New-VdcCapiApplication/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/New-VdcCapiApplication.ps1

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/New-VdcObject.ps1

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Find-VdcCertificate/

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Get-VdcObject/

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-create.php

    #>


    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'NonIis')]
    [Alias('New-TppCapiApplication')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [string] $Path,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string[]] $ApplicationName,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('CertificateDN')]
        [String] $CertificatePath,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('CredentialDN')]
        [String] $CredentialPath,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String] $FriendlyName,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String] $Description,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [Int] $WinRmPort,

        [Parameter()]
        [Switch] $Disable,

        [Parameter(Mandatory, ParameterSetName = 'Iis')]
        [ValidateNotNullOrEmpty()]
        [String] $WebSiteName,

        [Parameter(ParameterSetName = 'Iis')]
        [ValidateNotNullOrEmpty()]
        [Alias('BindingIpAddress')]
        [ipaddress] $BindingIp,

        [Parameter(ParameterSetName = 'Iis')]
        [ValidateNotNullOrEmpty()]
        [Int] $BindingPort,

        [Parameter(ParameterSetName = 'Iis')]
        [ValidateNotNullOrEmpty()]
        [String] $BindingHostName,

        [Parameter(ParameterSetName = 'Iis')]
        [ValidateNotNullOrEmpty()]
        [bool] $CreateBinding,

        [Parameter()]
        [switch] $PushCertificate,

        [Parameter()]
        [switch] $SkipExistenceCheck,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        if ( $PushCertificate.IsPresent -and (-not $PSBoundParameters.ContainsKey('CertificatePath')) ) {
            throw 'A CertificatePath must be provided when using PushCertificate'
        }

        if ( -not $PSBoundParameters.ContainsKey('SkipExistenceCheck') ) {

            if ( $PSBoundParameters.ContainsKey('CertificatePath') ) {
                # issue 129
                $certName = $CertificatePath.Split('\')[-1]
                $certPath = $CertificatePath.Substring(0, $CertificatePath.LastIndexOf("\$certName"))

                $certObject = Find-VdcCertificate -Path $certPath

                if ( -not $certObject -or ($certName -notin $certObject.Name) ) {
                    throw ('A certificate object could not be found at ''{0}''' -f $CertificatePath)
                }
            }

            # ensure the credential exists and is actually of type credential
            if ( $PSBoundParameters.ContainsKey('CredentialPath') ) {

                $credObject = Get-VdcObject -Path $CredentialPath

                if ( -not $credObject -or $credObject.TypeName -notlike '*credential*' ) {
                    throw ('A credential object could not be found at ''{0}''' -f $CredentialPath)
                }
            }
        }

        $params = @{
            Path      = ''
            Class     = 'CAPI'
            Attribute = @{
                'Driver Name' = 'appcapi'
            }
            PassThru  = $true

        }

        if ( $PSBoundParameters.ContainsKey('FriendlyName') ) {
            $params.Attribute.Add('Friendly Name', $FriendlyName)
        }

        if ( $PSBoundParameters.ContainsKey('CertificatePath') ) {
            $params.Attribute.Add('Certificate', $CertificatePath)
        }

        if ( $PSBoundParameters.ContainsKey('CredentialPath') ) {
            $params.Attribute.Add('Credential', $CredentialPath)
        }

        if ( $Disable.IsPresent ) {
            $params.Attribute.Add('Disabled', '1')
        }

        if ( $PSBoundParameters.ContainsKey('WebSiteName') ) {
            $params.Attribute.Add('Update IIS', '1')
            $params.Attribute.Add('Web Site Name', $WebSiteName)
        }

        if ( $PSBoundParameters.ContainsKey('BindingIp') ) {
            $params.Attribute.Add('Binding IP Address', $BindingIp.ToString())
        }

        if ( $PSBoundParameters.ContainsKey('BindingPort') ) {
            $params.Attribute.Add('Binding Port', $BindingPort.ToString())
        }

        if ( $PSBoundParameters.ContainsKey('BindingHostName') ) {
            $params.Attribute.Add('Hostname', $BindingHostName)
        }

        if ( $PSBoundParameters.ContainsKey('CreateBinding') ) {
            $params.Attribute.Add('Create Binding', ([int]$CreateBinding).ToString())
        }

        if ( $PSBoundParameters.ContainsKey('WinRmPort') ) {
            $params.Attribute.Add('Port', $WinRmPort.ToString())
        }
    }

    process {

        if ( -not $PSBoundParameters.ContainsKey('SkipExistenceCheck') ) {

            # ensure the parent path exists and is of type device
            if ( $PSBoundParameters.ContainsKey('ApplicationName') ) {
                $devicePath = $Path
            }
            else {
                $deviceName = $Path.Split('\')[-1]
                $devicePath = $Path -replace ('\\+{0}' -f $deviceName), ''
            }

            $device = Get-VdcObject -Path $devicePath

            if ( $device ) {
                if ( $device.TypeName -ne 'Device' ) {
                    throw ('A device object could not be found at ''{0}''' -f $devicePath)
                }
            }
            else {
                throw ('No object was found at the parent path ''{0}''' -f $devicePath)
            }
        }

        if ( $PSBoundParameters.ContainsKey('ApplicationName') ) {
            $appPaths = $ApplicationName | ForEach-Object {
                $Path + "\$_"
            }
        }
        else {
            $appPaths = @($Path)
        }

        if ( $PSCmdlet.ShouldProcess($Path, 'Create CAPI application(s)') ) {
            foreach ($thisPath in $appPaths) {

                $params.Path = $thisPath


                $response = New-VdcObject @params

                if ( $PassThru ) {
                    $response
                }
            }

            if ( $PushCertificate.IsPresent ) {
                $params = @{
                    Path                = $CertificatePath
                    AdditionalParameter = @{
                        ApplicationDN = @($appPaths)
                    }
                    Push                = $true
                }

                Invoke-VdcCertificateAction @params
            }

        }
    }

    end {}
}
#EndRegion './Public/New-VdcCapiApplication.ps1' 352
#Region './Public/New-VdcCertificate.ps1' -1

function New-VdcCertificate {
    <#
    .SYNOPSIS
    Enrolls or provisions a new certificate

    .DESCRIPTION
    Enrolls or provisions a new certificate.
    Prior to TLSPDC 22.1, this function is asynchronous and will always return success.
    Beginning with 22.1, you can control this behavior.
    See https://docs.venafi.com/Docs/currentSDK/TopNav/Content/SDK/WebSDK/r-SDK-Certificates-API-settings.php.

    .PARAMETER Path
    The folder DN path for the new certificate.

    .PARAMETER Name
    Name of the certifcate object.
    If CommonName isn't provided, this value will be used.

    .PARAMETER CommonName
    Subject Common Name. If CommonName isn't provided, Name will be used.

    .PARAMETER Csr
    The PKCS#10 Certificate Signing Request (CSR).
    If this value is provided, any Subject DN fields and the KeyBitSize in the request are ignored.

    .PARAMETER CertificateType
    Type of certificate to be created. The default is X.509 Server Certificate.

    .PARAMETER CertificateAuthorityPath
    The path of the Certificate Authority Template object for enrolling the certificate.
    If the value is missing, it is expected a policy has been applied to Path.

    .PARAMETER CertificateAuthorityAttribute
    Name/value pairs providing any CA attributes to store with the Certificate object.
    During enrollment, these values will be submitted to the CA.

    .PARAMETER ManagementType
    The level of management that Trust Protection Platform applies to the certificate:
    - Enrollment: Default. Issue a new certificate, renewed certificate, or key generation request to a CA for enrollment. Do not automatically provision the certificate.
    - Provisioning: Issue a new certificate, renewed certificate, or key generation request to a CA for enrollment. Automatically install or provision the certificate.
    - Monitoring: Allow Trust Protection Platform to monitor the certificate for expiration and renewal.
    - Unassigned: Certificates are neither enrolled or monitored by Trust Protection Platform.

    .PARAMETER SubjectAltName
    A list of Subject Alternate Names.
    The value must be 1 or more hashtables with the SAN type and value.
    Acceptable SAN types are OtherName, Email, DNS, URI, and IPAddress.
    You can provide more than 1 of the same SAN type with multiple hashtables.

    .PARAMETER CustomField
    Hashtable of custom field(s) to be updated when creating the certificate.
    This is required when the custom fields are mandatory.
    The key is the name, not guid, of the custom field.

    .PARAMETER NoWorkToDo
    Turn off lifecycle processing for this certificate update

    .PARAMETER Device
    An array of hashtables for devices to be created.
    Available parameters can be found at https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Certificates-request.php.
    If provisioning applications as well, those should be provided with the Application parameter.

    .PARAMETER Application
    An array of hashtables for applications to be created.
    Available parameters can be found at https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Certificates-request-ApplicationsParameter.php.
    In addition to the application parameters, a key/value must be provided for the associated device.
    The key needs to be 'DeviceName' and the value is the ObjectName from the device.
    See the example.

    .PARAMETER TimeoutSec
    Introduced in 22.1, this controls the wait time, in seconds, for a CA to issue/renew a certificate.
    The default is 60 seconds.

    .PARAMETER PassThru
    Return an object representing the newly created certificate.
    If devices and/or applications were created, a 'Device' property will be available as well.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    None

    .OUTPUTS
    TppObject, if PassThru is provided
    If devices and/or applications were created, a 'Device' property will be available as well.

    .EXAMPLE
    New-VdcCertificate -Path '\ved\policy\folder' -Name 'mycert.com'
    Create certificate by name. A CA template policy must be defined.

    .EXAMPLE
    New-VdcCertificate -Path '\ved\policy\folder' -Name 'mycert.com' -CertificateAuthorityPath '\ved\policy\CA Templates\my template'
    Create certificate by name with specific CA template

    .EXAMPLE
    New-VdcCertificate -Path '\ved\policy\folder' -CertificateAuthorityPath '\ved\policy\CA Templates\my template' -Csr '-----BEGIN CERTIFICATE REQUEST-----\nMIIDJDCCAgwCAQAw...-----END CERTIFICATE REQUEST-----'
    Create certificate using a CSR

    .EXAMPLE
    New-VdcCertificate -Path '\ved\policy\folder' -Name 'mycert.com' -CertificateAuthorityPath '\ved\policy\CA Templates\my template' -CustomField @{''=''}
    Create certificate and update custom fields

    .EXAMPLE
    New-VdcCertificate -Path '\ved\policy\folder' -CommonName 'mycert.com' -CertificateAuthorityPath '\ved\policy\CA Templates\my template' -PassThru
    Create certificate using common name. Return the created object.

    .EXAMPLE
    New-VdcCertificate -Path '\ved\policy\folder' -Name 'mycert.com' -CertificateAuthorityPath '\ved\policy\CA Templates\my template' -SubjectAltName @{'Email'='me@x.com'},@{'IPAddress'='1.2.3.4'}
    Create certificate including subject alternate names

    .EXAMPLE
    New-VdcCertificate -Path '\ved\policy\folder' -Name 'mycert.com' -Device @{'PolicyDN'=$DevicePath; 'ObjectName'='MyDevice'; 'Host'='1.2.3.4'} -Application @{'DeviceName'='MyDevice'; 'ObjectName'='BasicApp'; 'DriverName'='appbasic'}
    Create a new certificate with associated device and app objects

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/New-VdcCertificate/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/New-VdcCertificate.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Certificates-request.php

    #>


    [CmdletBinding(DefaultParameterSetName = 'ByName', SupportsShouldProcess)]
    [Alias('New-TppCertificate')]

    param (

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('PolicyDN')]
        [String] $Path,

        [Parameter(Mandatory, ParameterSetName = 'ByName', ValueFromPipeline)]
        [Parameter(Mandatory, ParameterSetName = 'ByNameWithDevice', ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [String] $Name,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [Alias('Subject')]
        [String] $CommonName,

        [Parameter()]
        [string] $Csr,

        [Parameter()]
        [String] $CertificateType,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('CertificateAuthorityDN')]
        [Alias('CADN')]
        [String] $CertificateAuthorityPath,

        [Parameter()]
        [Hashtable] $CertificateAuthorityAttribute,

        [Parameter()]
        [TppManagementType] $ManagementType,

        [Parameter()]
        [Hashtable[]] $SubjectAltName,

        [Parameter()]
        [Hashtable] $CustomField,

        [Parameter()]
        [switch] $NoWorkToDo,

        [Parameter(ParameterSetName = 'ByName')]
        [Parameter(Mandatory, ParameterSetName = 'ByNameWithDevice')]
        [hashtable[]] $Device,

        [Parameter(ParameterSetName = 'ByNameWithDevice')]
        [hashtable[]] $Application,

        [Parameter()]
        [Alias('WorkToDoTimeout')]
        [int32] $TimeoutSec = 60,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        if ( $PSBoundParameters.ContainsKey('SubjectAltName') ) {

            $errors = $SubjectAltName | ForEach-Object {
                $_.GetEnumerator() | ForEach-Object {

                    $thisKey = $_.Key
                    $thisValue = $_.Value

                    switch ($thisKey) {
                        'OtherName' {
                            # no validaton
                        }

                        'Email' {
                            try {
                                $null = [mailaddress]$thisValue
                            }
                            catch {
                                ('''{0}'' is not a valid email' -f $thisValue)
                            }
                        }

                        'DNS' {
                            # no validaton
                        }

                        'URI' {
                            # no validaton
                        }

                        'IPAddress' {
                            try {
                                $null = [ipaddress]$thisValue
                            }
                            catch {
                                ('''{0}'' is not a valid IP Address' -f $thisValue)
                            }
                        }

                        Default {
                            # invalid type name provided
                            ('''{0}'' is not a valid SAN type. Valid values include OtherName, Email, DNS, URI, and IPAddress.' -f $thisKey)
                        }
                    }
                }
            }

            if ( $errors ) {
                throw $errors
            }
        }

        $params = @{

            Method  = 'Post'
            UriLeaf = 'certificates/request'
            Body    = @{
                PolicyDN             = $Path
                Origin               = 'VenafiPS'
                CASpecificAttributes = @(
                    @{
                        'Name'  = 'Origin'
                        'Value' = 'VenafiPS'
                    }
                )
                SetWorkToDo          = -not $NoWorkToDo
            }
        }

        if ( $CertificateType ) {
            $params.Body.Add('CertificateType', $CertificateType)
        }

        if ( $PSBoundParameters.ContainsKey('CertificateAuthorityAttribute') ) {
            $CertificateAuthorityAttribute.GetEnumerator() | ForEach-Object {

                $params.Body.CASpecificAttributes +=
                @{
                    'Name'  = $_.Key
                    'Value' = $_.Value
                }
            }
        }

        if ( $Csr ) {
            $params.Body.Add('PKCS10', ($Csr -replace "`n|`r"))
        }

        if ( $PSBoundParameters.ContainsKey('CertificateAuthorityPath') ) {
            $params.Body.Add('CADN', $CertificateAuthorityPath)
        }

        if ( $PSBoundParameters.ContainsKey('CommonName') ) {
            $params.Body.Add('Subject', $CommonName)
        }

        if ( $PSBoundParameters.ContainsKey('ManagementType') ) {
            $params.Body.Add('ManagementType', [enum]::GetName([TppManagementType], $ManagementType))
        }

        $params.Body.Add('WorkToDoTimeout', $TimeoutSec)

        if ( $PSBoundParameters.ContainsKey('SubjectAltName') ) {
            $newSan = @($SubjectAltName | ForEach-Object {
                    $_.GetEnumerator() | ForEach-Object {
                        @{
                            'TypeName' = $_.Key
                            'Name'     = $_.Value
                        }
                    }
                }
            )
            $params.Body.Add('SubjectAltNames', $newSan)
        }

        if ( $PSBoundParameters.ContainsKey('CustomField') ) {
            $newCf = $CustomField.GetEnumerator() | ForEach-Object {

                @{
                    'Name'   = $_.Key
                    'Values' = @($_.Value)
                }
            }
            $params.Body.Add('CustomFields', @($newCf))
        }

        if ( $Device ) {
            # convert apps to array of custom objects to make it easier to search
            $appCustom = @($Application | ForEach-Object { [pscustomobject] $_ })

            # loop through devices and append any apps found
            $updatedDevice = foreach ($thisDevice in $Device) {

                $thisApplication = $appCustom | Where-Object { $_.DeviceName -eq $thisDevice.ObjectName }

                if ( $thisApplication ) {
                    $finalAppList = foreach ($app in $thisApplication | Select-Object -Property * -ExcludeProperty DeviceName) {
                        $ht = @{}
                        $app.psobject.properties | ForEach-Object { $ht[$_.Name] = $_.Value }
                        $ht
                    }

                    $thisDevice.Applications = @($finalAppList)
                }

                $thisDevice
            }
            $params.Body.Add('Devices', @($updatedDevice))

        }
    }

    process {

        $params.Body.ObjectName = $Name
        if ( -not $PSBoundParameters.ContainsKey('CommonName')) {
            $params.Body.Subject = $Name
        }

        if ( $PSCmdlet.ShouldProcess("$Path\$Name", 'Create new certificate') ) {

            try {
                $response = Invoke-VenafiRestMethod @params
                Write-Verbose ($response | Out-String)

                if ( $PassThru ) {
                    $newCert = Get-VdcObject -Path $response.CertificateDN
                    if ( $Device ) {
                        $newCert | Add-Member @{ 'Device' = @{'Path' = $response.Devices.DN } }
                        if ( $Application ) {
                            $newCert.Device.Application = $response.Devices.Applications.DN
                        }
                    }
                    $newCert
                }
            }
            catch {
                Write-Error $_
                continue
            }
        }
    }
}
#EndRegion './Public/New-VdcCertificate.ps1' 398
#Region './Public/New-VdcCustomField.ps1' -1


function New-VdcCustomField {
    <#
    .SYNOPSIS
    Create new custom field in Venafi of the specified class and type.

    .DESCRIPTION
    Creates a new custom field in Venafi. Required parameters are name, label, class and type.
    See Venafi docs for information about other metadata items.

    .PARAMETER Name
    Name of new custom field

    .PARAMETER Label
    Label of new custom field

    .PARAMETER Class
    Class of new custom field, either 'Device' or 'X509 Certificate'

    .PARAMETER Type
    Type of new custom field, one of 'String', 'List', 'DateTime', 'Identity'

    .PARAMETER AllowedValue
    An array of allowable values for the Custom Field.

    .PARAMETER Single
    Controls the number of input selections. If not used the user can select
    multiple values for the Custom Field. Otherwise the user can select only
    one value

    .PARAMETER Help
    The tool tip to show to the user how to populate the Custom Field.

    .PARAMETER ErrorMessage
    The error message that you define for this Custom Field.

    .PARAMETER Mandatory
    Determines if a value is required in the custom field. If not used a value is
    optional, otherwise the user must enter a value.

    .PARAMETER Policyable
    Determines if the custom field can be used in a policy setting to apply to
    all objects in the policy. If not used the custom field cannot apply to a policy.

    .PARAMETER RegEx
    The regular expression to control user input.

    .PARAMETER RenderHidden
    Controls the visibility of the custom field in the UI. By default custom fields
    are visible in the class they are created for. This switch hides them.

    .PARAMETER RenderReadOnly
    Controls if the custom field is read only. By default a user can change the custom
    field value. This switch makes it the custom field value cannot be changed.

    .PARAMETER DateOnly
    Controls if the custom field requires a calendar date. By default a date is not required.

    .PARAMETER TimeOnly
    Controls if the custom field requires a time of day. By default a time is not required.

    .PARAMETER PassThru
    Return a PSCustomObject with the following properties:
        AllowedValues
        Class
        DateOnly
        DN
        ErrorMessage
        Guid
        Help
        Label
        Mandatory
        Name
        Policyable
        RegularExpression
        RenderHidden
        RenderReadOnly
        Single
        TimeOnly
        Type

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    None

    .OUTPUTS
    PSCustomObject, if PassThru provided

    .EXAMPLE
    New-VdcCustomField -Name "Last Date" -Label "Last Date" -Class "X509 Certificate" -Type "String" -RenderReadOnly -Help "Last Date of certificate import"
    Create new custom field for certificates

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/New-VdcCustomField/

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Metadata-DefineItem.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Metadata-Item.php

    #>


    [CmdletBinding(SupportsShouldProcess)]
    [Alias('New-TppCustomField')]

    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Label,

        [Parameter(Mandatory)]
        [ValidateSet('Device', 'X509 Certificate')]
        [ValidateNotNullOrEmpty()]
        [string[]]$Class,

        [Parameter(Mandatory)]
        [ValidateSet('String', 'List', 'DateTime', 'Identity')]
        [ValidateNotNullOrEmpty()]
        [string]$Type,

        [Parameter()]
        [string[]]$AllowedValue,

        [Parameter()]
        [switch]$Single,

        [Parameter()]
        [string]$Help,

        [Parameter()]
        [string]$ErrorMessage,

        [Parameter()]
        [switch]$Mandatory,

        [Parameter()]
        [switch]$Policyable,

        [Parameter()]
        [string]$RegEx,

        [Parameter()]
        [switch]$RenderHidden,

        [Parameter()]
        [switch]$RenderReadOnly,

        [Parameter()]
        [switch]$DateOnly,

        [Parameter()]
        [switch]$TimeOnly,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $resultObj = @()

        if ( $Type -eq 'String' ) {
            $ItemType = '1'
        }
        elseif ( $Type -eq 'List' ) {
            $ItemType = '2'
        }
        elseif ( $Type -eq 'DateTime' ) {
            $ItemType = '4'
        }
        elseif ( $Type -eq 'Identity' ) {
            $ItemType = '5'
        }

        $params = @{

            Method        = 'Post'
            UriLeaf       = 'Metadata/DefineItem'
            Body          = @{
                "Item" = @{
                    "Name"           = $Name
                    "Classes"        = @($Class)
                    "Label"          = $Label
                    "Type"           = $ItemType
                    "Single"         = $Single.ToString().ToLower()
                    "Mandatory"      = $Mandatory.ToString().ToLower()
                    "Policyable"     = $Policyable.ToString().ToLower()
                    "RenderHidden"   = $RenderHidden.ToString().ToLower()
                    "RenderReadOnly" = $RenderReadOnly.ToString().ToLower()
                    "DateOnly"       = $DateOnly.ToString().ToLower()
                    "TimeOnly"       = $TimeOnly.ToString().ToLower()
                }
            }
        }
        if ( $PSBoundParameters.ContainsKey('Help') ) {
            $params.Body.Item['Help'] = $Help
        }
        if ( $PSBoundParameters.ContainsKey('AllowedValue') ) {
            $params.Body.Item['AllowedValues'] = @($AllowedValue)
        }
        if ( $PSBoundParameters.ContainsKey('ErrorMessage') ) {
            $params.Body.Item['ErrorMessage'] = $ErrorMessage
        }
        if ( $PSBoundParameters.ContainsKey('RegEx') ) {
            $params.Body.Item['RegularExpression'] = $RegEx
        }
    }

    end {
        if ( $PSCmdlet.ShouldProcess($Label) ) {

            $response = Invoke-VenafiRestMethod @params

            if ( $response.Result -eq [TppMetadataResult]::Success ) {
                if (( $PassThru )) {
                    $resultObj = [PSCustomObject] @{
                        "Path"  = $response.Item.DN
                        "Label" = $response.Item.Label
                        "Name"  = $response.Item.Name
                        "Guid"  = [guid]$response.Item.Guid
                        "Type"  = $response.Item.Type
                        "Class" = $response.Item.Classes
                    }
                    if ($response.Item.Mandatory) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "Mandatory" -Value $response.Item.Mandatory
                    }
                    if ($response.Item.Policyable) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "Policyable" -Value $response.Item.Policyable
                    }
                    if ($response.Item.RenderHidden) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "RenderHidden" -Value $response.Item.RenderHidden
                    }
                    if ($response.Item.RenderReadOnly) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "RenderReadOnly" -Value $response.Item.RenderReadOnly
                    }
                    if ($response.Item.DateOnly) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "DateOnly" -Value $response.Item.DateOnly
                    }
                    if ($response.Item.TimeOnly) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "TimeOnly" -Value $response.Item.TimeOnly
                    }
                    if ($response.Item.Help) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "Help" -Value $response.Item.Help
                    }
                    if ($response.Item.AllowedValues) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "AllowedValues" -Value $response.Item.AllowedValues
                    }
                    if ($response.Item.Single) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "Single" -Value $response.Item.Single
                    }
                    if ($response.Item.ErrorMessage) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "ErrorMessage" -Value $response.Item.ErrorMessage
                    }
                    if ($response.Item.RegularExpression) {
                        $resultObj | Add-Member -MemberType NoteProperty -Name "RegularExpression" -Value $response.Item.RegularExpression
                    }
                    return $resultObj
                }
            }
            else {
                throw $response.Error
            }
        }
    }
}
#EndRegion './Public/New-VdcCustomField.ps1' 280
#Region './Public/New-VdcDevice.ps1' -1

function New-VdcDevice {
    <#
    .SYNOPSIS
    Add a new device

    .DESCRIPTION
    Add a new device with optional attributes

    .PARAMETER Path
    Full path to the new device.
    Alternatively, you can provide just the policy path, but then DeviceName must be provided.

    .PARAMETER DeviceName
    Name of 1 or more devices to create. Path must be of type Policy to use this.

    .PARAMETER Description
    Device description

    .PARAMETER CredentialPath
    Path to the credential which has permissions to update the device

    .PARAMETER Hostname
    Hostname or IP Address of the device

    .PARAMETER PassThru
    Return a TppObject representing the newly created policy.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    TppObject, if PassThru provided

    .EXAMPLE
    $newPolicy = New-VdcDevice -Path '\VED\Policy\MyFolder\device' -PassThru
    Create device with full path to device and returning the object created

    .EXAMPLE
    $policyPath | New-VdcDevice -DeviceName 'myDevice' -Hostname 1.2.3.4
    Pipe policy path and provide device details

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/New-VdcDevice/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/New-VdcDevice.ps1

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/New-VdcObject/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/New-VdcObject.ps1

    #>


    [CmdletBinding(SupportsShouldProcess)]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [string] $Path,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String[]] $DeviceName,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String] $Description,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [string] $CredentialPath,

        [Parameter()]
        [Alias('IpAddress')]
        [string] $Hostname,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        # leave .Validate() out as New-VdcObject will do it for us later

        $params = @{
            Path       = ''
            Class      = 'Device'
            PassThru   = $true

            Attribute  = @{ }
        }

        if ( $PSBoundParameters.ContainsKey('Description') ) {
            $params.Attribute['Description'] = $Description
        }

        if ( $PSBoundParameters.ContainsKey('CredentialPath') ) {
            $params.Attribute['Credential'] = $CredentialPath
        }

        if ( $PSBoundParameters.ContainsKey('Hostname') ) {
            $params.Attribute['Host'] = $Hostname
        }

        # if no attributes were provided, we need to pull out the attribute key
        # otherwise the request will fail
        if ( $params.Attribute.Count -eq 0 ) {
            $params.Remove('Attribute')
        }
    }

    process {

        if ( $PSBoundParameters.ContainsKey('DeviceName') ) {
            $devicePaths = $DeviceName | ForEach-Object {
                $Path + "\$_"
            }
        }
        else {
            $devicePaths = @($Path)
        }

        foreach ($thisPath in $devicePaths) {

            $params.Path = $thisPath

            if ( $PSCmdlet.ShouldProcess($thisPath, 'Create Device Object') ) {

                $response = New-VdcObject @params

                if ( $PassThru ) {
                    $response
                }
            }
        }
    }
}
#EndRegion './Public/New-VdcDevice.ps1' 166
#Region './Public/New-VdcObject.ps1' -1

function New-VdcObject {
    <#
    .SYNOPSIS
    Create a new object

    .DESCRIPTION
    Generic use function to create a new object if a specific function hasn't been created yet for the class.

    .PARAMETER Path
    Full path, including name, for the object to be created.
    If the root path is excluded, \ved\policy will be prepended.

    .PARAMETER Class
    Class name of the new object.
    See https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/SchemaReference/r-SDK-CNattributesWhere.php for more info.

    .PARAMETER Attribute
    Hashtable with initial values for the new object.
    These will be specific to the object class being created.

    .PARAMETER PushCertificate
    If creating an application object, you can optionally push the certificate once the creation is complete.
    Only available if a 'Certificate' key containing the certificate path is provided for Attribute.

    .PARAMETER Force
    Force the creation of missing parent policy folders when the class is either Policy or Device.

    .PARAMETER PassThru
    Return a TppObject representing the newly created object.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .EXAMPLE
    New-VdcObject -Path '\VED\Policy\Test Device' -Class 'Device' -Attribute @{'Description'='new device testing'}

    Create a new object

    .EXAMPLE
    New-VdcObject -Path 'missing\folder\again' -Class 'Policy' -Force

    Create a new object as well as any missing policy folders in the path

    .EXAMPLE
    New-VdcObject -Path '\VED\Policy\Test Device' -Class 'Device' -Attribute @{'Description'='new device testing'} -PassThru

    Create a new object and return the resultant object

    .EXAMPLE
    New-VdcObject -Path '\VED\Policy\Test Device\App' -Class 'Basic' -Attribute @{'Driver Name'='appbasic';'Certificate'='\Ved\Policy\mycert.com'}

    Create a new Basic application and associate it to a device and certificate

    .INPUTS
    none

    .OUTPUTS
    TppObject, if PassThru provided

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/New-VdcObject/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/New-VdcObject.ps1

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Add-VdcCertificateAssociation.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-create.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/SchemaReference/r-SDK-CNattributesWhere.php

    #>


    [CmdletBinding(DefaultParameterSetName = 'NonApplicationObject', SupportsShouldProcess)]
    [Alias('New-TppObject')]

    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $Path,

        [Parameter(Mandatory)]
        [String] $Class,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [Hashtable] $Attribute,

        [Parameter()]
        [Alias('ProvisionCertificate')]
        [switch] $PushCertificate,

        [Parameter()]
        [switch] $Force,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

    if ( $PushCertificate -and (-not $Attribute.Certificate) ) {
        Write-Warning 'A ''Certificate'' key containing the certificate path must be provided for Attribute when using PushCertificate, eg. -Attribute @{''Certificate''=''\Ved\Policy\mycert.com''}. Certificate provisioning will not take place.'
    }

    $newPath = $Path | ConvertTo-VdcFullPath

    $params = @{

        Method        = 'Post'
        UriLeaf       = 'config/create'
        Body          = @{
            ObjectDN = $newPath
            Class    = $Class
        }
    }

    if ( $PSBoundParameters.ContainsKey('Attribute') ) {
        # api requires a list of hashtables for nameattributelist
        # with 2 items per hashtable, with key names 'name' and 'value'
        # this is cumbersome for the user so allow them to pass a standard hashtable and convert it for them
        $updatedAttribute = @($Attribute.GetEnumerator() | ForEach-Object { @{'Name' = $_.name; 'Value' = $_.value } })
        $params.Body.Add('NameAttributeList', $updatedAttribute)
    }

    if ( $PSCmdlet.ShouldProcess($newPath, ('Create {0} Object' -f $Class)) ) {

        $retryCount = 0
        do {
            $retryCreate = $false

            $response = Invoke-VenafiRestMethod @params

            switch ($response.Result) {

                1 {
                    Write-Verbose "Successfully created $Class at $newPath"
                    $returnObject = ConvertTo-VdcObject -Path $response.Object.DN -Guid $response.Object.Guid -TypeName $response.Object.TypeName
                }

                400 {
                    # occurs when a parent object, anywhere in the path, is missing

                    # with these classes we know the parent is a policy so we can create them
                    if ( $Class -in 'Policy', 'Device' ) {
                        if ( -not $Force ) {
                            throw "Part of the path $newPath does not exist. Use -Force to create the missing policy folders."
                        } else {

                            $pathSplit = $newPath.Split('\')

                            # create the parent policy folders
                            # don't try and create \ved or \ved\policy levels
                            for ($i = 3; $i -lt ($pathSplit.Count - 1); $i++) {
                                if ( -not (Find-VdcObject -Path ($pathSplit[0..($i - 1)] -join '\') -Pattern $pathSplit[$i])) {
                                    Write-Verbose ('Creating missing policy folder {0}' -f ($pathSplit[0..$i] -join '\'))
                                    New-VdcPolicy -Path ($pathSplit[0..$i] -join '\')
                                }
                            }

                            $retryCreate = $true
                        }
                    } else {
                        throw "Part of path $newPath does not exist."
                    }
                }

                401 {
                    throw "$newPath already exists"
                }

                Default {
                    throw ('Error creating object: {0}, {1}' -f $response.Result, $_)
                }
            }

            $retryCount++

        } until (
            -not $retryCreate -or $retryCount -gt 2
        )

        if ( $Attribute.Certificate ) {
            $associateParams = @{
                CertificatePath = $Attribute.Certificate
                ApplicationPath = $response.Object.DN
            }
            if ( $PushCertificate.IsPresent ) {
                $associateParams.Add('PushCertificate', $true)
            }

            Add-VdcCertificateAssociation @associateParams
        }

        if ( $PassThru ) {
            $returnObject
        }
    }
}
#EndRegion './Public/New-VdcObject.ps1' 209
#Region './Public/New-VdcPolicy.ps1' -1

function New-VdcPolicy {
    <#
    .SYNOPSIS
    Add a new policy folder

    .DESCRIPTION
    Add a new policy folder(s). Add object attributes or policy attributes at the same time.

    .PARAMETER Path
    Full path to the new policy folder.
    If the root path is excluded, \ved\policy will be prepended.
    If used with -Name, this will be the root path and subfolders will be created.

    .PARAMETER Name
    One of more policy folders to create under -Path.

    .PARAMETER Attribute
    Hashtable with names and values to be set on the policy itself.
    If used with -Class, this will set policy attributes.
    If setting a custom field, you can use either the name or guid as the key.
    To clear a value overwriting policy, set the value to $null.

    .PARAMETER Class
    Use with -Attribute to set policy attributes at policy creation time.
    If unsure of the class name, add the value through the TLSPDC UI and go to Support->Policy Attributes to find it.

    .PARAMETER Lock
    Use with -PolicyAttribute and -Class to lock the policy attribute

    .PARAMETER Description
    Deprecated. Use -Attribute @{''Description''=''my description''} instead.

    .PARAMETER Force
    Force the creation of missing parent policy folders

    .PARAMETER PassThru
    Return a TppObject representing the newly created policy.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    TppObject, if PassThru provided

    .EXAMPLE
    $newPolicy = New-VdcPolicy -Path 'new'

    Create a new policy folder

    .EXAMPLE
    $newPolicy = New-VdcPolicy -Path 'existing' -Name 'new1', 'new2', 'new3'

    Create multiple policy folders

    .EXAMPLE
    $newPolicy = New-VdcPolicy -Path 'new1\new2\new3' -Force

    Create a new policy folder named new3 and create new1 and new2 if they do not exist

    .EXAMPLE
    $newPolicy = New-VdcPolicy -Path 'new' -Attribute {'Description'='my new policy folder'}

    Create a new policy folder setting attributes on the object at creation time

    .EXAMPLE
    $newPolicy = New-VdcPolicy -Path 'new' -Class 'X509 Certificate' -Attribute {'State'='UT'}

    Create a new policy folder setting policy attributes (not object attributes)

    .EXAMPLE
    $newPolicy = New-VdcPolicy -Path 'new' -Class 'X509 Certificate' -Attribute {'State'='UT'} -Lock

    Create a new policy folder setting policy attributes (not object attributes) and locking them

    .EXAMPLE
    $newPolicy = New-VdcPolicy -Path 'new' -PassThru

    Create a new policy folder returning the policy object created

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/New-VdcPolicy/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/New-VdcPolicy.ps1

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/New-VdcObject/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/New-VdcObject.ps1

    #>


    [CmdletBinding(SupportsShouldProcess)]
    [Alias('New-TppPolicy')]

    param (
        [Parameter(Mandatory, ParameterSetName = 'Path', ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'Name', ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'PathWithPolicyAttribute', Mandatory, ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'NameWithPolicyAttribute', Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string] $Path,

        [Parameter(ParameterSetName = 'Name', Mandatory)]
        [Parameter(ParameterSetName = 'NameWithPolicyAttribute', Mandatory)]
        [string[]] $Name,

        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'Name')]
        [String] $Description,

        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'Name')]
        [Parameter(ParameterSetName = 'PathWithPolicyAttribute', Mandatory)]
        [Parameter(ParameterSetName = 'NameWithPolicyAttribute', Mandatory)]
        [hashtable] $Attribute,

        [Parameter(ParameterSetName = 'PathWithPolicyAttribute', Mandatory)]
        [Parameter(ParameterSetName = 'NameWithPolicyAttribute', Mandatory)]
        [string] $Class,

        [Parameter(ParameterSetName = 'PathWithPolicyAttribute')]
        [Parameter(ParameterSetName = 'NameWithPolicyAttribute')]
        [switch] $Lock,

        [Parameter()]
        [switch] $Force,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        $params = @{
            Class         = 'Policy'
            PassThru      = $true
            Force         = $Force
        }

        if ( ($PSBoundParameters.ContainsKey('Description') -or $PSBoundParameters.ContainsKey('Attribute')) -and $PSCmdlet.ParameterSetName -in 'Path', 'Name' ) {
            $params.Attribute = @{}

            if ( $PSBoundParameters.ContainsKey('Description') ) {
                $params.Attribute += @{'Description' = $Description }
            }

            if ( $PSBoundParameters.ContainsKey('Attribute') ) {
                $params.Attribute += $Attribute
            }
        }
    }

    process {

        $newPath = $Path | ConvertTo-VdcFullPath

        if ( $PSCmdlet.ParameterSetName -in 'Path', 'PathWithPolicyAttribute' ) {

            $params.Path = $newPath

            if ( $PSCmdlet.ShouldProcess($newPath, 'Create Policy') ) {

                $response = New-VdcObject @params

                if ( $PSBoundParameters.ContainsKey('Class') ) {
                    $response | Set-VdcAttribute -Attribute $Attribute -Class $Class -Lock:$Lock
                }

                if ( $PassThru ) {
                    $response
                }
            }
        } else {
            foreach ($thisName in $Name) {
                $PSBoundParameters['Path'] = "$newPath\$thisName"
                $null = $PSBoundParameters.Remove('Name')

                New-VdcPolicy @PSBoundParameters
            }
        }

    }
}
#EndRegion './Public/New-VdcPolicy.ps1' 195
#Region './Public/New-VdcTeam.ps1' -1

function New-VdcTeam {
    <#
    .SYNOPSIS
    Create a new team

    .DESCRIPTION
    Create a new TLSPDC team

    .PARAMETER Name
    Team name

    .PARAMETER Owner
    1 or more owners for the team
    Provide the identity ID property from Find-VdcIdentity or Get-VdcIdentity.

    .PARAMETER Member
    1 or more members for the team
    Provide the identity ID property from Find-VdcIdentity or Get-VdcIdentity.

    .PARAMETER Policy
    1 or more policy folder paths this team manages.

    .PARAMETER Product
    1 or more product names, 'TLS', 'SSH', and/or 'Code Signing'.

    .PARAMETER Description
    Team description or purpose.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token key can also provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .EXAMPLE
    New-VenafiTeam -Name 'My New Team' -Member 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}' -Owner 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e7}' -Product 'TLS'

    Create a new team

    .EXAMPLE
    New-VenafiTeam -Name 'My New Team' -Member 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}' -Owner 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e7}' -Product 'TLS' -Policy '\ved\policy\myfolder'

    Create a new team and assign it to a policy

    .EXAMPLE
    New-VenafiTeam -Name 'My New Team' -Member 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}' -Owner 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e7}' -Product 'TLS' -Description 'One amazing team'

    Create a new team with optional description

    .EXAMPLE
    New-VenafiTeam -Name 'My New Team' -Member 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}' -Owner 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e7}' -Product 'TLS' -PassThru

    Name : My New Team
    ID : local:{a6053090-e309-49d9-98a7-28cbe7896c27}
    Path : \VED\Identity\My New Team
    FullName : local:My New Team
    IsGroup : True
    Members : @{Name=sample-user; ID=local:{6baad36c-7cac-48c8-8e54-000cc22ad88f};
               Path=\VED\Identity\sample-user; FullName=local:sample-user; IsGroup=False}
    Owners : @{Name=sample-owner; ID=local:{d1a76bc7-d3a6-431b-9bea-d2d8780ecd86};
               Path=\VED\Identity\sample-owner; FullName=local:sample-owner; IsGroup=False}

    Create a new team returning the new team

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Teams.php
    #>


    [CmdletBinding()]

    param (

        [Parameter(Mandatory)]
        [string] $Name,

        [Parameter(Mandatory)]
        [string[]] $Owner,

        [Parameter(Mandatory)]
        [string[]] $Member,

        [Parameter()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid policy path"
                }
            })]
        [string[]] $Policy,

        [Parameter(Mandatory)]
        [ValidateSet('TLS', 'SSH', 'Code Signing')]
        [string[]] $Product,

        [Parameter()]
        [string] $Description,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        $params = @{
            Method  = 'Post'
            UriLeaf = 'Teams/'
        }

        $members = foreach ($thisMember in $Member) {
            if ( $thisMember.StartsWith('local') ) {
                $memberIdentity = Get-VdcIdentity -ID $thisMember
                @{
                    'PrefixedName'      = $memberIdentity.FullName
                    'PrefixedUniversal' = $memberIdentity.ID
                }
            }
            else {
                @{'PrefixedUniversal' = $thisMember }
            }
        }
        $owners = foreach ($thisOwner in $Owner) {
            if ( $thisOwner.StartsWith('local') ) {
                $ownerIdentity = Get-VdcIdentity -ID $thisOwner
                @{
                    'PrefixedName'      = $ownerIdentity.FullName
                    'PrefixedUniversal' = $ownerIdentity.ID
                }
            }
            else {
                @{'PrefixedUniversal' = $thisOwner }
            }
        }

        $params.Body = @{
            'Name'     = @{'PrefixedName' = "local:$Name" }
            'Members'  = @($members)
            'Owners'   = @($owners)
            'Products' = @($Product)
        }

        if ( $Policy ) {
            $params.Body.Add('Assets', @($Policy))
        }

        if ( $Description ) {
            $params.Body.Add('Description', $Description)
        }

        $response = Invoke-VenafiRestMethod @params | Select-Object -ExpandProperty ID

        if ( $PassThru ) {
            $response | Get-VdcTeam
        }
    }
}
#EndRegion './Public/New-VdcTeam.ps1' 165
#Region './Public/New-VdcToken.ps1' -1

function New-VdcToken {
    <#
    .SYNOPSIS
    Get a new access token or refresh an existing one

    .DESCRIPTION
    Get an access token and refresh token (if enabled) to be used with New-VenafiSession or other scripts/utilities that take such a token.
    You can also refresh an existing access token if you have the associated refresh token.
    Authentication can be provided as integrated, credential, or certificate.

    .PARAMETER AuthServer
    Auth server or url, eg. venafi.company.com

    .PARAMETER ClientId
    Application/integration ID configured in Venafi for token-based authentication.
    Case sensitive.

    .PARAMETER Scope
    Hashtable with Scopes and privilege restrictions.
    The key is the scope and the value is one or more privilege restrictions separated by commas.
    A privilege restriction of none or read, use a value of $null.
    Scopes include Agent, Certificate, Code Signing, Configuration, Restricted, Security, SSH, and statistics.
    See https://docs.venafi.com/Docs/current/TopNav/Content/SDK/AuthSDK/r-SDKa-OAuthScopePrivilegeMapping.php
    Using a scope of {'all'='core'} will set all scopes except for admin.
    Using a scope of {'all'='admin'} will set all scopes including admin.
    Usage of the 'all' scope is not suggested for production.

    .PARAMETER Credential
    Username / password credential used to request API Token

    .PARAMETER State
    A session state, redirect URL, or random string to prevent Cross-Site Request Forgery (CSRF) attacks

    .PARAMETER Jwt
    JSON web token.
    Available in TLSPDC v22.4 and later.
    Ensure jwt mapping has been configured in VCC, Access Management->JWT Mappings.

    .PARAMETER Certificate
    Certificate used to request API token. Certificate authentication must be configured for remote web sdk clients, https://docs.venafi.com/Docs/current/TopNav/Content/CA/t-CA-ConfiguringInTPPandIIS-tpp.php.

    .PARAMETER RefreshToken
    Provide -RefreshToken along with -ClientId to obtain a new access and refresh token.
    You can either provide a String, SecureString, or PSCredential.
    If providing a credential, the username is not used.

    .PARAMETER SkipCertificateCheck
    Bypass certificate validation when connecting to the server.
    This can be helpful for pre-prod environments where ssl isn't setup on the website or you are connecting via IP.

    .PARAMETER VenafiSession
    VenafiSession object created from New-VenafiSession method.

    .EXAMPLE
    New-VdcToken -AuthServer 'https://mytppserver.example.com' -Scope @{ Certificate = "manage,discover"; Configuration = "manage" } -ClientId 'MyAppId' -Credential $credential
    Get a new token with OAuth

    .EXAMPLE
    New-VdcToken -AuthServer 'mytppserver.example.com' -Scope @{ Certificate = "manage,discover"; Configuration = "manage" } -ClientId 'MyAppId'
    Get a new token with Integrated authentication

    .EXAMPLE
    New-VdcToken -AuthServer 'mytppserver.example.com' -Scope @{ Certificate = "manage,discover"; Configuration = "manage" } -ClientId 'MyAppId' -Certificate $cert
    Get a new token with certificate authentication

    .EXAMPLE
    New-VdcToken -AuthServer 'mytppserver.example.com' -ClientId 'MyApp' -RefreshToken $refreshCred
    Refresh an existing access token by providing the refresh token directly

    .EXAMPLE
    New-VdcToken -VenafiSession $mySession
    Refresh an existing access token by providing a VenafiSession object

    .INPUTS
    None

    .OUTPUTS
    PSCustomObject with the following properties:
        Server
        AccessToken
        RefreshToken
        Scope
        Identity
        TokenType
        ClientId
        Expires
        RefreshExpires (This property is null when TLSPDC version is less than 21.1)
    #>


    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Integrated')]
    [Alias('New-TppToken')]
    [OutputType([PSCustomObject])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Generating cred from api call response data')]
    [OutputType([System.Boolean])]

    param (
        [Parameter(ParameterSetName = 'OAuth', Mandatory)]
        [Parameter(ParameterSetName = 'Integrated', Mandatory)]
        [Parameter(ParameterSetName = 'Certificate', Mandatory)]
        [Parameter(ParameterSetName = 'Jwt', Mandatory)]
        [Parameter(ParameterSetName = 'RefreshToken', Mandatory)]
        [ValidateScript(
            {
                $validateMe = if ( $_ -notlike 'https://*') {
                    'https://{0}' -f $_
                }
                else {
                    $_
                }

                try {
                    $null = [System.Uri]::new($validateMe)
                    $true
                }
                catch {
                    throw 'Please enter a valid server, https://venafi.company.com or venafi.company.com'
                }
            }
        )]
        [Alias('Server')]
        [string] $AuthServer,

        [Parameter(ParameterSetName = 'OAuth', Mandatory)]
        [Parameter(ParameterSetName = 'Integrated', Mandatory)]
        [Parameter(ParameterSetName = 'Certificate', Mandatory)]
        [Parameter(ParameterSetName = 'RefreshToken', Mandatory)]
        [Parameter(ParameterSetName = 'Jwt', Mandatory)]
        [string] $ClientId,

        [Parameter(ParameterSetName = 'OAuth', Mandatory)]
        [Parameter(ParameterSetName = 'Integrated', Mandatory)]
        [Parameter(ParameterSetName = 'Certificate', Mandatory)]
        [Parameter(ParameterSetName = 'Jwt', Mandatory)]
        [hashtable] $Scope,

        [Parameter(ParameterSetName = 'OAuth', Mandatory)]
        [System.Management.Automation.PSCredential] $Credential,

        [Parameter(ParameterSetName = 'Integrated')]
        [Parameter(ParameterSetName = 'OAuth')]
        [string] $State,

        [Parameter(ParameterSetName = 'Jwt', Mandatory)]
        [string] $Jwt,

        [Parameter(ParameterSetName = 'Certificate', Mandatory)]
        [X509Certificate] $Certificate,

        [Parameter(ParameterSetName = 'RefreshToken', Mandatory)]
        [ValidateScript(
            {
                if ( $_ -is [string] -or $_ -is [securestring] -or $_ -is [pscredential] ) {
                    $true
                }
                else {
                    throw 'Unsupported type. Provide either a String, SecureString, or PSCredential.'
                }
            }
        )]
        [psobject] $RefreshToken,

        [Parameter()]
        [switch] $SkipCertificateCheck,

        [Parameter(ParameterSetName = 'RefreshSession', Mandatory)]
        [ValidateScript( {
                if ( -not $_.Token.RefreshToken ) {
                    throw 'VenafiSession does not have a refresh token. To get a new access token, create a new session with New-VenafiSession.'
                }

                if ( $_.Token.RefreshExpires -and $_.Token.RefreshExpires -lt (Get-Date) ) {
                    throw "The refresh token has expired. Retrieve a new access token with New-VenafiSession."
                }

                $true
            })]
        [pscustomobject] $VenafiSession

    )

    $params = @{
        Method               = 'Post'
        UriRoot              = 'vedauth'
        Body                 = @{}
        SkipCertificateCheck = $SkipCertificateCheck
    }

    if ( $PsCmdlet.ParameterSetName -eq 'RefreshSession' ) {
        $params.Server = $VenafiSession.Token.Server
        $params.UriLeaf = 'authorize/token'
        $params.Body = @{
            client_id     = $VenafiSession.Token.ClientId
            refresh_token = $VenafiSession.Token.RefreshToken.GetNetworkCredential().password
        }

        # workaround for bug pre 21.3 where client id needs to be lowercase
        if ( $VenafiSession.Version -lt [Version]::new('21', '3', '0') ) {
            $params.Body.client_id = $params.Body.client_id.ToLower()
        }
    }
    else {

        $AuthUrl = $AuthServer
        # add prefix if just server url was provided
        if ( $AuthServer -notlike 'https://*') {
            $AuthUrl = 'https://{0}' -f $AuthUrl
        }
        $params.Server = $AuthUrl

        if ( $PsCmdlet.ParameterSetName -eq 'RefreshToken' ) {
            $params.UriLeaf = 'authorize/token'
            $params.Body = @{
                client_id = $ClientId
            }
            $params.Body.refresh_token = $RefreshToken | ConvertTo-PlaintextString
        }
        else {
            # obtain new token
            $scopeString = switch ($Scope.all) {
                'core' {
                    'agent:delete;certificate:approve,delete,discover,manage,revoke;configuration:delete,manage;restricted:delete,manage;security:delete,manage;ssh:approve,delete,discover,manage;statistics'
                }

                'admin' {
                    'admin:delete,viewlogs,recyclebin;agent:delete;certificate:delete,discover,manage,revoke;configuration:delete,manage;restricted:delete,manage;security:delete,manage;ssh:approve,delete,discover,manage;statistics'
                }

                Default {
                    @(
                        $scope.GetEnumerator() | ForEach-Object {
                            if ($_.Value) {
                                '{0}:{1}' -f $_.Key, $_.Value
                            }
                            else {
                                $_.Key
                            }
                        }
                    ) -join ';'
                }
            }

            $params.Body = @{
                client_id = $ClientId
                scope     = $scopeString
            }
            $params.UriLeaf = 'authorize/{0}' -f $PSCmdlet.ParameterSetName.ToLower()

            switch ($PsCmdlet.ParameterSetName) {

                'Integrated' {
                    $params.UseDefaultCredentials = $true
                }

                'OAuth' {
                    $params.Body.username = $Credential.UserName
                    $params.Body.password = $Credential.GetNetworkCredential().Password
                }

                'Certificate' {
                    $params.Certificate = $Certificate
                }

                'Jwt' {
                    $params.Body.jwt = $Jwt
                }

                Default {
                    throw ('Unknown parameter set {0}' -f $PSCmdlet.ParameterSetName)
                }
            }

            if ( $State ) {
                $params.Body.state = $State
            }

        }
    }

    if ( $PSCmdlet.ShouldProcess($params.Server, 'New access token') ) {

        if ( $PsCmdlet.ParameterSetName -eq 'RefreshToken' ) {
            try {
                $response = Invoke-VenafiRestMethod @params
            }
            catch {
                # workaround bug pre 21.3 where client_id must be lowercase
                if ( $_ -like '*The client_id value being requested with the refresh token does not match the client_id of the access token making the call*') {
                    $params.Body.client_id = $params.Body.client_id.ToLower()
                    $response = Invoke-VenafiRestMethod @params
                }
                else {
                    throw $_
                }
            }
        }
        else {
            $response = Invoke-VenafiRestMethod @params
        }

        $response | Write-VerboseWithSecret

        $newToken = [PSCustomObject] @{
            Server         = $params.Server
            AccessToken    = New-Object System.Management.Automation.PSCredential('AccessToken', ($response.access_token | ConvertTo-SecureString -AsPlainText -Force))
            RefreshToken   = $null
            Scope          = $Scope
            Identity       = $response.identity
            TokenType      = $response.token_type
            ClientId       = $params.Body.client_id
            Expires        = ([datetime] '1970-01-01 00:00:00').AddSeconds($response.Expires)
            RefreshExpires = $null
        }

        if ( $response.refresh_token ) {
            $newToken.RefreshToken = New-Object System.Management.Automation.PSCredential('RefreshToken', ($response.refresh_token | ConvertTo-SecureString -AsPlainText -Force))
            # refresh_until added in 21.1
            if ($response.refresh_until) {
                $newToken.RefreshExpires = ([datetime] '1970-01-01 00:00:00').AddSeconds($response.refresh_until)
            }
        }

        $newToken
    }

}
#EndRegion './Public/New-VdcToken.ps1' 326
#Region './Public/New-VenafiSession.ps1' -1

function New-VenafiSession {
    <#
    .SYNOPSIS
    Create a new Venafi TLSPDC or TLSPC session

    .DESCRIPTION
    Authenticate a user and create a new session with which future calls can be made.
    Key based username/password and windows integrated are supported as well as token-based integrated, oauth, and certificate.
    By default, a session variable will be created and automatically used with other functions unless -PassThru is used.
    Tokens and TLSPC keys can be saved in a vault for future calls.

    .PARAMETER Server
    Server or url to access vedsdk, venafi.company.com or https://venafi.company.com.
    If AuthServer is not provided, this will be used to access vedauth as well for token-based authentication.
    If just the server name is provided, https:// will be appended.

    .PARAMETER Credential
    Username and password used for token-based authentication. Not required for integrated authentication.

    .PARAMETER ClientId
    Application/integration ID configured in Venafi for token-based authentication.
    Case sensitive.

    .PARAMETER Scope
    Hashtable with Scopes and privilege restrictions.
    The key is the scope and the value is one or more privilege restrictions separated by commas, @{'certificate'='delete,manage'}.
    Scopes include Agent, Certificate, Code Signing, Configuration, Restricted, Security, SSH, and statistics.
    For no privilege restriction or read access, use a value of $null.
    For a scope to privilege mapping, see https://docs.venafi.com/Docs/current/TopNav/Content/SDK/AuthSDK/r-SDKa-OAuthScopePrivilegeMapping.php
    Using a scope of {'all'='core'} will set all scopes except for admin.
    Using a scope of {'all'='admin'} will set all scopes including admin.
    Usage of the 'all' scope is not suggested for production.

    .PARAMETER State
    A session state, redirect URL, or random string to prevent Cross-Site Request Forgery (CSRF) attacks

    .PARAMETER AccessToken
    Provide an existing access token to create a session.
    You can either provide a String, SecureString, or PSCredential.
    If providing a credential, the username is not used.

    .PARAMETER VaultAccessTokenName
    Name of the SecretManagement vault entry for the access token; the name of the vault must be VenafiPS.
    This value can be provided standalone or with credentials. First time use requires it to be provided with credentials to retrieve the access token to populate the vault.
    With subsequent uses, it can be provided standalone and the access token will be retrieved without the need for credentials.

    .PARAMETER RefreshToken
    Provide an existing refresh token to create a session.
    You can either provide a String, SecureString, or PSCredential.
    If providing a credential, the username is not used.

    .PARAMETER VaultRefreshTokenName
    Name of the SecretManagement vault entry for the refresh token; the name of the vault must be VenafiPS.
    This value can be provided standalone or with credentials. Each time this is used, a new access and refresh token will be obtained.
    First time use requires it to be provided with credentials to retrieve the refresh token and populate the vault.
    With subsequent uses, it can be provided standalone and the refresh token will be retrieved without the need for credentials.

    .PARAMETER Jwt
    JSON web token.
    Available in TLSPDC v22.4 and later.
    Ensure JWT mapping has been configured in VCC, Access Management->JWT Mappings.

    .PARAMETER Certificate
    Certificate for TLSPDC token-based authentication

    .PARAMETER AuthServer
    If you host your authentication service, vedauth, is on a separate server than vedsdk, use this parameter to specify the url eg., venafi.company.com or https://venafi.company.com.
    If AuthServer is not provided, the value provided for Server will be used.
    If just the server name is provided, https:// will be appended.

    .PARAMETER VcKey
    Api key from your TLSPC instance. The api key can be found under your user profile->preferences.
    You can either provide a String, SecureString, or PSCredential.
    If providing a credential, the username is not used.

    .PARAMETER VcRegion
    TLSPC region to connect to. Valid values are 'us' and 'eu'. Defaults to 'us'.

    .PARAMETER VaultVcKeyName
    Name of the SecretManagement vault entry for the TLSPC key.
    First time use requires it to be provided with -VcKey to populate the vault.
    With subsequent uses, it can be provided standalone and the key will be retrieved without the need for -VcKey.
    The server associated with the region will be saved and restored when this parameter is used on subsequent use.

    .PARAMETER SkipCertificateCheck
    Bypass certificate validation when connecting to the server.
    This can be helpful for pre-prod environments where ssl isn't setup on the website or you are connecting via IP.
    You can also create an environment variable named VENAFIPS_SKIP_CERT_CHECK and set it to 1 for the same effect.

    .PARAMETER TimeoutSec
    Specifies how long the request can be pending before it times out. Enter a value in seconds. The default value, 0, specifies an indefinite time-out.

    .PARAMETER PassThru
    Optionally, send the session object to the pipeline instead of script scope.

    .OUTPUTS
    VenafiSession, if -PassThru is provided

    .EXAMPLE
    New-VenafiSession -Server venafitpp.mycompany.com -ClientId MyApp -Scope @{'certificate'='manage'}
    Create token-based session using Windows Integrated authentication with a certain scope and privilege restriction

    .EXAMPLE
    New-VenafiSession -Server venafitpp.mycompany.com -Credential $cred -ClientId MyApp -Scope @{'certificate'='manage'}
    Create token-based session

    .EXAMPLE
    New-VenafiSession -Server venafitpp.mycompany.com -Certificate $myCert -ClientId MyApp -Scope @{'certificate'='manage'}
    Create token-based session using a client certificate

    .EXAMPLE
    New-VenafiSession -Server venafitpp.mycompany.com -AuthServer tppauth.mycompany.com -ClientId MyApp -Credential $cred
    Create token-based session using oauth authentication where the vedauth and vedsdk are hosted on different servers

    .EXAMPLE
    $sess = New-VenafiSession -Server venafitpp.mycompany.com -Credential $cred -PassThru
    Create session and return the session object instead of setting to script scope variable

    .EXAMPLE
    New-VenafiSession -Server venafitpp.mycompany.com -AccessToken $accessCred
    Create session using an access token obtained outside this module

    .EXAMPLE
    New-VenafiSession -Server venafitpp.mycompany.com -RefreshToken $refreshCred -ClientId MyApp
    Create session using a refresh token

    .EXAMPLE
    New-VenafiSession -Server venafitpp.mycompany.com -RefreshToken $refreshCred -ClientId MyApp -VaultRefreshTokenName TppRefresh
    Create session using a refresh token and store the newly created refresh token in the vault

    .EXAMPLE
    New-VenafiSession -VcKey $cred
    Create session against TLSPC

    .EXAMPLE
    New-VenafiSession -VcKey $cred -VcRegion 'eu'
    Create session against TLSPC in EU region

    .EXAMPLE
    New-VenafiSession -VaultVcKeyName vaas-key
    Create session against TLSPC with a key stored in a vault

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/New-VenafiSession/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/New-VenafiSession.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/API_Reference/r-SDK-POST-Authorize.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/API_Reference/r-SDK-GET-Authorize-Integrated.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/AuthSDK/r-SDKa-POST-Authorize-Integrated.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/AuthSDK/r-SDKa-POST-AuthorizeOAuth.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/AuthSDK/r-SDKa-POST-AuthorizeCertificate.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/AuthSDK/r-SDKa-POST-AuthorizeJwt.php

    .LINK
    https://github.com/PowerShell/SecretManagement

    .LINK
    https://github.com/PowerShell/SecretStore
    #>


    [CmdletBinding(DefaultParameterSetName = 'KeyIntegrated')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Not needed')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Converting secret to credential')]

    param(
        [Parameter(Mandatory, ParameterSetName = 'KeyCredential')]
        [Parameter(Mandatory, ParameterSetName = 'KeyIntegrated')]
        [Parameter(Mandatory, ParameterSetName = 'TokenOAuth')]
        [Parameter(Mandatory, ParameterSetName = 'TokenIntegrated')]
        [Parameter(Mandatory, ParameterSetName = 'TokenCertificate')]
        [Parameter(Mandatory, ParameterSetName = 'TokenJwt')]
        [Parameter(Mandatory, ParameterSetName = 'AccessToken')]
        [Parameter(Mandatory, ParameterSetName = 'RefreshToken')]
        [Parameter(ParameterSetName = 'VaultAccessToken')]
        [Parameter(ParameterSetName = 'VaultRefreshToken')]
        [Alias('ServerUrl', 'Url')]
        [ValidateScript(
            {
                $validateMe = if ( $_ -notlike 'https://*') {
                    'https://{0}' -f $_
                }
                else {
                    $_
                }

                try {
                    $null = [System.Uri]::new($validateMe)
                    $true
                }
                catch {
                    throw 'Please enter a valid server, https://venafi.company.com or venafi.company.com'
                }
            }
        )]
        [string] $Server,

        [Parameter(Mandatory, ParameterSetName = 'KeyCredential')]
        [Parameter(Mandatory, ParameterSetName = 'TokenOAuth')]
        [System.Management.Automation.PSCredential] $Credential,

        [Parameter(Mandatory, ParameterSetName = 'TokenIntegrated')]
        [Parameter(Mandatory, ParameterSetName = 'TokenOAuth')]
        [Parameter(Mandatory, ParameterSetName = 'TokenCertificate')]
        [Parameter(Mandatory, ParameterSetName = 'TokenJwt')]
        [Parameter(ParameterSetName = 'RefreshToken', Mandatory)]
        [Parameter(ParameterSetName = 'VaultRefreshToken')]
        [string] $ClientId,

        [Parameter(Mandatory, ParameterSetName = 'TokenIntegrated')]
        [Parameter(Mandatory, ParameterSetName = 'TokenOAuth')]
        [Parameter(Mandatory, ParameterSetName = 'TokenCertificate')]
        [Parameter(Mandatory, ParameterSetName = 'TokenJwt')]
        [Parameter(ParameterSetName = 'VaultAccessToken')]
        [Parameter(ParameterSetName = 'VaultRefreshToken')]
        [hashtable] $Scope,

        [Parameter(ParameterSetName = 'TokenIntegrated')]
        [Parameter(ParameterSetName = 'TokenOAuth')]
        [string] $State,

        [Parameter(Mandatory, ParameterSetName = 'AccessToken')]
        [psobject] $AccessToken,

        [Parameter(Mandatory, ParameterSetName = 'RefreshToken')]
        [psobject] $RefreshToken,

        [Parameter(Mandatory, ParameterSetName = 'TokenJwt')]
        [string] $Jwt,

        [Parameter(Mandatory, ParameterSetName = 'TokenCertificate')]
        [X509Certificate] $Certificate,

        [Parameter(Mandatory, ParameterSetName = 'VaultAccessToken')]
        [Parameter(ParameterSetName = 'AccessToken')]
        [Parameter(ParameterSetName = 'TokenIntegrated')]
        [Parameter(ParameterSetName = 'TokenOAuth')]
        [Parameter(ParameterSetName = 'TokenCertificate')]
        [string] $VaultAccessTokenName,

        [Parameter(Mandatory, ParameterSetName = 'VaultRefreshToken')]
        [Parameter(ParameterSetName = 'RefreshToken')]
        [Parameter(ParameterSetName = 'TokenIntegrated')]
        [Parameter(ParameterSetName = 'TokenOAuth')]
        [Parameter(ParameterSetName = 'TokenCertificate')]
        [string] $VaultRefreshTokenName,

        [Parameter(ParameterSetName = 'TokenOAuth')]
        [Parameter(ParameterSetName = 'TokenIntegrated')]
        [Parameter(ParameterSetName = 'TokenCertificate')]
        [Parameter(ParameterSetName = 'RefreshToken')]
        [ValidateScript(
            {
                $validateMe = if ( $_ -notlike 'https://*') {
                    'https://{0}' -f $_
                }
                else {
                    $_
                }

                try {
                    $null = [System.Uri]::new($validateMe)
                    $true
                }
                catch {
                    throw 'Please enter a valid server, https://venafi.company.com or venafi.company.com'
                }
            }
        )]
        [string] $AuthServer,

        [Parameter(Mandatory, ParameterSetName = 'Vc')]
        [Alias('VaasKey')]
        [psobject] $VcKey,

        [Parameter(ParameterSetName = 'Vc')]
        [ValidateScript(
            {
                if ( $_ -notin ($script:VcRegions).Keys ) {
                    throw ('{0} is not a valid region. Valid regions include {1}.' -f $_, (($script:VcRegions).Keys -join ','))
                }
                $true
            }
        )]
        [string] $VcRegion = 'us',

        [Parameter(ParameterSetName = 'Vc')]
        [Parameter(Mandatory, ParameterSetName = 'VaultVcKey')]
        [Alias('VaultVaasKeyName')]
        [string] $VaultVcKeyName,

        [Parameter()]
        [Int32] $TimeoutSec = 0,

        [Parameter()]
        [switch] $PassThru,

        [Parameter(ParameterSetName = 'TokenOAuth')]
        [Parameter(ParameterSetName = 'TokenIntegrated')]
        [Parameter(ParameterSetName = 'TokenCertificate')]
        [Parameter(ParameterSetName = 'TokenJwt')]
        [Parameter(ParameterSetName = 'AccessToken')]
        [Parameter(ParameterSetName = 'RefreshToken')]
        [Parameter(ParameterSetName = 'VaultAccessToken')]
        [Parameter(ParameterSetName = 'VaultRefreshToken')]
        [switch] $SkipCertificateCheck
    )

    $isVerbose = if ($PSBoundParameters.Verbose -eq $true) { $true } else { $false }

    $serverUrl = $Server
    # add prefix if just server url was provided
    if ( $Server -notlike 'https://*') {
        $serverUrl = 'https://{0}' -f $serverUrl
    }

    $authServerUrl = $serverUrl
    if ( $AuthServer ) {
        $authServerUrl = $AuthServer
        if ( $authServerUrl -notlike 'https://*') {
            $authServerUrl = 'https://{0}' -f $authServerUrl
        }
    }

    $newSession = [pscustomobject] @{
        Platform             = 'VDC'
        Server               = $serverUrl
        TimeoutSec           = $TimeoutSec
        SkipCertificateCheck = $SkipCertificateCheck.IsPresent
    }

    Write-Verbose ('Parameter set: {0}' -f $PSCmdlet.ParameterSetName)

    if ( $ClientId -and $ClientId -inotmatch 'venafips' ) {
        Write-Warning 'When creating your API Integration in Venafi, please ensure the id begins with “VenafiPS-” like the example below. Be sure to adjust the scope for your specific use case.
        {
          "id": "VenafiPS-<USE CASE>",
          "name": "VenafiPS for <USE CASE>",
          "description": "This application uses the VenafiPS module to automate <USE CASE>",
          "scope": "certificate:manage;configuration:manage"
        }'

    }

    if ( $PSBoundParameters.Keys -like 'Vault*') {
        # ensure the appropriate setup has been performed
        if ( -not (Get-Module -Name Microsoft.PowerShell.SecretManagement -ListAvailable)) {
            throw 'Vault functionality requires the module Microsoft.PowerShell.SecretManagement as well as a vault named ''VenafiPS''. See the github readme for guidance, https://github.com/Venafi/VenafiPS#tokenkey-secret-storage.'
        }

        $vault = Get-SecretVault -Name 'VenafiPS' -ErrorAction SilentlyContinue
        if ( -not $vault ) {
            throw 'A SecretManagement vault named ''VenafiPS'' could not be found'
        }
    }

    # if ( $PSCmdlet.ShouldProcess($Server, 'New session') ) {
    Switch ($PsCmdlet.ParameterSetName)    {

        { $_ -in 'KeyCredential', 'KeyIntegrated' } {

            Write-Warning 'Key-based authentication has been deprecated. Get started with token authentication today, https://docs.venafi.com/Docs/current/TopNav/Content/SDK/AuthSDK/t-SDKa-Setup-OAuth.php.'

        }

        { $_ -in 'TokenOAuth', 'TokenIntegrated', 'TokenCertificate', 'TokenJwt' } {
            $params = @{
                AuthServer           = $authServerUrl
                ClientId             = $ClientId
                Scope                = $Scope
                SkipCertificateCheck = $SkipCertificateCheck
            }

            if ($Credential) {
                $params.Credential = $Credential
            }

            if ($Certificate) {
                $params.Certificate = $Certificate
            }

            if ( $PSBoundParameters.ContainsKey('Jwt') ) {
                $params.Jwt = $Jwt
            }

            if ($State) {
                $params.State = $State
            }

            $token = New-VdcToken @params -Verbose:$isVerbose
            $newSession | Add-Member @{Token = $token }
        }

        'AccessToken' {
            $newSession | Add-Member @{'Token' = [PSCustomObject]@{
                    Server      = $authServerUrl
                    # we don't have the expiry so create one
                    # rely on the api call itself to fail if access token is invalid
                    Expires     = (Get-Date).AddMonths(12)
                    AccessToken = $null
                }
            }

            $newSession.Token.AccessToken = if ( $AccessToken -is [string] ) { New-Object System.Management.Automation.PSCredential('AccessToken', ($AccessToken | ConvertTo-SecureString -AsPlainText -Force)) }
            elseif ($AccessToken -is [pscredential]) { $AccessToken }
            elseif ($AccessToken -is [securestring]) { New-Object System.Management.Automation.PSCredential('AccessToken', $AccessToken) }
            else { throw 'Unsupported type for -AccessToken. Provide either a String, SecureString, or PSCredential.' }

            # validate token
            $null = Invoke-VenafiRestMethod -UriRoot 'vedauth' -UriLeaf 'Authorize/Verify' -VenafiSession $newSession
        }

        'VaultAccessToken' {
            $tokenSecret = Get-Secret -Name $VaultAccessTokenName -Vault 'VenafiPS' -ErrorAction SilentlyContinue
            if ( -not $tokenSecret ) {
                throw "'$VaultAccessTokenName' secret not found in vault VenafiPS."
            }

            # check if metadata was stored or we should get from params
            $secretInfo = Get-SecretInfo -Name $VaultAccessTokenName -Vault 'VenafiPS' -ErrorAction SilentlyContinue

            if ( $secretInfo.Metadata.Count -gt 0 ) {
                $newSession.Server = $secretInfo.Metadata.Server
                $newSession.Token = [PSCustomObject]@{
                    Server      = $secretInfo.Metadata.AuthServer
                    AccessToken = $tokenSecret
                    ClientId    = $secretInfo.Metadata.ClientId
                    Scope       = $secretInfo.Metadata.Scope
                }
                $newSession.SkipCertificateCheck = [bool] $secretInfo.Metadata.SkipCertificateCheck
                $newSession.TimeoutSec = $secretInfo.Metadata.TimeoutSec
            }
            else {
                throw 'Server and ClientId metadata not found. Execute New-VenafiSession -Server $server -Credential $cred -ClientId $clientId -Scope $scope -VaultAccessToken $secretName and attempt the operation again.'
            }

        }

        'RefreshToken' {
            $params = @{
                AuthServer = $authServerUrl
                ClientId   = $ClientId
            }
            $params.RefreshToken = if ( $RefreshToken -is [string] ) { New-Object System.Management.Automation.PSCredential('RefreshToken', ($RefreshToken | ConvertTo-SecureString -AsPlainText -Force)) }
            elseif ($RefreshToken -is [pscredential]) { $RefreshToken }
            elseif ($RefreshToken -is [securestring]) { New-Object System.Management.Automation.PSCredential('RefreshToken', $RefreshToken) }
            else { throw 'Unsupported type for -RefreshToken. Provide either a String, SecureString, or PSCredential.' }

            $newToken = New-VdcToken @params
            $newSession | Add-Member @{ 'Token' = $newToken }
        }

        'VaultRefreshToken' {
            $tokenSecret = Get-Secret -Name $VaultRefreshTokenName -Vault 'VenafiPS' -ErrorAction SilentlyContinue
            if ( -not $tokenSecret ) {
                throw "'$VaultRefreshTokenName' secret not found in vault VenafiPS."
            }

            # check if metadata was stored or we should get from params
            $secretInfo = Get-SecretInfo -Name $VaultRefreshTokenName -Vault 'VenafiPS' -ErrorAction SilentlyContinue

            if ( $secretInfo.Metadata.Count -gt 0 ) {
                $params = @{
                    AuthServer           = $secretInfo.Metadata.AuthServer
                    ClientId             = $secretInfo.Metadata.ClientId
                    SkipCertificateCheck = [bool] $secretInfo.Metadata.SkipCertificateCheck
                }
            }
            else {
                throw 'Server and ClientId metadata not found. Execute New-VenafiSession -Server $server -Credential $cred -ClientId $clientId -Scope $scope -VaultRefreshToken $secretName and attempt the operation again.'
            }

            $params.RefreshToken = $tokenSecret

            $newToken = New-VdcToken @params
            $newSession | Add-Member @{ 'Token' = $newToken }
            $newSession.Server = $newToken.Server
            $newSession.Token.Scope = $secretInfo.Metadata.Scope | ConvertFrom-Json
            $newSession.SkipCertificateCheck = [bool] $secretInfo.Metadata.SkipCertificateCheck
            $newSession.TimeoutSec = $secretInfo.Metadata.TimeoutSec
        }

        'Vc' {
            $newSession.Platform = 'VC'
            $newSession.Server = ($script:VcRegions).$VcRegion
            $key = if ( $VcKey -is [string] ) { New-Object System.Management.Automation.PSCredential('VcKey', ($VcKey | ConvertTo-SecureString -AsPlainText -Force)) }
            elseif ($VcKey -is [pscredential]) { $VcKey }
            elseif ($VcKey -is [securestring]) { New-Object System.Management.Automation.PSCredential('VcKey', $VcKey) }
            else { throw 'Unsupported type for -VcKey. Provide either a String, SecureString, or PSCredential.' }
            $newSession | Add-Member @{ 'Key' = $key }

            if ( $VaultVcKeyName ) {
                $metadata = @{
                    Server     = $newSession.Server
                    TimeoutSec = [int]$newSession.TimeoutSec
                }
                Set-Secret -Name $VaultVcKeyName -Secret $newSession.Key -Vault 'VenafiPS' -Metadata $metadata
            }
        }

        'VaultVcKey' {
            $keySecret = Get-Secret -Name $VaultVcKeyName -Vault 'VenafiPS' -ErrorAction SilentlyContinue
            if ( -not $keySecret ) {
                throw "'$VaultVcKeyName' secret not found in vault VenafiPS."
            }

            $secretInfo = Get-SecretInfo -Name $VaultVcKeyName -Vault 'VenafiPS' -ErrorAction SilentlyContinue

            if ( $secretInfo.Metadata.Count -gt 0 ) {
                $newSession.Server = $secretInfo.Metadata.Server
            }
            else {
                throw 'Server metadata not found. Execute New-VenafiSession -VcKey $key -VaultVcKeyName $secretName and attempt the operation again.'
            }

            $newSession.Platform = 'VC'
            $newSession | Add-Member @{ 'Key' = $keySecret }
        }

        Default {
            throw ('Unknown parameter set {0}' -f $PSCmdlet.ParameterSetName)
        }
    }

    # will fail if user is on an older version. this isn't required so bypass on failure
    # only applicable to tpp
    if ( $newSession.Platform -eq 'VDC' ) {
        $newSession | Add-Member @{ Version = (Get-VdcVersion -VenafiSession $newSession -ErrorAction SilentlyContinue) }
        $certFields = 'X509 Certificate', 'Device', 'Application Base' | Get-VdcCustomField -VenafiSession $newSession -ErrorAction SilentlyContinue
        # make sure we remove duplicates
        $newSession | Add-Member @{ CustomField = $certFields.Items | Sort-Object -Property Guid -Unique }
    }
    else {

        $newSession | Add-Member @{
            User = (Invoke-VenafiRestMethod -UriLeaf 'useraccounts' -VenafiSession $newSession -ErrorAction SilentlyContinue | Select-Object -ExpandProperty user | Select-Object @{
                    'n' = 'userId'
                    'e' = {
                        $_.Id
                    }
                }, * -ExcludeProperty id)
        }
    }

    if ( $VaultAccessTokenName -or $VaultRefreshTokenName ) {
        # save secret and all associated metadata to be retrieved later
        $metadata = @{
            Server               = $newSession.Server
            AuthServer           = $newSession.Token.Server
            ClientId             = $newSession.Token.ClientId
            Scope                = $newSession.Token.Scope | ConvertTo-Json -Compress
            SkipCertificateCheck = [int]$newSession.SkipCertificateCheck
            TimeoutSec           = [int]$newSession.TimeoutSec
        }

        $metadata | ConvertTo-Json | Write-Verbose

        if ( $VaultAccessTokenName ) {
            Set-Secret -Name $VaultAccessTokenName -Secret $newSession.Token.AccessToken -Vault 'VenafiPS' -Metadata $metadata
        }
        else {
            if ( $newSession.Token.RefreshToken ) {
                Set-Secret -Name $VaultRefreshTokenName -Secret $newSession.Token.RefreshToken -Vault 'VenafiPS' -Metadata $metadata
            }
            else {
                Write-Warning 'Refresh token not provided by server and will not be saved in the vault'
            }
        }
    }

    if ( $PassThru ) {
        $newSession
    }
    else {
        $Script:VenafiSession = $newSession
    }
}
#EndRegion './Public/New-VenafiSession.ps1' 589
#Region './Public/Read-VdcLog.ps1' -1

function Read-VdcLog {
    <#
    .SYNOPSIS
    Read entries from the TLSPDC log

    .DESCRIPTION
    Read entries from the TLSPDC log.

    .PARAMETER Path
    Path to search for related records

    .PARAMETER EventId
    Event ID as found in Logging->Event Definitions

    .PARAMETER Severity
    Filter records by severity

    .PARAMETER StartTime
    Start time of events

    .PARAMETER EndTime
    End time of events

    .PARAMETER Text1
    Filter matching results of Text1

    .PARAMETER Text2
    Filter matching results of Text2

    .PARAMETER Value1
    Filter matching results of Value1

    .PARAMETER Value2
    Filter matching results of Value2

    .PARAMETER First
    Return only these number of records

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    PSCustomObject
        EventId
        ClientTimestamp
        Component
        ComponentId
        ComponentSubsystem
        Data
        Grouping
        Id
        Name
        ServerTimestamp
        Severity
        SourceIP
        Text1
        Text2
        Value1
        Value2

    .EXAMPLE
    Read-VdcLog -First 10

    Get the most recent 10 log items

    .EXAMPLE
    $capiObject | Read-VdcLog

    Find all events for a specific object

    .EXAMPLE
    Read-VdcLog -EventId '00130003'

    Find all events with event ID '00130003', Certificate Monitor - Certificate Expiration Notice

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-GET-Log.php

    #>


    [CmdletBinding()]

    param (

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('DN')]
        [string] $Path,

        [Parameter()]
        [string] $EventId,

        [Parameter()]
        [TppEventSeverity] $Severity,

        [Parameter()]
        [DateTime] $StartTime,

        [Parameter()]
        [DateTime] $EndTime,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Text1,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Text2,

        [Parameter()]
        [int] $Value1,

        [Parameter()]
        [int] $Value2,

        [Parameter()]
        [int] $First,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{
            VenafiSession = $VenafiSession
            Method        = 'Get'
            UriLeaf       = 'Log/'
            Body          = @{ }
        }

        switch ($PSBoundParameters.Keys) {

            'EventId' {
                $params.Body.Add('Id', [uint32]('0x{0}' -f $EventId))
            }

            'Severity' {
                $params.Body.Add('Severity', $Severity)
            }

            'StartTime' {
                $params.Body.Add('FromTime', ($StartTime | ConvertTo-UtcIso8601) )
            }

            'EndTime' {
                $params.Body.Add('ToTime', ($EndTime | ConvertTo-UtcIso8601) )
            }

            'Text1' {
                $params.Body.Add('Text1', $Text1)
            }

            'Text2' {
                $params.Body.Add('Text2', $Text2)
            }

            'Value1' {
                $params.Body.Add('Value1', $Value1)
            }

            'Value2' {
                $params.Body.Add('Value2', $Value2)
            }

            'First' {
                $params.Body.Add('Limit', $First)
            }
        }
    }

    process {

        if ( $PSBoundParameters.ContainsKey('Path') ) {
            $params.Body.Component = $Path
        }

        Invoke-VenafiRestMethod @params |
        Select-Object -ExpandProperty LogEvents |
        Select-Object -Property @{'n' = 'EventId'; 'e' = { '{0:x8}' -f $_.Id } }, *
    }
}
#EndRegion './Public/Read-VdcLog.ps1' 198
#Region './Public/Remove-VcApplication.ps1' -1

function Remove-VcApplication {
    <#
    .SYNOPSIS
    Remove a application

    .DESCRIPTION
    Remove a application from TLSPC

    .PARAMETER ID
    Application ID, this is the guid/uuid

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcApplication -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'
    Remove a application

    .EXAMPLE
    Remove-VcApplication -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Confirm:$false
    Remove a application bypassing the confirmation prompt
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('applicationId')]
        [string] $ID,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
        $allObjects = [System.Collections.Generic.List[object]]::new()
    }

    process {
        if ( $PSCmdlet.ShouldProcess($ID, "Delete application") ) {
            $allObjects.Add($ID)
        }
    }

    end {
        Invoke-VenafiParallel -InputObject $allObjects -ScriptBlock {
            $null = Invoke-VenafiRestMethod -Method 'Delete' -UriRoot 'outagedetection/v1' -UriLeaf "applications/$PSItem"
        } -ThrottleLimit $ThrottleLimit -VenafiSession $VenafiSession
    }
}
#EndRegion './Public/Remove-VcApplication.ps1' 61
#Region './Public/Remove-VcCertificate.ps1' -1

function Remove-VcCertificate {
    <#
    .SYNOPSIS
    Remove a certificate

    .DESCRIPTION
    Remove a certificate

    .PARAMETER ID
    Certificate ID of a certificate that has been retired

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcCertificate -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'

    Remove a certificate

    .EXAMPLE
    Find-VcCertificate | Remove-VcCertificate

    Remove multiple certificates based on a search

    .EXAMPLE
    Remove-VcCertificate -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Confirm:$false

    Remove a certificate bypassing the confirmation prompt

    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('certificateId')]
        [string] $ID,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
        $allObjects = [System.Collections.Generic.List[object]]::new()
    }

    process {
        if ( $PSCmdlet.ShouldProcess($ID, "Delete certificate") ) {
            $allObjects.Add($ID)
        }
    }

    end {

        # handle certs differently since you send them all in 1 call
        # and parallel functionality not needed
        $params = @{
            Method  = 'Post'
            UriRoot = 'outagedetection/v1'
            UriLeaf = 'certificates/deletion'
            Body    = @{
                certificateIds = $allObjects
            }
        }
        $null = Invoke-VenafiRestMethod @params
    }
}
#EndRegion './Public/Remove-VcCertificate.ps1' 75
#Region './Public/Remove-VcConnector.ps1' -1

function Remove-VcConnector {
    <#
    .SYNOPSIS
    Remove a connector

    .DESCRIPTION
    Remove a connector from TLSPC

    .PARAMETER ID
    Connector ID, this is the guid/uuid

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcConnector -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'
    Remove a connector

    .EXAMPLE
    Remove-VcConnector -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Confirm:$false
    Remove a connector bypassing the confirmation prompt
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('connectorId')]
        [ValidateScript(
            {
                if ( -not (Test-IsGuid -InputObject $_ ) ) {
                    throw "$_ is not a valid connector id format"
                }
                $true
            }
        )]
        [string] $ID,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {
        if ( $PSCmdlet.ShouldProcess($ID, "Delete connector") ) {
            $null = Invoke-VenafiRestMethod -Method 'Delete' -UriLeaf "plugins/$ID"
        }
    }
}
#EndRegion './Public/Remove-VcConnector.ps1' 59
#Region './Public/Remove-VcIssuingTemplate.ps1' -1

function Remove-VcIssuingTemplate {
    <#
    .SYNOPSIS
    Remove a issuing template

    .DESCRIPTION
    Remove a issuing template from TLSPC

    .PARAMETER ID
    Issuing template ID, this is the guid/uuid

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcIssuingTemplate -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'
    Remove a issuing template

    .EXAMPLE
    Remove-VcIssuingTemplate -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Confirm:$false
    Remove a issuing template bypassing the confirmation prompt
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('issuingTemplateId')]
        [string] $ID,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
        $allObjects = [System.Collections.Generic.List[object]]::new()
    }

    process {
        if ( $PSCmdlet.ShouldProcess($ID, "Delete issuing template") ) {
            $allObjects.Add($ID)
        }
    }

    end {
        Invoke-VenafiParallel -InputObject $allObjects -ScriptBlock {
            $null = Invoke-VenafiRestMethod -Method 'Delete' -UriLeaf "certificateissuingtemplates/$PSItem"
        } -ThrottleLimit $ThrottleLimit -VenafiSession $VenafiSession
    }
}
#EndRegion './Public/Remove-VcIssuingTemplate.ps1' 61
#Region './Public/Remove-VcMachine.ps1' -1

function Remove-VcMachine {
    <#
    .SYNOPSIS
    Remove a machine

    .DESCRIPTION
    Remove a machine from TLSPC

    .PARAMETER ID
    Machine ID, this is the guid/uuid

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcMachine -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'
    Remove a machine

    .EXAMPLE
    Remove-VcMachine -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Confirm:$false
    Remove a machine bypassing the confirmation prompt
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('machineId')]
        [string] $ID,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
        $allObjects = [System.Collections.Generic.List[object]]::new()
    }

    process {
        if ( $PSCmdlet.ShouldProcess($ID, "Delete machine") ) {
            $allObjects.Add($ID)
        }
    }

    end {
        Invoke-VenafiParallel -InputObject $allObjects -ScriptBlock {
            $null = Invoke-VenafiRestMethod -Method 'Delete' -UriLeaf "machines/$PSItem"
        } -ThrottleLimit $ThrottleLimit -VenafiSession $VenafiSession
    }
}
#EndRegion './Public/Remove-VcMachine.ps1' 61
#Region './Public/Remove-VcMachineIdentity.ps1' -1

function Remove-VcMachineIdentity {
    <#
    .SYNOPSIS
    Remove a machine identity

    .DESCRIPTION
    Remove a machine identity from TLSPC

    .PARAMETER ID
    Machine Identity ID, this is the guid/uuid

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcMachineIdentity -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'
    Remove a machine identity

    .EXAMPLE
    Remove-VcMachineIdentity -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Confirm:$false
    Remove a machine identity bypassing the confirmation prompt
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('machineIdentityId')]
        [string] $ID,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
        $allObjects = [System.Collections.Generic.List[object]]::new()
    }

    process {
        if ( $PSCmdlet.ShouldProcess($ID, "Delete machine identity") ) {
            $allObjects.Add($ID)
        }
    }

    end {
        Invoke-VenafiParallel -InputObject $allObjects -ScriptBlock {
            $null = Invoke-VenafiRestMethod -Method 'Delete' -UriLeaf "machineidentities/$PSItem"
        } -ThrottleLimit $ThrottleLimit -VenafiSession $VenafiSession
    }
}
#EndRegion './Public/Remove-VcMachineIdentity.ps1' 61
#Region './Public/Remove-VcSatelliteWorker.ps1' -1

function Remove-VcSatelliteWorker {
    <#
    .SYNOPSIS
    Remove a vsatellite worker

    .DESCRIPTION
    Remove a vsatellite worker from TLSPC

    .PARAMETER ID
    Worker ID

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcSatelliteWorker -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'

    Remove a worker

    .EXAMPLE
    Remove-VcSatelliteWorker -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Confirm:$false

    Remove a worker bypassing the confirmation prompt

    .EXAMPLE
    Get-VcSatelliteWorker -VSatellite 'My vsat1' | Remove-VcSatelliteWorker

    Remove all workers associated with a specific vsatellite
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Alias('vsatelliteWorkerId')]
        [guid] $ID,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {
        if ( $PSCmdlet.ShouldProcess($ID, "Delete VSatellite Worker") ) {
            $null = Invoke-VenafiRestMethod -Method 'Delete' -UriLeaf "edgeworkers/$ID"
        }
    }

    end {
    }
}
#EndRegion './Public/Remove-VcSatelliteWorker.ps1' 61
#Region './Public/Remove-VcTag.ps1' -1

function Remove-VcTag {
    <#
    .SYNOPSIS
    Remove a tag

    .DESCRIPTION
    Remove a tag from TLSPC

    .PARAMETER ID
    Tag ID/name

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    Name

    .EXAMPLE
    Remove-VcTag -ID 'MyTag'
    Remove a tag

    .EXAMPLE
    Remove-VcTag -ID 'MyTag' -Confirm:$false
    Remove a tag bypassing the confirmation prompt
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('tagId')]
        [string] $ID,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
        $allObjects = [System.Collections.Generic.List[object]]::new()
    }

    process {
        if ( $PSCmdlet.ShouldProcess($ID, "Delete Tag") ) {
            $allObjects.Add($ID)
        }
    }

    end {
        Invoke-VenafiParallel -InputObject $allObjects -ScriptBlock {
            $null = Invoke-VenafiRestMethod -Method 'Delete' -UriLeaf "tags/$PSItem"
        } -ThrottleLimit $ThrottleLimit -VenafiSession $VenafiSession
    }
}
#EndRegion './Public/Remove-VcTag.ps1' 61
#Region './Public/Remove-VcTeam.ps1' -1

function Remove-VcTeam {
    <#
    .SYNOPSIS
    Remove a team

    .DESCRIPTION
    Remove a team from TLSPC

    .PARAMETER ID
    Team ID, this is the guid/uuid

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcTeam -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'
    Remove a team

    .EXAMPLE
    Remove-VcTeam -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Confirm:$false
    Remove a team bypassing the confirmation prompt
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('teamId')]
        [string] $ID,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
        $allObjects = [System.Collections.Generic.List[object]]::new()
    }

    process {
        if ( $PSCmdlet.ShouldProcess($ID, "Delete Team") ) {
            $allObjects.Add($ID)
        }
    }

    end {
        Invoke-VenafiParallel -InputObject $allObjects -ScriptBlock {
            $null = Invoke-VenafiRestMethod -Method 'Delete' -UriLeaf "teams/$PSItem"
        } -ThrottleLimit $ThrottleLimit -VenafiSession $VenafiSession
    }
}
#EndRegion './Public/Remove-VcTeam.ps1' 61
#Region './Public/Remove-VcTeamMember.ps1' -1

function Remove-VcTeamMember {
    <#
    .SYNOPSIS
    Remove team member

    .DESCRIPTION
    Remove a team member from TLSPC

    .PARAMETER ID
    Team ID, this is the unique guid obtained from Get-VcTeam.

    .PARAMETER Member
    1 or more members to remove from the team
    This is the unique guid obtained from Get-VcIdentity.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcTeamMember -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Member @('ca7ff555-88d2-4bfc-9efa-2630ac44c1f3', 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f4')

    Remove members from a team

    .EXAMPLE
    Remove-VcTeamMember -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Member 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f3' -Confirm:$false

    Remove members from a team with no confirmation prompting

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html#/Teams/removeMember
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('teamId')]
        [string] $ID,

        [Parameter(Mandatory)]
        [string[]] $Member,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        $params = @{
            Method  = 'Delete'
            UriLeaf = "teams/$ID/members"
            Body    = @{
                'members' = @($Member)
            }
        }

        if ( $PSCmdlet.ShouldProcess($ID, "Delete team members") ) {
            $null = Invoke-VenafiRestMethod @params
        }
    }
}
#EndRegion './Public/Remove-VcTeamMember.ps1' 72
#Region './Public/Remove-VcTeamOwner.ps1' -1

function Remove-VcTeamOwner {
    <#
    .SYNOPSIS
    Remove team owner

    .DESCRIPTION
    Remove a team owner from TLSPC

    .PARAMETER ID
    Team ID, the unique guid obtained from Get-VcTeam.

    .PARAMETER Owner
    1 or more owners to remove from the team
    This is the unique guid obtained from Get-VcIdentity.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcTeamOwner -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Owner @('ca7ff555-88d2-4bfc-9efa-2630ac44c1f3', 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f4')

    Remove owners from a team

    .EXAMPLE
    Remove-VcTeamOwner -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Owner 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f3' -Confirm:$false

    Remove an owner from a team with no confirmation prompting

    .LINK
    https://api.venafi.cloud/webjars/swagger-ui/index.html#/Teams/removeOwner
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('teamId')]
        [string] $ID,

        [Parameter(Mandatory)]
        [string[]] $Owner,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        # get team details and ensure at least 1 owner will remain
        $thisTeam = Get-VcTeam -ID $ID
        $ownerCompare = Compare-Object -ReferenceObject $thisTeam.owners -DifferenceObject $Owner
        if ( -not ($ownerCompare | Where-Object { $_.SideIndicator -eq '<=' }) ) {
            throw 'A team must have at least one owner and you are attempting to remove them all'
        }

        $params = @{
            Method  = 'Delete'
            UriLeaf = "teams/$ID/owners"
            Body    = @{
                'owners' = @($Owner)
            }
        }

        if ( $PSCmdlet.ShouldProcess($ID, "Delete team owners") ) {
            $null = Invoke-VenafiRestMethod @params
        }
    }
}
#EndRegion './Public/Remove-VcTeamOwner.ps1' 79
#Region './Public/Remove-VcWebhook.ps1' -1

function Remove-VcWebhook {
    <#
    .SYNOPSIS
    Remove a webhook

    .DESCRIPTION
    Remove a webhook from TLSPC

    .PARAMETER ID
    Webhook ID, this is the guid/uuid

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VcWebhook -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2'
    Remove a webhook

    .EXAMPLE
    Remove-VcWebhook -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Confirm:$false
    Remove a webhook bypassing the confirmation prompt
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('webhookId')]
        [string] $ID,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
        $allObjects = [System.Collections.Generic.List[object]]::new()
    }

    process {
        if ( $PSCmdlet.ShouldProcess($ID, "Delete Webhook") ) {
            $allObjects.Add($ID)
        }
    }

    end {
        Invoke-VenafiParallel -InputObject $allObjects -ScriptBlock {
            $null = Invoke-VenafiRestMethod -Method 'Delete' -UriLeaf "connectors/$PSItem"
        } -ThrottleLimit $ThrottleLimit -VenafiSession $VenafiSession
    }
}
#EndRegion './Public/Remove-VcWebhook.ps1' 61
#Region './Public/Remove-VdcCertificate.ps1' -1

function Remove-VdcCertificate {
    <#
    .SYNOPSIS
    Remove a certificate

    .DESCRIPTION
    Removes a Certificate object, all associated objects including pending workflow tickets, and the corresponding Secret Store vault information.
    You must either be a Master Admin or have Delete permission to the objects and have certificate:delete token scope.
    Run this in parallel with PowerShell v7+ when you have a large number to process.

    .PARAMETER Path
    Path to the certificate to remove

    .PARAMETER KeepAssociatedApps
    Provide this switch to remove associations prior to certificate removal

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    None

    .EXAMPLE
    $cert | Remove-VdcCertificate
    Remove a certificate via pipeline

    .EXAMPLE
    Remove-VdcCertificate -Path '\ved\policy\my cert'
    Remove a certificate and any associated app

    .EXAMPLE
    Remove-VdcCertificate -Path '\ved\policy\my cert' -KeepAssociatedApps
    Remove a certificate and first remove all associations, keeping the apps

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Remove-VdcCertificate/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Remove-VdcCertificate.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-DELETE-Certificates-Guid.php

    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [Alias('Remove-TppCertificate')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('DN', 'CertificateDN')]
        [String] $Path,

        [Parameter()]
        [switch] $KeepAssociatedApps,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
        $allCerts = [System.Collections.Generic.List[string]]::new()

        # use in shouldprocess messaging below
        $appsMessage = if ($KeepAssociatedApps) { 'but keep associated apps' } else { 'and associated apps' }
    }

    process {
        if ( $PSCmdlet.ShouldProcess($Path, "Remove certificate $appsMessage") ) {
            $allCerts.Add($Path)
        }
    }

    end {

        Invoke-VenafiParallel -InputObject $allCerts -ScriptBlock {
            $guid = $PSItem | ConvertTo-VdcGuid -ErrorAction SilentlyContinue
            if ( -not $guid ) {
                Write-Error "'$PSItem' is not a valid path"
                return
            }

            if ($using:KeepAssociatedApps) {
                $associatedApps = ($PSItem | Get-VdcAttribute -Attribute "Consumers").Consumers
                if ( $associatedApps ) {
                    Remove-VdcCertificateAssociation -Path $PSItem -ApplicationPath $associatedApps -Confirm:$false
                }
            }

            $null = Invoke-VenafiRestMethod -Method Delete -UriLeaf "Certificates/$guid"
        } -ThrottleLimit $ThrottleLimit -ProgressTitle 'Deleting certificates'
    }
}
#EndRegion './Public/Remove-VdcCertificate.ps1' 120
#Region './Public/Remove-VdcCertificateAssociation.ps1' -1

function Remove-VdcCertificateAssociation {
    <#
    .SYNOPSIS
    Remove certificate associations

    .DESCRIPTION
    Dissociates one or more Application objects from an existing certificate.
    Optionally, you can remove the application objects and corresponding orphaned device objects that no longer have any applications

    .PARAMETER Path
    Path to the certificate

    .PARAMETER ApplicationPath
    List of application object paths to dissociate

    .PARAMETER OrphanCleanup
    Delete the Application object after dissociating it. Only delete the corresponding Device DN when it has no child objects.
    Otherwise retain the Device path and its children.

    .PARAMETER All
    Remove all associated application objects

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    None

    .EXAMPLE
    Remove-VdcCertificateAssociation -Path '\ved\policy\my cert' -ApplicationPath '\ved\policy\my capi'
    Remove a single application object association

    .EXAMPLE
    Remove-VdcCertificateAssociation -Path '\ved\policy\my cert' -ApplicationPath '\ved\policy\my capi' -OrphanCleanup
    Disassociate and delete the application object

    .EXAMPLE
    Remove-VdcCertificateAssociation -Path '\ved\policy\my cert' -RemoveAll
    Remove all certificate associations

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Certificates-Dissociate.php

    .NOTES
    You must have:
    - Write permission to the Certificate object.
    - Write or Associate permission to Application objects that are associated with the certificate
    - Delete permission to Application and device objects when specifying -OrphanCleanup

    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = 'RemoveOne')]
    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid path"
                }
            })]
        [Alias('DN', 'CertificateDN')]
        [String] $Path,

        [Parameter(Mandatory, ParameterSetName = 'RemoveOne')]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid application path"
                }
            })]
        [String[]] $ApplicationPath,

        [Parameter()]
        [switch] $OrphanCleanup,

        [Parameter(Mandatory, ParameterSetName = 'RemoveAll')]
        [switch] $All,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{
            Method  = 'Post'
            UriLeaf = 'Certificates/Dissociate'
            Body    = @{ }
        }
    }

    process {

        $shouldProcessAction = "Remove associations"

        if ( -not ($Path | Test-TppObject -ExistOnly) ) {
            Write-Error ("Certificate path {0} does not exist" -f $Path)
            Continue
        }

        $params.Body = @{
            'CertificateDN' = $Path
        }

        if ( $OrphanCleanup ) {
            $params.Body.DeleteOrphans = $true
            $shouldProcessAction += ' AND ORPHANS'
        }

        if ( $PSBoundParameters.ContainsKey('ApplicationPath') ) {
            $params.Body.ApplicationDN = @($ApplicationPath)
        }
        else {
            $associatedApps = $Path | Get-VdcAttribute -Attribute "Consumers" | Select-Object -ExpandProperty Consumers

            if ( $associatedApps ) {
                $params.Body.ApplicationDN = @($associatedApps)
            }
            else {
                # no associations to process, no need to continue
                Write-Warning "No associations for path '$Path'"
                Return
            }
        }

        try {
            if ( $PSCmdlet.ShouldProcess($Path, $shouldProcessAction) ) {
                $null = Invoke-VenafiRestMethod @params
            }
        }
        catch {
            $myError = $_.ToString() | ConvertFrom-Json
            Write-Error ('Error removing associations from certificate {0}: {1}' -f $Path, $myError.Error)
        }
    }
}
#EndRegion './Public/Remove-VdcCertificateAssociation.ps1' 151
#Region './Public/Remove-VdcClient.ps1' -1

function Remove-VdcClient {
    <#
    .SYNOPSIS
        Remove registered client agents

    .DESCRIPTION
        Remove registered client agents.
        Provide an array of client IDs to remove a large list at once.

    .PARAMETER ClientId
        Unique id for one or more clients

    .PARAMETER RemoveAssociatedDevice
        For a registered Agent, delete the associated Device objects, and only certificates that belong to the associated device.
        Delete any related Discovery information. Preserve unrelated device, certificate, and Discovery information in other locations of the Policy tree and Secret Store.

    .PARAMETER VenafiSession
        Authentication for the function.
        The value defaults to the script session object $VenafiSession created by New-VenafiSession.
        A TLSPDC token can also be provided.
        If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
        ClientId

    .OUTPUTS
        None

    .EXAMPLE
        Remove-VdcClient -ClientId 1234, 5678
        Remove clients

    .EXAMPLE
        Remove-VdcClient -ClientId 1234, 5678 -RemoveAssociatedDevice
        Remove clients and associated devices

    .LINK
        http://VenafiPS.readthedocs.io/en/latest/functions/Remove-VdcClient/

    .LINK
        https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Remove-VdcClient.ps1

    .LINK
        https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-ClientDelete.php

    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [Alias('Remove-TppClient')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [String[]] $ClientID,

        [Parameter()]
        [Alias('RemoveAssociatedDevices')]
        [switch] $RemoveAssociatedDevice,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{

            Method        = 'Post'
            UriLeaf       = 'Client/Delete'
            Body          = @{}
        }
    }

    process {

        if ( $PSCmdlet.ShouldProcess('Remove {0} clients' -f $ClientID.Count) ) {
            # 5000 clients at a time is an api limitation
            for ($i = 0; $i -lt $ClientID.Count; $i += 5000) {

                $clientIds = $ClientID[$i..($i + 4999)] | ForEach-Object {
                    @{ 'ClientId' = $_ }
                }

                $params.Body.Clients = [array] $clientIds
                $params.Body.DeleteAssociatedDevices = $RemoveAssociatedDevice.IsPresent.ToString().ToLower()

                $response = Invoke-VenafiRestMethod @params

                if ( $response.Errors ) {
                    Write-Error ($response.Errors | ConvertTo-Json)
                }
            }
        }
    }
}
#EndRegion './Public/Remove-VdcClient.ps1' 97
#Region './Public/Remove-VdcEngineFolder.ps1' -1

function Remove-VdcEngineFolder {
    <#
    .SYNOPSIS
    Remove TLSPDC processing engine assignment(s) from policy folder(s)

    .DESCRIPTION
    Remove TLSPDC processing engine assignment(s) from policy folder(s).

    If you do not supply a list of TLSPDC processing engines, then all processing engines will be removed from the supplied list of policy folders.

    If you do not supply a list of policy folders, then all policy folder assignments will be removed from the supplied list of processing engines.

    Supplying both a list of policy folders and processing engines will result in the removal of the specified engines from the list of policy folders.

    Errors due to a policy engine not being assigned to the listed policy folder are ignored.

    .PARAMETER FolderPath
    The full DN path to one or more policy folders (string array).
    .PARAMETER EnginePath
    The full DN path to one or more TLSPDC processing engines (string array).
    .PARAMETER Force
    Suppress the confirmation prompt before removing engine/folder assignments.
    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided, but this requires an environment variable VDC_SERVER to be set.
    .INPUTS
    FolderPath[], EnginePath[]
    .OUTPUTS
    None
    .EXAMPLE
    Remove-VdcEngineFolder -FolderPath '\VED\Policy\Certificates\Web Team' -EnginePath @('\VED\Engines\MYVENAFI01','\VED\Engines\MYVENAFI02')
    Remove policy folder '\VED\Policy\Certificates\Web Team' from the processing engines MYVENAFI01 and MYVENAFI02.
    .EXAMPLE
    Remove-VdcEngineFolder -FolderPath @('\VED\Policy\Certificates\Web Team','\VED\Policy\Certificates\Database Team')
    Remove all processing engine assignments for the policy folders '\VED\Policy\Certificates\Web Team' and '\VED\Policy\Certificates\Database Team'.
    .EXAMPLE
    Remove-VdcEngineFolder -EnginePath @('\VED\Engines\MYVENAFI01','\VED\Engines\MYVENAFI02') -Confirm:$false
    Removed all policy folder assignments from the processing engines MYVENAFI01 and MYVENAFI02. Suppress the confirmation prompt.
    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Remove-VdcEngineFolder/
    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Remove-VdcEngineFolder.ps1
    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-DELETE-ProcessingEngines-Folder-fguid.php
    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-DELETE-ProcessingEngines-Folder-fguid-eguid.php
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [Alias('Remove-TppEngineFolder')]

    param (
        [Parameter(Mandatory, ParameterSetName = 'AllEngines', ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'Matrix', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) { $true }
                else { throw "'$_' is not a valid DN path" }
            })]
        [Alias('FolderDN', 'Folder')]
        [String[]] $FolderPath,

        [Parameter(Mandatory, ParameterSetName = 'AllFolders', ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'Matrix', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) { $true }
                else { throw "'$_' is not a valid DN path" }
            })]
        [Alias('EngineDN', 'Engine')]
        [String[]] $EnginePath,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC' -Verbose:$false

        $params = @{

            Method        = 'Delete'
        }

        $apiCall = "ProcessingEngines/Folder"
    }

    process {
        if ($FolderPath) {
            [TppObject[]] $FolderList = @()
            foreach ($path in $FolderPath) {
                try {
                    $folder = Get-VdcObject -Path $path
                    if ($folder.TypeName -eq 'Policy') {
                        $FolderList += $folder
                    }
                    else {
                        Write-Warning ("TLSPDC object '$($path)' is not a policy ($($folder.TypeName))")
                        Continue
                    }
                }
                catch {
                    Write-Warning ("TLSPDC object '$($path)' does not exist")
                    Continue
                }
            }
            if ($FolderList.Count -eq 0) {
                Write-Warning "All supplied policy folders are invalid"
                Return
            }
        }

        if ($EnginePath) {
            [TppObject[]] $EngineList = @()
            foreach ($path in $EnginePath) {
                try {
                    $engine = Get-VdcObject -Path $path
                    if ($engine.TypeName -eq 'Venafi Platform') {
                        $EngineList += $engine
                    }
                    else {
                        Write-Warning ("TLSPDC object '$($path)' is not an engine ($($engine.TypeName))")
                        Continue
                    }
                }
                catch {
                    Write-Warning ("TLSPDC object '$($path)' does not exist")
                    Continue
                }
            }
            if ($EngineList.Count -eq 0) {
                Write-Warning "All supplied processing engines are invalid"
                Return
            }
        }

        if ($PSCmdlet.ParameterSetName -eq 'AllEngines') {
            $shouldProcessAction = "Remove ALL processing engine assignments"
            if ($FolderList.Count -gt 1) { $shouldProcessTarget = "$($FolderList.Count) folders" }
            else { $shouldProcessTarget = "$($FolderList.Path)" }
            if ($PSCmdlet.ShouldProcess($shouldProcessTarget, $shouldProcessAction)) {
                foreach ($folder in $FolderList) {
                    $uriLeaf = "$($apiCall)/{$($folder.Guid)}"
                    try {
                        $null = Invoke-VenafiRestMethod @params -UriLeaf $uriLeaf
                    }
                    catch {
                        $myError = $_.ToString() | ConvertFrom-Json
                        Write-Warning ("Error removing processing engines from folder policy '$($folder.Path)': $($myError.Error)")
                    }
                }
            }
        }
        else {
            if ($PSCmdlet.ParameterSetName -eq 'AllFolders') {
                $shouldProcessAction = "Remove ALL policy folder assignments"
                if ($EngineList.Count -gt 1) { $shouldProcessTarget = "$($EngineList.Count) processing engines" }
                else { $shouldProcessTarget = "$($EngineList.Name)" }
            }
            else {
                # ParameterSetName='Matrix'
                if ($FolderList.Count -gt 1) { $shouldProcessAction += "Remove $($FolderList.Count) folders" }
                else { $shouldProcessAction = "Remove $($FolderList.Path)" }
                if ($EngineList.Count -gt 1) { $shouldProcessTarget = "$($EngineList.Count) processing engines" }
                else { $shouldProcessTarget = "$($EngineList.Name)" }
            }
            if ($PSCmdlet.ShouldProcess($shouldProcessTarget, $shouldProcessAction)) {
                foreach ($engine in $EngineList) {
                    Write-Verbose ("Processing Engine: '$($engine.Path)'")
                    if ($PSCmdlet.ParameterSetName -eq 'AllFolders') {
                        [TppObject[]] $FolderList = @()
                        $FolderList += ($engine | Get-VdcEngineFolder)
                        Switch ($FolderList.Count) {
                            0 { $countMessage = 'NO folders' }
                            1 { $countMessage = '1 folder' }
                            Default { $countMessage = "$($_) folders" }
                        }
                        Write-Verbose "Found $($countMessage) to remove from engine '$($engine.Name)'"
                    }
                    foreach ($folder in $FolderList) {
                        $uriLeaf = "$($apiCall)/{$($folder.Guid)}/{$($engine.Guid)}"
                        try {
                            $null = Invoke-VenafiRestMethod @params -UriLeaf $uriLeaf
                        }
                        catch {
                            $myError = $_.ToString() | ConvertFrom-Json
                            Write-Warning ("Error removing engine '$($engine.Path)' from folder policy '$($folder.Path)': $($myError.Error)")
                        }
                    }
                }
            }
        }
    }
}
#EndRegion './Public/Remove-VdcEngineFolder.ps1' 196
#Region './Public/Remove-VdcObject.ps1' -1

function Remove-VdcObject {
    <#
    .SYNOPSIS
    Remove TLSPDC objects

    .DESCRIPTION
    Remove a TLSPDC object and optionally perform a recursive removal.
    This process can be very destructive as it will remove anything you send it!!!
    Run this in parallel with PowerShell v7+ when you have a large number to process.

    .PARAMETER Path
    Full path to an existing object

    .PARAMETER Recursive
    Remove recursively, eg. everything within a policy folder

    .PARAMETER ThrottleLimit
    Limit the number of threads when running in parallel; the default is 100.
    Setting the value to 1 will disable multithreading.
    On PS v5 the ThreadJob module is required. If not found, multithreading will be disabled.


    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    None

    .EXAMPLE
    Remove-VdcObject -Path '\VED\Policy\My empty folder'
    Remove an object

    .EXAMPLE
    Remove-VdcObject -Path '\VED\Policy\folder' -Recursive
    Remove an object and all objects contained

    .EXAMPLE
    Find-VdcObject -Class 'capi' | Remove-VdcObject
    Find 1 or more objects and remove them

    .EXAMPLE
    Remove-VdcObject -Path '\VED\Policy\folder' -Confirm:$false
    Remove an object without prompting for confirmation. Be careful!

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Remove-VdcObject/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Remove-VdcObject.ps1

    .LINK
    https://docs.venafi.com/Docs/currentSDK/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-delete.php

    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [Alias('Remove-TppObject')]

    param (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [String] $Path,

        [Parameter()]
        [switch] $Recursive,

        [Parameter()]
        [int32] $ThrottleLimit = 100,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
        $allItems = [System.Collections.Generic.List[string]]::new()
    }

    process {
        if ( $PSCmdlet.ShouldProcess($Path, "Remove object") ) {
            $allItems.Add($Path)
        }
    }

    end {
        Invoke-VenafiParallel -InputObject $allItems -ScriptBlock {

            $params = @{

                Method  = 'Post'
                UriLeaf = 'config/Delete'
                Body    = @{
                    ObjectDN  = $PSItem
                    Recursive = [int] (($using:Recursive).IsPresent)
                }
            }

            $response = Invoke-VenafiRestMethod @params

            if ( $response.Result -ne 1 ) {
                Write-Error $response.Error
                return
            }
        } -ThrottleLimit $ThrottleLimit -ProgressTitle 'Removing objects'
    }
}
#EndRegion './Public/Remove-VdcObject.ps1' 114
#Region './Public/Remove-VdcPermission.ps1' -1

function Remove-VdcPermission {
    <#
    .SYNOPSIS
    Remove permissions from TLSPDC objects

    .DESCRIPTION
    Remove permissions from TLSPDC objects
    You can opt to remove permissions for a specific user or all assigned

    .PARAMETER Path
    Full path to an object. You can also pipe in a TppObject

    .PARAMETER IdentityId
    Prefixed Universal Id of the user or group to have their permissions removed

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path, Guid, IdentityId

    .OUTPUTS
    None

    .EXAMPLE
    Find-VdcObject -Path '\VED\Policy\My folder' | Remove-VdcPermission
    Remove all permissions from a specific object

    .EXAMPLE
    Find-VdcObject -Path '\VED' -Recursive | Remove-VdcPermission -IdentityId 'AD+blah:879s8d7f9a8ds7f9s8d7f9'
    Remove all permissions for a specific user

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Remove-VdcPermission/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Remove-VdcPermission.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-DELETE-Permissions-object-guid-principal.php

    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = 'ByGuid')]
    [Alias('Remove-TppPermission')]

    param (

        [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ByPath')]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                } else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [String[]] $Path,

        [Parameter(Mandatory, ParameterSetName = 'ByGuid', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('ObjectGuid')]
        [guid[]] $Guid,

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateScript( {
                if ( $_ | Test-VdcIdentityFormat -Format 'Universal' ) {
                    $true
                } else {
                    throw "'$_' is not a valid Prefixed Universal Id format. See https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-IdentityInformation.php."
                }
            })]
        [Alias('PrefixedUniversalId')]
        [string[]] $IdentityId,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{
            Method     = 'Delete'
            UriLeaf    = 'placeholder'
        }
    }

    process {

        Write-Verbose ('Parameter set: {0}' -f $PSCmdLet.ParameterSetName)

        if ( $PSCmdLet.ParameterSetName -eq 'ByPath' ) {
            $inputObject = $Path
        } else {
            $inputObject = $Guid
        }

        foreach ($thisInputObject in $inputObject) {
            if ( $PSCmdLet.ParameterSetName -eq 'ByPath' ) {
                $thisGuid = $thisInputObject | ConvertTo-VdcGuid
            } else {
                $thisGuid = $thisInputObject
            }

            $uriBase = "Permissions/object/{$thisGuid}"
            $params.UriLeaf = $uriBase

            if ( $PSBoundParameters.ContainsKey('IdentityId') ) {
                $identities = $IdentityId
            } else {
                # get list of identities permissioned to this object
                $getParams = $params.Clone()
                $getParams.Method = 'Get'
                $identities = Invoke-VenafiRestMethod @getParams
            }

            foreach ( $thisIdentity in $identities ) {

                $params.UriLeaf = $uriBase

                if ( $thisIdentity.StartsWith('local:') ) {
                    # format of local is local:universalId
                    $type, $id = $thisIdentity.Split(':')
                    $params.UriLeaf += "/local/$id"
                } else {
                    # external source, eg. AD, LDAP
                    # format is type+name:universalId
                    $type, $name, $id = $thisIdentity -Split { $_ -in '+', ':' }
                    $params.UriLeaf += "/$type/$name/$id"
                }

                if ( $PSCmdlet.ShouldProcess($thisGuid, "Remove permissions for $thisIdentity") ) {
                    try {
                        Invoke-VenafiRestMethod @params
                    } catch {
                        Write-Error ("Failed to remove permissions on path $thisGuid, user/group $thisIdentity. $_")
                    }
                }
            }
        }
    }
}
#EndRegion './Public/Remove-VdcPermission.ps1' 147
#Region './Public/Remove-VdcTeam.ps1' -1

function Remove-VdcTeam {
    <#
    .SYNOPSIS
    Remove a team

    .DESCRIPTION
    Remove a team from TLSPDC

    .PARAMETER ID
    Team ID, the "local" ID.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VdcTeam -ID 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}'
    Remove a team

    .EXAMPLE
    Remove-VdcTeam -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Confirm:$false
    Remove a team bypassing the confirmation prompt

    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Alias('PrefixedUniversal', 'Guid')]
        [string] $ID,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        # check if just a guid or prefixed universal id
        if ( Test-VdcIdentityFormat -ID $ID -Format 'Local' ) {
            $guid = [guid]($ID.Replace('local:', ''))
        }
        else {
            try {
                $guid = [guid] $ID
            }
            catch {
                Write-Error "$ID is not a valid team id"
                Continue
            }
        }

        $params = @{
            Method  = 'Delete'
            UriLeaf = ('Teams/local/{{{0}}}' -f $guid.ToString())
        }

        if ( $PSCmdlet.ShouldProcess($ID, "Delete team") ) {
            $null = Invoke-VenafiRestMethod @params
        }

    }
}
#EndRegion './Public/Remove-VdcTeam.ps1' 74
#Region './Public/Remove-VdcTeamMember.ps1' -1

function Remove-VdcTeamMember {
    <#
    .SYNOPSIS
    Remove team member

    .DESCRIPTION
    Remove a team member from TLSPDC

    .PARAMETER ID
    Team ID, the ID property from Find-VdcIdentity or Get-VdcTeam.

    .PARAMETER Member
    1 or more members to remove from the team
    This is the identity ID property from Find-VdcIdentity or Get-VdcIdentity.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VdcTeamMember -ID 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}' -Member 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e7}'

    Remove members from a team

    .EXAMPLE
    Remove-VdcTeamMember -ID 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}' -Member 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e7}' -Confirm:$false

    Remove members from a team bypassing the confirmation prompt

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-PUT-Teams-RemoveTeamMembers.php
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('PrefixedUniversal', 'Guid')]
        [string] $ID,

        [Parameter(Mandatory)]
        [string[]] $Member,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        $teamName = Get-VdcIdentity -ID $ID | Select-Object -ExpandProperty FullName
        $members = foreach ($thisMember in $Member) {
            if ( $thisMember.StartsWith('local') ) {
                $teamIdentity = Get-VdcIdentity -ID $thisMember
                @{
                    'PrefixedName'      = $teamIdentity.FullName
                    'PrefixedUniversal' = $teamIdentity.ID
                }
            }
            else {
                @{'PrefixedUniversal' = $thisMember }
            }
        }
        $params = @{
            Method  = 'Put'
            UriLeaf = 'Teams/RemoveTeamMembers'
            Body    = @{
                'Team'    = @{'PrefixedName' = $teamName }
                'Members' = @($members)
            }
        }

        if ( $PSCmdlet.ShouldProcess($ID, "Delete team members") ) {
            $null = Invoke-VenafiRestMethod @params
        }
    }
}
#EndRegion './Public/Remove-VdcTeamMember.ps1' 87
#Region './Public/Remove-VdcTeamOwner.ps1' -1

function Remove-VdcTeamOwner {
    <#
    .SYNOPSIS
    Remove team owner

    .DESCRIPTION
    Remove a team owner from TLSPDC

    .PARAMETER ID
    Team ID, the ID property from Find-VdcIdentity or Get-VdcTeam.

    .PARAMETER Owner
    1 or more owners to remove from the team
    This is the identity ID property from Find-VdcIdentity or Get-VdcIdentity.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .EXAMPLE
    Remove-VdcTeamOwner -ID 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e6}' -Owner 'local:{803f332e-7576-4696-a5a2-8ac6be6b14e7}'

    Remove owners from a team

    .LINK
    https://docs.venafi.com/Docs/21.4SDK/TopNav/Content/SDK/WebSDK/r-SDK-PUT-Teams-DemoteTeamOwners.php
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('PrefixedUniversal', 'Guid')]
        [string] $ID,

        [Parameter(Mandatory)]
        [string[]] $Owner,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        # get team details and ensure at least 1 owner will remain
        $thisTeam = Get-VdcTeam -ID $ID
        $ownerCompare = Compare-Object -ReferenceObject $thisTeam.owners.ID -DifferenceObject $Owner
        if ( -not ($ownerCompare | Where-Object { $_.SideIndicator -eq '<=' }) ) {
            throw 'A team must have at least one owner and you are attempting to remove them all'
        }

        # $teamName = Get-VdcIdentity -ID $ID | Select-Object -ExpandProperty FullName
        $owners = foreach ($thisOwner in $Owner) {
            if ( $thisOwner.StartsWith('local') ) {
                $ownerIdentity = Get-VdcIdentity -ID $thisOwner
                @{
                    'PrefixedName'      = $ownerIdentity.FullName
                    'PrefixedUniversal' = $ownerIdentity.ID
                }
            }
            else {
                @{'PrefixedUniversal' = $thisOwner }
            }
        }

        $params = @{
            Method  = 'Put'
            UriLeaf = 'Teams/DemoteTeamOwners'
            Body    = @{
                'Team'   = @{'PrefixedName' = $thisTeam.FullName }
                'Owners' = @($owners)
            }
        }

        if ( $PSCmdlet.ShouldProcess($ID, "Delete team owners") ) {
            $null = Invoke-VenafiRestMethod @params

            # we've only demoted the owners to members. now remove them
            Remove-VdcTeamMember -ID $ID -Member $Owner
        }
    }
}
#EndRegion './Public/Remove-VdcTeamOwner.ps1' 93
#Region './Public/Rename-VdcObject.ps1' -1

function Rename-VdcObject {
    <#
    .SYNOPSIS
    Rename and/or move an object

    .DESCRIPTION
    Rename and/or move an object

    .PARAMETER Path
    Full path to an existing object

    .PARAMETER NewPath
    New path, including name

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    none

    .OUTPUTS

    .EXAMPLE
    Rename-VdcObject -Path '\VED\Policy\My Devices\OldDeviceName' -NewPath '\ved\policy\my devices\NewDeviceName'
    Rename an object

    .EXAMPLE
    Rename-VdcObject -Path '\VED\Policy\My Devices\DeviceName' -NewPath '\ved\policy\new devices folder\DeviceName'
    Move an object

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Rename-VdcObject/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Rename-VdcObject.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-renameobject.php

    #>


    [CmdletBinding()]
    [Alias('Rename-TppObject')]

    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                } else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('SourceDN')]
        [String] $Path,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String] $NewPath,

        [Parameter()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

    $params = @{

        Method     = 'Post'
        UriLeaf    = 'config/RenameObject'
        Body       = @{
            ObjectDN    = $Path
            NewObjectDN = $NewPath
        }
    }

    $response = Invoke-VenafiRestMethod @params

    if ( $response.Result -ne 1 ) {
        throw $response.Error
    }
}
#EndRegion './Public/Rename-VdcObject.ps1' 87
#Region './Public/Revoke-VdcGrant.ps1' -1

function Revoke-VdcGrant {

    <#
    .SYNOPSIS
    Revoke all grants for a specific user

    .DESCRIPTION
    Revoke all grants for a specific user.
    You must either be an administrator or oauth administrator to perform this action.
    Also, your token must have the admin:delete scope.
    Available in TLSPDC v22.3 and later.

    .PARAMETER ID
    Prefixed universal id for the user. To search, use Find-VdcIdentity.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    ID

    .OUTPUTS
    None

    .EXAMPLE
    Revoke-VdcGrant -ID local:{9e9db8d6-234a-409c-8299-e3b81ce2f916}

    Revoke all grants for a user

    .EXAMPLE
    Get-VdcIdentity -ID me@x.com | Revoke-VdcGrant

    Revoke all grants getting universal id from other identity functions

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Revoke-VdcGrant/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Revoke-VdcGrant.ps1

    .LINK
    https://doc.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-oauth-revokegrants.htm

    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [Alias('Revoke-TppGrant')]

    param (
        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateScript( {
                if ( Test-VdcIdentityFormat -ID $_ -Format 'Universal' ) {
                    $true
                } else {
                    throw "'$_' is not a valid prefixed universal identity format. See https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-IdentityInformation.php."
                }
            })]
        [Alias('PrefixedUniversalID', 'IdentityID')]
        [string[]] $ID
    )

    begin {

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        if ( $VenafiSessionNested.Version -lt [Version]::new('22', '3', '0') ) {
            throw 'Revoke-VdcGrant is available on TLSPDC v22.3 and greater'
        }

        $params = @{
            Method        = 'Post'
            UriLeaf       = 'oauth/revokegrants'
            Body          = @{}
            FullResponse  = $true
        }
    }

    process {

        foreach ($thisID in $ID) {
            $params.Body.GranteePrefixedUniversal = $thisID

            if ( $PSCmdlet.ShouldProcess($thisID, 'Revoke all grants') ) {
                $response = Invoke-VenafiRestMethod @params

                switch ( $response.StatusCode ) {
                    200 {
                        if ( $response.Result -eq 1 ) {
                            Write-Error 'Grant revocation was unsuccessful'
                        }
                    }

                    401 {
                        if ( $response.Error.error -eq 'insufficient_rights' ) {
                            throw 'The token user account does not have sufficient permissions for this request. You must be an administrator or OAuth administrator.'
                        }
                    }

                    403 {
                        throw 'The access token provided does not have the admin:delete scope. Create a new token with this scope and try again.'
                    }

                    Default {
                        throw $response.Error
                    }
                }
            }
        }
    }
}
#EndRegion './Public/Revoke-VdcGrant.ps1' 114
#Region './Public/Revoke-VdcToken.ps1' -1

function Revoke-VdcToken {
    <#
    .SYNOPSIS
    Revoke a token

    .DESCRIPTION
    Revoke a token and invalidate the refresh token if provided/available.
    This could be an access token retrieved from this module or from other means.

    .PARAMETER AuthServer
    Server name or URL for the vedauth service

    .PARAMETER AccessToken
    Provide an existing access token to revoke.
    You can either provide a String, SecureString, or PSCredential.
    If providing a credential, the username is not used.

    .PARAMETER VenafiPsToken
    Token object obtained from New-VdcToken

    .PARAMETER Force
    Bypass the confirmation prompt

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    VenafiPsToken

    .OUTPUTS
    none

    .EXAMPLE
    Revoke-VdcToken
    Revoke token stored in session variable $VenafiSession from New-VenafiSession

    .EXAMPLE
    Revoke-VdcToken -Force
    Revoke token bypassing confirmation prompt

    .EXAMPLE
    Revoke-VdcToken -AuthServer venafi.company.com -AccessToken $cred
    Revoke a token obtained from TLSPDC, not necessarily via VenafiPS

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Revoke-VdcToken/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Revoke-VdcToken.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/AuthSDK/r-SDKa-GET-Revoke-Token.php

    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = 'Session')]
    [Alias('Revoke-TppToken')]

    param (
        [Parameter(Mandatory, ParameterSetName = 'AccessToken')]
        [ValidateScript(
            {
                $validateMe = if ( $_ -notlike 'https://*') {
                    'https://{0}' -f $_
                }
                else {
                    $_
                }

                try {
                    $null = [System.Uri]::new($validateMe)
                    $true
                }
                catch {
                    throw 'Please enter a valid server, https://venafi.company.com or venafi.company.com'
                }
            }
        )]
        [Alias('Server')]
        [string] $AuthServer,

        [Parameter(Mandatory, ParameterSetName = 'AccessToken')]
        [ValidateScript(
            {
                if ( $_ -is [string] -or $_ -is [securestring] -or $_ -is [pscredential] ) {
                    $true
                }
                else {
                    throw 'Unsupported type. Provide either a String, SecureString, or PSCredential.'
                }
            }
        )]
        [psobject] $AccessToken,

        [Parameter(Mandatory, ParameterSetName = 'VenafiPsToken', ValueFromPipeline)]
        [pscustomobject] $VenafiPsToken,

        [Parameter()]
        [switch] $Force,

        [Parameter(ParameterSetName = 'Session')]
        [psobject] $VenafiSession = $script:VenafiSession
    )

    begin {
        $params = @{
            Method  = 'Get'
            UriRoot = 'vedauth'
            UriLeaf = 'Revoke/Token'
        }
    }

    process {

        Write-Verbose ('Parameter set: {0}' -f $PSCmdlet.ParameterSetName)

        switch ($PsCmdlet.ParameterSetName) {
            'Session' {
                $params.VenafiSession = $VenafiSession
                $target = $VenafiSession.Server
            }

            'AccessToken' {
                $AuthUrl = $AuthServer
                # add prefix if just server was provided
                if ( $AuthServer -notlike 'https://*') {
                    $AuthUrl = 'https://{0}' -f $AuthUrl
                }

                $params.Server = $target = $AuthUrl

                $accessTokenString = $AccessToken | ConvertTo-PlaintextString

                $params.Header = @{'Authorization' = 'Bearer {0}' -f $accessTokenString }
            }

            'VenafiPsToken' {
                if ( -not $VenafiPsToken.Server -or -not $VenafiPsToken.AccessToken ) {
                    throw 'Not a valid VenafiPsToken'
                }

                $params.Server = $target = $VenafiPsToken.Server
                $params.Header = @{'Authorization' = 'Bearer {0}' -f $VenafiPsToken.AccessToken.GetNetworkCredential().password }
            }

            Default {
                throw ('Unknown parameter set {0}' -f $PSCmdlet.ParameterSetName)
            }
        }

        Write-Verbose ($params | Out-String)

        if ( $Force ) {
            $ConfirmPreference = 'None'
        }

        if ( $PSCmdlet.ShouldProcess($target) ) {
            Invoke-VenafiRestMethod @params
        }
    }
}
#EndRegion './Public/Revoke-VdcToken.ps1' 165
#Region './Public/Search-VdcHistory.ps1' -1

function Search-VdcHistory {

    <#
    .SYNOPSIS
    Search TLSPDC history for items with specific attributes

    .DESCRIPTION
    Items in the secret store matching the key/value provided will be found and their details returned with their associated 'current' item.
    As this function may return details on many items, optional parallel processing has been implemented.
    Be sure to use PowerShell v7+ to take advantage.

    .PARAMETER Path
    Starting path to associated current items to limit the search.
    The default is \VED\Policy.

    .PARAMETER Attribute
    Name and value to search.
    See https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Secretstore-lookupbyassociation.php for more details.
    Note, ValidFrom will perform a greater than or equal comparison and ValidTo will perform a less than or equal comparison.
    Currently, one 1 name/value pair can be used.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    None

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Search-VdcHistory -Attribute @{'ValidTo' = (Get-Date)}

    Name : test.gdb.com
    TypeName : X509 Server Certificate
    Path : \VED\Policy\Certificates\test.gdb.com
    History : {@{AIACAIssuerURL=System.Object[]; AIAKeyIdentifier=F2E970BA11A64D616E78592911D7CC; C=US;
               CDPURI=0::False; EnhancedKeyUsage=Server Authentication(1.3.6.1.5.5.7.3.1).........}}

    Find historical items that are still active

    .EXAMPLE
    Search-VdcHistory -Attribute @{'ValidTo' = (Get-Date)} -Path '\ved\policy\certs'

    Find historical items that are still active and the current item starts with a specific path

    #>


    [CmdletBinding()]
    [Alias('Search-TppHistory')]

    param (
        [Parameter()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                } else {
                    throw "'$_' is not a valid DN path"
                }
            }
        )]
        [string] $Path = '\VED\Policy',

        [Parameter(Mandatory)]
        [hashtable] $Attribute,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [psobject] $VenafiSession
    )

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

    $activeVaultId = Find-VdcVaultId -Attribute $Attribute
    if ( -not $activeVaultId ) {
        return
    }

    Write-Warning ('Found {0} matching vault items' -f $activeVaultId.Count)

    # we have vault ids, now get path to current item
    $owners = if ($PSVersionTable.PSVersion.Major -lt 6) {
        $activeVaultId | ForEach-Object {
            Invoke-VenafiRestMethod -UriLeaf "secretstore/ownerlookup" -Body @{'Namespace' = 'config'; 'VaultID' = $_ } -Method Post
        }
    } else {
        $activeVaultId | ForEach-Object -ThrottleLimit 100 -Parallel {
            Import-Module VenafiPS
            New-VenafiSession -Server ($using:VenafiSession).Server -AccessToken ($using:VenafiSession).Token.AccessToken
            Invoke-VenafiRestMethod -UriLeaf "secretstore/ownerlookup" -Body @{'Namespace' = 'config'; 'VaultID' = $_ } -Method Post
        }
    }

    # limit current certs to the path provided
    $certs = $owners.owners.where{ $_ -like "$Path*" } | Select-Object -Unique

    Write-Warning ('Getting details on {0} current items' -f $certs.Count)

    # scriptblock which will be used for PS v5 and core
    $sbGeneric = {

        $thisDetailedCert = Get-VdcCertificate -Path $_ -IncludePreviousVersions -ErrorAction SilentlyContinue
        if ( -not $thisDetailedCert ) { return }

        $history = $thisDetailedCert.PreviousVersions | Where-Object { $_.VaultId -in $activeVaultId } | Select-Object -ExpandProperty CertificateDetails
        if ( $history ) {
            $thisDetailedCert | Select-Object Name, TypeName, Path,
            @{
                'n' = 'History'
                'e' = { $history }
            }
        }
    }

    # needed for parallel in ps7
    $sbGenericString = $sbGeneric.ToString()

    if ($PSVersionTable.PSVersion.Major -lt 6) {
        $certs | ForEach-Object -Process $sbGeneric
    } else {
        $certs | ForEach-Object -ThrottleLimit 100 -Parallel {
            Import-Module VenafiPS
            $VenafiSession = New-VenafiSession -Server ($using:VenafiSession).Server -AccessToken ($using:VenafiSession).Token.AccessToken -PassThru
            $activeVaultId = $using:activeVaultId

            Invoke-Expression $using:sbGenericString
        }
    }
}
#EndRegion './Public/Search-VdcHistory.ps1' 133
#Region './Public/Set-VcApplication.ps1' -1

function Set-VcApplication {
    <#
    .SYNOPSIS
    Update an existing application

    .DESCRIPTION
    Update details of existing applications.
    Additional properties will be available in the future.

    .PARAMETER Application
    The application to update. Specify either ID or name.

    .PARAMETER Name
    Provide a new name for the application if you wish to change it.

    .PARAMETER TeamOwner
    Associate a team as an owner of this application

    .PARAMETER IssuingTemplate
    Associate one or more issuing templates by ID or name

    .PARAMETER NoOverwrite
    Append to existing details as opposed to overwriting

    .PARAMETER PassThru
    Return the newly updated object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Set-VcApplication -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Name 'ThisAppNameIsBetter'

    Rename an existing application

    .EXAMPLE
    Set-VcApplication -ID 'MyApp' -TeamOwner 'GreatTeam'

    Change the owner to this team

    .EXAMPLE
    Set-VcApplication -ID 'MyApp' -TeamOwner 'GreatTeam' -NoOverwrite

    Append this team to the list of owners

    .EXAMPLE
    Set-VcApplication -ID 'MyApp' -IssuingTemplate 'Template1', 'Template2'

    Update the templates associated with application. This will overwrite any existing templates configured.
    #>


    [CmdletBinding(SupportsShouldProcess)]

    param (

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Alias('applicationId', 'ID')]
        [string] $Application,

        [Parameter()]
        [string] $Name,

        [Parameter()]
        [string[]] $TeamOwner,

        [Parameter()]
        [string[]] $IssuingTemplate,

        [Parameter()]
        [switch] $NoOverwrite,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [Alias('Key')]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        $params = @{

            Method  = 'Put'
            UriRoot = 'outagedetection/v1'
            Body    = @{}
        }

        switch ($PSBoundParameters.Keys ) {
            'TeamOwner' {
                $allTeams = Get-VcTeam -All
            }
            Default {}
        }
    }

    process {

        $thisApp = Get-VcApplication -Application $Application

        if ( -not $thisApp ) {
            # process the next one in the pipeline if we don't have a valid ID this time
            Write-Error "Application $Application does not exist"
            Continue
        }

        $params.UriLeaf = 'applications/{0}' -f $thisApp.applicationId

        # set required fields to existing values by default
        $params.Body.name = if ( $Name ) {
            $Name
        }
        else {
            $thisApp.name
        }
        $params.Body.ownerIdsAndTypes = $thisApp.ownerIdsAndTypes

        switch ( $PSBoundParameters.Keys ) {
            'TeamOwner' {
                $params.Body.ownerIdsAndTypes = @()
                foreach ($owner in $TeamOwner ) {

                    if ( Test-IsGuid($owner) ) {
                        $thisOwner = $owner
                    }
                    else {
                        $thisOwner = $allTeams | Where-Object { $_.name -eq $owner } | Select-Object -ExpandProperty teamId
                    }

                    if ( $thisOwner ) {
                        $params.Body.ownerIdsAndTypes += @{
                            ownerId   = $thisOwner
                            ownerType = 'TEAM'
                        }
                    }
                    else {
                        Write-Error "Team $ID does not exist"
                    }
                }

                if ( $NoOverwrite -and $thisApp.ownerIdsAndTypes ) {
                    $params.Body.ownerIdsAndTypes += $thisApp.ownerIdsAndTypes
                }
            }

            'IssuingTemplate' {
                $newT = @{}
                # grab existing templates as our starting point if we're not overwriting
                # issuingTempate is of type PSNoteProperty so we need to iterate and add to hashtable
                if ( $NoOverwrite -and $thisApp.issuingTemplate ) {
                    $thisApp.issuingTemplate | ForEach-Object {
                        $newT[$_.name] = $_.issuingTemplateId
                    }
                }

                foreach ($template in $IssuingTemplate ) {
                    $t = Get-VcIssuingTemplate -IssuingTemplate $template
                    if ( $t ) {
                        $newT[$t.name] = $t.issuingTemplateId
                    }
                    else {
                        Write-Error "Issuing template $template does not exist"
                    }
                }

                if ( $newT.Count -gt 0 ) {
                    $params.Body.certificateIssuingTemplateAliasIdMap = $newT

                }
            }
        }

        if ( $PSCmdlet.ShouldProcess($params.Body.name, "Update application") ) {
            $response = Invoke-VenafiRestMethod @params
        }

        if ( $PassThru ) {
            $response | ConvertTo-VaasTeam
        }
    }
}
#EndRegion './Public/Set-VcApplication.ps1' 191
#Region './Public/Set-VcCertificate.ps1' -1

function Set-VcCertificate {
    <#
    .SYNOPSIS
    Update a certificate

    .DESCRIPTION
    Associate one or more certificates with one or more applications or tags.
    The associated applications/tags can either replace or be added to existing.
    By default, applications/tags will be replaced.

    .PARAMETER Certificate
    Certificate ID or name to be associated.
    If a name is provided and multiple certificates are found, they will all be associated.
    Tab completion can be used for a list of certificate names to choose from.
    Type 3 or more characters for tab completion to work.

    .PARAMETER Application
    One or more application IDs or names.
    Tab completion can be used for a list of application names.

    .PARAMETER Tag
    One of more tag names or name/value pairs.
    To specify a name/value pair, use the format 'name:value'.

    .PARAMETER NoOverwrite
    Append to existing as opposed to overwriting

    .PARAMETER PassThru
    Return the newly updated certificate object(s)

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    Certificate

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Add-VcCertificateAssociation -Certificate '7ac56ec0-2017-11ee-9417-a17dd25b82f9' -Application '96fc9310-67ec-11eb-a8a7-794fe75a8e6f'

    Associate a certificate to an application

    .EXAMPLE
    Add-VcCertificateAssociation -Certificate '7ac56ec0-2017-11ee-9417-a17dd25b82f9' -Application '96fc9310-67ec-11eb-a8a7-794fe75a8e6f', 'a05013bd-921d-440c-bc22-c9ead5c8d548'

    Associate a certificate to multiple applications

    .EXAMPLE
    Find-VcCertificate -First 5 | Add-VcCertificateAssociation -Application 'My Awesome App'

    Associate multiple certificates to 1 application by name

    .EXAMPLE
    Add-VcCertificateAssociation -Certificate '7ac56ec0-2017-11ee-9417-a17dd25b82f9' -Tag 'MyTagName'

    Associate a certificate to a tag

    .EXAMPLE
    Add-VcCertificateAssociation -Certificate '7ac56ec0-2017-11ee-9417-a17dd25b82f9' -Tag 'MyTagName:MyTagValue'

    Associate a certificate to a tag name/value pair

    .EXAMPLE
    Add-VcCertificateAssociation -Certificate '7ac56ec0-2017-11ee-9417-a17dd25b82f9' -Tag 'Tag1', 'MyTagName:MyTagValue'

    Associate a certificate to multiple tags

    .EXAMPLE
    Add-VcCertificateAssociation -Certificate 'www.barron.com' -Application '96fc9310-67ec-11eb-a8a7-794fe75a8e6f' -NoOverwrite

    Associate a certificate, by name, to an additonal application, keeping the existing application in place
    #>


    [CmdletBinding(DefaultParameterSetName = 'Application')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('certificateID')]
        [string] $Certificate,

        [Parameter(Mandatory, ParameterSetName = 'Application')]
        [Alias('applicationID')]
        [string[]] $Application,

        [Parameter(Mandatory, ParameterSetName = 'Tag')]
        [string[]] $Tag,

        [Parameter()]
        [switch] $NoOverwrite,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [Alias('Key')]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        switch ($PSCmdlet.ParameterSetName) {
            'Application' {
                $apps = $Application | Get-VcData -Type 'Application' -FailOnNotFound

                $params = @{
                    Method  = 'Patch'
                    UriRoot = 'outagedetection/v1'
                    UriLeaf = 'applications/certificates'
                    Body    = @{
                        action                 = 'REPLACE'
                        targetedApplicationIds = @($apps)
                    }
                }

                if ( $NoOverwrite ) {
                    $params.Body.action = 'ADD'
                }
            }

            'Tag' {
                # ensure tags exist and have the correct case
                # tag assignment will fail if the tag name is not exact
                $tagNames = $Tag | Get-VcData -Type 'Tag' -Name -FailOnNotFound

                $params = @{
                    Method  = 'Patch'
                    UriLeaf = 'tagsassignment'
                    Body    = @{
                        action       = 'REPLACE'
                        targetedTags = @($tagNames)
                        entityType   = 'CERTIFICATE'
                    }
                }

                if ( $NoOverwrite ) {
                    $params.Body.action = 'ADD'
                }

            }
        }

        $allCerts = [System.Collections.Generic.List[string]]::new()
    }

    process {
        if ( Test-IsGuid($Certificate) ) {
            $allCerts.Add($Certificate)
        }
        else {
            # search by name
            $certIDs = Get-VcData -InputObject $Certificate -Type 'Certificate'
            foreach ($certID in @($certIDs)) {
                $allCerts.Add($certID)
            }
        }
    }

    end {
        switch ($PSCmdlet.ParameterSetName ) {
            'Application' {
                $params.Body.certificateIds = $allCerts

                $response = Invoke-VenafiRestMethod @params

                if ( $PassThru ) {
                    $response.certificates
                }
            }

            'Tag' {
                $params.Body.entityIds = $allCerts

                $response = Invoke-VenafiRestMethod @params

                if ( $PassThru ) {
                    $response.tagsAssignInformation | Select-Object -Property @{'n' = 'certificateId'; 'e' = { $_.entityId } }, * -ExcludeProperty entityId, entityType
                }
            }
        }
    }
}
#EndRegion './Public/Set-VcCertificate.ps1' 188
#Region './Public/Set-VcConnector.ps1' -1

function Set-VcConnector {
    <#
    .SYNOPSIS
    Update an existing connector

    .DESCRIPTION
    Update a new machine, CA, TPP, or credential connector.
    You can either update the manifest or disable/reenable it.

    .PARAMETER ManifestPath
    Path to an updated manifest for an existing connector.
    Ensure the manifest has the deployment element which is not needed when testing in the simulator.
    See https://github.com/Venafi/vmware-avi-connector?tab=readme-ov-file#manifest for details.

    .PARAMETER ID
    Connector ID to update.
    If not provided, the ID will be looked up by the name in the manifest provided by ManifestPath.
    Note that if both ManifestPath and ID are provided and the name in the manifest is different than the one associated with ID, the name will be changed.

    .PARAMETER Disable
    Disable or reenable a connector

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    Connector

    .EXAMPLE
    Set-VcConnector -ManifestPath '/tmp/manifest_v2.json'

    Update an existing connector with the same name as in the manifest

    .EXAMPLE
    Set-VcConnector -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -ManifestPath '/tmp/manifest_v2.json'

    Update an existing connector utilizing a specific connector ID

    .EXAMPLE
    Set-VcConnector -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Disable

    Disable a connector

    .EXAMPLE
    Get-VcConnector -ID 'My connector' | Set-VcConnector -Disable

    Disable a connector by name

    .EXAMPLE
    Set-VcConnector -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Disable:$false

    Reenable a disabled connector

    #>


    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Manifest')]

    param (

        [Parameter(ParameterSetName = 'Manifest', Mandatory)]
        [ValidateScript(
            {
                if ( -not ( Test-Path $_ ) ) {
                    throw "The manifest path $_ cannot be found"
                }
                $true
            }
        )]
        [string] $ManifestPath,

        [Parameter(ParameterSetName = 'Manifest', ValueFromPipelineByPropertyName)]
        [Parameter(ParameterSetName = 'Disable', Mandatory, ValueFromPipelineByPropertyName)]
        [Alias('connectorId', 'connector')]
        [ValidateScript(
            {
                if ( -not (Test-IsGuid -InputObject $_ ) ) {
                    throw "$_ is not a valid connector id format"
                }
                $true
            }
        )]
        [string] $ID,

        [Parameter(ParameterSetName = 'Disable', Mandatory)]
        [switch] $Disable,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'
    }

    process {

        switch ($PSCmdLet.ParameterSetName) {
            'Manifest' {
                $manifestObject = Get-Content -Path $ManifestPath -Raw | ConvertFrom-Json
                $manifest = $manifestObject.manifest

                # if connector is provided, update that specific one
                # if not, use the name from the manifest to find the existing connector id

                if ( $ID ) {
                    $connectorId = $ID
                }
                else {
                    $thisConnector = Get-VcConnector -ID $manifest.name
                    if ( -not $thisConnector ) {
                        throw ('An existing connector with the name {0} was not found' -f $manifest.name)
                    }
                    $connectorId = $thisConnector.connectorId
                }

                # ensure deployment is provided which is not needed during simulator testing
                if ( -not $manifest.deployment ) {
                    throw 'A deployment element was not found in the manifest. See https://github.com/Venafi/vmware-avi-connector?tab=readme-ov-file#manifest for details.'
                }

                $params = @{
                    Method  = 'Patch'
                    UriLeaf = "plugins/$connectorId"
                    Body    = @{
                        manifest = $manifest
                    }
                }

                if ( $PSCmdlet.ShouldProcess($manifest.name, 'Update connector') ) {
                    $null = Invoke-VenafiRestMethod @params
                }
            }

            'Disable' {

                if ( $Disable ) {
                    if ( $PSCmdlet.ShouldProcess($ID, "Disable connector") ) {
                        $null = Invoke-VenafiRestMethod -Method 'Post' -UriLeaf "plugins/$ID/disablements"
                    }
                }
                else {
                    $null = Invoke-VenafiRestMethod -Method 'Delete' -UriLeaf "plugins/$ID/disablements"
                }
            }
        }
    }
}
#EndRegion './Public/Set-VcConnector.ps1' 150
#Region './Public/Set-VcTeam.ps1' -1

function Set-VcTeam {
    <#
    .SYNOPSIS
    Update an existing team

    .DESCRIPTION
    Update name, role, and/or user matching rules for existing teams.

    .PARAMETER ID
    Team ID or name

    .PARAMETER Name
    Provide a new name for the team if you wish to change it.

    .PARAMETER Role
    Provide a new role for the team if you wish to change it.
    Accepted values are 'System Admin', 'PKI Admin', 'Resource Owner', or 'Guest'

    .PARAMETER UserMatchingRule
    Rule(s) for user membership which matches SSO claim data.
    Each rule has 3 parts, ClaimName, Operator, and ClaimValue, in the form of a hashtable.
    A list/array of hashtables is supported.
    For a singlepart claim, the operator can be 'equals', 'does not equal', 'starts with', or 'ends with'.
    For a multivalue claim where ClaimValue will be an array, the operator can be 'contains' or 'does not contain'.
    ClaimName and ClaimValue are case sensitive.
    When providing user AD groups or other groups they are most commonly provided as multivalue claims.
    This parameter will overwrite existing rules by default. To append use -NoOverwrite.

    .PARAMETER NoOverwrite
    Append to existing user matching rules as opposed to overwriting

    .PARAMETER PassThru
    Return the newly updated team object

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPC key can also provided.

    .INPUTS
    ID

    .OUTPUTS
    PSCustomObject

    .EXAMPLE
    Set-VcTeam -ID 'MyTeam' -Name 'ThisTeamIsBetter'

    Rename an existing team

    .EXAMPLE
    Set-VcTeam -ID 'ca7ff555-88d2-4bfc-9efa-2630ac44c1f2' -Role 'PKI Admin'

    Change the role for an existing team

    .EXAMPLE
    Set-VcTeam -ID 'MyTeam' -UserMatchingRule @{'ClaimName'='MyClaim';'Operator'='equals';'ClaimValue'='matchme'}

    Replace a teams user matching rules

    .EXAMPLE
    Set-VcTeam -ID 'MyTeam' -UserMatchingRule @{'ClaimName'='MyClaim';'Operator'='equals';'ClaimValue'='matchme'} -NoOverwrite

    Update a teams user matching rules, appending instead of overwriting

    .EXAMPLE
    Set-VcTeam -ID 'MyTeam' -Name 'ThisTeamIsBetter' -PassThru

    Rename an existing team and return the updated team object

    .EXAMPLE
    Get-VcTeam -All | Where-Object {$_.name -like '*shouldnt be sysadmin*'} | Set-VcTeam -NewRole 'PKI Admin'

    Update many teams
    #>


    [CmdletBinding(DefaultParameterSetName = 'NoOverwrite')]
    [Alias('Set-VaasTeam')]

    param (

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Alias('teamId', 'ID')]
        [string] $Team,

        [Parameter()]
        [string] $Name,

        [Parameter()]
        [ValidateSet('System Admin', 'PKI Admin', 'Resource Owner', 'Guest')]
        [string] $Role,

        [Parameter()]
        [Parameter(Mandatory, ParameterSetName = 'Overwrite')]
        [ValidateScript({
                foreach ($rule in $_) {
                    if ( $rule.Keys -contains 'ClaimName' -and $rule.Keys -contains 'Operator' -and $rule.Keys -contains 'ClaimValue' ) {
                        if ( $rule.Operator.Replace(' ', '_').ToUpper() -notin 'EQUALS', 'NOT_EQUALS', 'CONTAINS', 'NOT_CONTAINS', 'STARTS_WITH', 'ENDS_WITH') {
                            throw 'Operator must be one of the following: Equals, Not Equals, Contains, Not Contains, Starts With, or Ends With'
                        }
                        $true
                    }
                    else {
                        throw "NewUserMatchingRule is an array of hashtables where each hashtable must contain keys 'ClaimName', 'Operator', and 'ClaimValue'."
                    }
                }
            })]
        [hashtable[]] $UserMatchingRule,

        [Parameter(Mandatory, ParameterSetName = 'Overwrite')]
        [switch] $NoOverwrite,

        [Parameter()]
        [switch] $PassThru,

        [Parameter()]
        [Alias('Key')]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VC'

        $params = @{
            VenafiSession = $VenafiSession
            Method        = 'Patch'
            Body          = @{}
        }

        if ( $Name ) {
            $params.Body.name = $Name
        }

        if ( $Role ) {
            $params.Body.role = $Role.Replace(' ', '_').ToUpper()
        }

        if ( $UserMatchingRule ) {
            [array]$params.Body.userMatchingRules = foreach ($rule in $UserMatchingRule) {
                @{
                    claimName = $rule.ClaimName
                    operator  = $rule.Operator.Replace(' ', '_').ToUpper()
                    value     = $rule.ClaimValue
                }
            }
        }
    }

    process {

        $thisID = Get-VcData -InputObject $Team -Type 'Team' -Object
        if ( -not $thisID ) {
            Write-Error "Team '$Team' does not exist"
            return
        }

        if ( $NoOverwrite -and $thisID.userMatchingRules ) {
            $params.Body.userMatchingRules += $thisID.userMatchingRules
        }

        $params.UriLeaf = "teams/$($thisID.teamId)"

        $response = Invoke-VenafiRestMethod @params

        if ( $PassThru ) {
            $response | ConvertTo-VcTeam
        }
    }
}
#EndRegion './Public/Set-VcTeam.ps1' 170
#Region './Public/Set-VdcAttribute.ps1' -1

function Set-VdcAttribute {
    <#
    .SYNOPSIS
    Sets a value on an objects attribute or policies (policy attributes)

    .DESCRIPTION
    Set the value on an objects attribute. The attribute can either be built-in or custom.
    You can also set policies (policy attributes).

    .PARAMETER Path
    Path to the object to modify

    .PARAMETER Attribute
    Hashtable with names and values to be set.
    If setting a custom field, you can use either the name or guid as the key.
    To clear a value overwriting policy, set the value to $null.

    .PARAMETER Class
    Required when setting policy attributes. Provide the class name to set the value for.
    If unsure of the class name, add the value through the TLSPDC UI and go to Support->Policy Attributes to find it.

    .PARAMETER Lock
    Lock the value on the policy. Only applicable to setting policies.

    .PARAMETER BypassValidation
    Bypass data validation. Only applicable to custom fields.

    .PARAMETER NoOverwrite
    Add to any existing value, if there is one, as opposed to overwriting.
    Unlike overwriting, adding can only be a single value, not an array.
    Not applicable to custom fields.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    None

    .EXAMPLE
    Set-VdcAttribute -Path '\VED\Policy\My Folder\app.company.com' -Attribute @{'Consumers'='\VED\Policy\myappobject.company.com'}

    Set the value on an object

    .EXAMPLE
    Set-VdcAttribute -Path '\VED\Policy\My Folder\app.company.com' -Attribute @{'Management Type'=$null}

    Clear the value on an object, reverting to policy if applicable

    .EXAMPLE
    Set-VdcAttribute -Path '\VED\Policy\My Folder\app.company.com' -Attribute @{'My custom field Label'='new custom value'}

    Set the value on a custom field

    .EXAMPLE
    Set-VdcAttribute -Path '\VED\Policy\My Folder\app.company.com' -Attribute @{'My custom field Label'='new custom value'} -BypassValidation

    Set the value on a custom field bypassing field validation

    .EXAMPLE
    Set-VdcAttribute -Path '\VED\Policy\My Folder' -Class 'X509 Certificate' -Attribute @{'Notification Disabled'='0'}

    Set a policy attribute

    .EXAMPLE
    Set-VdcAttribute -Path '\VED\Policy\My Folder' -Class 'X509 Certificate' -Attribute @{'Notification Disabled'='0'} -Lock

    Set a policy attribute and lock the value

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Set-VdcAttribute/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Set-VdcAttribute.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Metadata-Set.php

    .LINK
    https://docs.venafi.com/Docs/currentSDK/TopNav/Content/SDK/WebSDK/r-SDK-POST-Metadata-SetPolicy.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-addvalue.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-addpolicyvalue.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-write.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-writepolicy.php
    #>


    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'NotPolicy')]
    [Alias('Set-TppAttribute')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Being flagged incorrectly')]

    param (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('DN')]
        [String] $Path,

        [Parameter(Mandatory)]
        [hashtable] $Attribute,

        [Parameter(Mandatory, ParameterSetName = 'Policy')]
        [Alias('ClassName', 'PolicyClass')]
        [string] $Class,

        [Parameter(ParameterSetName = 'Policy')]
        [switch] $Lock,

        [Parameter()]
        [switch] $BypassValidation,

        [Parameter()]
        [switch] $NoOverwrite,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{

            Method        = 'Post'
        }

        $baseFields = @()
        $customFields = @()

        $Attribute.GetEnumerator() | ForEach-Object {

            $thisKey = $_.Key

            if ($null -ne $_.Value) {
                if ( $_.Value.GetType().BaseType.Name -eq 'Array' ) {
                    $thisValue = @($_.Value)
                }
                else {
                    $thisValue = ($_.Value).ToString()
                }
            }
            else {
                # cannot add 'null', only overwrite to blank out the value
                $NoOverwrite = $false
                $thisValue = $_.Value
                $BypassValidation = $true
            }
            $customFieldError = $null

            $customField = $VenafiSessionNested.CustomField | Where-Object { $_.Label -eq $thisKey -or $_.Guid -eq $thisKey }
            Write-Verbose ('found custom field {0} - {1}' -f $customField.DN, $customField|ConvertTo-Json)
            if ( $customField ) {
                if ( -not $BypassValidation ) {
                    switch ( $customField.Type.ToString() ) {
                        '1' {
                            # string
                            if ( $customField.RegularExpression -and $thisValue -notmatch $customField.RegularExpression ) {
                                $customFieldError = 'regular expression ''{0}'' validation failed' -f $customField.RegularExpression
                            }
                        }

                        '2' {
                            # list
                            if ( $thisValue -notin $customField.AllowedValues ) {
                                $customFieldError = 'value is not in the list of allowed values ''{0}''' -f $customField.AllowedValues
                            }
                        }

                        '5' {
                            # identity
                            if ( -not ($thisValue | Test-VdcIdentity -ExistOnly) ) {
                                $customFieldError = 'value is not a valid identity'
                            }
                        }

                        '4' {
                            # date/time
                            try {
                                [datetime] $thisValue
                            }
                            catch {
                                $customFieldError = 'value is not a valid date'
                            }
                        }

                        Default {
                            $customFieldError = 'unknown custom field type'
                        }
                    }
                }

                if ( $customFieldError ) {
                    Write-Error ('The value ''{0}'' for field ''{1}'' encountered an error, {2}' -f $thisValue, $thisKey, $customFieldError)
                }
                else {
                    $customFields += @{
                        ItemGuid = $customField.Guid
                        List     = if ($null -eq $thisValue) { , @() } else { , @($thisValue) }
                    }
                }
            }
            else {
                $baseFields += @{
                    Name  = $thisKey
                    Value = if ($null -eq $thisValue) { '' } else { $thisValue }
                    # Value = if ($null -eq $thisValue) { , @() } else { , @($thisValue) }
                }
            }
        }
    }

    process {

        if ( -not $PSCmdlet.ShouldProcess($Path) ) {
            continue
        }

        # built-in fields and custom fields have different APIs and payloads
        # as do attributes and policy attributes...

        if ( $baseFields.Count -gt 0 ) {

            if ( $PSBoundParameters.ContainsKey('Class') ) {
                # config/WritePolicy and AddPolicyValue only allows 1 key/value per call
                foreach ($field in $baseFields) {

                    $params.Body = @{
                        ObjectDN      = $Path
                        Class         = $Class
                        AttributeName = $field.Name
                        Locked        = [int]$Lock.ToBool()
                    }

                    if ( $NoOverwrite ) {
                        $params.UriLeaf = 'config/AddPolicyValue'
                        $params.Body.Value = $field.Value
                    }
                    else {
                        $params.UriLeaf = 'config/WritePolicy'
                        $params.Body.Values = @($field.Value)
                    }

                    $response = Invoke-VenafiRestMethod @params

                    if ( $response.Result -ne 1 ) {
                        Write-Error $response.Error
                    }
                }
            }
            else {

                if ( $NoOverwrite ) {

                    $params.UriLeaf = 'config/AddValue'
                    foreach ( $field in $baseFields ) {
                        $params.Body = @{
                            ObjectDN      = $Path
                            AttributeName = $field.Name
                            Value         = $field.Value
                        }

                        $response = Invoke-VenafiRestMethod @params

                        if ( $response.Result -ne 1 ) {
                            Write-Error $response.Error
                        }

                    }
                }
                else {
                    $params.UriLeaf = 'config/Write'
                    $params.Body = @{
                        ObjectDN      = $Path
                        AttributeData = @($baseFields)
                    }

                    $response = Invoke-VenafiRestMethod @params

                    if ( $response.Result -ne 1 ) {
                        Write-Error $response.Error
                    }
                }
            }
        }

        if ( $customFields.Count -gt 0 ) {

            if ( $PSBoundParameters.ContainsKey('Class') ) {
                $params.UriLeaf = 'Metadata/SetPolicy'
                $params.Body = @{
                    DN          = $Path
                    ConfigClass = $Class
                    GuidData    = $customFields
                    Locked      = [int]$Lock.ToBool()
                }
            }
            else {

                $params.UriLeaf = 'Metadata/Set'
                $params.Body = @{
                    DN           = $Path
                    GuidData     = $customFields
                    KeepExisting = $true
                }
            }

            $response = Invoke-VenafiRestMethod @params

            if ( $response.Result -ne [TppMetadataResult]::Success ) {
                Write-Error $response.Error
            }
        }
    }
}
#EndRegion './Public/Set-VdcAttribute.ps1' 334
#Region './Public/Set-VdcCredential.ps1' -1

function Set-VdcCredential {
    <#
    .SYNOPSIS
    Update credential values

    .DESCRIPTION
    Update values for credential objects in TLSPDC.


    .PARAMETER Path
    The full path to the credential object

    .PARAMETER Password
    New password for a password, username/password, or certificate credential. Provide a string, SecureString, or PSCredential.

    .PARAMETER Username
    New username for a username/password credential.
    A password is also required.

    .PARAMETER Certificate
    PKCS #12 certificate object for a certificate credential.
    You can provide either a certificate object or CertificatePath to a .p12 or .pfx file.
    A private key password is also required for -Password.

    .PARAMETER CertificatePath
    Path to a certificate for a certificate credential.
    You can provide either a local path to a .p12 or .pfx file or a certificate object with -Certificate.
    A private key password is also required for -Password.

    .PARAMETER CertificateLinkPath
    Provide a path to an existing TLSPDC certificate object to be used as the certificate for a certificate credential.

    .PARAMETER Expiration
    Expiration date in UTC for the credential. Provide a DateTime object.
    This can be set for username or password credentials.

    .PARAMETER Value
    Hashtable containing the keys/values to be updated.
    This parameter will be deprecated in a future release. Use specific parameters for the credential type.
    The values allowed to be updated are specific to the object type.
    See https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Credentials-FriendlyName.php for details.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path

    .OUTPUTS
    None

    .EXAMPLE
    Set-VdcCredential -Path '\VED\Policy\Password Credential' -Password 'my-new-password'

    Set a new password for a password credential

    .EXAMPLE
    Set-VdcCredential -Path '\VED\Policy\UsernamePassword Credential' -Password 'my-new-password' -Username 'greg'

    Set a new password for a username/password credential

    .EXAMPLE
    Set-VdcCredential -Path '\VED\Policy\Certificate Credential' -Password 'my-pk-password' -Certificate $p12

    Set a new certificate for a certificate credential

    .EXAMPLE
    Set-VdcCredential -Path '\VED\Policy\Password Credential' -Password 'my-new-password' -Expiration (Get-Date).AddDays(30)

    Set a new password for a password credential and set the expiration date to 30 days from now

    .EXAMPLE
    Set-VdcCredential -Path '\VED\Policy\Certificate Credential' -CertificateLinkPath '\VED\Policy\Certificates\newcert.domain.com'

    Set an existing TLSPDC certificate object as the certificate for a certificate credential

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Set-VdcCredential/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Set-VdcCredential.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Credentials-update.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Credentials-FriendlyName.php

    #>


    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Password')]
    [Alias('Set-TppCredential')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUsernameAndPasswordParams', '', Justification = 'Password is used for more than just username/password credentials so a credential object is not appropriate.')]

    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [String] $Path,

        [Parameter(ParameterSetName = 'Password', Mandatory)]
        [Parameter(ParameterSetName = 'UsernamePassword', Mandatory)]
        [Parameter(ParameterSetName = 'Certificate', Mandatory)]
        [Parameter(ParameterSetName = 'CertificatePath', Mandatory)]
        [ValidateScript(
            {
                if ( $_ -is [string] -or $_ -is [securestring] -or $_ -is [pscredential] ) {
                    $true
                }
                else {
                    throw 'Unsupported type. Provide either a String, SecureString, or PSCredential.'
                }
            }
        )]
        [psobject] $Password,

        [Parameter(ParameterSetName = 'UsernamePassword', Mandatory)]
        [string] $Username,

        [Parameter(ParameterSetName = 'Certificate', Mandatory)]
        [ValidateScript(
            {
                if ( -not $_.HasPrivateKey ) {
                    throw 'Certificate must contain a private key'
                }
                $true
            }
        )]
        [System.Security.Cryptography.X509Certificates.X509Certificate2] $Certificate,

        [Parameter(Mandatory, ParameterSetName = 'CertificatePath', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript(
            {
                if ( -not (Test-Path -Path (Resolve-Path -Path $_) -PathType Leaf) ) {
                    throw "'$_' is not a valid file path"
                }

                if ([System.IO.Path]::GetExtension((Resolve-Path -Path $_)) -notin '.pfx', '.p12') {
                    throw "$_ is not a .p12 or .pfx file"
                }

                $true
            }
        )]
        [String] $CertificatePath,

        [Parameter(ParameterSetName = 'CertificateLinkPath', Mandatory)]
        [ValidateScript(
            {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            }
        )]
        [string] $CertificateLinkPath,

        [Parameter(ParameterSetName = 'Password')]
        [Parameter(ParameterSetName = 'UsernamePassword')]
        [Parameter(ParameterSetName = 'Certificate')]
        [Parameter(ParameterSetName = 'CertificatePath')]
        [Parameter(ParameterSetName = 'CertificateLinkPath')]
        [datetime] $Expiration,

        [Parameter(ParameterSetName = 'OldValue', Mandatory)]
        [hashtable] $Value,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{

            Method  = 'Post'
            UriLeaf = 'Credentials/Update'
            Body    = @{
                Values = @()
            }
        }

        if ( $PSBoundParameters.ContainsKey('Password') ) {
            $newPassword = $Password | ConvertTo-PlaintextString

            $params.Body.Values += @{
                'Name'  = 'Password'
                'Type'  = 'string'
                'Value' = $newPassword
            }
        }

        if ( $Username ) {
            $params.Body.Values += @{
                'Name'  = 'Username'
                'Type'  = 'string'
                'Value' = $Username
            }
        }

        if ( $Certificate ) {
            $params.Body.Values += @{
                'Name'  = 'Certificate'
                'Type'  = 'byte[]'
                'Value' = [System.Convert]::ToBase64String($Certificate.Export(1))
            }
        }

        if ( $CertificatePath ) {
            if ($PSVersionTable.PSVersion.Major -lt 6) {
                $cert = Get-Content $CertificatePath -Encoding Byte
            }
            else {
                $cert = Get-Content $CertificatePath -AsByteStream
            }
            $params.Body.Values += @{
                'Name'  = 'Certificate'
                'Type'  = 'byte[]'
                'Value' = [System.Convert]::ToBase64String($cert)
            }
        }

        if ( $Expiration ) {
            $params.Body.Expiration = '/Date({0})/' -f [int64]($Expiration.ToUniversalTime() - [datetime]'1970-01-01T00:00:00Z').TotalMilliseconds
        }

        # used with -Value parameter, to be deprecated
        $CredTypes = @{
            'Password Credential'          = @{
                'FriendlyName' = 'Password'
                'ValueName'    = @{
                    'Password' = 'string'
                }
            }
            'Username Password Credential' = @{
                'FriendlyName' = 'UsernamePassword'
                'ValueName'    = @{
                    'Username' = 'string'
                    'Password' = 'string'
                }
            }
            'Certificate Credential'       = @{
                'FriendlyName' = 'Certificate'
                'ValueName'    = @{
                    'Certificate' = 'byte[]'
                    'Password'    = 'string'
                }
            }
        }
    }

    process {

        # lookup path so we know the type of cred we're dealing with
        $tppObject = Get-VdcObject -Path $Path
        $thisType = $tppObject.TypeName


        # ensure the values looking to be updated are appropriate for this object type
        if ( $Value ) {
            Write-Warning "-Value will be deprecated in a future release. Use specific parameters for the credential type instead."

            if ( -not $CredTypes[$thisType] ) {
                throw "Credential type '$thisType' is not supported yet. Submit an enhancement request."
            }

            $friendlyName = $CredTypes[$thisType].FriendlyName
            $params.Body.FriendlyName = $friendlyName

            $newValues = $Value.GetEnumerator() | ForEach-Object {
                $thisValue = $CredTypes[$thisType].ValueName[$_.Key]
                if ( $thisValue ) {
                    @{
                        'Name'  = $_.Key
                        'Type'  = $thisValue
                        'Value' = $_.Value
                    }
                }
                else {
                    throw ('''{0}'' is not a valid item for type ''{1}''' -f $_.Key, $thisType)
                }
            }

            $params.Body.Values = @($newValues)
        }
        else {
            switch ($tppObject.TypeName) {
                'Password Credential' {
                    $params.Body.FriendlyName = 'Password'
                }
                'Username Password Credential' {
                    $params.Body.FriendlyName = 'UsernamePassword'
                }
                'Certificate Credential' {
                    $params.Body.FriendlyName = 'Certificate'
                }
                Default {
                    Write-Error "$Path : credential type '$thisType' is not supported yet. Submit an enhancement request."
                    continue
                }
            }

            if ( $PSCmdlet.ParameterSetName -notlike ('{0}*' -f $params.Body.FriendlyName) ) {
                Write-Error "$Path : the credential type for this object, $thisType, does not match the parameters provided"
                continue
            }
        }

        # certificate link path workaround
        if ( $CertificateLinkPath ) {
            $certParams = @{
                Path      = $Path
                Attribute = @{
                    'Certificate' = $CertificateLinkPath
                }
            }

            Set-VdcAttribute @certParams
            return
        }


        if ( $PSCmdlet.ShouldProcess( $Path )) {
            $params.Body.CredentialPath = $Path
            $response = Invoke-VenafiRestMethod @params

            if ( $response.Result -ne 1 ) {
                Write-Error $response.Error
            }
        }
    }
}
#EndRegion './Public/Set-VdcCredential.ps1' 347
#Region './Public/Set-VdcPermission.ps1' -1

function Set-VdcPermission {
    <#
    .SYNOPSIS
    Set explicit permissions for TLSPDC objects

    .DESCRIPTION
    Adds, modifies, or removes explicit permissions on TLSPDC objects.
    You can provide a complete permission object or modify individual permissions.

    .PARAMETER Path
    Path to an object

    .PARAMETER Guid
    Guid representing a unique object

    .PARAMETER IdentityId
    The id that represents the user or group. You can use Find-VdcIdentity or Get-VdcPermission to get the id.

    .PARAMETER Permission
    TppPermission object to set.
    You can create a new object and modify it or get an existing object with Get-VdcPermission.

    .PARAMETER Force
    When setting a TppPermission object with -Permission and one already exists, use this to overwrite

    .PARAMETER IsAssociateAllowed
    Associate or disassociate an Application and Device object with a certificate.
    Push the certificate and private key to the Application object.
    Retry the certificate installation.

    .PARAMETER IsCreateAllowed
    The caller can create subordinate objects, such as Devices and Applications. Create permission grants implicit View permission.

    .PARAMETER IsDeleteAllowed
    The caller can delete objects.

    .PARAMETER IsManagePermissionsAllowed
    The caller can grant other user or group Identities permission to the current object or subordinate objects.

    .PARAMETER IsPolicyWriteAllowed
    The caller can modify policy values on folders.
    Also requires View permission.
    Manage Policy permission grants implicit Read permission and Write permission.

    .PARAMETER IsPrivateKeyReadAllowed
    The caller can download the private key for Policy and Certificate objects.

    .PARAMETER IsPrivateKeyWriteAllowed
    The caller can upload the private key for Policy, Certificate, and Private Key Credential objects to Trust Protection Platform.

    .PARAMETER IsReadAllowed
    The caller can view and read object data from the Policy tree.
    However, to view subordinate objects, View permission or higher permissions is also required.

    .PARAMETER IsRenameAllowed
    The caller can rename and move Policy tree objects.
    Move capability also requires Rename permission to the object and Create permission to the target folder.

    .PARAMETER IsRevokeAllowed
    The caller can invalidate a certificate.
    Also requires Write permission to the certificate.

    .PARAMETER IsViewAllowed
    The caller can confirm that the object is present in the Policy tree.

    .PARAMETER IsWriteAllowed
    The caller can edit object attributes.
    To move objects in the tree, the caller must have Write permission to the objects and Create permission to the target folder.
    Write permission grants implicit Read permission.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Guid, IdentityId, Permission

    .OUTPUTS
    None

    .EXAMPLE
    Set-VdcPermission -Guid '1234abcd-g6g6-h7h7-faaf-f50cd6610cba' -IdentityId 'AD+mydomain.com:azsxdcfvgbhnjmlk09877654321' -Permission $TppPermObject

    Permission a user/group on an object specified by guid

    .EXAMPLE
    Set-VdcPermission -Path '\ved\policy\my folder' -IdentityId 'AD+mydomain.com:azsxdcfvgbhnjmlk09877654321' -Permission $TppPermObject

    Permission a user/group on an object specified by path

    .EXAMPLE
    Get-VdcPermission -Path '\ved\policy\my folder' -IdentityId 'AD+mydomain.com:azsxdcfvgbhnjmlk09877654321' -Explicit | Set-VdcPermission -IdentityId $newId

    Permission a user/group based on permissions of an existing user/group

    .EXAMPLE
    Get-VdcPermission -Path '\ved\policy\my folder' -IdentityId 'AD+mydomain.com:azsxdcfvgbhnjmlk09877654321' -Explicit | Set-VdcPermission -IsWriteAllowed

    Add specific permission(s) for a specific user/group associated with an object

    .EXAMPLE
    Get-VdcPermission -Path '\ved\policy\my folder' -Explicit | Set-VdcPermission -IsAssociateAllowed -IsWriteAllowed

    Add specific permission(s) for all existing user/group associated with an object

    .EXAMPLE
    Get-VdcPermission -Path '\ved\policy\my folder' -Explicit | Set-VdcPermission -IsAssociateAllowed:$false

    Remove specific permission(s) for all existing user/group associated with an object

    .EXAMPLE
    $id = Find-VdcIdentity -Name 'brownstein' | Select-Object -ExpandProperty Id
    Find-VdcObject -Path '\VED' -Recursive | Get-VdcPermission -IdentityId $id | Set-VdcPermission -Permission $TppPermObject -Force

    Reset permissions for a specific user/group for all objects. Note the use of -Force to overwrite existing permissions.

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Set-VdcPermission/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Set-VdcPermission.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Permissions-object-guid-principal.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-PUT-Permissions-object-guid-principal.php

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-Permissions-Effective.php

    .NOTES
    Confirmation impact is set to Medium, set ConfirmPreference accordingly.
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'PermissionObjectGuid')]
    [Alias('Set-TppPermission')]

    param (
        [Parameter(Mandatory, ParameterSetName = 'PermissionObjectPath')]
        [Parameter(Mandatory, ParameterSetName = 'PermissionPath')]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('DN')]
        [String] $Path,

        [Parameter(Mandatory, ParameterSetName = 'PermissionObjectGuid', ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'PermissionGuid', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('ObjectGuid')]
        [guid] $Guid,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateScript( {
                if ( $_ | Test-VdcIdentityFormat -Format 'Universal' ) {
                    $true
                }
                else {
                    throw "'$_' is not a valid Prefixed Universal Id format. See https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-IdentityInformation.php."
                }
            })]
        [Alias('PrefixedUniversalId', 'ID')]
        [string] $IdentityId,

        [Parameter(Mandatory, ParameterSetName = 'PermissionObjectPath', ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'PermissionObjectGuid', ValueFromPipelineByPropertyName)]
        [Alias('ExplicitPermissions')]
        [TppPermission] $Permission,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsAssociateAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsCreateAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsDeleteAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsManagePermissionsAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsPolicyWriteAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsPrivateKeyReadAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsPrivateKeyWriteAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsReadAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsRenameAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsRevokeAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsViewAllowed,

        [Parameter(ParameterSetName = 'PermissionPath')]
        [Parameter(ParameterSetName = 'PermissionGuid')]
        [switch] $IsWriteAllowed,

        [Parameter()]
        [switch] $Force,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

    }

    process {
        Write-Verbose "Parameterset = $($PSCmdlet.ParameterSetName)"

        $params = @{

            Method        = 'Post'
            UriLeaf       = 'placeholder'
            Body          = $null
            FullResponse  = $true
        }

        if ( $Path ) {
            $thisGuid = $Path | ConvertTo-VdcGuid
        }
        else {
            $thisGuid = $Guid
        }
        $params.UriLeaf = "Permissions/Object/{$thisGuid}"

        if ( $IdentityId.StartsWith('local:') ) {
            # format of local is local:universalId
            $type, $id = $IdentityId.Split(':')
            $params.UriLeaf += "/$type/$id"
        }
        else {
            # external source, eg. AD, LDAP
            # format is type+name:universalId
            $type, $name, $id = $IdentityId -Split { $_ -in '+', ':' }
            $params.UriLeaf += "/$type/$name/$id"
        }

        if ( $PSCmdlet.ParameterSetName -like 'PermissionObject*' ) {
            $params.Body = $Permission.ToHashtable()
        }
        else {
            Write-Verbose "Getting existing permissions for $IdentityId"
            $thisPerm = $thisGuid | Get-VdcPermission -IdentityId $IdentityId -Explicit | Select-Object -ExpandProperty ExplicitPermissions

            if ( $thisPerm ) {
                Write-Verbose 'Existing identity found will be updated'
                $params.Method = 'Put'
            }
            else {
                Write-Verbose 'Existing identity not found. Only the permissions switches set will be true, all others will be false.'
                $thisPerm = [TppPermission]::new()
            }

            foreach ($k in $PSBoundParameters.Keys) {
                if ($k -in 'IsAssociateAllowed', 'IsCreateAllowed', 'IsDeleteAllowed', 'IsManagePermissionsAllowed', 'IsPolicyWriteAllowed', 'IsPrivateKeyReadAllowed', 'IsPrivateKeyWriteAllowed', 'IsReadAllowed', 'IsRenameAllowed', 'IsRevokeAllowed', 'IsViewAllowed', 'IsWriteAllowed') {
                    $thisPerm.$k = $PSBoundParameters[$k]
                }
            }

            $params.Body = $thisPerm.ToHashtable()
        }

        if ( $PSCmdlet.ShouldProcess($Path, "Set permission for $IdentityId") ) {
            try {

                $response = Invoke-VenafiRestMethod @params
                switch ( $response.StatusCode ) {

                    { $_ -in 200, 201 } {
                        # success
                    }

                    409 {
                        # user/group already has permissions defined on this object
                        # need to use a put method instead
                        if ( $Force ) {

                            Write-Verbose "Existing user/group found and Force option provided, updating existing permissions"
                            $params.Method = 'Put'
                            $response = Invoke-VenafiRestMethod @params
                            if ( $response.StatusCode -ne 200 ) {
                                Write-Error ('Failed to update permission with error {0}' -f $response.Error)
                            }
                        }
                        else {
                            # force option not provided, let the user know what's up
                            Write-Error ('Permission for {0} already exists. To override, provide the -Force option.' -f $IdentityId)
                        }
                    }

                    default {
                        Write-Error ('Failed to create permission with error {0}, {1}' -f [int]$response.StatusCode, $response.Error)
                    }
                }
            }
            catch {
                Write-Error ("Failed to set permissions on $Path, user/group $IdentityId. $_")
            }
        }
    }
}
#EndRegion './Public/Set-VdcPermission.ps1' 334
#Region './Public/Set-VdcWorkflowTicketStatus.ps1' -1

function Set-VdcWorkflowTicketStatus {
    <#
    .SYNOPSIS
    Set workflow ticket status

    .DESCRIPTION
    Set workflow ticket status for a certificate

    .PARAMETER TicketGuid
    Guid representing a unique ticket

    .PARAMETER Status
    The new status to assign to the ticket.
    Possible values include "Pending", "Approved", "Approved After", "Approved Between", and "Rejected".

    .PARAMETER Explanation
    Explanation for the status change

    .PARAMETER ScheduledStart
    Specifies the time after which the ticket should be processed.
    ScheduledStart must be specified when the "Approved After" or "Approved Between" statuses are set

    .PARAMETER ScheduledStop
    Specifies the time before which the ticket should be processed.
    ScheduledStop must be specified when the "Approved Between" status is set

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    TicketGuid

    .OUTPUTS
    None

    .EXAMPLE
    Get-VdcWorkflowTicket -Path '\VED\policy\myapp.company.com' | Set-VdcWorkflowTicketStatus -Status Approved
    Approve all tickets for a certificate

    .EXAMPLE
    Get-VdcWorkflowTicket -Path '\VED\policy\myapp.company.com' | Set-VdcWorkflowTicketStatus -Status 'Approved After' -ScheduledStart (Get-Date).AaaDays(3) -Explanation 'weekend upgrade'
    Approve all tickets for a certificate after a certain date with an explanation

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Set-VdcWorkflowTicketStatus/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Set-VdcWorkflowTicketStatus.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Workflow-ticket-updatestatus.php

    #>


    [CmdletBinding(SupportsShouldProcess)]
    [Alias('Set-TppWorkflowTicketStatus')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Guid[]] $TicketGuid,

        [Parameter(Mandatory)]
        [ValidateSet('Pending', 'Approved', 'Approved After', 'Approved Between', 'Rejected')]
        [String] $Status,

        [Parameter()]
        [String] $Explanation,

        [Parameter()]
        [DateTime] $ScheduledStart,

        [Parameter()]
        [DateTime] $ScheduledStop,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        switch ($Status) {
            'Approved After' {
                if ( -not $ScheduledStart ) {
                    throw "A status of 'Approved After' requires a value for ScheduledStart"
                }
                if ( $ScheduledStop ) {
                    throw "Do not provide a scheduled stop with a status of 'Approved After'"
                }
            }
            'Approved Between' {
                if ( -not $ScheduledStart -or -not $ScheduledStop ) {
                    throw "A status of 'Approved Between' requires a value for ScheduledStart and ScheduledStop"
                }
            }
            Default {
            }
        }

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'
    }

    process {

        foreach ($thisGuid in $TicketGuid) {
            $params = @{

                Method     = 'Post'
                UriLeaf    = 'Workflow/Ticket/UpdateStatus'
                Body       = @{
                    'GUID'   = $thisGuid
                    'Status' = $Status
                }
            }

            if ( $PSBoundParameters.ContainsKey('Explanation') ) {
                $params.Body.Explanation = $Explanation
            }

            if ( $PSBoundParameters.ContainsKey('ScheduledStart') ) {
                $params.Body.ScheduledStart = ($ScheduledStart | ConvertTo-UtcIso8601)
            }

            if ( $PSBoundParameters.ContainsKey('ScheduledStop') ) {
                $params.Body.ScheduledStop = ($ScheduledStop | ConvertTo-UtcIso8601)
            }

            if ( $PSCmdlet.ShouldProcess($params.Body.GUID, 'Set workflow ticket status') ) {

                $response = Invoke-VenafiRestMethod @params

                if ( -not ($response.Result -eq [TppWorkflowResult]::Success) ) {
                    throw ("Error setting workflow ticket status, error is {0}" -f [enum]::GetName([TppWorkflowResult], $response.Result))
                }
            }
        }
    }
}
#EndRegion './Public/Set-VdcWorkflowTicketStatus.ps1' 143
#Region './Public/Test-VdcIdentity.ps1' -1

function Test-VdcIdentity {
    <#
    .SYNOPSIS
    Test if an identity exists

    .DESCRIPTION
    Provided with a prefixed universal id, find out if an identity exists.

    .PARAMETER Identity
    The id that represents the user or group.

    .PARAMETER ExistOnly
    Only return boolean instead of ID and Exists list. Helpful when validating just 1 identity.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Identity

    .OUTPUTS
    PSCustomObject will be returned with properties 'ID', a System.String, and 'Exists', a System.Boolean.

    .EXAMPLE
    'local:78uhjny657890okjhhh', 'AD+mydomain.com:azsxdcfvgbhnjmlk09877654321' | Test-VdcIdentity

    Test multiple identities

    .EXAMPLE
    Test-VdcIdentity -Identity 'AD+mydomain.com:azsxdcfvgbhnjmlk09877654321' -ExistOnly

    Retrieve existence for only one identity, returns boolean

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Test-VdcIdentity/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Test-VdcIdentity.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Identity-Validate.php

    #>


    [CmdletBinding()]
    [Alias('Test-TppIdentity')]

    param (

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateScript( {
                if ( $_ | Test-VdcIdentityFormat ) {
                    $true
                } else {
                    throw "'$_' is not a valid Prefixed Universal Id format. See https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-IdentityInformation.php."
                }
            })]
        [Alias('PrefixedUniversal', 'Contact', 'IdentityId', 'FullName')]
        [string[]] $ID,

        [Parameter()]
        [Switch] $ExistOnly,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{

            Method        = 'Post'
            UriLeaf       = 'Identity/Validate'
        }
    }

    process {

        foreach ( $thisID in $ID ) {

            $params.Body = @{
                'ID' = @{}
            }

            if ( Test-VdcIdentityFormat -ID $thisID -Format 'Universal' ) {
                $params.Body.ID.PrefixedUniversal = $thisId
            } else {
                $params.Body.ID.PrefixedName = $thisId
            }

            $response = Invoke-VenafiRestMethod @params

            if ( $ExistOnly ) {
                $null -ne $response.Id
            } else {
                [PSCustomObject] @{
                    Identity = $thisId
                    Exists   = ($null -ne $response.Id)
                }
            }
        }
    }
}
#EndRegion './Public/Test-VdcIdentity.ps1' 108
#Region './Public/Test-VdcObject.ps1' -1

function Test-VdcObject {
    <#
    .SYNOPSIS
    Test if an object exists

    .DESCRIPTION
    Provided with either a DN path or GUID, find out if an object exists.

    .PARAMETER Path
    DN path to object. Provide either this or Guid. This is the default if both are provided.

    .PARAMETER Guid
    Guid which represents a unqiue object. Provide either this or Path.

    .PARAMETER ExistOnly
    Only return boolean instead of Object and Exists list. Helpful when validating just 1 object.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    Path or Guid.

    .OUTPUTS
    PSCustomObject will be returned with properties 'Object', a System.String, and 'Exists', a System.Boolean.

    .EXAMPLE
    $multDNs | Test-VdcObject
    Object Exists
    -------- -----
    \VED\Policy\My folder1 True
    \VED\Policy\My folder2 False

    Test for existence by Path

    .EXAMPLE
    Test-VdcObject -Path '\VED\Policy\My folder' -ExistOnly

    Retrieve existence for only one object

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Test-VdcObject/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Test-VdcObject.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-isvalid.php

    #>


    [CmdletBinding(DefaultParameterSetName = 'DN')]
    [Alias('Test-TppObject')]

    param (
        [Parameter(Mandatory, ParameterSetName = 'DN', ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ( $_ | Test-TppDnPath ) {
                    $true
                } else {
                    throw "'$_' is not a valid DN path"
                }
            })]
        [Alias('DN')]
        [string[]] $Path,

        [Parameter(Mandatory, ParameterSetName = 'GUID', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Guid[]] $Guid,

        [Parameter()]
        [Switch] $ExistOnly,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {
        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $params = @{

            Method     = 'Post'
            UriLeaf    = 'config/IsValid'
            Body       = @{}
        }
    }

    process {

        Switch ($PsCmdlet.ParameterSetName)    {
            'DN' {
                $paramSetValue = $Path
            }

            'GUID' {
                $paramSetValue = $Guid
            }
        }

        foreach ( $thisValue in $paramSetValue ) {

            Switch ($PsCmdlet.ParameterSetName)    {
                'DN' {
                    $params.Body = @{
                        'ObjectDN' = $thisValue
                    }
                }

                'GUID' {
                    $params.Body = @{
                        'ObjectGUID' = "{$thisValue}"
                    }
                }
            }

            $response = Invoke-VenafiRestMethod @params

            if ( $ExistOnly ) {
                $response.Result -eq 1
            } else {
                [PSCustomObject] @{
                    Object = $thisValue
                    Exists = ($response.Result -eq 1)
                }
            }
        }
    }
}
#EndRegion './Public/Test-VdcObject.ps1' 134
#Region './Public/Test-VdcToken.ps1' -1

function Test-VdcToken {
    <#
    .SYNOPSIS
    Test if a TLSPDC token is valid

    .DESCRIPTION
    Use the TLSPDC API call 'Authorize/Verify' to test if the current token is valid.

    .PARAMETER AuthServer
    Auth server or url, venafi.company.com or https://venafi.company.com.
    This will be used to access vedauth for token-based authentication.
    If just the server name is provided, https:// will be appended.

    .PARAMETER AccessToken
    Access token retrieved outside this module.
    You can either provide a String, SecureString, or PSCredential.
    If providing a credential, the username is not used.

    .PARAMETER VaultAccessTokenName
    Name of the SecretManagement vault entry for the access token; the name of the vault must be VenafiPS.

    .PARAMETER VenafiPsToken
    Token object obtained from New-VdcToken

    .PARAMETER VenafiSession
    VenafiSession object to validate.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.

    .PARAMETER GrantDetail
    Provides detailed info about the token object from the TLSPDC server response as an output. Supported on TLSPDC 20.4 and later.

    .INPUTS
    AccessToken

    .OUTPUTS
    Boolean (default)
    PSCustomObject (GrantDetail)
        ClientId
        AccessIssued
        GrantIssued
        Scope
        Identity
        RefreshExpires

    .EXAMPLE
    Test-VdcToken
    Verify that accesstoken stored in $VenafiSession object is valid.

    .EXAMPLE
    $VenafiPsToken | Test-VdcToken
    Verify that token object from pipeline is valid. Can be used to validate directly object from New-VdcToken.

    .EXAMPLE
    Test-VdcToken -AuthServer venafi.mycompany.com -AccessToken $cred
    Verify that PsCredential object containing accesstoken is valid.

    .EXAMPLE
    Test-VdcToken -VaultAccessTokenName access-token
    Verify access token stored in VenafiPS vault, metadata stored with secret

    .EXAMPLE
    Test-VdcToken -VaultAccessTokenName access-token -AuthServer venafi.mycompany.com
    Verify access token stored in VenafiPS vault providing server to authenticate against

    .EXAMPLE
    Test-VdcToken -GrantDetail
    Verify that accesstoken stored in $VenafiSession object is valid and return PsCustomObject as output with details.

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Test-VdcToken/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Test-VdcToken.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/AuthSDK/r-SDKa-GET-Authorize-Verify.php

    #>


    [CmdletBinding(DefaultParameterSetName = 'Session')]
    [Alias('Test-TppToken')]
    [OutputType([System.Boolean])]

    param (
        [Parameter(Mandatory, ParameterSetName = 'AccessToken')]
        [Parameter(ParameterSetName = 'VaultAccessToken')]
        [ValidateScript(
            {
                $validateMe = if ( $_ -notlike 'https://*') {
                    'https://{0}' -f $_
                }
                else {
                    $_
                }

                try {
                    $null = [System.Uri]::new($validateMe)
                    $true
                }
                catch {
                    throw 'Please enter a valid server, https://venafi.company.com or venafi.company.com'
                }
            }
        )]
        [Alias('Server')]
        [string] $AuthServer,

        [Parameter(Mandatory, ParameterSetName = 'AccessToken', ValueFromPipeline)]
        [ValidateScript(
            {
                if ( $_ -is [string] -or $_ -is [securestring] -or $_ -is [pscredential] ) {
                    $true
                }
                else {
                    throw 'Unsupported type. Provide either a String, SecureString, or PSCredential.'
                }
            }
        )]
        [psobject] $AccessToken,

        [Parameter(Mandatory, ParameterSetName = 'VenafiPsToken')]
        [ValidateScript(
            {
                if ( -not $_.Server -or -not $_.AccessToken ) {
                    throw 'Not a valid VenafiPsToken'
                }
                $true
            }
        )]
        [pscustomobject] $VenafiPsToken,

        [Parameter(Mandatory, ParameterSetName = 'VaultAccessToken')]
        [string] $VaultAccessTokenName,

        [Parameter()]
        [switch] $GrantDetail,

        [Parameter(ParameterSetName = 'Session')]
        [psobject] $VenafiSession = $script:VenafiSession
    )

    begin {
        $params = @{
            Method  = 'Get'
            UriRoot = 'vedauth'
            UriLeaf = 'Authorize/Verify'
        }

        $serverUrl = $AuthServer
        # add prefix if just server url was provided
        if ( $serverUrl -notlike 'https://*') {
            $serverUrl = 'https://{0}' -f $serverUrl
        }
    }

    process {

        Write-Verbose ('Parameter set: {0}' -f $PSCmdlet.ParameterSetName)

        switch ($PsCmdlet.ParameterSetName) {
            'Session' {
                if ( -not $VenafiSession ) {
                    throw [System.ArgumentNullException] 'VenafiSession'
                }
                if ( $VenafiSession -isnot [pscustomobject] -or -not $VenafiSession.Token ) {
                    throw [System.ArgumentException]::new('Must provide a VenafiSession object', 'VenafiSession')
                }

                $params.VenafiSession = $VenafiSession
            }

            'AccessToken' {
                $params.Server = $serverUrl

                $accessTokenString = $AccessToken | ConvertTo-PlaintextString

                $params.Header = @{'Authorization' = 'Bearer {0}' -f $accessTokenString }
            }

            'VaultAccessToken' {
                # ensure the appropriate setup has been performed
                if ( -not (Get-Module -Name Microsoft.PowerShell.SecretManagement -ListAvailable)) {
                    throw 'The module Microsoft.PowerShell.SecretManagement is required as well as a vault named ''VenafiPS''. See the github readme for guidance, https://github.com/Venafi/VenafiPS#tokenkey-secret-storage.'
                }

                $vault = Get-SecretVault -Name 'VenafiPS' -ErrorAction SilentlyContinue
                if ( -not $vault ) {
                    throw 'A SecretManagement vault named ''VenafiPS'' could not be found'
                }

                $tokenSecret = Get-Secret -Name $VaultAccessTokenName -Vault 'VenafiPS' -ErrorAction SilentlyContinue
                if ( -not $tokenSecret ) {
                    throw "'$VaultAccessTokenName' secret not found in vault VenafiPS."
                }

                # check if metadata was stored
                $secretInfo = Get-SecretInfo -Name $VaultAccessTokenName -Vault 'VenafiPS' -ErrorAction SilentlyContinue

                if ( $secretInfo.Metadata.Count -gt 0 ) {
                    $params.Server = $secretInfo.Metadata.AuthServer
                }
                else {
                    if ( -not $AuthServer ) {
                        throw '-AuthServer is a required parameter'
                    }

                    $params.Server = $serverUrl
                }
                $params.Header = @{'Authorization' = 'Bearer {0}' -f $tokenSecret.GetNetworkCredential().password }
            }

            'VenafiPsToken' {
                if ( -not $VenafiPsToken.Server -or -not $VenafiPsToken.AccessToken ) {
                    throw 'Not a valid VenafiPsToken'
                }

                $params.Server = $VenafiPsToken.Server
                $params.Header = @{'Authorization' = 'Bearer {0}' -f $VenafiPsToken.AccessToken.GetNetworkCredential().password }
            }

            Default {
                throw ('Unknown parameter set {0}' -f $PSCmdlet.ParameterSetName)
            }
        }

        Write-Verbose ($params | Out-String)

        $response = Invoke-VenafiRestMethod @params -FullResponse

        if ( $GrantDetail ) {

            switch ( $response.StatusCode ) {

                200 {
                    $responseData = $response.Content | ConvertFrom-Json
                    [PSCustomObject] @{
                        ClientId       = $responseData.application
                        AccessIssued   = ([datetime] '1970-01-01 00:00:00').AddSeconds($responseData.access_issued_on_unix_time)
                        GrantIssued    = ([datetime] '1970-01-01 00:00:00').AddSeconds($responseData.grant_issued_on_unix_time)
                        Scope          = $responseData.scope
                        Identity       = $responseData.identity
                        RefreshExpires = ([datetime] '1970-01-01 00:00:00').AddSeconds($responseData.expires_unix_time)
                    }
                }

                Default {
                    throw ('Grant has been revoked, has expired, or the refresh token is invalid')
                }
            }

        }
        else {

            switch ( $response.StatusCode ) {
                200 {
                    $true
                }

                401 {
                    $false
                }

                Default {
                    throw ('Grant has been revoked, has expired, or the refresh token is invalid')
                }
            }
        }
    }
}
#EndRegion './Public/Test-VdcToken.ps1' 270
#Region './Public/Write-VdcLog.ps1' -1

function Write-VdcLog {
    <#
    .SYNOPSIS
    Write entries to the TLSPDC log

    .DESCRIPTION
    Write entries to the log for custom event groups.
    It is not permitted to write to the default log.
    Ensure the group and event id are correct as the api will not fail if incorrect.

    .PARAMETER CustomEventGroup
    ID containing hex values between 0100-0299 referring to the created custom group.

    .PARAMETER EventId
    Event ID from within the EventGroup provided, a 4 character hex value.
    Only provide the 4 character event id, do not precede with group ID.

    .PARAMETER Component
    Path to the item this event is associated with

    .PARAMETER Severity
    Severity of the event

    .PARAMETER SourceIp
    The IP that originated the event

    .PARAMETER ComponentID
    Component ID that originated the event

    .PARAMETER ComponentSubsystem
    Component subsystem that originated the event

    .PARAMETER Text1
    String data to write to log. See link for event ID messages for more info.

    .PARAMETER Text2
    String data to write to log. See link for event ID messages for more info.

    .PARAMETER Value1
    Integer data to write to log. See link for event ID messages for more info.

    .PARAMETER Value2
    Integer data to write to log. See link for event ID messages for more info.

    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can also be provided.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.

    .INPUTS
    none

    .OUTPUTS
    none

    .EXAMPLE
    Write-VdcLog -CustomEventGroup '0200' -EventId '0001' -Component '\ved\policy\mycert.com'
    Log an event to a custom group

    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Write-VdcLog/

    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Write-VdcLog.ps1

    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Log.php

    #>


    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'DefaultGroup')]
    [Alias('Write-TppLog')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'DefaultGroup')]
        [string] $EventGroup,

        [Parameter(Mandatory, ParameterSetName = 'CustomGroup')]
        [ValidateScript({
            if ( $_ -match '^0[1-2]\d\d$' ) {
                $true
            } else {
                throw "$_ is an invalid group. Custom event groups must be betwen 0100-0299."
            }
        })]
        [string] $CustomEventGroup,

        [Parameter(Mandatory)]
        [ValidateScript({
            if ( $_ -match '^[0-9a-fA-F]{4}$' ) {
                $true
            } else {
                throw "$_ is an invalid event ID. Event IDs must be a 4 character hex value."
            }
        })]
        [string] $EventId,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String] $Component,

        [Parameter()]
        [TppEventSeverity] $Severity,

        [Parameter()]
        [ipaddress] $SourceIp,

        [Parameter()]
        [int] $ComponentID,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $ComponentSubsystem,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Text1,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Text2,

        [Parameter()]
        [int] $Value1,

        [Parameter()]
        [int] $Value2,

        [Parameter()]
        [psobject] $VenafiSession
    )

    if ( $PSCmdlet.ParameterSetName -eq 'DefaultGroup' ) {
        throw 'Writing to built-in event groups is no longer supported by Venafi. You can write to custom event groups. -EventGroup will be deprecated in a future release.'
    }

    Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

    # the event id is the group id coupled with the event id
    $fullEventId = "$CustomEventGroup$EventId"

    # convert the hex based eventid to decimal equivalent
    $decEventId = [System.Convert]::ToInt64($fullEventId, 16)

    $params = @{

        Method     = 'Post'
        UriLeaf    = 'Log/'
        Body       = @{
            ID        = $decEventId
            Component = $Component
        }
    }

    if ( $PSBoundParameters.ContainsKey('Severity') ) {
        $params.Body.Add('Severity', [TppEventSeverity]::$Severity)
    }

    if ( $PSBoundParameters.ContainsKey('SourceIp') ) {
        $params.Body.Add('SourceIp', $SourceIp.ToString())
    }

    if ( $PSBoundParameters.ContainsKey('ComponentID') ) {
        $params.Body.Add('ComponentID', $ComponentID)
    }

    if ( $PSBoundParameters.ContainsKey('ComponentSubsystem') ) {
        $params.Body.Add('ComponentSubsystem', $ComponentSubsystem)
    }

    if ( $PSBoundParameters.ContainsKey('Text1') ) {
        $params.Body.Add('Text1', $Text1)
    }

    if ( $PSBoundParameters.ContainsKey('Text2') ) {
        $params.Body.Add('Text2', $Text2)
    }

    if ( $PSBoundParameters.ContainsKey('Value1') ) {
        $params.Body.Add('Value1', $Value1)
    }

    if ( $PSBoundParameters.ContainsKey('Value2') ) {
        $params.Body.Add('Value2', $Value2)
    }

    if ( $PSCmdlet.ShouldProcess($Component, 'Write log entry') ) {

        $response = Invoke-VenafiRestMethod @params

        if ( $response.LogResult -eq 1 ) {
            throw "Writing to the TLSPDC log failed. Ensure you have View permission and Read permission to the default SQL channel object."
        }
    }
}
#EndRegion './Public/Write-VdcLog.ps1' 198
#Region './VenafiPS.psm1' -1

# Force TLS 1.2 if currently set lower
if ([Net.ServicePointManager]::SecurityProtocol.value__ -lt 3072) {
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
}

# the new version will be replaced below during deployment
$script:ModuleVersion = '6.6.1'

$script:VcRegions = @{
    'us' = 'https://api.venafi.cloud'
    'eu' = 'https://api.venafi.eu'
}
$Script:VenafiSession = $null
$script:ThreadJobAvailable = ($null -ne (Get-Module -Name Microsoft.PowerShell.ThreadJob -ListAvailable))
$script:DevMode = $script:ModuleVersion -match 'NEW_VERSION'
$script:ParallelImportPath = $PSCommandPath

Export-ModuleMember -Alias * -Variable VenafiSession -Function *

# vaas fields to ensure the values are upper case
$script:vaasValuesToUpper = 'certificateStatus', 'signatureAlgorithm', 'signatureHashAlgorithm', 'encryptionType', 'versionType', 'certificateSource', 'deploymentStatus'
# vaas fields proper case
$script:vaasFields = @(
    'certificateId',
    'applicationIds',
    'companyId',
    'managedCertificateId',
    'fingerprint',
    'certificateName',
    'issuerCertificateIds',
    'certificateStatus',
    'statusModificationUserId',
    'modificationDate',
    'statusModificationDate',
    'validityStart',
    'validityEnd',
    'selfSigned',
    'signatureAlgorithm',
    'signatureHashAlgorithm',
    'encryptionType',
    'keyCurve',
    'subjectKeyIdentifierHash',
    'authorityKeyIdentifierHash',
    'serialNumber',
    'subjectDN',
    'subjectCN',
    'subjectO',
    'subjectST',
    'subjectC',
    'subjectAlternativeNamesByType',
    'subjectAlternativeNameDns',
    'issuerDN',
    'issuerCN',
    'issuerST',
    'issuerL',
    'issuerC',
    'keyUsage',
    'extendedKeyUsage',
    'ocspNoCheck',
    'versionType',
    'activityDate',
    'activityType',
    'activityName',
    'criticality'
)

$vcGenericArgCompleterSb = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $objectType = $parameterName
    if ( $parameterName -eq 'ID' ) {
        # figure out object type based on function name since 'ID' is used in many functions

    }

    switch ($objectType) {
        'Application' {
            if ( -not $script:vcApplication ) {
                $script:vcApplication = Get-VcApplication -All | Sort-Object -Property name
            }
            $script:vcApplication | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { "'$($_.name)'" }
        }

        'MachineType' {
            if ( -not $script:vcMachineType ) {
                $script:vcMachineType = Invoke-VenafiRestMethod -UriLeaf 'plugins?pluginType=MACHINE' |
                Select-Object -ExpandProperty machineTypes |
                Select-Object -Property @{'n' = 'machineTypeId'; 'e' = { $_.Id } }, * -ExcludeProperty id |
                Sort-Object -Property machineType
            }
            $script:vcMachineType | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { "'$($_.machineType)'" }
        }

        'IssuingTemplate' {
            if ( -not $script:vcIssuingTemplate ) {
                $script:vcIssuingTemplate = Get-VcIssuingTemplate -All | Sort-Object -Property name
            }
            $script:vcIssuingTemplate | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { "'$($_.name)'" }
        }

        'VSatellite' {
            if ( -not $script:vcVSatellite ) {
                $script:vcVSatellite = Get-VcSatellite -All | Sort-Object -Property name
            }
            $script:vcVSatellite | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { "'$($_.name)'" }
        }

        'Certificate' {
            # there might be a ton of certs so ensure they provide at least 3 characters
            if ( $wordToComplete.Length -ge 3 ) {
                Find-VcCertificate -Name $wordToComplete | ForEach-Object { "'$($_.certificateName)'" }
            }
        }
    }
}

# 'Application', 'MachineType', 'IssuingTemplate', 'VSatellite', 'CertificateID' | ForEach-Object {
'Application', 'MachineType', 'VSatellite', 'Certificate' | ForEach-Object {
    Register-ArgumentCompleter -CommandName '*-Vc*' -ParameterName $_ -ScriptBlock $vcGenericArgCompleterSb
}

$vdcPathArgCompleterSb = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    if ( -not $wordToComplete ) {
        # if no word provided, default to \ved\policy
        $wordToComplete = '\VED\Policy\'
    }

    # if the path starts with ' or ", that will come along for the ride so ensure we trim that first
    $fullWord = $wordToComplete.Trim("`"'") | ConvertTo-VdcFullPath
    $leaf = $fullWord.Split('\')[-1]
    $parent = $fullWord.Substring(0, $fullWord.LastIndexOf("\$leaf"))

    # get items in parent folder
    $objs = Find-VdcObject -Path $parent
    $objs | Where-Object { $_.name -like "$leaf*" } | ForEach-Object {
        if ( $_.TypeName -eq 'Policy' ) {
            "'$($_.Path)\"
        }
        else {
            "'$($_.Path)"
        }
    }
}
Register-ArgumentCompleter -CommandName '*-Vdc*' -ParameterName 'Path' -ScriptBlock $vdcPathArgCompleterSb

$script:functionConfig = @{
    'Add-VdcAdaptableHash'             = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'restricted=manage,delete'
    }
    'Add-VdcCertificateAssociation'    = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'certificate=manage'
    }
    'Add-VdcEngineFolder'              = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'Add-VdcTeamMember'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'Add-VdcTeamOwner'                 = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'Convert-VdcObject'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'ConvertTo-VdcGuid'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'ConvertTo-VdcPath'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'Export-VdcCertificate'            = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'certificate=manage'
    }
    'Find-VdcClient'                   = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'agent=$null'
    }
    'Find-VdcEngine'                   = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=$null'
    }
    'Find-VdcIdentity'                 = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=$null'
    }
    'Find-VdcObject'                   = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=$null'
    }
    'Find-VdcVaultId'                  = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'restricted=$null'
    }
    'Find-VcObject'                    = @{
        'TppVersion'    = ''
        'TppTokenScope' = ''
    }
    'Find-VdcCertificate'              = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'certificate=$null'
    }
    'Get-VdcAttribute'                 = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=$null'
    }
    'Get-VdcClassAttribute'            = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'Get-VdcCredential'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'security=manage'
    }
    'Get-VdcCustomField'               = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'Get-VdcEngineFolder'              = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'Get-VdcIdentityAttribute'         = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=$null'
    }
    'Get-VdcObject'                    = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'Get-VdcPermission'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'security=$null'
    }
    'Get-VdcSystemStatus'              = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'Get-VdcVersion'                   = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'Get-VdcWorkflowTicket'            = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'Get-VdcCertificate'               = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'certificate=$null'
    }
    'Get-VdcIdentity'                  = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=$null'
    }
    'Get-VdcTeam'                      = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'Import-VdcCertificate'            = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'certificate=discover'
    }
    'Import-VcCertificate'             = @{
        'TppVersion'    = ''
        'TppTokenScope' = ''
    }
    'Invoke-VdcCertificateAction'      = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'certificate=manage for Reset, Renew, Push, and Validate. certificate=revoke for Revoke. certificate=delete for Delete.'
    }
    'Move-VdcObject'                   = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'New-VdcCapiApplication'           = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'New-VdcCertificate'               = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'certificate=manage'
    }
    'New-VdcCustomField'               = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'New-VdcDevice'                    = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'New-VdcObject'                    = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage. If a certificate is provided as an attribute, certificate=manage as well.'
    }
    'New-VdcPolicy'                    = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'New-VdcToken'                     = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'New-VcCertificate'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = ''
    }
    'New-VcConnector'                  = @{
        'TppVersion'    = ''
        'TppTokenScope' = ''
    }
    'New-VenafiSession'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = ''
    }
    'New-VenafiTeam'                   = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'Read-VenafiLog'                   = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'Remove-VdcCertificate'            = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'certificate=delete. If using KeepAssociatedApps, configuration=$null,certificate=manage as well.'
    }
    'Remove-VdcCertificateAssociation' = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'certificate=manage. If using -All, configuration=$null as well.'
    }
    'Remove-VdcClient'                 = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'agent=delete'
    }
    'Remove-VdcEngineFolder'           = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=delete'
    }
    'Remove-VdcObject'                 = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=delete'
    }
    'Remove-VdcPermission'             = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'security=delete'
    }
    'Remove-VenafiTeam'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=delete'
    }
    'Remove-VenafiTeamMember'          = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'Remove-VenafiTeamOwner'           = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'Rename-VdcObject'                 = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'Revoke-VdcCertificate'            = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'certificate=revoke'
    }
    'Revoke-VdcGrant'                  = @{
        'TppVersion'    = '22.3'
        'TppTokenScope' = 'admin=delete'
    }
    'Revoke-VdcToken'                  = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'Search-VdcHistory'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'restricted=$null, certificate=$null'
    }
    'Set-VdcAttribute'                 = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=manage'
    }
    'Set-VdcCredential'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'security=manage'
    }
    'Set-VdcPermission'                = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'security=manage'
    }
    'Set-VdcWorkflowTicketStatus'      = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'approve with any scope'
    }
    'Test-VdcIdentity'                 = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=$null'
    }
    'Test-VdcObject'                   = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'configuration=$null'
    }
    'Test-VdcToken'                    = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
    'Write-VdcLog'                     = @{
        'TppVersion'    = ''
        'TppTokenScope' = 'any scope'
    }
}

# ModuleVersion will get updated during the build and this will not run
# this is only needed during development since all files will be merged into one psm1
if ( $script:DevMode ) {
    $folders = @('Enum', 'Classes', 'Public', 'Private')
    $publicFunction = @()

    foreach ( $folder in $folders) {

        $files = Get-ChildItem -Path "$PSScriptRoot\$folder\*.ps1" -Recurse

        Foreach ( $thisFile in $files ) {
            Try {
                Write-Verbose ('dot sourcing {0}' -f $thisFile.FullName)
                . $thisFile.fullname
                # if ( $folder -eq 'Public' ) {
                Export-ModuleMember -Function $thisFile.Basename
                $publicFunction += $thisFile.BaseName
                # }
            }
            Catch {
                Write-Error ("Failed to import function {0}: {1}" -f $thisFile.fullname, $folder)
            }
        }
    }
}


#EndRegion './VenafiPS.psm1' 450

# SIG # Begin signature block
# MIIhhAYJKoZIhvcNAQcCoIIhdTCCIXECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAceVpSNJ82GXFT
# TXrz8rBv7cZXJo6IRJi30mYyLjCIDqCCGoMwggd8MIIFZKADAgECAhAD8JaetbYh
# gELYx9Duvn5RMA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQK
# Ew5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBD
# b2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjQwODIxMDAw
# MDAwWhcNMjUwOTEyMjM1OTU5WjCBgzELMAkGA1UEBhMCVVMxDTALBgNVBAgTBFV0
# YWgxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MRUwEwYDVQQKEwxWZW5hZmksIElu
# Yy4xHjAcBgNVBAsTFVByb2Zlc3Npb25hbCBTZXJ2aWNlczEVMBMGA1UEAxMMVmVu
# YWZpLCBJbmMuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxungR693
# DIQWU/77slVOdl/qc5leazfxKxl7tkKdNFwCDFrDOAn4/5lIKCl/DZc9RYL5F7Kd
# sdw9Qgi96JWgw0Z29FWcyxYW4xX7IHHhlNzrt/Qc2u+DDk7IZGrMi+OQcGNYxvvG
# HkgZ640nTkX/rPcsctOiYMg57vXUjtYS+u5RfhsJCw3cqCWynTzxKxoQxsTR4NK8
# CW4B/6JYh2V2ni17Bbve074PVKY4Hv+Zmc3F5OHrnfEo51Rv/UcrEdoVvq+Qz5tr
# 89TAd8/QenZ+oomRXG0EmL5UdRn+1Csdk8xoBhHBhgTRmtH+dYwyBSFdWrJ5yTqC
# r198PCe1s4r4eVhrWQdlmI8IWf1uQABq38e/jtLYNJFauurelEN6Fp1bItXuL/Ne
# ncPjOQgmxiufVZ+BYjLYSB4N7vudToDV69B3h8qtVyq0TdAEvyYNcla36li3Ob5K
# qrop+duc37BmHWasgcDXx+Vo6+SyfSaZVNgvw64ihxoVZr/BCkS57zvE/NBnn5rL
# WoVtFWd7tDaoElwCjVbJFNK6RoWCN11aKJKNvUNycb5bR8e3FiuJFZ8rm/4WJvx0
# GNBD53FxKT3fZcZmClb43+C4+s03a6p05X+EIBNDsAlfS44yFy+7t7u8eyM7vmDV
# aVPacOo6yYUPTPxQBENoQeGsecQlWuUQPgcCAwEAAaOCAgMwggH/MB8GA1UdIwQY
# MBaAFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1UdDgQWBBTTo4VJ8oyO4K/8Z6vN
# vRU44blkbjA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRw
# Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM
# MAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI
# QTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20v
# RGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0Ex
# LmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3Nw
# LmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNl
# cnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0
# MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAgEAk5iA6e3z
# vlKHWEzVa8GVTb0XfMnSj6UcB8WbKmjFR0FlI33Vy8LvoCyX4TamxGJxmbR/BT6V
# lwPMI4UTQCUS4b3N+yGDUBmqqCLqN0W0OJbun6Y0UscEcWr8RsAY0lp3SFjcUa1c
# ixLsSI/9cQTV+6bIJUScvlXEOYIenjiIO/RTX9y+8/lfgVBYS69bjDUzU/WxcZ3R
# vGZ5sPVko6al7frjz0uL/F/aCbCw2N0eO/sqI4IfWQZ3CVromQjBEzogj7K2OuB3
# V2Gw3IfSvQobmuTtveOhwp/ma+4V46YYx0mQKrDS0jniMGYstoJeFGMhvr9oac0Q
# +wO/PmHBp93m9SfoiUTmKYJMIu9+Jd+8rF6m2/SqRESk1HKD+RgGURt6B38K6dcf
# URJXY/r/wgnH0Xb2yMorFOKGkyX2D5hMs1pzgvL3F3B2/SmH2RORDA45UfOCtWQE
# 6yYnGjZmabCZl4YFWZsoo9pZXXK3b5narfokdh3AEH2RpIEBtWuZYPgqf8X2a5rt
# QrskHMEhY3WHZTZNnncsXSe1cAbPlnDge+byzWYyVc1PtiWE9krTdyJ+TKaTeJTU
# NW73unKsU5JMwA77ASchFEfPtLBLjKJh44vFzYuRJGIXoUK2bdPk3JLmvApx6Ty5
# PP8HdjkQzchgpQHVVQe3KAR1ppN42x/9ESEwggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqG
# SIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMx
# CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMy
# RGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcg
# Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXH
# JQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMf
# UBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w
# 1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRk
# tFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYb
# qMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUm
# cJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP6
# 5x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzK
# QtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo
# 80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjB
# Jgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXche
# MBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB
# /wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU
# 7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoG
# CCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29j
# c3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDig
# NqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v
# dEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZI
# hvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd
# 4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiC
# qBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl
# /Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeC
# RK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYT
# gAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/
# a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37
# xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmL
# NriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0
# YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJ
# RyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIG
# vDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMzBDANBgkqhkiG9w0BAQsFADBjMQsw
# CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRp
# Z2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENB
# MB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIzNTk1OVowQjELMAkGA1UEBhMCVVMx
# ETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAg
# MjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL5qc5/2lSGrljC6
# W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4IxHRGd7+L660x5XltSVhhK64zi9Ce
# C9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO7o5tLuslxdr9Qq82aKcpA9O//X6Q
# E+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bpLEx7pZ7avVnpUVmPvkxT8c2a2yC0
# WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8hIOYe4jl7/r419CvEYVIrH6sN00y
# x49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5WHhHVO+NBikDO0mlUh902wS/Eeh8
# F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSupWJNstVkiqLq+ISTdEjJKGjVfIcsg
# A4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5XiPVdsn5n10jxmGpxoMc6iPkoaDh
# i6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBqU0R4k+8h6gYldp4FCMgrXdKWfM4N
# 0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd1aepSeNeREXAu2xUDEW8aqzFQDYm
# r9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1rxvbKmLqfY/M/SdV6mwWTyeVy5Z/
# JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMC
# B4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAE
# GTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3Mp
# dpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3DigVkGalY17uT5IfdqBbMFoGA1Ud
# HwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
# dXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUF
# BwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20w
# WAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZI
# hvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lDkfYR25tOCB3RKE/P09x7gUsmXqt4
# 0ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0HvqT00nFSXgmUrDKNSQqGTdpjHsPy+
# LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzUy34VarPnvIWrqVogK0qM8gJhh/+q
# DEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJKlTnCVaM2UeUUW/8z3fvjxhN6hdT
# 98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXtgVQxiBlTVYzqfLDbe9PpBKDBfk+r
# abTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJmLbJ6ZbQ/xll/HjO9JbNVekBv2Tg
# em+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7cIqV0yef4uaZFORNekUgQHTqddms
# PCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagLDBzpmk9104WQzYuVNsxyoVLObhx3
# RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7MhKRyrBe7ucykW7eaCuWBsBb4HOKR
# FVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoHNHT9l3ZDBD+XgbF+23/zBjeCtxz+
# dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceENc2Sg8h3KeFUCS7tpFk7CrDqkMYIG
# VzCCBlMCAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu
# Yy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJT
# QTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAD8JaetbYhgELYx9Duvn5RMA0GCWCGSAFl
# AwQCAQUAoIGIMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCSqGSIb3DQEJ
# BTEPFw0yNDExMjgxNTI2NTZaMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEV
# MC8GCSqGSIb3DQEJBDEiBCAO8ALA69ftSuhbDiFZ81T7e4cvhxU+IfIix1ozDJKP
# ajANBgkqhkiG9w0BAQEFAASCAgBXyTGIa3+SEAoXfEmxIBMQNI+JCAJxTFWgSs3a
# CwXPSFyt7JKeBmR7uasGisJUAcZ57ng9IFOEPROvbtA3TAOBZHn7Al8neyjdJOD+
# I7C4kaeHFoYWgUTdDZBzNeODBZpEI2PXAqbmHPHHKOMBPBwNaQ7QChKwhkzsZXP6
# oIMDcReDA5dP1hBqujRUiZ/Bqcqk4i0ml65jkpM65q17o/V+kY5CoV+2Ne6hvHgV
# vQP4159DuchkH3ugBk9ksx7eWKb6C2f0n5c7dWKbdK+NwM+BcZRE3jGrLcL4XYhc
# oCK61I7kFlz/8gkBO6XhmnVrbKopHlC3W2uLMPixzFZ95WJo/Ol/Ye2HWGonqTic
# kYDenwhDLb6wGlPlR1/FxTdc5K/17rB8Y6WmXxEVNrlbM/dTELHkITT7d3u7rkmJ
# L/qdhjnQlm/Dy5SA0UGUFlr+iLIQQYrMTNxK/VXdbEEBesMpD9luTqtXDi45ggWg
# rgpQH2t7uSYOHZuRnHgc4AXcuOi+n0Mwm5KlYURWjTrX8NTO/CuqnhiQrImuur3i
# yXjr21mZLbl/S3S6pOo1Pv3L1cVrB803QqaZG46l7KsL5e/6kYqXYtAN4WthMpp3
# 50Z36kkto55q6+iobjXFr48mnve2vDuLN4idO1IOmHdLcdSKTGd8VtdM7pAiIvCX
# L43xpKGCAyAwggMcBgkqhkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYT
# AlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQg
# VHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAuuZrxa
# un+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZI
# hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNDExMjgxNTI2NTZaMC8GCSqGSIb3DQEJ
# BDEiBCAAvqoy+v6xqlJtUxO1jtlOzlyTOXRpUXSycvxDTmwVuzANBgkqhkiG9w0B
# AQEFAASCAgCCy/1/JrXD5VtpDxX4rKyis1BgpFoA/+qU77hRJZ8Et0czLIk29rre
# vkpExW4m3pkFC8bgRVsOJrGhrINe4P0Xqu6sgeMqc1GOjSYXpIlDqDZp4DxbGLFD
# uFM747iejP+odtygzNHRDL0+zLxrqomcKedhBKYpGzaZArd4DWMuPI270GrCFVaz
# PThw2I1gShmIzEG0IeA6LbouP1CndDnRRCwv2WYelYzzraQIui4Yn3r+PWDsOqdY
# xXomRgH93BUbkIhmoKdgp6063rfpF1cuOmdTWRzQemTSmwSjIP2xwkUaFIFJVega
# ojewdkmlcYZSOg/t/npNDubX7mODsU/QLfHD5Og/1pq4ha39CulBGgW1Zm2x9oFZ
# S3uoW47rD9HANwIIkmeiBpL56yEYfx/lygrj4tZJ2UUkt4XomWO38lNpBm5Gxmeh
# rFeCOiaAhK/J9sz+1ArjDJv6pop0NiPhUCp5SpSU7F2oxvsBZupPwutlRWzxe+ow
# HaBOzedoZfuphwmIYF7QSPJhY4W4hZvIvJ9Ljrk14ixWsdJAraKsxd7VgzJIYDFG
# CfuQ9pf8QntanxONHbIDP4QrQ0+3lALnJK/g7k4gUH7zJHDrHfSddtCBa3KB28It
# wqzWWw0TNPJobSqnlSqrA3HISP9SZtQuCTRTajsC8Sz1FAfhth9i1A==
# SIG # End signature block