powershell

PowerShell | AutoCompleter not working with TAB completion with custom array


I'm creating a dynamic set for a domain controller parameter in one of my PS1 scripts. I am using [ValidateSet] in the parameters but wanted to make this dynamic using Register-ArgumentCompleter.

I've tried multiple ways in PS7 to use AutoCompleter to dynamically choose Domain Controllers that have the PDC role. I keep getting the default file options. I tried setting $null as per this article: Stack Flow. I couldn't get it to work.

Here is my code:

[CmdletBinding()] 
param(  
    [System.Management.Automation.PSCredential]
    $Credential,
    [switch]$vcenterAccess = $false,
    [string]$OutputPath = $ENV:HOMEPATH + '\Documents',
    [string]$duzit = 'CF3' + (Get-Date -format 'yyMMdd'),
    [switch]$listDCs,
    [switch]$examples,
    [string]$DomainController
)

$validPDCsb = {
    param ($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParams)
    $allPDCs = @()
    $allPDCs = @(Get-AllDomainControllers -Role PDC).Name
    $allPDCs | ? { $_ -like "*$wordToComplete*" } | % {
        [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
    }
    if (-not $allPDCs) {
        $null
    }
}

Register-ArgumentCompleter -ParameterName DomainController -ScriptBlock $validPDCsb
Function Get-CompanyServer {
    if ($listDCs) { Get-DCList; exit }
    if ($examples) { Get-Examples; exit }
    Import-Module ImportExcel
    $startTime = Get-Date
    $startTime
    $domainName = (Get-DomainName -DomainController $DomainController)
    Write-Verbose ('DOMAIN: ' + $domainName)
    if ($vcenterAccess -eq $true) { Connect-vCenter -Credential $Credential }
    Get-ComputerList -DomainController $DomainController
    $elapsedTime = $(get-date) - $startTime
    $elapsedTime = $elapsedTime.Minutes
    Write-Verbose ([string]$startTime + ' (Run time: ' + [string]$elapsedTime + ' Minutes)')
    $serverCount = Get-ServerCount -DomainController $DomainController
    Write-Verbose ('SERVER COUNT: ' + [string]$serverCount)
    $FileOutput = ($OutputPath + '\' + $duzit + '-' + $domainName + '.xlsx')
    Write-Host 'FILE SAVED: ' $FileOutput
    Write-Verbose ('DUZIT (save this): ' + $duzit)
    Get-Date
}
Function Get-ComputerList($DomainController) {
    #Write-Verbose "Generating Computer List..."
    $serverList = @()
    [int]$counter = (Get-ADComputer -Filter { operatingSystem -like '*Windows server*' } -Server $DomainController -ResultSetSize 500 -Credential $Credential).Count
    [int]$s = 0
    Get-ADComputer -Filter { operatingSystem -like '*Windows server*' } -Server $DomainController -Properties * -ResultPageSize 500 -Credential $Credential | ? { ($_.OperatingSystem -like '*Windows server*') -or ($_.OperatingSystem -eq $null) } | select name, enabled, IPv4Address, operatingSystem, description, canonicalName | % {
        $s++
        $serverList += $_
        Write-Progress -Activity ('Processing Computers [' + $s + '/' + $counter + '] : ' + $_.Name) -PercentComplete ($s / $counter * 100) -CurrentOperation $counter[$_]
    }
    #$serverList = @(Get-ADComputer -Filter {operatingSystem -like "*Windows server*"} -Server $DomainController -Properties * -ResultPageSize 500 -Credential $Credential | ? {($_.OperatingSystem -like "*Windows server*") -or ($_.OperatingSystem -eq $null)} | select name,enabled,IPv4Address,operatingSystem,description,canonicalName)
    #[int]$count = $serverList.Count
    $i = 0
    $n = 0
    Write-Verbose ("Systems found: '" + $counter + "'")
    Write-Verbose 'PHASE ONE: Testing Connections...'
    Write-Progress -Completed -Activity 'Completed'
    foreach ($server in $serverList) {
        $i++
        $status = 'Down'
        $p1sequence = [string]$i + '/' + [string]$counter
        $elapsedTime = $(get-date) - $startTime
        $elapsedTime = $elapsedTime.Minutes
        Write-Verbose ($($server.name) + ': ' + $p1sequence)
        if (Test-Connection $server.IPv4Address -Quiet) { $status = 'Up' }$server | Add-Member -MemberType NoteProperty -Name 'Status' -Value $status -Force
    }
    $elapsedTime = $(get-date) - $startTime
    $elapsedTime = $elapsedTime.Minutes
    Write-Verbose ('PHASE ONE: ' + $([string]$elapsedTime) + ' Minutes')
    if ($vcenterAccess -eq $true) {
        Write-Verbose 'PHASE TWO: Testing vCenter Connections...'
        foreach ($server in $serverList) {
            $n++
            $p2sequence = [string]$n + '/' + [string]$counter
            Write-Verbose ($($server.name) + ': ' + $p2sequence)
            $vCenter = 'N/A'
            $power = 'N/A'
            $power = (Get-VM $server.Name).PowerState; $server | Add-Member -MemberType NoteProperty -Name 'PowerState' -Value $power -Force
            $vCenter = (Get-VM $server.Name).Uid.Split(':')[0].Split('@')[1]; $server | Add-Member -MemberType NoteProperty -Name 'vCenter' -Value $vcenter -Force 
        }
        $elapsedTime = $(get-date) - $startTime
        $elapsedTime = $elapsedTime.Minutes
        Write-Verbose ('PHASE TWO: ' + $([string]$elapsedTime) + ' Minutes')
    }
    $serverList = $serverList | sort name
    #$serverList
    $FileOutput = ($OutputPath + '\' + $duzit + '-' + $domainName + '.xlsx') 
    $serverList | Export-Excel -Path $FileOutput -WorksheetName $domainName -AutoSize -AutoFilter
}
Function Connect-vCenter {
    Param(
        [System.Management.Automation.PSCredential]$Credential
    )
    Write-Verbose 'Connecting vCenter Instances...'
    Connect-VIServer corpvc01 -Credential $Credential -Force
    Connect-VIServer ostpvc01 -Credential $Credential -Force
    Connect-VIServer 10.200.52.2 -Credential $Credential -Force #AVS Azure East
    Connect-VIServer 10.201.52.2 -Credential $Credential -Force #AVS Azure West
}
Function Get-ServerCount ($DomainController) {
    $serverList = @(Get-ADComputer -Filter { operatingSystem -like '*Windows server*' } -Server $DomainController -Properties * -ResultPageSize 500 | ? { ($_.OperatingSystem -like '*Windows server*') -or ($_.OperatingSystem -eq $null) } | select name, enabled, IPv4Address, operatingSystem, description, canonicalName)
    [int]$counter = $serverList.Count
    $counter
}
Function Get-Examples {
    $currentColor = ($Host.UI.RawUI.ForegroundColor)
    $Host.UI.RawUI.ForegroundColor = 'Yellow'
    Write-Host 'SET DOMAIN ADMIN CREDS'
    Write-Host "`$Cred = Get-Credential"
    Write-Host ''
    Write-Host 'EXAMPLE COMMAND: CFEXTRANET'
    Write-Host "Get-CompanyServer -Credential `$Cred -DomainController xdc1.cfextranet.com -Duzit `"CF3231114`" -OutputPath `"C:\Scripts\Lists\Excel`" -vCenterAccess:`$true -Verbose"
    Write-Host ''
    Write-Host 'EXAMPLE COMMAND: CFIORG Domain'
    Write-Host "Get-CompanyServer -Credential `$Cred -DomainController DONCFIDVDC02 -Duzit `"CF3231114`" -OutputPath `"C:\Scripts\Lists\Excel`" -vCenterAccess:`$false -Verbose"
    Write-Host "Get-CompanyServer -Credential `$Cred -DomainController CORCFILGDC01 -Duzit `"CF3231114`" -OutputPath `C:\Scripts\Lists\Excel`" -vCenterAccess:`$true -Verbose"
    Write-Host "Get-CompanyServer -Credential `$Cred -DomainController CORCFIORGDC01 -Duzit `"CF3231114`" -OutputPath `"C:\Scripts\Lists\Excel`" -vCenterAccess:`$true -Verbose"
    Write-Host "Get-CompanyServer -Credential `$Cred -DomainController MEDCFIMHDC01 -Duzit `"CF3231114`" -OutputPath `"C:\Scripts\Lists\Excel`" -vCenterAccess:`$false -Verbose"
    Write-Host "Get-CompanyServer -Credential `$Cred -DomainController CORCFITRDC01 -Duzit `"CF3231114`" -OutputPath `"C:\Scripts\Lists\Excel`" -vCenterAccess:`$false -Verbose"
    Write-Host ''
    Write-Host 'EXAMPLE COMMAND: GROWHOW'
    Write-Host "Get-CompanyServer -Credential `$Cred -DomainController incghsdc1.growhow.local -Duzit `"CF3231114`" -OutputPath `"C:\Scripts\Lists\Excel`" -vCenterAccess:`$false -Verbose"
    Write-Host ''
    $Host.UI.RawUI.ForegroundColor = 'Green'
    Write-Host '-DUZIT: This alphanumeric value is a combination of CF3 (3 denoting Report) and date string 11/14/23 (231114).' 
    Write-Host '-DUZIT: This must be kept when generating multiple reports for the one master report (REF: Merge-CompanyServer.ps1)'
    Write-Host '-DUZIT: This value is gernerated automatically but you can force a value depending on your report groupings'
    Write-Host '-OUTPUT PATH: Make this a consistent diretory to group all your reportrs for the master report (REF: Merge-CompanyServer.ps1)'
    Write-Host '-DOMAIN CONTROLLER: (Get-CompanyServer -listDCs). Use a specific DC for each of the domains you are generating the single report'
    Write-Host '-CREDENTIAL: These are your Domain Admin creds that are needed to log into vCenter and DCs'
    Write-Host '-VCENTER ACCESS: CFILG, CFEXTRANET and CFIORG are the only domains where we have vCenter access. Set the value to true for those reports'
    Write-Host '-VERBOSE: ALWAYS use the verbose swtich to see the activity on the report creation.'
    $Host.UI.RawUI.ForegroundColor = $currentColor
}
Function Get-DomainName ($DomainController) {
    if ($DomainController -like '*FILG*') { $domainName = 'CFILG' }
    if ($DomainController -like '*FIMH*') { $domainName = 'CFIMH' }
    if ($DomainController -like '*FITR*') { $domainName = 'CFITR' }
    if ($DomainController -like '*FIORG*') { $domainName = 'CFIORG' }
    if ($DomainController -like '*FIDV*') { $domainName = 'CFIDV' }
    if ($DomainController -like 'xdc*') { $domainName = 'CFEXTRANET' }
    if ($DomainController -like '*GHSDC*') { $domainName = 'GROWHOW' }
    $domainName
}
Function Get-DCList {
    $dclist = (Get-ADForest).Domains | % { Get-ADDomainController -Filter * -Server $_ } | ft -Property Name, Domain, IPv4Address, OperatingSystem, OperationMasterRoles -AutoSize
    $cfextranet = (Get-ADForest -Server xdc1.cfextranet.com).Domains | % { Get-ADDomainController -Filter * -Server $_ } | ft -Property Name, Domain, IPv4Address, OperatingSystem, OperationMasterRoles -AutoSize
    $growhow = (Get-ADForest -Server INCGHSDC1.GROWHOW.LOCAL).Domains | % { Get-ADDomainController -Filter * -Server $_ } | ft -Property Name, Domain, IPv4Address, OperatingSystem, OperationMasterRoles -AutoSize
    $cfextranet
    $dclist
    $growhow
}
Function Get-AllDomainControllers {
    $domains = (Get-ADForest).domains
    $dcs = foreach ($domain in $domains) { Get-ADDomainController -Discover -DomainName $domain }
    switch ($Role) {
        'None' {
            $result = foreach ($dc in $dcs) {
                $ForestMode = (Get-ADForest -Credential $Credential).ForestMode
                $DomainMode = (Get-ADDomain -Server $dc -Credential $Credential).DomainMode
                Get-ADDomainController -Filter * -Server $dc -Credential $Credential | select Name, HostName, IPv4Address, Domain, OperatingSystem, @{Name = 'DomainMode'; Expression = { $DomainMode } }, @{Name = 'ForestMode'; Expression = { $ForestMode } }, IsGlobalCatalog, OperationMasterRoles, Site, ServerObjectGuid
            }
        }
        'PDC' {
            $result = foreach ($dc in $dcs) {
                $DomainMode = (Get-ADDomain -Server $dc -Credential $Credential).DomainMode
                Get-ADDomainController -Filter * -Server $dc -Credential $Credential | ? { $_.OperationMasterRoles -match 'PDCEmulator' } | select Name, HostName, IPv4Address, Domain, OperatingSystem, @{Name = 'DomainMode'; Expression = { $DomainMode } }, IsGlobalCatalog, OperationMasterRoles, Site, ServerObjectGuid
            }
        }
        'RID' {
            $result = foreach ($dc in $dcs) {
                $DomainMode = (Get-ADDomain -Server $dc -Credential $Credential).DomainMode
                Get-ADDomainController -Filter * -Server $dc -Credential $Credential | ? { $_.OperationMasterRoles -match 'RIDMaster' } | select HostName, IPv4Address, Domain, OperatingSystem, @{Name = 'DomainMode'; Expression = { $DomainMode } }, IsGlobalCatalog, OperationMasterRoles, Site, ServerObjectGuid
            }
        }
        'Infrastructure' {
            $result = foreach ($dc in $dcs) {
                $DomainMode = (Get-ADDomain -Server $dc -Credential $Credential).DomainMode
                Get-ADDomainController -Filter * -Server $dc -Credential $Credential | ? { $_.OperationMasterRoles -match 'InfrastructureMaster' } | select Name, HostName, IPv4Address, Domain, OperatingSystem, @{Name = 'DomainMode'; Expression = { $DomainMode } }, IsGlobalCatalog, OperationMasterRoles, Site, ServerObjectGuid
            }
        }
        'Naming' {
            $ForestMode = (Get-ADForest -Credential $Credential).ForestMode
            $result = foreach ($dc in $dcs) { Get-ADDomainController -Filter * -Server $dc -Credential $Credential | ? { $_.OperationMasterRoles -match 'DomainNamingMaster' } | select Name, HostName, IPv4Address, Domain, OperatingSystem, @{Name = 'ForestMode'; Expression = { $ForestMode } }, IsGlobalCatalog, OperationMasterRoles, Site, ServerObjectGuid }
        }
        'Schema' {
            $SchemaServer = (Get-ADForest -Credential $Credential).SchemaMaster
            $ForestMode = (Get-ADForest -Credential $Credential).ForestMode
            $SchemaVersion = Get-ADObject (Get-ADRootDSE).schemaNamingContext -Property objectVersion -Server $SchemaServer -Credential $Credential
            $result = foreach ($dc in $dcs) { Get-ADDomainController -Filter * -Server $dc -Credential $Credential | ? { $_.OperationMasterRoles -match 'SchemaMaster' } | select Name, HostName, IPv4Address, Domain, OperatingSystem, @{Name = 'SchemaVersion'; Expression = { $SchemaVersion.objectVersion } }, @{Name = 'ForestMode'; Expression = { $ForestMode } }, IsGlobalCatalog, OperationMasterRoles, Site, ServerObjectGuid }
        }
    }
    $result
}

Get-CompanyServer

Solution

  • Here's a minimal example:

    param(
      [ArgumentCompleter({
        param($commandName, $parameterName, $wordToComplete) # , $commandAst, $preBoundParameters omitted    
        # Hard-coded sample list of PDCs
        # Place the real code to find the PDCs *directly in here*.
        # You can NOT call functions defined later in the script here.
        [array] $allPDCs = 'foo', 'bar', 'baz'
        # Return the ones that prefix-match what was typed so far, if anything.
        $allPDCs -like "$wordToComplete*"
      })]
      [string] $DomainController
    )
    
    $DomainController # diagnostic output of the argument
    

    A -DomainController argument then tab-completes to foo, bar or baz, also on first invocation.