UsefulArgumentCompleters.psm1

using namespace System
using namespace System.Collections.Generic
using namespace System.Management.Automation
using namespace System.Management.Automation.Language
class CompletionHelper
{
    # Chars that PowerShell treats as quote chars
    hidden static [char[]] $QuoteChars = [char[]]@(
        [char]"'"
        [char]'"'
        [char]0x2018 # left single quotation mark
        [char]0x2019 # right single quotation mark
        [char]0x201a # single low-9 quotation mark
        [char]0x201b # single high-reversed-9 quotation mark
        [char]0x201c # left double quotation mark
        [char]0x201d # right double quotation mark
        [char]0x201E # low double left quote used in german.
    )

    # Chars that cannot be used at the start of a barequote string
    hidden static [char[]] $BadStartChars = [char[]]@(
        [char]'<'
        [char]'>'
        [char]'#'
        [char]'&'
        [char]'-'
        [char]0x2013 # EnDash
        [char]0x2014 # EmDash
        [char]0x2015 # HorizontalBar
    )

    # Chars that cause issues when used anywhere in a barequote string
    hidden static [char[]] $BadChars = [char[]]@(
        [char]'$'
        [char]'`' # Backtick
        [char]' ' # Space
        [char]'"'
        [char]','
        [char]';'
        [char]'('
        [char]')'
        [char]'{'
        [char]'}'
        [CompletionHelper]::QuoteChars
    )

    # Cache for saving expensive or static object collections used for completions
    hidden static [Dictionary[string,Object[]]] $ObjectCache = ([Dictionary[string,Object[]]]::new([System.StringComparer]::OrdinalIgnoreCase))

    static [string] AddQuotesIfNeeded([string] $CompletionText)
    {
        $Result = if ($CompletionText.Contains("'"))
        {
            "'$($CompletionText.Replace("'","''"))'"
        }
        elseif ($CompletionText.IndexOfAny([CompletionHelper]::BadStartChars) -eq 0 -or $CompletionText.IndexOfAny([CompletionHelper]::BadChars) -ne -1)
        {
            "'$CompletionText'"
        }
        else
        {
            $CompletionText
        }
         return $Result
    }

    static [string] TrimQuotes([string] $Text)
    {
        return $Text.Trim([CompletionHelper]::QuoteChars)
    }

    static [CompletionResult] NewParamCompletionResult([string] $Text)
    {
        return [CompletionResult]::new(
            [CompletionHelper]::AddQuotesIfNeeded($Text),
            $Text,
            [CompletionResultType]::ParameterValue,
            $Text
        )
    }

    static [CompletionResult] NewParamCompletionResult([string] $Text, [string]$ToolTip)
    {
        return [CompletionResult]::new(
            [CompletionHelper]::AddQuotesIfNeeded($Text),
            $Text,
            [CompletionResultType]::ParameterValue,
            $ToolTip
        )
    }

    # Validates that the string is a valid PowerShell command with no nested expressions
    hidden static [bool] CommandIsSafe([string] $Command)
    {
        $ParsedTokens = $null
        $Errors = $null
        $null = [Parser]::ParseInput($Command, [ref]$ParsedTokens, [ref]$Errors)
        if ($Errors.Count -gt 0)
        {
            return $false
        }
        foreach ($Token in $ParsedTokens)
        {
            if
            (
                $Token -isnot [StringLiteralToken] -and
                $Token -isnot [ParameterToken] -and
                $Token.Kind -ne [TokenKind]::EndOfInput -and
                $Token.Kind -ne [TokenKind]::Identifier
            )
            {
                return $false
            }
        }
        return $true
    }

    static [Object[]] GetCachedResults([string] $Command, [bool] $ValidateInput)
    {
        $Result = $null
        if ([CompletionHelper]::ObjectCache.TryGetValue($Command, [ref] $Result))
        {
            return $Result
        }
        $Result = if (!$ValidateInput -or [CompletionHelper]::CommandIsSafe($Command))
        {
            try
            {
                Invoke-Expression -Command $Command
            }
            catch
            {
                return $null
            }
        }
        else
        {
            return $null
        } 
        [CompletionHelper]::ObjectCache.Add($Command, $Result)
        return $Result
    }
}
Register-ArgumentCompleter -CommandName Get-ADUser,Get-ADComputer,Get-ADGroup,Get-ADOrganizationalUnit,Get-ADServiceAccount,Get-ADReplicationSite -ParameterName Properties -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $Values = @{
        'Get-ADUser' = @(
            'AccountExpirationDate'
            'accountExpires'
            'AccountLockoutTime'
            'AccountNotDelegated'
            'adminCount'
            'AllowReversiblePasswordEncryption'
            'AuthenticationPolicy'
            'AuthenticationPolicySilo'
            'BadLogonCount'
            'badPasswordTime'
            'badPwdCount'
            'c'
            'CannotChangePassword'
            'CanonicalName'
            'Certificates'
            'City'
            'CN'
            'co'
            'codePage'
            'Company'
            'CompoundIdentitySupported'
            'Country'
            'countryCode'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Department'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'Division'
            'DoesNotRequirePreAuth'
            'dSCorePropagationData'
            'EmailAddress'
            'EmployeeID'
            'EmployeeNumber'
            'Enabled'
            'Fax'
            'garbageCollPeriod'
            'GivenName'
            'HomeDirectory'
            'HomedirRequired'
            'HomeDrive'
            'HomePage'
            'HomePhone'
            'Initials'
            'instanceType'
            'internetEncoding'
            'isDeleted'
            'KerberosEncryptionType'
            'l'
            'LastBadPasswordAttempt'
            'LastKnownParent'
            'lastLogoff'
            'lastLogon'
            'LastLogonDate'
            'lastLogonTimestamp'
            'legacyExchangeDN'
            'LockedOut'
            'lockoutTime'
            'logonCount'
            'LogonWorkstations'
            'mail'
            'mailNickname'
            'Manager'
            'MemberOf'
            'MNSLogonAccount'
            'mobile'
            'MobilePhone'
            'Modified'
            'modifyTimeStamp'
            'mS-DS-ConsistencyGuid'
            'msDS-ExternalDirectoryObjectId'
            'msDS-KeyCredentialLink'
            'msDS-User-Account-Control-Computed'
            'msExchAddressBookFlags'
            'msExchArchiveQuota'
            'msExchArchiveWarnQuota'
            'msExchBypassAudit'
            'msExchCalendarLoggingQuota'
            'msExchDumpsterQuota'
            'msExchDumpsterWarningQuota'
            'msExchGroupSecurityFlags'
            'msExchMailboxAuditEnable'
            'msExchMailboxAuditLogAgeLimit'
            'msExchMailboxFolderSet'
            'msExchMDBRulesQuota'
            'msExchModerationFlags'
            'msExchPoliciesIncluded'
            'msExchProvisioningFlags'
            'msExchRecipientDisplayType'
            'msExchRecipientSoftDeletedStatus'
            'msExchRecipientTypeDetails'
            'msExchRemoteRecipientType'
            'msExchSafeSendersHash'
            'msExchTransportRecipientSettingsFlags'
            'msExchUMDtmfMap'
            'msExchUMEnabledFlags2'
            'msExchUserAccountControl'
            'msExchVersion'
            'msRTCSIP-DeploymentLocator'
            'msRTCSIP-FederationEnabled'
            'msRTCSIP-InternetAccessEnabled'
            'msRTCSIP-Line'
            'msRTCSIP-OptionFlags'
            'msRTCSIP-PrimaryUserAddress'
            'msRTCSIP-UserEnabled'
            'msTSExpireDate'
            'msTSLicenseVersion'
            'msTSLicenseVersion2'
            'msTSLicenseVersion3'
            'msTSManagingLS'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'objectSid'
            'Office'
            'OfficePhone'
            'Organization'
            'OtherName'
            'PasswordExpired'
            'PasswordLastSet'
            'PasswordNeverExpires'
            'PasswordNotRequired'
            'physicalDeliveryOfficeName'
            'POBox'
            'PostalCode'
            'PrimaryGroup'
            'primaryGroupID'
            'PrincipalsAllowedToDelegateToAccount'
            'ProfilePath'
            'ProtectedFromAccidentalDeletion'
            'protocolSettings'
            'proxyAddresses'
            'pwdLastSet'
            'SamAccountName'
            'sAMAccountType'
            'ScriptPath'
            'sDRightsEffective'
            'ServicePrincipalNames'
            'showInAddressBook'
            'SID'
            'SIDHistory'
            'SmartcardLogonRequired'
            'sn'
            'State'
            'StreetAddress'
            'Surname'
            'targetAddress'
            'telephoneNumber'
            'Title'
            'TrustedForDelegation'
            'TrustedToAuthForDelegation'
            'UseDESKeyOnly'
            'userAccountControl'
            'userCertificate'
            'UserPrincipalName'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
        )
        'Get-ADComputer' = @(
            'AccountExpirationDate'
            'accountExpires'
            'AccountLockoutTime'
            'AccountNotDelegated'
            'AllowReversiblePasswordEncryption'
            'AuthenticationPolicy'
            'AuthenticationPolicySilo'
            'BadLogonCount'
            'badPasswordTime'
            'badPwdCount'
            'CannotChangePassword'
            'CanonicalName'
            'Certificates'
            'CN'
            'codePage'
            'CompoundIdentitySupported'
            'countryCode'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'DNSHostName'
            'DoesNotRequirePreAuth'
            'dSCorePropagationData'
            'Enabled'
            'HomedirRequired'
            'HomePage'
            'instanceType'
            'IPv4Address'
            'IPv6Address'
            'isCriticalSystemObject'
            'isDeleted'
            'KerberosEncryptionType'
            'LastBadPasswordAttempt'
            'LastKnownParent'
            'lastLogoff'
            'lastLogon'
            'LastLogonDate'
            'lastLogonTimestamp'
            'localPolicyFlags'
            'Location'
            'LockedOut'
            'logonCount'
            'ManagedBy'
            'MemberOf'
            'MNSLogonAccount'
            'Modified'
            'modifyTimeStamp'
            'ms-Mcs-AdmPwdExpirationTime'
            'msDS-SupportedEncryptionTypes'
            'msDS-User-Account-Control-Computed'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'objectSid'
            'OperatingSystem'
            'OperatingSystemHotfix'
            'OperatingSystemServicePack'
            'OperatingSystemVersion'
            'PasswordExpired'
            'PasswordLastSet'
            'PasswordNeverExpires'
            'PasswordNotRequired'
            'PrimaryGroup'
            'primaryGroupID'
            'PrincipalsAllowedToDelegateToAccount'
            'ProtectedFromAccidentalDeletion'
            'PSShowComputerName'
            'pwdLastSet'
            'SamAccountName'
            'sAMAccountType'
            'sDRightsEffective'
            'ServiceAccount'
            'servicePrincipalName'
            'ServicePrincipalNames'
            'SID'
            'SIDHistory'
            'TrustedForDelegation'
            'TrustedToAuthForDelegation'
            'UseDESKeyOnly'
            'userAccountControl'
            'userCertificate'
            'UserPrincipalName'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
        )
        'Get-ADGroup' = @(
            'adminCount'
            'CanonicalName'
            'CN'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'dSCorePropagationData'
            'GroupCategory'
            'GroupScope'
            'groupType'
            'HomePage'
            'instanceType'
            'isCriticalSystemObject'
            'isDeleted'
            'LastKnownParent'
            'ManagedBy'
            'member'
            'MemberOf'
            'Members'
            'Modified'
            'modifyTimeStamp'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'objectSid'
            'ProtectedFromAccidentalDeletion'
            'SamAccountName'
            'sAMAccountType'
            'sDRightsEffective'
            'SID'
            'SIDHistory'
            'systemFlags'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
        )
        'Get-ADOrganizationalUnit' = @(
            'CanonicalName'
            'City'
            'CN'
            'Country'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'dSCorePropagationData'
            'instanceType'
            'isDeleted'
            'LastKnownParent'
            'LinkedGroupPolicyObjects'
            'ManagedBy'
            'Modified'
            'modifyTimeStamp'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'ou'
            'PostalCode'
            'ProtectedFromAccidentalDeletion'
            'sDRightsEffective'
            'State'
            'StreetAddress'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
        )
        'Get-ADServiceAccount' = @(
            'AccountExpirationDate'
            'accountExpires'
            'AccountLockoutTime'
            'AccountNotDelegated'
            'AllowReversiblePasswordEncryption'
            'AuthenticationPolicy'
            'AuthenticationPolicySilo'
            'BadLogonCount'
            'badPasswordTime'
            'badPwdCount'
            'CannotChangePassword'
            'CanonicalName'
            'Certificates'
            'CN'
            'codePage'
            'CompoundIdentitySupported'
            'countryCode'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'DNSHostName'
            'DoesNotRequirePreAuth'
            'dSCorePropagationData'
            'Enabled'
            'HomedirRequired'
            'HomePage'
            'HostComputers'
            'instanceType'
            'isCriticalSystemObject'
            'isDeleted'
            'KerberosEncryptionType'
            'LastBadPasswordAttempt'
            'LastKnownParent'
            'lastLogoff'
            'lastLogon'
            'LastLogonDate'
            'lastLogonTimestamp'
            'localPolicyFlags'
            'LockedOut'
            'logonCount'
            'ManagedPasswordIntervalInDays'
            'MemberOf'
            'MNSLogonAccount'
            'Modified'
            'modifyTimeStamp'
            'msDS-GroupMSAMembership'
            'msDS-ManagedPasswordId'
            'msDS-ManagedPasswordInterval'
            'msDS-ManagedPasswordPreviousId'
            'msDS-SupportedEncryptionTypes'
            'msDS-User-Account-Control-Computed'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'objectSid'
            'PasswordExpired'
            'PasswordLastSet'
            'PasswordNeverExpires'
            'PasswordNotRequired'
            'PrimaryGroup'
            'primaryGroupID'
            'PrincipalsAllowedToDelegateToAccount'
            'PrincipalsAllowedToRetrieveManagedPassword'
            'ProtectedFromAccidentalDeletion'
            'pwdLastSet'
            'SamAccountName'
            'sAMAccountType'
            'sDRightsEffective'
            'ServicePrincipalNames'
            'SID'
            'SIDHistory'
            'TrustedForDelegation'
            'TrustedToAuthForDelegation'
            'UseDESKeyOnly'
            'userAccountControl'
            'userCertificate'
            'UserPrincipalName'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
        )
        'Get-ADReplicationSite' = @(
            'AutomaticInterSiteTopologyGenerationEnabled'
            'AutomaticTopologyGenerationEnabled'
            'CanonicalName'
            'CN'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'dSCorePropagationData'
            'instanceType'
            'InterSiteTopologyGenerator'
            'isDeleted'
            'LastKnownParent'
            'ManagedBy'
            'Modified'
            'modifyTimeStamp'
            'msExchServerSiteBL'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'ProtectedFromAccidentalDeletion'
            'RedundantServerTopologyEnabled'
            'ReplicationSchedule'
            'ScheduleHashingEnabled'
            'sDRightsEffective'
            'showInAdvancedViewOnly'
            'Subnets'
            'systemFlags'
            'TopologyCleanupEnabled'
            'TopologyDetectStaleEnabled'
            'TopologyMinimumHopsEnabled'
            'UniversalGroupCachingEnabled'
            'UniversalGroupCachingRefreshSite'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
            'WindowsServer2000BridgeheadSelectionMethodEnabled'
            'WindowsServer2000KCCISTGSelectionBehaviorEnabled'
            'WindowsServer2003KCCBehaviorEnabled'
            'WindowsServer2003KCCIgnoreScheduleEnabled'
            'WindowsServer2003KCCSiteLinkBridgingEnabled'
        )
    }
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Item in $Values[$commandName])
    {
        if ($Item.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Item)
        }
    }
}
Register-ArgumentCompleter -CommandName Get-ComputerInfo -ParameterName Property -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    foreach ($Property in [Microsoft.PowerShell.Commands.ComputerInfo].GetProperties() | Sort-Object -Property Name)
    {
        if ($Property.Name.StartsWith($TrimmedWord, [System.StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Property.Name)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    foreach ($Culture in [CompletionHelper]::GetCachedResults('[cultureinfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures)', $false))
    {
        if ($null -eq $Culture)
        {
            continue
        }

        if ($Culture.Name -eq [string]::Empty -and $Culture.DisplayName.StartsWith($TrimmedWord))
        {
            if ($commandName -in 'Get-InstalledLanguage','Install-Language','Set-SystemPreferredUILanguage','Uninstall-Language')
            {
                continue
            }
            [CompletionResult]::new(
                "''",
                $Culture.DisplayName,
                [CompletionResultType]::ParameterValue,
                $Culture.DisplayName
            )
            continue
        }

        if ($Culture.Name.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Culture.Name, $Culture.DisplayName)
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName UICulture    -CommandName Update-Help,Save-Help,New-PSSessionOption
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Culture      -CommandName New-PSSessionOption
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Language     -CommandName Set-WinUILanguageOverride,Get-InstalledLanguage,Install-Language,Set-SystemPreferredUILanguage,Uninstall-Language
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName SystemLocale -CommandName Set-WinSystemLocale
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName CultureInfo  -CommandName Set-Culture
Register-ArgumentCompleter -CommandName Disconnect-VIServer -ParameterName Server -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    foreach ($Item in $global:DefaultVIServers.Name)
    {
        if ($Item.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Item)
        }
    }
}
Register-ArgumentCompleter -CommandName @(
    'Copy-NetFirewallRule'
    'Copy-NetIPsecMainModeCryptoSet'
    'Copy-NetIPsecMainModeRule'
    'Copy-NetIPsecPhase1AuthSet'
    'Copy-NetIPsecPhase2AuthSet'
    'Copy-NetIPsecQuickModeCryptoSet'
    'Copy-NetIPsecRule'
    'Disable-NetFirewallRule'
    'Disable-NetIPsecMainModeRule'
    'Disable-NetIPsecRule'
    'Enable-NetFirewallRule'
    'Enable-NetIPsecMainModeRule'
    'Enable-NetIPsecRule'
    'Get-NetFirewallAddressFilter'
    'Get-NetFirewallApplicationFilter'
    'Get-NetFirewallDynamicKeywordAddress'
    'Get-NetFirewallInterfaceFilter'
    'Get-NetFirewallInterfaceTypeFilter'
    'Get-NetFirewallPortFilter'
    'Get-NetFirewallProfile'
    'Get-NetFirewallRule'
    'Get-NetFirewallSecurityFilter'
    'Get-NetFirewallServiceFilter'
    'Get-NetFirewallSetting'
    'Get-NetIPsecMainModeCryptoSet'
    'Get-NetIPsecMainModeRule'
    'Get-NetIPsecPhase1AuthSet'
    'Get-NetIPsecPhase2AuthSet'
    'Get-NetIPsecQuickModeCryptoSet'
    'Get-NetIPsecRule'
    'New-NetFirewallRule'
    'New-NetIPsecMainModeCryptoSet'
    'New-NetIPsecMainModeRule'
    'New-NetIPsecPhase1AuthSet'
    'New-NetIPsecPhase2AuthSet'
    'New-NetIPsecQuickModeCryptoSet'
    'New-NetIPsecRule'
    'Open-NetGPO'
    'Remove-NetFirewallDynamicKeywordAddress'
    'Remove-NetFirewallRule'
    'Remove-NetIPsecMainModeCryptoSet'
    'Remove-NetIPsecMainModeRule'
    'Remove-NetIPsecPhase1AuthSet'
    'Remove-NetIPsecPhase2AuthSet'
    'Remove-NetIPsecQuickModeCryptoSet'
    'Remove-NetIPsecRule'
    'Rename-NetFirewallRule'
    'Rename-NetIPsecMainModeCryptoSet'
    'Rename-NetIPsecMainModeRule'
    'Rename-NetIPsecPhase1AuthSet'
    'Rename-NetIPsecPhase2AuthSet'
    'Rename-NetIPsecQuickModeCryptoSet'
    'Rename-NetIPsecRule'
    'Set-NetFirewallAddressFilter'
    'Set-NetFirewallApplicationFilter'
    'Set-NetFirewallInterfaceFilter'
    'Set-NetFirewallInterfaceTypeFilter'
    'Set-NetFirewallPortFilter'
    'Set-NetFirewallProfile'
    'Set-NetFirewallRule'
    'Set-NetFirewallSecurityFilter'
    'Set-NetFirewallServiceFilter'
    'Set-NetFirewallSetting'
    'Set-NetIPsecMainModeCryptoSet'
    'Set-NetIPsecMainModeRule'
    'Set-NetIPsecPhase1AuthSet'
    'Set-NetIPsecPhase2AuthSet'
    'Set-NetIPsecQuickModeCryptoSet'
    'Set-NetIPsecRule'
    'Show-NetFirewallRule'
    'Show-NetIPsecRule'
    'Sync-NetIPsecRule'
    'Update-NetIPsecRule'
    'Get-DAPolicyChange'
) -ParameterName PolicyStore -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    $ValidValues = @(
        if ($commandName -ne "Open-NetGPO")
        {
            'PersistentStore'
            'ActiveStore'
            'RSOP'
            'SystemDefaults'
            'StaticServiceStore'
            'ConfigurableServiceStore'
        }
        [CompletionHelper]::GetCachedResults('Get-GPO -All', $false)
    )
    foreach ($PolicyStore in $ValidValues)
    {
        if ($null -eq $PolicyStore)
        {
            continue
        }
        $PolicyStoreName = if ($PolicyStore -is [string])
        {
            $PolicyStore
        }
        else
        {
            "$($PolicyStore.DomainName)\$($PolicyStore.DisplayName)"
        }
        
        if ($PolicyStoreName.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($PolicyStoreName)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    $KnownPorts = @(
        @{Name="Any"                    ;Value="Any"             ;ToolTip='Any port'}
        @{Name="RPC"                    ;Value="RPC"             ;ToolTip='TCP port 49152-65535. RPC dynamic port range.'}
        @{Name="RPCEPMap"               ;Value="RPCEPMap"        ;ToolTip='TCP Port 135. RPC Endpoint mapper.'}
        if ($parameterName -eq "LocalPort")
        {
            @{Name="PlayToDiscovery"    ;Value="PlayToDiscovery" ;ToolTip='PlayToDiscovery'}
            @{Name="Teredo"             ;Value="Teredo"          ;ToolTip='Teredo'}
            @{Name="IPHTTPSIn"          ;Value="IPHTTPSIn"       ;ToolTip='IPHTTPSIn'}
            @{Name="IPHTTPSOut"         ;Value="IPHTTPSOut"      ;ToolTip='IPHTTPSOut'}
        }
        else
        {
            @{Name="IPHTTPS"            ;Value="IPHTTPS"      ;ToolTip='IPHTTPS'}
        }
        @{Name="FTPData"                ;Value="20"              ;ToolTip='TCP Port 20. FTP (File Transfer Protocol) data transfer.'}
        @{Name="FTPControl"             ;Value="21"              ;ToolTip='TCP Port 21. FTP (File Transfer Protocol) control.'}
        @{Name="SSH"                    ;Value="22"              ;ToolTip='TCP port 22. SSH (Secure Shell).'}
        @{Name="SCP"                    ;Value="22"              ;ToolTip='TCP port 22. SCP (Secure Copy).'} #Obviously it's the same as SSH, but it's more convenient to have multiple text options.
        @{Name="SFTP_SSH"               ;Value="22"              ;ToolTip='TCP port 22. SFTP (SSH File Transfer Protocol).'}
        @{Name="Telnet"                 ;Value="23"              ;ToolTip='TCP port 23. Telnet.'}
        @{Name="SMTP"                   ;Value="25"              ;ToolTip='TCP port 25. SMTP (Simple Mail Transfer Protocol).'}
        @{Name="DNS"                    ;Value="53"              ;ToolTip='TCP and UDP port 53. DNS (Domain Name System)'}
        @{Name="DHCPServer"             ;Value="67"              ;ToolTip='UDP port 67. DHCP (Dynamic Host Configuration Protocol) server port'}
        @{Name="DHCPClient"             ;Value="68"              ;ToolTip='UDP port 68. DHCP (Dynamic Host Configuration Protocol) client port'}
        @{Name="TFTP"                   ;Value="69"              ;ToolTip='UDP port 69. TFTP (Trivial File Transfer Protocol)'}
        @{Name="HTTP"                   ;Value="80"              ;ToolTip='TCP and UDP port 80. HTTP (Hypertext Transfer Protocol)'}
        @{Name="Kerberos"               ;Value="88"              ;ToolTip='TCP and UDP port 88. Kerberos authentication.'}
        @{Name="SFTP"                   ;Value="115"             ;ToolTip='TCP port 115. SFTP (Simple File Transfer Protocol)'}
        @{Name="NTP"                    ;Value="123"             ;ToolTip='UDP port 123. NTP (Network Time Protocol)'}
        @{Name="NetBiosNameService"     ;Value="137"             ;ToolTip='TCP and UDP port 137. Netbios name registration and resolution.'}
        @{Name="NetBiosDatagramService" ;Value="138"             ;ToolTip='UDP port 138. Netbios datagram service.'}
        @{Name="NetBiosSessionService"  ;Value="139"             ;ToolTip='TCP port 139. Netbios session service.'}
        @{Name="IMAP"                   ;Value="143"             ;ToolTip='TCP port 143. IMAP (Internet Message Access Protocol).'}
        @{Name="SNMP"                   ;Value="161"             ;ToolTip='UDP port 161. SNMP (Simple Network Management Protocol)'}
        @{Name="SNMPTrap"               ;Value="162"             ;ToolTip='TCP and UDP port 162. SNMP (Simple Network Management Protocol) trap'}
        @{Name="LDAP"                   ;Value="389"             ;ToolTip='TCP port 389. LDAP (Lightweight Directory Access Protocol)'}
        @{Name="HTTPS"                  ;Value="443"             ;ToolTip='TCP and UDP port 443. HTTPS (Hypertext Transfer Protocol Secure)'}
        @{Name="SMB"                    ;Value="445"             ;ToolTip='TCP and UDP port 445. SMB (Server Message Block)'}
        @{Name="KerberosSetPassword"    ;Value="464"             ;ToolTip='TCP and UDP port 464. Kerberos update password.'}
        @{Name="LDAPS"                  ;Value="636"             ;ToolTip='TCP port 636. LDAPS (Lightweight Directory Access Protocol over TLS/SSL)'}
        @{Name="IMAPS"                  ;Value="993"             ;ToolTip='TCP port 993. IMAPS (Internet Message Access Protocol over TLS/SSL)'}
        @{Name="MSSQL"                  ;Value="1433"            ;ToolTip='TCP and UDP port 1433. Default instance MS SQL server port.'}
        @{Name="KMS"                    ;Value="1688"            ;ToolTip='TCP port 1688. Default KMS (Key management service) port.'}
        @{Name="LDAPGC"                 ;Value="3268"            ;ToolTip='TCP and UDP port 3268. LDAP global catalog.'}
        @{Name="LDAPSGC"                ;Value="3269"            ;ToolTip='TCP and UDP port 3269. LDAPS global catalog.'}
        @{Name="RDP"                    ;Value="3389"            ;ToolTip='TCP and UDP port 3389. Default RDP port.'}
        @{Name="WinRM"                  ;Value="5985"            ;ToolTip='TCP port 5985. Default WinRM port.'}
        @{Name="WinRMHttps"             ;Value="5986"            ;ToolTip='TCP port 5986. Default WinRM HTTPs port.'}
    )
    foreach ($Port in $KnownPorts)
    {
        if ($Port['Name'].StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionResult]::new(
                $Port['Value'],
                $Port['Name'],
                [CompletionResultType]::ParameterValue,
                $Port['ToolTip']
            )
        }
    }
}
$Commands = @(
    'Find-NetIPsecRule'
    'New-NetFirewallRule'
    'New-NetIPsecRule'
    'Set-NetFirewallPortFilter'
    'Set-NetFirewallRule'
    'Set-NetIPsecRule'
)
Register-ArgumentCompleter -CommandName $Commands -ParameterName LocalPort -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName $Commands -ParameterName RemotePort -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName @(
    'Find-NetIPsecRule'
    'Get-NetFirewallPortFilter'
    'New-NetFirewallRule'
    'New-NetIPsecRule'
    'Set-NetFirewallPortFilter'
    'Set-NetFirewallRule'
    'Set-NetIPsecRule'
) -ParameterName Protocol -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    $KnownProtocols = @(
        @{Name="Any";        Value="Any"}
        @{Name="TCP";        Value="TCP"}
        @{Name="UDP";        Value="UDP"}
        @{Name="ICMPv4";     Value="ICMPv4"}
        @{Name="ICMPv6";     Value="ICMPv6"}
        @{Name="HOPOPT";     Value="0"}
        @{Name="IGMP";       Value="2"}
        @{Name="IPv6";       Value="41"}
        @{Name="IPv6-Route"; Value="43"}
        @{Name="IPv6-Frag";  Value="44"}
        @{Name="GRE";        Value="47"}
        @{Name="IPv6-NoNxt"; Value="59"}
        @{Name="IPv6-Opts";  Value="60"}
        @{Name="VRRP";       Value="112"}
        @{Name="PGM";        Value="113"}
        @{Name="L2TP";       Value="115"}
    )
    foreach ($Protocol in $KnownProtocols)
    {
        if ($Protocol['Name'].StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionResult]::new(
                $Protocol['Value'],
                $Protocol['Name'],
                [CompletionResultType]::ParameterValue,
                $Protocol['Value']
            )
        }
    }
}
Register-ArgumentCompleter -CommandName @(
    'Get-NetFirewallServiceFilter'
    'New-NetFirewallRule'
    'Set-NetFirewallRule'
    'Set-NetFirewallServiceFilter'
) -ParameterName Service -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    
    if ("Any".StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
    {
        [CompletionHelper]::NewParamCompletionResult("Any")
    }
    foreach ($Service in [CompletionHelper]::GetCachedResults('Get-Service', $false))
    {
        if ($null -eq $Service)
        {
            continue
        }
        if ($Service.Name.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Service.Name, $Service.DisplayName)
        }
    }
}
Register-ArgumentCompleter -CommandName Set-WinHomeLocation -ParameterName GeoId -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    $AllRegions = [CompletionHelper]::GetCachedResults(@'
foreach ($Culture in [cultureinfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures))
{
    if ($Culture.IsNeutralCulture -or $Culture.Name -eq "")
    {
        continue
    }
    [System.Globalization.RegionInfo]::new($Culture.Name)
}
'@
, $false)

    foreach ($RegionInfo in $AllRegions)
    {
        if ($null -eq $RegionInfo)
        {
            continue
        }

        if ($RegionInfo.EnglishName.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionResult]::new(
                $RegionInfo.GeoId,
                $RegionInfo.EnglishName,
                [CompletionResultType]::ParameterValue,
                $RegionInfo.EnglishName
            )
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    $Values = switch ($parameterName)
    {
        'LocalAddress'
        {
            Get-NetTCPConnection -LocalAddress "$TrimmedWord*" | Select-Object -ExpandProperty LocalAddress | Sort-Object -Unique
            break
        }
        'RemoteAddress'
        {
            Get-NetTCPConnection -RemoteAddress "$TrimmedWord*" | Select-Object -ExpandProperty RemoteAddress | Sort-Object -Unique
            break
        }
        'LocalPort'
        {
            Get-NetTCPConnection | Select-Object -ExpandProperty LocalPort | Sort-Object -Unique
            break
        }
        'RemotePort'
        {
            Get-NetTCPConnection | Select-Object -ExpandProperty RemotePort | Sort-Object -Unique
            break
        }
    }
    foreach ($Value in $Values)
    {
        if ($Value -like "$wordToComplete*")
        {
            [CompletionHelper]::NewParamCompletionResult($Value)
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -CommandName Get-NetTCPConnection -ParameterName LocalAddress
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -CommandName Get-NetTCPConnection -ParameterName RemoteAddress
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -CommandName Get-NetTCPConnection -ParameterName LocalPort
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -CommandName Get-NetTCPConnection -ParameterName RemotePort
Register-ArgumentCompleter -CommandName Get-NetTCPConnection -ParameterName OwningProcess -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    foreach ($Process in Get-Process -Name "$TrimmedWord*")
    {
        $ListItem = "$($Process.Name) $($Process.Id)"
        if ($ListItem.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionResult]::new(
                $Process.Id,
                $ListItem,
                [CompletionResultType]::ParameterValue,
                $ListItem
            )
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($TimeZone in [CompletionHelper]::GetCachedResults('Get-TimeZone -ListAvailable', $false))
    {
        if ($null -eq $TimeZone)
        {
            continue
        }
        $MatchText = if ($parameterName -eq "Name")
        {
            $TimeZone.StandardName
        }
        else
        {
            $TimeZone.Id
        }

        if ($MatchText.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($MatchText, $TimeZone.DisplayName)
        }
    }
}
Register-ArgumentCompleter -CommandName Get-TimeZone -ParameterName Name -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Get-TimeZone -ParameterName Id -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName New-ItemProperty -ParameterName PropertyType -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    
    $ResolveArgument = if ($null -ne $fakeBoundParameters['Path'])
    {
        @{LiteralPath = $fakeBoundParameters['Path']}
    }
    elseif ($null -ne $fakeBoundParameters['LiteralPath'])
    {
        @{LiteralPath = $fakeBoundParameters['LiteralPath']}
    }
    else
    {
        @{Path = '.\'}
    }
    
    $Values = switch ((Resolve-Path @ResolveArgument | Select-Object -First 1).Provider.Name)
    {
        'Registry'
        {
            @(
                @{Name = "String";      ToolTip = "A normal string."}
                @{Name = "ExpandString";ToolTip = "A string that contains unexpanded references to environment variables that are expanded when the value is retrieved."}
                @{Name = "Binary";      ToolTip = "Binary data in any form."}
                @{Name = "DWord";       ToolTip = "A 32-bit binary number."}
                @{Name = "MultiString"; ToolTip = "An array of strings."}
                @{Name = "Qword";       ToolTip = "A 64-bit binary number"}
                @{Name = "Unknown";     ToolTip = "An unsupported registry data type"}
            )
            break
        }
        Default
        {
            return
        }
    }

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Item in $Values)
    {
        if ($Item['Name'].StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Item['Name'], $Item['ToolTip'])
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($User in Get-LocalUser)
    {
        $MatchText = if ($parameterName -eq "Name")
        {
            $User.Name
        }
        else
        {
            $User.SID
        }

        if ($MatchText.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($MatchText, "$($User.Name) $($User.FullName)")
        }
    }
}
Register-ArgumentCompleter -CommandName Get-LocalUser,Set-LocalUser,Enable-LocalUser,Disable-LocalUser,Remove-LocalUser,Rename-LocalUser -ParameterName Name -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Get-LocalUser,Set-LocalUser,Enable-LocalUser,Disable-LocalUser,Remove-LocalUser,Rename-LocalUser -ParameterName SID -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Update-Module,Uninstall-Module -ParameterName Name -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Module in [CompletionHelper]::GetCachedResults('Get-Module -ListAvailable | Where-Object -Property RepositorySourceLocation -NE $null | Select-Object -ExpandProperty Name | Sort-Object -Unique', $false))
    {
        if ($null -eq $Module)
        {
            continue
        }
        if ($Module.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Module)
        }
    }
}
Register-ArgumentCompleter -CommandName Find-Module,Install-Module,Save-Module -ParameterName Name -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Module in [CompletionHelper]::GetCachedResults("Import-Csv -Delimiter ',' -LiteralPath '$PSScriptRoot\PSGallery.csv'", $false))
    {
        if ($null -eq $Module)
        {
            continue
        }
        if ($Module.Name.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Module.Name, "CompanyName: $($Module.CompanyName)")
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    foreach ($Adapter in [CompletionHelper]::GetCachedResults("Get-NetAdapter", $false))
    {
        if ($null -eq $Adapter)
        {
            continue
        }
        if ($Adapter.Name.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Adapter.Name, $Adapter.InterfaceDescription)
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName InterfaceAlias -CommandName @(
    'Get-DnsClient'
    'Get-DnsClientServerAddress'
    'Get-NetConnectionProfile'
    'Get-NetIPAddress'
    'Get-NetIPConfiguration'
    'Get-NetIPInterface'
    'Get-NetNeighbor'
    'Get-NetRoute'
    'New-NetFirewallRule'
    'New-NetIPAddress'
    'New-NetIPsecRule'
    'New-NetNeighbor'
    'New-NetRoute'
    'New-SmbMultichannelConstraint'
    'Remove-NetIPAddress'
    'Remove-NetNeighbor'
    'Remove-NetRoute'
    'Remove-SmbMultichannelConstraint'
    'Set-DhcpServerv4Binding'
    'Set-DhcpServerv6Binding'
    'Set-DnsClient'
    'Set-DnsClientServerAddress'
    'Set-NetConnectionProfile'
    'Set-NetFirewallInterfaceFilter'
    'Set-NetFirewallRule'
    'Set-NetIPAddress'
    'Set-NetIPConfiguration'
    'Set-NetIPInterface'
    'Set-NetIPsecRule'
    'Set-NetNeighbor'
    'Set-NetRoute'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Name -CommandName @(
    'Add-NetLbfoTeamMember'
    'Add-NetLbfoTeamNic'
    'Disable-NetAdapter'
    'Disable-NetAdapterBinding'
    'Disable-NetAdapterChecksumOffload'
    'Disable-NetAdapterEncapsulatedPacketTaskOffload'
    'Disable-NetAdapterIPsecOffload'
    'Disable-NetAdapterLso'
    'Disable-NetAdapterPacketDirect'
    'Disable-NetAdapterPowerManagement'
    'Disable-NetAdapterQos'
    'Disable-NetAdapterRdma'
    'Disable-NetAdapterRsc'
    'Disable-NetAdapterRss'
    'Disable-NetAdapterSriov'
    'Disable-NetAdapterUso'
    'Disable-NetAdapterVmq'
    'Enable-NetAdapter'
    'Enable-NetAdapterBinding'
    'Enable-NetAdapterChecksumOffload'
    'Enable-NetAdapterEncapsulatedPacketTaskOffload'
    'Enable-NetAdapterIPsecOffload'
    'Enable-NetAdapterLso'
    'Enable-NetAdapterPacketDirect'
    'Enable-NetAdapterPowerManagement'
    'Enable-NetAdapterQos'
    'Enable-NetAdapterRdma'
    'Enable-NetAdapterRsc'
    'Enable-NetAdapterRss'
    'Enable-NetAdapterSriov'
    'Enable-NetAdapterUso'
    'Enable-NetAdapterVmq'
    'Get-NetAdapter'
    'Get-NetAdapterAdvancedProperty'
    'Get-NetAdapterBinding'
    'Get-NetAdapterChecksumOffload'
    'Get-NetAdapterEncapsulatedPacketTaskOffload'
    'Get-NetAdapterHardwareInfo'
    'Get-NetAdapterIPsecOffload'
    'Get-NetAdapterLso'
    'Get-NetAdapterPacketDirect'
    'Get-NetAdapterPowerManagement'
    'Get-NetAdapterQos'
    'Get-NetAdapterRdma'
    'Get-NetAdapterRsc'
    'Get-NetAdapterRss'
    'Get-NetAdapterSriov'
    'Get-NetAdapterSriovVf'
    'Get-NetAdapterStatistics'
    'Get-NetAdapterUso'
    'Get-NetAdapterVmq'
    'Get-NetAdapterVmqQueue'
    'Get-NetAdapterVPort'
    'Get-NetLbfoTeamMember'
    'Get-NetLbfoTeamNic'
    'New-NetAdapterAdvancedProperty'
    'Remove-NetAdapterAdvancedProperty'
    'Remove-NetLbfoTeamMember'
    'Rename-NetAdapter'
    'Reset-NetAdapterAdvancedProperty'
    'Restart-NetAdapter'
    'Set-NetAdapter'
    'Set-NetAdapterAdvancedProperty'
    'Set-NetAdapterBinding'
    'Set-NetAdapterChecksumOffload'
    'Set-NetAdapterEncapsulatedPacketTaskOffload'
    'Set-NetAdapterIPsecOffload'
    'Set-NetAdapterLso'
    'Set-NetAdapterPacketDirect'
    'Set-NetAdapterPowerManagement'
    'Set-NetAdapterQos'
    'Set-NetAdapterRdma'
    'Set-NetAdapterRsc'
    'Set-NetAdapterRss'
    'Set-NetAdapterSriov'
    'Set-NetAdapterUso'
    'Set-NetAdapterVmq'
    'Set-NetLbfoTeamMember'
    'Set-NetLbfoTeamNic'
)
Register-ArgumentCompleter -CommandName New-Partition -ParameterName GptType -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $Types = @(
        @{Name = "Recovery"; Guid="{de94bba4-06d1-4d40-a16a-bfd50179d6ac}"}
        @{Name = "EFI";      Guid="{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}"}
        @{Name = "MSR";      Guid="{e3c9e316-0b5c-4db8-817d-f92df00215ae}"}
        @{Name = "Basic";    Guid="{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"}
    )

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Item in $Types)
    {
        if ($Item['Name'].StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionResult]::new(
                "'$($Item['Guid'])'",
                $Item['Name'],
                [CompletionResultType]::ParameterValue,
                $Item['Name']
            )
        }
    }
}
Register-ArgumentCompleter -CommandName Enable-WindowsOptionalFeature,Disable-WindowsOptionalFeature,Get-WindowsOptionalFeature -ParameterName FeatureName -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $FoundFeatures = if ($fakeBoundParameters["Path"])
    {
        [CompletionHelper]::GetCachedResults("Get-WindowsOptionalFeature -Path '$($fakeBoundParameters['Path'])'", $true)
    }
    else
    {
        [CompletionHelper]::GetCachedResults('Get-WindowsOptionalFeature -Online', $false)
    }
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Feature in $FoundFeatures)
    {
        if ($null -eq $Feature)
        {
            continue
        }
        if ($Feature.FeatureName.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Feature.FeatureName)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Counter in [CompletionHelper]::GetCachedResults("Get-Counter -ListSet * | Sort-Object -Property CounterSetName", $false))
    {
        if ($null -eq $Counter)
        {
            continue
        }
        if ($parameterName -eq "ListSet")
        {
            if ($Counter.CounterSetName.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
            {
                [CompletionHelper]::NewParamCompletionResult($Counter.CounterSetName)
            }
        }
        else
        {
            foreach ($Path in $Counter.Paths)
            {
                if ($Path.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
                {
                    [CompletionHelper]::NewParamCompletionResult($Path)
                }
            }
        }        
    }
}
Register-ArgumentCompleter -CommandName Get-Counter -ParameterName ListSet -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Get-Counter -ParameterName Counter -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Get-PnpDevice -ParameterName Class -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Class in [CompletionHelper]::GetCachedResults("Get-PnpDevice | Select-Object -ExpandProperty Class | Sort-Object -Unique", $false))
    {
        if ($null -eq $Class)
        {
            continue
        }
        if ($Class.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Class)
        }
    }
}
Register-ArgumentCompleter -CommandName Register-ArgumentCompleter -ParameterName ParameterName -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $CommandToFind = $fakeBoundParameters['CommandName']
    if ([string]::IsNullOrEmpty($CommandToFind))
    {
        return
    }
    $CommandInfo = Get-Command -Name $CommandToFind
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Key in $CommandInfo.Parameters.Keys)
    {
        if (!$Key.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            continue
        }
        [CompletionHelper]::NewParamCompletionResult($Key)
    }
}
Register-ArgumentCompleter -CommandName @(
    'Get-ADComputer'
    'Get-ADFineGrainedPasswordPolicy'
    'Get-ADGroup'
    'Get-ADObject'
    'Get-ADOptionalFeature'
    'Get-ADOrganizationalUnit'
    'Get-ADServiceAccount'
    'Get-ADUser'
    'Search-ADAccount'
) -ParameterName SearchBase -ScriptBlock {
    #This is not actually an argument completer, it's more like a CLI OU navigator
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    $GetOuParams = @{
        SearchScope = [Microsoft.ActiveDirectory.Management.ADSearchScope]::OneLevel
        Filter      = '*'
    }
    if ($TrimmedWord.Length -gt 0)
    {
        $GetOuParams.Add('SearchBase', $TrimmedWord)
    }

    foreach ($Ou in Get-ADOrganizationalUnit @GetOuParams)
    {
        [CompletionResult]::new(
            "'$($Ou.DistinguishedName)'",
            $Ou.Name,
            [CompletionResultType]::ParameterValue,
            $Ou.DistinguishedName
        )
    }
}
Register-ArgumentCompleter -CommandName Split-WindowsImage -ParameterName FileSize -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    
    $SizeConfigs = @(
        @{Name = "Fat32"; Size="4096"}
    )

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Item in $SizeConfigs)
    {
        if ($Item['Name'].StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionResult]::new(
                $Item['Size'],
                $Item['Name'],
                [CompletionResultType]::ParameterValue,
                "$($Item['Size'])MB for $($Item['Name'])"
            )
        }
    }
}
Register-ArgumentCompleter -CommandName Start-Process -ParameterName Verb -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    $FilePath = $fakeBoundParameters['FilePath']
    if ([string]::IsNullOrWhiteSpace($FilePath))
    {
        return
    }

    $Executable = [CompletionHelper]::GetCachedResults("Get-Command -Name '$FilePath' -CommandType Application -ErrorAction Ignore", $true)
    if ($null -eq $Executable)
    {
        return
    }

    $CommandInfo = [CompletionHelper]::GetCachedResults("[System.Diagnostics.ProcessStartInfo]::new('A$($Executable.Extension)')", $false)

    foreach ($Verb in $CommandInfo.Verbs)
    {
        if ($Verb.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Verb)
        }
    }
}
Register-ArgumentCompleter -CommandName Test-NetConnection -ParameterName Port -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    $KnownPorts = @(
        @{Name="FTPData"                ;Value="20"   ;ToolTip='TCP Port 20. FTP (File Transfer Protocol) data transfer.'}
        @{Name="FTPControl"             ;Value="21"   ;ToolTip='TCP Port 21. FTP (File Transfer Protocol) control.'}
        @{Name="SSH"                    ;Value="22"   ;ToolTip='TCP port 22. SSH (Secure Shell).'}
        @{Name="SCP"                    ;Value="22"   ;ToolTip='TCP port 22. SCP (Secure Copy).'} #Obviously it's the same as SSH, but it's more convenient to have multiple text options.
        @{Name="SFTP_SSH"               ;Value="22"   ;ToolTip='TCP port 22. SFTP (SSH File Transfer Protocol).'}
        @{Name="Telnet"                 ;Value="23"   ;ToolTip='TCP port 23. Telnet.'}
        @{Name="SMTP"                   ;Value="25"   ;ToolTip='TCP port 25. SMTP (Simple Mail Transfer Protocol).'}
        @{Name="DNS"                    ;Value="53"   ;ToolTip='TCP and UDP port 53. DNS (Domain Name System)'}
        @{Name="HTTP"                   ;Value="80"   ;ToolTip='TCP and UDP port 80. HTTP (Hypertext Transfer Protocol)'}
        @{Name="Kerberos"               ;Value="88"   ;ToolTip='TCP and UDP port 88. Kerberos authentication.'}
        @{Name="SFTP"                   ;Value="115"  ;ToolTip='TCP port 115. SFTP (Simple File Transfer Protocol)'}
        @{Name="RPCEPMap"               ;Value="135"  ;ToolTip='TCP Port 135. RPC Endpoint mapper.'}
        @{Name="NetBiosNameService"     ;Value="137"  ;ToolTip='TCP and UDP port 137. Netbios name registration and resolution.'}
        @{Name="NetBiosSessionService"  ;Value="139"  ;ToolTip='TCP port 139. Netbios session service.'}
        @{Name="IMAP"                   ;Value="143"  ;ToolTip='TCP port 143. IMAP (Internet Message Access Protocol).'}
        @{Name="SNMPTrap"               ;Value="162"  ;ToolTip='TCP and UDP port 162. SNMP (Simple Network Management Protocol) trap'}
        @{Name="LDAP"                   ;Value="389"  ;ToolTip='TCP port 389. LDAP (Lightweight Directory Access Protocol)'}
        @{Name="HTTPS"                  ;Value="443"  ;ToolTip='TCP and UDP port 443. HTTPS (Hypertext Transfer Protocol Secure)'}
        @{Name="SMB"                    ;Value="445"  ;ToolTip='TCP and UDP port 445. SMB (Server Message Block)'}
        @{Name="KerberosSetPassword"    ;Value="464"  ;ToolTip='TCP and UDP port 464. Kerberos update password.'}
        @{Name="LDAPS"                  ;Value="636"  ;ToolTip='TCP port 636. LDAPS (Lightweight Directory Access Protocol over TLS/SSL)'}
        @{Name="IMAPS"                  ;Value="993"  ;ToolTip='TCP port 993. IMAPS (Internet Message Access Protocol over TLS/SSL)'}
        @{Name="MSSQL"                  ;Value="1433" ;ToolTip='TCP and UDP port 1433. Default instance MS SQL server port.'}
        @{Name="KMS"                    ;Value="1688" ;ToolTip='TCP port 1688. Default KMS (Key management service) port.'}
        @{Name="LDAPGC"                 ;Value="3268" ;ToolTip='TCP and UDP port 3268. LDAP global catalog.'}
        @{Name="LDAPSGC"                ;Value="3269" ;ToolTip='TCP and UDP port 3269. LDAPS global catalog.'}
        @{Name="RDP"                    ;Value="3389" ;ToolTip='TCP and UDP port 3389. Default RDP port.'}
        @{Name="WinRM"                  ;Value="5985" ;ToolTip='TCP port 5985. Default WinRM port.'}
        @{Name="WinRMHttps"             ;Value="5986" ;ToolTip='TCP port 5986. Default WinRM HTTPs port.'}
    )
    foreach ($Port in $KnownPorts)
    {
        if ($Port['Name'].StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionResult]::new(
                $Port['Value'],
                $Port['Name'],
                [CompletionResultType]::ParameterValue,
                $Port['ToolTip']
            )
        }
    }
}
Register-ArgumentCompleter -CommandName Add-WindowsCapability,Get-WindowsCapability,Remove-WindowsCapability -ParameterName Name -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $FoundFeatures = if ($fakeBoundParameters["Path"])
    {
        [CompletionHelper]::GetCachedResults("Get-WindowsCapability -Path '$($fakeBoundParameters['Path'])'", $true)
    }
    else
    {
        [CompletionHelper]::GetCachedResults('Get-WindowsCapability -Online', $false)
    }
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Feature in $FoundFeatures)
    {
        if ($null -eq $Feature)
        {
            continue
        }
        if ($Feature.Name.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Feature.Name)
        }
    }
}
Register-ArgumentCompleter -CommandName Export-WindowsImage,New-WindowsImage -ParameterName CompressionType -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($CompressionType in "None","Fast","Max")
    {
        if ($CompressionType.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($CompressionType)
        }
    }
}
$Scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    foreach ($Log in [CompletionHelper]::GetCachedResults('Get-WinEvent -ListLog *', $false))
    {
        if ($null -eq $Log)
        {
            continue
        }
        if ($Log.LogName -like "*$TrimmedWord*")
        {
            [CompletionHelper]::NewParamCompletionResult($Log.LogName)
        }
    }
}
Register-ArgumentCompleter -CommandName Get-WinEvent -ParameterName LogName -ScriptBlock $Scriptblock
Register-ArgumentCompleter -CommandName Get-WinEvent -ParameterName ListLog -ScriptBlock $Scriptblock
$Scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    foreach ($Log in [CompletionHelper]::GetCachedResults('Get-WinEvent -ListProvider *', $false))
    {
        if ($null -eq $Log)
        {
            continue
        }
        if ($Log.Name -like "*$TrimmedWord*")
        {
            [CompletionHelper]::NewParamCompletionResult($Log.Name)
        }
    }
}
Register-ArgumentCompleter -CommandName Get-WinEvent -ParameterName ProviderName -ScriptBlock $Scriptblock
Register-ArgumentCompleter -CommandName Get-WinEvent -ParameterName ListProvider -ScriptBlock $Scriptblock
<#
.SYNOPSIS
    Lists all the argument completers supported by this module.
 
.PARAMETER CommandName
    Filters the displayed argument completers to the specified commands.
 
.PARAMETER CommandName
    Filters the displayed argument completers to the specified parameters.
#>

function Get-UsefulArgumentCompleter
{
    Param
    (
        [Parameter()]
        [SupportsWildcards()]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
            $Commands = [CompletionHelper]::GetCachedResults("Import-Csv -Path '$PSScriptRoot\ArgumentCompleters.csv' -Delimiter ','", $false) | Select-Object -ExpandProperty CommandName | Sort-Object -Unique
            foreach ($Item in $Commands)
            {
                if ($Item.StartsWith($TrimmedWord), [StringComparison]::OrdinalIgnoreCase)
                {
                    [CompletionHelper]::NewParamCompletionResult($Item)
                }
            }
        })]
        [string[]]
        $CommandName = "*",

        [Parameter()]
        [SupportsWildcards()]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
            $Parameters = [CompletionHelper]::GetCachedResults("Import-Csv -Path '$PSScriptRoot\ArgumentCompleters.csv' -Delimiter ','", $false) | Select-Object -ExpandProperty ParameterName | Sort-Object -Unique
            foreach ($Item in $Parameters)
            {
                if ($Item.StartsWith($TrimmedWord), [StringComparison]::OrdinalIgnoreCase)
                {
                    [CompletionHelper]::NewParamCompletionResult($Item)
                }
            }
        })]
        [string[]]
        $ParameterName = "*"
    )
    End
    {
        foreach ($Completer in [CompletionHelper]::GetCachedResults("Import-Csv -Path '$PSScriptRoot\ArgumentCompleters.csv' -Delimiter ','", $false))
        {
            $CommandMatch = $false
            foreach ($Name in $CommandName)
            {
                if ($Completer.CommandName -like $Name)
                {
                    $CommandMatch = $true
                    break
                }
            }

            if (!$CommandMatch)
            {
                continue
            }

            foreach ($Name in $ParameterName)
            {
                if ($Completer.ParameterName -like $Name)
                {
                    $Completer
                    break
                }
            }
        }
    }
}
<#
.SYNOPSIS
    Imports optional argument completers.
    This is intended to handle argument completers that may conflict with other commands such as Hyper-V VS PowerCLI commands.
 
.PARAMETER OptionalCompleter
    Name of completer to load.
#>

function Import-UsefulArgumentCompleterSet
{
    Param
    (
        [Parameter(Mandatory)]
        [Alias("Completer")]
        [ValidateSet("Hyperv")]
        [string[]]
        $OptionalCompleter
    )
    End
    {
        switch ($OptionalCompleter)
        {
            'HyperV'
            {
                & $Script:HypervCompleters
                break
            }
        }
    }
}

if ($PSEdition -eq 'Desktop')
{
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    foreach ($Package in [CompletionHelper]::GetCachedResults('Get-AppxPackage | Sort-Object -Property Name', $false))
    {
        if ($null -eq $Package)
        {
            continue
        }

        $MatchText = if ($parameterName -eq "Package")
        {
            $Package.PackageFullName
        }
        else
        {
            $Package.Name
        }

        if ($MatchText -like "*$TrimmedWord*")
        {
            [CompletionHelper]::NewParamCompletionResult($MatchText, $Package.Name)
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Name    -CommandName Get-AppxPackage
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Package -CommandName Get-AppxPackageManifest,Move-AppxPackage,Remove-AppxPackage,Reset-AppxPackage
Register-ArgumentCompleter -CommandName Get-AppxPackage -ParameterName Publisher -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)

    foreach ($Publisher in [CompletionHelper]::GetCachedResults('Get-AppxPackage | Select-Object -ExpandProperty Publisher | Sort-Object -Unique', $false))
    {
        if ($null -eq $Publisher)
        {
            continue
        }

        if ($Publisher -like "*$TrimmedWord*")
        {
            [CompletionHelper]::NewParamCompletionResult($Publisher)
        }
    }
}
Register-ArgumentCompleter -CommandName Get-CimInstance -ParameterName Property -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    if (!$fakeBoundParameters['ClassName'])
    {
        return
    }

    $GetCimClassParams = @{ClassName = $fakeBoundParameters['ClassName']}
    if ($fakeBoundParameters['Namespace'])
    {
        $GetCimClassParams.Add('Namespace',$fakeBoundParameters['Namespace'])
    }
    $FoundClass = Get-CimClass @GetCimClassParams -ErrorAction Ignore

    if (!$FoundClass)
    {
        return
    }

    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    foreach ($Item in $FoundClass.CimClassProperties)
    {
        if ($Item.Name.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionHelper]::NewParamCompletionResult($Item.Name, "$($Item.Name) [$($Item.CimType)]")
        }
    }
}
}

if ($PSEdition -eq 'Core')
{
}

$Script:HypervCompleters = {
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    
    $FoundVMs = if ($parameterName -eq "VMName" -or $parameterName -eq "Name")
    {
        Hyper-V\Get-VM -Name "$TrimmedWord*" -ErrorAction Ignore
    }
    elseif ($parameterName -eq 'VMId' -or $parameterName -eq 'Id')
    {
        Hyper-V\Get-VM -ErrorAction Ignore | Where-Object -FilterScript {$_.Id.Guid -like "$TrimmedWord*"}
    }
    else
    {
        return
    }

    foreach ($VM in $FoundVMs)
    {
        $MatchText = if ($parameterName -eq "Name" -or $parameterName -eq "VMName")
        {
            $VM.Name
        }
        else
        {
            "{$($VM.Id.Guid)}"
        }

        if ($MatchText.StartsWith($TrimmedWord, [StringComparison]::OrdinalIgnoreCase))
        {
            [CompletionResult]::new(
                [CompletionHelper]::AddQuotesIfNeeded($MatchText),
                $VM.Name,
                [CompletionResultType]::ParameterValue,
                $VM.Name
            )
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName VMName -CommandName @(
    'Add-VMAssignableDevice'
    'Add-VMDvdDrive'
    'Add-VMFibreChannelHba'
    'Add-VMGpuPartitionAdapter'
    'Add-VMHardDiskDrive'
    'Add-VMKeyStorageDrive'
    'Add-VMNetworkAdapter'
    'Add-VMNetworkAdapterAcl'
    'Add-VMNetworkAdapterExtendedAcl'
    'Add-VMNetworkAdapterRoutingDomainMapping'
    'Add-VMPmemController'
    'Add-VMRemoteFx3dVideoAdapter'
    'Add-VMScsiController'
    'Add-VMSwitchExtensionPortFeature'
    'Complete-VMFailover'
    'Connect-VMNetworkAdapter'
    'Disable-VMConsoleSupport'
    'Disable-VMIntegrationService'
    'Disable-VMResourceMetering'
    'Disable-VMTPM'
    'Disconnect-VMNetworkAdapter'
    'Enable-VMConsoleSupport'
    'Enable-VMIntegrationService'
    'Enable-VMReplication'
    'Enable-VMResourceMetering'
    'Enable-VMTPM'
    'Enter-PSSession'
    'Export-VMSnapshot'
    'Get-PSSession'
    'Get-VMAssignableDevice'
    'Get-VMBios'
    'Get-VMComPort'
    'Get-VMConnectAccess'
    'Get-VMDvdDrive'
    'Get-VMFibreChannelHba'
    'Get-VMFirmware'
    'Get-VMFloppyDiskDrive'
    'Get-VMGpuPartitionAdapter'
    'Get-VMHardDiskDrive'
    'Get-VMIdeController'
    'Get-VMIntegrationService'
    'Get-VMKeyProtector'
    'Get-VMKeyStorageDrive'
    'Get-VMMemory'
    'Get-VMNetworkAdapter'
    'Get-VMNetworkAdapterAcl'
    'Get-VMNetworkAdapterExtendedAcl'
    'Get-VMNetworkAdapterFailoverConfiguration'
    'Get-VMNetworkAdapterIsolation'
    'Get-VMNetworkAdapterRdma'
    'Get-VMNetworkAdapterRoutingDomainMapping'
    'Get-VMNetworkAdapterTeamMapping'
    'Get-VMNetworkAdapterVlan'
    'Get-VMPmemController'
    'Get-VMProcessor'
    'Get-VMRemoteFx3dVideoAdapter'
    'Get-VMReplication'
    'Get-VMScsiController'
    'Get-VMSecurity'
    'Get-VMSnapshot'
    'Get-VMStorageSettings'
    'Get-VMSwitchExtensionPortData'
    'Get-VMSwitchExtensionPortFeature'
    'Get-VMVideo'
    'Grant-VMConnectAccess'
    'Import-VMInitialReplication'
    'Invoke-Command'
    'Measure-VMReplication'
    'New-PSSession'
    'Remove-PSSession'
    'Remove-VMAssignableDevice'
    'Remove-VMDvdDrive'
    'Remove-VMFibreChannelHba'
    'Remove-VMGpuPartitionAdapter'
    'Remove-VMHardDiskDrive'
    'Remove-VMKeyStorageDrive'
    'Remove-VMNetworkAdapter'
    'Remove-VMNetworkAdapterAcl'
    'Remove-VMNetworkAdapterExtendedAcl'
    'Remove-VMNetworkAdapterRoutingDomainMapping'
    'Remove-VMNetworkAdapterTeamMapping'
    'Remove-VMPmemController'
    'Remove-VMRemoteFx3dVideoAdapter'
    'Remove-VMReplication'
    'Remove-VMSavedState'
    'Remove-VMScsiController'
    'Remove-VMSnapshot'
    'Remove-VMSwitchExtensionPortFeature'
    'Rename-VMNetworkAdapter'
    'Rename-VMSnapshot'
    'Reset-VMReplicationStatistics'
    'Reset-VMResourceMetering'
    'Restore-VMSnapshot'
    'Resume-VMReplication'
    'Revoke-VMConnectAccess'
    'Set-VMBios'
    'Set-VMComPort'
    'Set-VMDvdDrive'
    'Set-VMFibreChannelHba'
    'Set-VMFirmware'
    'Set-VMFloppyDiskDrive'
    'Set-VMGpuPartitionAdapter'
    'Set-VMHardDiskDrive'
    'Set-VMKeyProtector'
    'Set-VMKeyStorageDrive'
    'Set-VMMemory'
    'Set-VMNetworkAdapter'
    'Set-VMNetworkAdapterFailoverConfiguration'
    'Set-VMNetworkAdapterIsolation'
    'Set-VMNetworkAdapterRdma'
    'Set-VMNetworkAdapterRoutingDomainMapping'
    'Set-VMNetworkAdapterTeamMapping'
    'Set-VMNetworkAdapterVlan'
    'Set-VMProcessor'
    'Set-VMRemoteFx3dVideoAdapter'
    'Set-VMReplication'
    'Set-VMSecurity'
    'Set-VMSecurityPolicy'
    'Set-VMStorageSettings'
    'Set-VMSwitchExtensionPortFeature'
    'Set-VMVideo'
    'Start-VMFailover'
    'Start-VMInitialReplication'
    'Stop-VMFailover'
    'Stop-VMInitialReplication'
    'Stop-VMReplication'
    'Suspend-VMReplication'
    'Test-VMNetworkAdapter'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Name -CommandName @(
    'Checkpoint-VM'
    'Copy-VMFile'
    'Debug-VM'
    'Export-VM'
    'Get-VM'
    'Measure-VM'
    'Move-VMStorage'
    'New-VM'
    'Remove-VM'
    'Rename-VM'
    'Restart-VM'
    'Resume-VM'
    'Save-VM'
    'Set-VM'
    'Start-VM'
    'Stop-VM'
    'Suspend-VM'
    'Update-VMVersion'
    'Wait-VM'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName VMId -CommandName @(
    'Enter-PSSession'
    'Get-PSSession'
    'Get-VHD'
    'Get-VMConnectAccess'
    'Grant-VMConnectAccess'
    'Invoke-Command'
    'New-PSSession'
    'Remove-PSSession'
    'Revoke-VMConnectAccess'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Id -CommandName Get-VM
}