Access Template Link Reporting

The following can be used to retrieve live reporting on access template links.

<#
.SYNOPSIS
Retrieves Active Roles Access Template Link information.
.DESCRIPTION
The Get-ATLinkInfo.ps1 script allows you to report on Active Roles Access Template Link information based on providing a single property of an access template link's properties (Directory Object, Trustee, Access Template). The script will function only when supplying one of the three link properties. The script will error out if more than one of them are defined. The script provides the option to list the permissions found in the Access Template that is reported on. The script also allows for exporting an HTML report.
.PARAMETER Trustee
This parameter is used to define the Trustee of the Access Template Link. A single trustee can be defined, or a comma-separated list of trustees can be defined.
.PARAMETER AccessTemplate
This parameter is used to define the Access Template of the Access Template Link. A single access template can be defined, or a comma-separated list of access templates can be defined.
.PARAMETER DirectoryObject
This parameters is used to define the Directory Object of the Access Template Link. A single directory object can be defined, or a comma-separated list of director objects can be defined.
.PARAMETER ListPermissions
This parameter switch enables retrieving and listing the permissions contained in the access template(s).
.PARAMETER ExportToHTML
This parameters switch enables the export of HTML report(s).
.PARAMETER ExportPath
This optional parameter defines the export location for the HTML report(s). If not specified, the default location is C:\Temp.
.PARAMETER AdministrationService
This optional parameter defines the Active Roles Administration Service you wish to connect to for the reporting.
.PARAMETER UseClientLocaleDisplaySpecifiers
This opetional parameter instructs the script to use the current client's locale for display specifier checking when permissions are listed.
.EXAMPLE
Get-ATLinkInfo.ps1 -Trustee john.doe
Retrieves the access template links for user john.doe.
.EXAMPLE
Get-ATLinkInfo.ps1 -DirectoryObject domain.local/Users/Canada -ListPermissions
Retrieves the access template links on directory object domain.local/Users/Canada and lists the permissions for each access template.
.EXAMPLE
Get-ATLinkInfo.ps1 -AccessTemplate 'Users - Read All Properties' -ListPermissions -ExportToHTML
Retrieves the access template links for the access template 'Users - Read All Properties', lists the permissions for each access template and exports a single HTML report containing each link found and the permissions for the access template.
.EXAMPLE
Get-ATLinkInfo.ps1 -Trustee john.doe -ListPermissions -ExportToHTML -ExportPath C:\Temp\ATLinkInfo
Retrieves the access template links for user john.doe, lists the permissions and exports an HTML report for each link found to the folder C:\Temp\ATLinkInfo.
.NOTES
It is required that you run this script as an Active Roles Administrator since most of the information being retrieved is only accessible by an Active Roles Administrator.
#>

[CmdletBinding()]
Param(
    [Parameter(Mandatory=$True,ParameterSetName='Trustee')]$Trustee,
    [Parameter(Mandatory=$True,ParameterSetName='AccessTemplate')]$AccessTemplate,
    [Parameter(Mandatory=$True,ParameterSetName='DirectoryObject')]$DirectoryObject,
    [switch]$ListPermissions,
    [switch]$ExportToHTML = $False,
    [string]$ExportPath = "C:\Temp\",
    [string]$AdministrationService,
    [switch]$UseClientLocaleForDisplaySpecifiers = $False
)

$ErrorActionPreference = "continue"

$ExportFileName = ""

if ($AdministrationService){
    Connect-QADService -Service $AdministrationService -Proxy | Out-Null
} else {
    Connect-QADService -Proxy | Out-Null
}

function Get-DisplaySpecifierPath{
    # If -UseClientLocaleForDisplaySpecifiers, the local is calculated and the appropriate display specifiers for that language is used instead of the default EN-US.
    if ($UseClientLocaleForDisplaySpecifiers){
        if (Get-QADObject ('CN=' + ('{0:x}' -f (Get-Culture).LCID) + ',CN=Consolidated Display Specifiers,CN=Application Configuration,CN=Configuration')){
            return ('CN=' + ('{0:x}' -f (Get-Culture).LCID) + ',CN=Consolidated Display Specifiers,CN=Application Configuration,CN=Configuration')
        } else {
            return 'CN=409,CN=Consolidated Display Specifiers,CN=Application Configuration,CN=Configuration'
        }
    } else {
        return 'CN=409,CN=Consolidated Display Specifiers,CN=Application Configuration,CN=Configuration'
    }
}

function Get-ClassDisplayName{
    # Retrieve the object class display name as well as checks to see if there is a Display Specifier for the object class and returns that value if so.
    Param(
        $guid
    )

    $ldapDisp = (Get-QADObject $guid -DontUseDefaultIncludedProperties -IncludedProperties ldapDisplayName).ldapDisplayName

    $DisplaySpec = Get-QADObject ($ldapDisp + '-Display') -SearchRoot (Get-DisplaySpecifierPath) -DontUseDefaultIncludedProperties -IncludedProperties classDisplayName,attributeDisplayNames

    if ($DisplaySpec.classDisplayName){
        return $DisplaySpec.classDisplayName
    } else {
        return $ldapDisp
    }
}

function Get-AttDisplayName{
    # Retrieve the attribute display name as well as checks to see if there is a Display Specifier for the attribute and returns that value if so.
    Param(
        $attGuid,
        $objGuid
    )

    $attClassDisp = Get-ClassDisplayName -guid $attGuid

    if ($objGuid -ne ""){
        $objLdapDisp = (Get-QADObject $objGuid -DontUseDefaultIncludedProperties -IncludedProperties ldapDisplayName).ldapDisplayName
        $DisplaySpec = Get-QADObject ($objLdapDisp + '-Display') -SearchRoot (Get-DisplaySpecifierPath) -DontUseDefaultIncludedProperties -IncludedProperties classDisplayName,attributeDisplayNames

        if ($DisplaySpec.attributeDisplayNames){
            $attDisp = ""
            $DisplaySpec.attributeDisplayNames | ForEach-Object{
                if($_.Split(",")[0] -eq $attClassDisp){
                    $attDisp = $_.Split(",")[1]
                }
            }
            if ($attDisp -eq ""){
                $attDisp = $attClassDisp
            }
        }
    } else {
        $attDisp = $attClassDisp
    }
    return $attDisp
}

function Get-ExtendedRights{
    # Retrive extended rights from the administration service.
    # These are not visible in the admin console.
    Param(
        $guid
    )
    $extRight = (Get-QADObject -SearchRoot 'CN=Extended Rights,CN=Application Configuration,CN=Configuration' -DontUseDefaultIncludedProperties -IncludedProperties rightsguid -LdapFilter "(rightsguid=$guid)").DisplayName.toString()

    return $extRight
}

function List-Permissions{
    # Permissions are stored in an SDDL-type format. The following parses these and returns the actual permissions that are set.
    Param(
        $AT
    )

    $permissions = @()
    $reg = [regex] "\[([^\[]*)\]" # REGEX to pull out SDDL entries without the closing brackets.

    ((Get-QARSAccessTemplate $AT -DontUseDefaultIncludedProperties -IncludedProperties edsvaEffectiveATEList).edsvaEffectiveATEList | Select-String $reg -AllMatches).Matches.Value | ForEach-Object {
        $type = ""
        $permission = ""
        $applyTo = ""

        $permArray = ($_ -replace ".$").SubString(1).Split(";") # Split the SDDL into an array.

        switch ($permArray[0]){
            "A" {$type = "Allow"}
            "D" {$type = "Deny"}
        }

        switch ($permArray[2]){
            "CCDCLCSWRPWPDTLOCRCOSDRCWDWO" {
                $permission = "Full Control"
            }
            "RP" {
                if ($permArray[3] -eq ""){
                    $attName = "All Properties"
                } else {
                    $attName = Get-AttDisplayName -attGuid $permArray[3] -objGuid $permArray[4]
                }
                $permission = "Read $attName"
            }
            "WP" {
                if ($permArray[3] -eq ""){
                    $attName = "All Properties"
                } else {
                    $attName = Get-AttDisplayName -attGuid $permArray[3] -objGuid $permArray[4]
                }
                $permission = "Write $attName"
            }
            "RPWP" {
                if ($permArray[3] -eq ""){
                    $attName = "All Properties"
                } else {
                    $attName = Get-AttDisplayName -attGuid $permArray[3] -objGuid $permArray[4]
                }
                $permission = "Read/Write $attName"
            }
            "CC" {
                if ($permArray[3] -eq ""){
                    $attName = "All Child"
                } else {
                    $attName = Get-AttDisplayName -attGuid $permArray[3] -objGuid $permArray[4]
                }
                $permission = "Create $attName Objects"
            }
            "DC" {
                if ($permArray[3] -eq ""){
                    $attName = "All Child"
                } else {
                    $attName = Get-AttDisplayName -attGuid $permArray[3] -objGuid $permArray[4]
                }
                $permission = "Delete $attName Objects"
            }
            "CCDC" {
                if ($permArray[3] -eq ""){
                    $attName = "All Child"
                } else {
                    $attName = Get-AttDisplayName -attGuid $permArray[3] -objGuid $permArray[4]
                }
                $permission = "Create/Delete $attName Objects"
            }
            "MT" {
                if ($permArray[3] -eq ""){
                    $attName = "All Child"
                } else {
                    $attName = Get-AttDisplayName -attGuid $permArray[3] -objGuid $permArray[4]
                }
                $permission = "Move $attName into this container"
            }
            "SD" {
                $permission = "Delete"
            }
            "DT" {
                $permission = "Delete Tree"
            }
            "RC" {
                $permission = "Read Control"
            }
            "WD" {
                $permission = "Write Control"
            }
            "RCWD" {
                $permission = "Read/Write Control"
            }
            "LC" {
                $permission = "List Contents"
            }
            "LO" {
                $permission = "List"
            }
            "CO" {
                $permission = "Copy"
            }
            "MF" {
                $permission = "Move Out"
            }
            "CR" {
                if ($permArray[3] -eq ""){
                    $permission = "All Extended Rights"
                } else {
                    $permission = (Get-ExtendedRights -guid $permArray[3])
                }
            }
            "SW" {
                if ($permArray[3] -eq ""){
                    $permission = "All Validated Writes"
                } else {
                    $permission = (Get-ExtendedRights -guid $permArray[3])
                }
            }
        }

        if ($permArray[4] -eq ""){
            $applyTo = "All Classes"
        } else {
            $applyTo = Get-ClassDisplayName -guid $permArray[4]
        }

        $permissions += [pscustomobject]@{
            Type = $type
            Permissions = $permission
            ApplyTo = $applyTo
        }
    }

    $permissions | Format-Table

    if ($ExportToHTML){
        '<p style="font-weight:bold;">Permissions Included in Access Template:</p>' | Out-File -FilePath (Join-Path -Path $ExportPath -ChildPath $ExportFileName) -Append
        ExportTo-HTML -inputObject $permissions
    }
}

function Get-ATLinkbyATguid{
    # Get Access Template Links by Access Template GUID.
    Param(
        $atDN,
        $atName
    )

    Write-Output "Retrieving access template links..."

    $results = @()

    Get-QARSAccessTemplateLink -AccessTemplate $atDN -DontUseDefaultIncludedProperties -IncludedProperties edsvaAccessTemplateDN,edsvaSecObjectDN,edsaTrusteeSID -SizeLimit 0 | ForEach-Object{
        $trustee = Get-QADObject $_.edsaTrusteeSID
        $target = Get-QADObject $_.edsvaSecObjectDN

        $results += [pscustomobject]@{
            Trustee = $trustee.DN
            Target = $target.DN
        }
    }

    if ($results){
        Write-Output "The following links were found for the access template '$atName'"
        Write-Output "==============================="

        ReportInfo-Export -ReportBy "template" -Source $atDN

        $results | ForEach-Object{
            Write-Output ("Trustee: " + $_.Trustee)
            Write-Output ("Target Object: " + $_.Target)
            Write-Output ""
        }

        if ($ExportToHTML){
            ExportTo-HTML -inputObject $results -newEntry
        }
    } else {
        ReportInfo-Export -ReportBy "template" -Source $atDN
        Write-Output "The following links were found for the access template '$atName'"
        Write-Output "==============================="
        Write-Output "NONE"
        Write-Output ""
        if ($ExportToHTML){
            ExportTo-HTML -inputObject "NONE" -newEntry
        }
    }

    if ($ListPermissions){
        Write-Output "Permissions Included in Access Template"
        List-Permissions -AT $atName
        Write-Output "==============================="
    }

    if ($ExportToHTML){
        '<div style="height:100px;"> </div>' | Out-File -FilePath (Join-Path -Path $ExportPath -ChildPath $ExportFileName) -Append
    }
}

function Get-ATLinkbyTrustee{
    # Get Access Template Links by Trustee.
    Param(
        $trusteeName
    )

    Write-Output "Retrieving access template links..."

    $results = @()

    $trusteeObject = Get-QADObject $trusteeName -DontUseDefaultIncludedProperties -IncludedProperties objectSid,name
    $tName = $trusteeObject.name

    Get-QARSAccessTemplateLink -Trustee $trusteeObject.dn -DontUseDefaultIncludedProperties -IncludedProperties edsvaAccessTemplateDN,edsvaSecObjectDN,edsaTrusteeSID -SizeLimit 0 | ForEach-Object{
        $template = Get-QARSAccessTemplate $_.edsvaAccessTemplateDN
        $target = Get-QADObject $_.edsvaSecObjectDN

        $results += [pscustomobject]@{
            Template = $template.DN
            Target = $target.DN
        }
    }


    if ($results){
        Write-Output "The following links were found for the trustee: '$tName'"
        Write-Output "==============================="

        ReportInfo-Export -ReportBy "trustee" -Source $trusteeObject.dn

        $results | ForEach-Object{
            Write-Output ("Access Template: " + $_.Template)
            Write-Output ("Target Object: " + $_.Target)
            Write-Output ""

            if ($ExportToHTML){
                ExportTo-HTML -inputObject $_ -newEntry
            }

            if ($ListPermissions){
                Write-Output "Permissions Included in Access Template"
                List-Permissions -AT $_.Template
                Write-Output "==============================="
            }
        }
    } else {
        ReportInfo-Export -ReportBy "trustee" -Source $trusteeObject.dn
        Write-Output "The following links were found for the trustee '$atName'"
        Write-Output "==============================="
        Write-Output "NONE"
        Write-Output ""
        if ($ExportToHTML){
            ExportTo-HTML -inputObject "NONE" -newEntry
        }
    }
}

function Get-ATLinkbyDirectoryObject{
    # Get Access Template Links by Directory Object.
    Param(
        $DirectoryObject
    )

    Write-Output "Retrieving access template links..."

    $results = @()

    Get-QARSAccessTemplateLink -DirectoryObject $DirectoryObject -DontUseDefaultIncludedProperties -IncludedProperties edsvaAccessTemplateDN,edsvaSecObjectDN,edsaTrusteeSID -SizeLimit 0 | ForEach-Object{
        $template = Get-QARSAccessTemplate $_.edsvaAccessTemplateDN
        $trustee = Get-QADObject $_.edsaTrusteeSID

        $results += [pscustomobject]@{
            Template = $template.DN
            Trustee = $trustee.DN
        }
    }

    if ($results){
        Write-Output "The following links were found for the directory object: '$DirectoryObject'"
        Write-Output "==============================="

        ReportInfo-Export -ReportBy "directoryobject" -Source $DirectoryObject.dn

        $results | ForEach-Object{
            Write-Output ("Access Template: " + $_.Template)
            Write-Output ("Target Object: " + $_.Trustee)
            Write-Output ""

            if ($ExportToHTML){
                ExportTo-HTML -inputObject $_ -newEntry
            }

            if ($ListPermissions){
                Write-Output "Permissions Included in Access Template"
                List-Permissions -AT $_.Template
                Write-Output "==============================="
            }
        }
    } else {
        ReportInfo-Export -ReportBy "directoryobject" -Source $DirectoryObject.dn
        Write-Output "The following links were found for the directory object '$atName'"
        Write-Output "==============================="
        Write-Output "NONE"
        Write-Output ""
        if ($ExportToHTML){
            ExportTo-HTML -inputObject "NONE" -newEntry
        }
    }
}

function SetExport-Filename{
    # Sets unique filename based on the date and time of run.
    $Script:ExportFileName = ("ATLinkInfo_" + (Get-Date -Format "yyyy-MM-dd_hh.mm.ss.fff") + ".html")
}

function Pre-Export{
    # Export the beginning of the HTML including CSS.
    if ($ExportToHTML){
        $preExport = '<html>
        <head>
            <title>ATLinkInfo Export</title>
            <style>
                body{
                    font-family: arial;
                }

                th, td {
                    text-align: left;
                    padding: 15px;
                    border-bottom: 1px solid #ddd;
                    border-right: 1px solid #ddd;
                }

                h1, p {
                    text-align:;
                }

                hr {
                    display: block;
                    margin-top: 0.5em;
                    margin-bottom: 0.5em;
                    margin-left: auto;
                    margin-right: auto;
                    border: 0px;
                    border-bottom: 1px solid #000;
                }

                table {
                    border-spacing: 10px;
                }

                .source {
                    font-weight: bold;
                }
            </style>
        </head>
        <body>
        <h1 style="text-align:center;">Access Template Link Report</h1>
        <p> </p>
        '

        $preExport | Out-File -FilePath (Join-Path -Path $ExportPath -ChildPath $ExportFileName)
    }
}

function Post-Export{
    # Export the end of the HTML.
    if ($ExportToHTML){
        $postExport = '</body>
        </html>'

        $postExport | Out-File -FilePath (Join-Path -Path $ExportPath -ChildPath $ExportFileName) -Append
        Write-Host ("Report exported to: " + (Join-Path -Path $ExportPath -ChildPath $ExportFileName))
        Set-Clipboard -Value (Join-Path -Path $ExportPath -ChildPath $ExportFileName)
        Write-Host ("The full path has been copied to the clipboard.")
    }
}

function ReportInfo-Export{
    # Export the report summary information as HTML.
    Param(
        $ReportBy,
        $Source
    )

    # Report summary changes based on the type of input object used.
    if ($ExportToHTML){
        $ReportInfo = "<p>The following Access Template Link information was found for "

        switch($ReportBy){
            "trustee" {
                $ReportInfo += "Trustee: <span class=source>$Source</span>"
            }

            "directoryobject" {
                $ReportInfo += "Directory Object: <span class=source>$Source</span>"
            }

            "template" {
                $ReportInfo += "Access Template: <span class=source>$Source</span>"
            }
        }

        $ReportInfo += "</p>"

        $ReportInfo | Out-File -FilePath (Join-Path -Path $ExportPath -ChildPath $ExportFileName) -Append
    }
}

function ExportTo-HTML{
    # Export the input information as HTML.
    Param(
        $inputObject,
        [switch]$newEntry
    )

    if ($newEntry){
    "<hr />" | Out-File -FilePath (Join-Path -Path $ExportPath -ChildPath $ExportFileName) -Append
    }

    if ($inputObject -eq "NONE"){
        $inputObject | Out-File -FilePath (Join-Path -Path $ExportPath -ChildPath $ExportFileName) -Append
    } else {
        $inputObject | ConvertTo-Html -Fragment | Out-File -FilePath (Join-Path -Path $ExportPath -ChildPath $ExportFileName) -Append
    }
}

function Begin-Processing{
    # Check parameters and begin processing.
    if ($ExportPath){
        if (-not (Test-Path -Path $ExportPath)){
            New-Item -Path $ExportPath -ItemType directory | Out-Null
        }
    }


    if ($DirectoryObject){
        SetExport-Filename
        Pre-Export
        foreach ($input in $DirectoryObject){
            $targetObject = Get-QADObject $input -DontUseDefaultIncludedProperties -ShowProgress -SizeLimit 0

            if ($null -eq $targetObject){
                Write-Error "Directory Object '$input' does not exist or you don't have permission to view it. Make sure to run as an Active Roles Administrator."
            } else {
                foreach ($object in $targetObject){
                    Get-ATLinkbyDirectoryObject -DirectoryObject $object
                    Write-Host "`n`n`n"
                }
            }
        }
        Post-Export
    }


    if ($AccessTemplate){
        SetExport-Filename
        Pre-Export
        foreach ($input in $AccessTemplate){
            $ATObject = Get-QARSAccessTemplate $input -DontUseDefaultIncludedProperties -IncludedProperties objectGUID -ShowProgress -SizeLimit 0

            if ($null -eq $ATObject){
                Write-Error "Access template '$input' does not exist or you don't have permission to view it. Make sure to run as an Active Roles Administrator."
            } else {
                foreach ($object in $ATObject){
                    Get-ATLinkbyATguid -atDN $Object.DN -atName $Object.Name
                    Write-Host "`n`n`n"
                }
            }
        }
        Post-Export
    }

    if ($Trustee){
        SetExport-Filename
        Pre-Export
        foreach ($input in $Trustee){
            $trusteeObject = Get-QADObject $input -DontUseDefaultIncludedProperties -ShowProgress -SizeLimit 0

            if ($null -eq $trusteeObject){
                Write-Error "Trustee '$input' does not exist or you don't have permission to view it. Make sure to run as an Active Roles Administrator."
            } else {
                foreach ($object in $trusteeObject){
                    Get-ATLinkbyTrustee -trusteeName $object
                    Write-Host "`n`n`n"
                }
            }
        }
        Post-Export
    }

    # Disconnect from the Administration Service.
    Disconnect-QADService | Out-Null
}

Begin-Processing

  • Hi Nick,

    Thanks for your creating this script and for sharing it with the community! I've spent quite some time in the past collecting screenshots and typing out AT access rules so am very happy there's finally an easier and more consistent approach!

    For me the script runs fine in our v7.3.1 test environment but in my prod v6.9 setup, I get this error:

    Get-QARSAccessTemplateLink : Cannot resolve SID for the given identity:
    '0105000000000005150000004EF45C86xxxxxxxxxxxxxxxxxxx'

    Can this be correct? Is the script intended for v7.x only? Or can I still workaround that SID-resolve error?

    PS: We're already planning an upgrade to move away from v6 but it's taking some time.. Hence why we're still on the old version.

    Thanks!

  • Apologies that I didn't see this reply when you submitted it. I've updated the post with the help of my colleague letting me know. 

  • Hi, in fact I have the same issue, the scripts works well on 7.4 but on 6.9 the trustee part stays empty. I didn't find how to resolve it...