ADReportingTools.psm1


# dot source functions
Get-ChildItem -path $PSScriptRoot\Functions\*.ps1 |
Foreach-Object {
    . $_.FullName
}

#region format and type updates
Update-TypeData -TypeName ADBranchMember -MemberType AliasProperty -MemberName DN -Value DistinguishedName -Force

Update-TypeData -TypeName "ADDomainControllerHealth" -MemberType ScriptProperty -MemberName "ServiceAlert" -Value {
    $list = "Stopped","StartPending","StopPending","ContinuePending","PausePending","Paused"
    if ($this.services.state.where({$list -contains $_})) {
        $True
    }
    Else {
        $False
    }
} -force

#endregion

#region define module variables

$ADUserReportingConfiguration = Get-Content $PSScriptRoot\configurations\aduser-categories.json | ConvertFrom-Json

#use $([char]0x1b) because it will work in Windows PowerShell and PowerShell 7.

$ADReportingToolsOptions = @{
    DistributionList   = "$([char]0x1b)[92m"
    Alert              = "$([char]0x1b)[91m"
    Warning            = "$([char]0x1b)[38;5;220m"
    Universal          = "$([char]0x1b)[38;5;170m"
    DomainLocal        = "$([char]0x1b)[38;5;191m"
    Other              = "$([char]0x1b)[38;5;212m"
    Protected          = "$([char]0x1b)[38;5;199m"
    Container          = "$([char]0x1b)[38;5;1456m"
    OrganizationalUnit = "$([char]0x1b)[38;5;191m"
    DomainDNS          = "$([char]0x1b)[1;4;38;5;227m"
    UserClass          = "$([char]0x1b)[30;104m"
    GroupClass         = "$([char]0x1b)[30;48;5;94m"
    ComputerClass      = "$([char]0x1b)[30;48;5;50m"
    IsDC               = "$([char]0x1b)[38;5;155m"
    IsServer           = "$([char]0x1b)[38;5;50m"
}

#endregion

#region import runspace

#launch a runspace to gather department information in the background
$newRunspace = [RunspaceFactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
[void]$newRunspace.Open()
$Global:ADReportingHash = [hashtable]::Synchronized(@{
        Note        = "This hashtable is used by the ADReportingTools module. Do not delete."
        Departments = @()
        DomainControllers = @()
        BackupLimit = 3
    }
)
$newRunspace.SessionStateProxy.SetVariable("ADReportingHash", $ADReportingHash)

$psCmd = [PowerShell]::Create()

[void]$pscmd.AddScript( {
    #this code will run in the background upon module import. Values will populate
    #the synchronized hashtable.
        $global:ADReportingHash.Departments = Get-ADUser -Filter "Department -like '*'" -Properties Department | Select-Object -ExpandProperty Department -Unique | Sort-Object
        $global:ADReportingHash.DomainControllers = (Get-ADDomain).ReplicaDirectoryServers

        #simulate a large environment for testing purposes
        #Start-Sleep -Seconds 30
        $global:ADReportingHash.Add("LastUpdated", (Get-Date))
    })

$psCmd.Runspace = $newRunspace

$handle = $psCmd.BeginInvoke()

$ADReportingHash.Add("Handle", $handle)

#start a job to clean up the runspace after it closes
[void](Start-ThreadJob -ScriptBlock {
        param($handle, $ps, $sleep)
        Write-Host "[$(Get-Date)] Sleeping in $sleep second loops"
        Write-Host "Watching this runspace"
        Write-Host ($ps.runspace | Out-String)
        do {
            Start-Sleep -Seconds $sleep
        } Until ($handle.IsCompleted)
        Write-Host "[$(Get-Date)] Closing runspace"

        $ps.runspace.close()
        Write-Host "[$(Get-Date)] Disposing runspace"
        $ps.runspace.Dispose()
        Write-Host "[$(Get-Date)] Disposing PowerShell"
        $ps.dispose()

        Write-Host "[$(Get-Date)] Ending job"
    } -ArgumentList $handle, $pscmd, 10)

#endregion

#region Other
Register-ArgumentCompleter -CommandName Get-ADDepartment -ParameterName Department -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)

    $global:ADReportingHash.Departments | Where-Object { $_ -like "$WordToComplete*" } |
    ForEach-Object {
        # completion text,listitem text,result type,Tooltip
        [System.Management.Automation.CompletionResult]::new("'$_'", $_, 'ParameterValue', $_)
    }
}

#Add auto complete for SERVER parameter to these commands
$cmds = 'Get-ADBranch','Get-ADCanonicalUser','Get-ADComputerReport','Get-ADDepartment','Get-ADDomainControllerHealth','Get-ADFSMO','Get-ADGroupR
eport'
,'Get-ADGroupUser','Get-ADSiteDetail','Get-ADSiteSummary','Get-ADSummary','Get-ADUserCategory','New-ADChangeReport','New-ADDomain
Report'
,'New-ADGroupReport','Show-DomainTree','Get-ADAccountAuthorizationGroup','Get-ADAccountResultantPasswordReplicationPolicy','Get-
ADAuthenticationPolicy'
,'Get-ADAuthenticationPolicySilo','Get-ADCentralAccessPolicy','Get-ADCentralAccessRule','Get-ADClaimTransformPol
icy'
,'Get-ADClaimType','Get-ADComputer','Get-ADComputerServiceAccount','Get-ADDefaultDomainPasswordPolicy','Get-ADDomain','Get-ADDomain
Controller'
,'Get-ADDomainControllerPasswordReplicationPolicy','Get-ADDomainControllerPasswordReplicationPolicyUsage','Get-ADFineGrained
PasswordPolicy'
,'Get-ADFineGrainedPasswordPolicySubject','Get-ADForest','Get-ADGroup','Get-ADGroupMember','Get-ADObject','Get-ADOptiona
lFeature'
,'Get-ADOrganizationalUnit','Get-ADPrincipalGroupMembership','Get-ADReplicationAttributeMetadata','Get-ADReplicationConnection
'
,'Get-ADReplicationQueueOperation','Get-ADReplicationSite','Get-ADReplicationSiteLink','Get-ADReplicationSiteLinkBridge','Get-ADReplic
ationSubnet'
,'Get-ADResourceProperty','Get-ADResourcePropertyList','Get-ADResourcePropertyValueType','Get-ADRootDSE','Get-ADServiceAcco
unt'
,'Get-ADTrust','Get-ADUser','Get-ADUserResultantPasswordPolicy'

foreach ($cmd in $cmds) {

    Register-ArgumentCompleter -CommandName $cmd  -ParameterName Server -ScriptBlock {
        param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)

        $global:ADReportingHash.DomainControllers | Where-Object { $_ -like "$WordToComplete*" } |
        ForEach-Object {
            # completion text,listitem text,result type,Tooltip
            [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
        }
    }
}

Export-ModuleMember -Variable ADUserReportingConfiguration,ADReportingToolsOptions,ADReportingDepartments

#endregion