34

I am working with a Powershell script that adds scheduled tasks to systems in our domain. When I run this script, it will prompt me for my password. I sometimes fat finger the password and the process starts, which locks out my account. Is there a way to verify my credentials to make sure that what I typed in will validate with the Domain?

I'd like to find a way to query the Domain controller. I've done some Google searches and I should be able to do a WMI query and trap for an error. I would like to avoid that style of validation if possible.

Any ideas? Thanks in advance.

4 Answers 4

31

I have this in my library:

$cred = Get-Credential #Read credentials
 $username = $cred.username
 $password = $cred.GetNetworkCredential().password

 # Get current domain using logged-on user's credentials
 $CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName
 $domain = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$UserName,$Password)

if ($domain.name -eq $null)
{
 write-host "Authentication failed - please verify your username and password."
 exit #terminate the script.
}
else
{
 write-host "Successfully authenticated with domain $domain.name"
}
4
  • 1
    If I'm not mistaken, this would end up sending the password in plain text across the network, right? If it does, then am I right in assuming that AccountManagement.PrincipalContext.ValidateCredentials() does not (if you provide a securestring for the password)? Jul 23, 2015 at 19:00
  • Why aren't you using the ActiveDirectory module to do your LDAP query? Mar 28, 2018 at 18:50
  • 3
    6 years ago there was no active directory module
    – Jim B
    Mar 28, 2018 at 23:36
  • 1
    This script also helps for situations where you can't install the AD PowerShell modules for one reason or another. Jul 11, 2018 at 16:03
19

This is what I've used in the past; it's supposed to work for local machine accounts and 'application directory', but so far I've only used it successfully with AD credentials:

    function Test-Credential {
    <#
    .SYNOPSIS
        Takes a PSCredential object and validates it against the domain (or local machine, or ADAM instance).

    .PARAMETER cred
        A PScredential object with the username/password you wish to test. Typically this is generated using the Get-Credential cmdlet. Accepts pipeline input.

    .PARAMETER context
        An optional parameter specifying what type of credential this is. Possible values are 'Domain','Machine',and 'ApplicationDirectory.' The default is 'Domain.'

    .OUTPUTS
        A boolean, indicating whether the credentials were successfully validated.

    #>
    param(
        [parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [System.Management.Automation.PSCredential]$credential,
        [parameter()][validateset('Domain','Machine','ApplicationDirectory')]
        [string]$context = 'Domain'
    )
    begin {
        Add-Type -assemblyname system.DirectoryServices.accountmanagement
        $DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::$context) 
    }
    process {
        $DS.ValidateCredentials($credential.UserName, $credential.GetNetworkCredential().password)
    }
}
7
  • I'd love to hear if someone notices this - I believe that when I use ValidateCredentials() in this manner with an incorrect password, it seems to trigger two (2) bad password attempts - I can't control the number of attempts threshold on our domain, and it's low, so I'd prefer not to have two bad attempts when I'm making a single call... can anyone see this as well? Jul 23, 2015 at 18:53
  • Are you using domain\user or UPN (user@domain) format? I'm not in a position to replicate this, but the following URL describes a similar problem: social.msdn.microsoft.com/Forums/vstudio/en-US/…
    – jbsmith
    Jul 27, 2015 at 21:36
  • You should be able to just pass $context as the argument to the constructor. PowerShell will automatically convert strings to an enum. Better yet, just make [System.DirectoryServices.AccountManagement.ContextType] the type of $context. Also, why are you using begin and process here? The pipeline seems like an odd way to use this function.
    – jpmc26
    Mar 8, 2017 at 22:53
  • @jpmc26: typing the $context parameter [System.DirectoryServices.AccountManagement.ContextType] is not an option, because the assembly containing isn't loaded until the function body is executed; using the pipeline is helpful if you want to validate multiple credentials.
    – mklement
    Mar 9, 2018 at 18:52
  • 1
    @mklement There's no reason the Add-Type call can't be moved outside the function, to before its definition is executed. I'm hesitant to have an Add-Type call unconditionally run repeatedly inside the function even if it's already loaded, anyway. Validating multiple credentials simultaneously seems like an odd situation in the first place. In the rare case that's what you want, you can easily wrap the call in ForEach-Object, so I don't see a reason to complication the function with it.
    – jpmc26
    Mar 9, 2018 at 19:00
1

I've found this post useful however it didn't solve my problem as I was trying to run it from a script with the local admin account logged on. It does not seem to work as local admin (only when logged on as a domain user).

However I did finally manage to get a working solution and since it was so much trouble I thought I'd share it here so anyone else with this problem will have the answer right here. Both answers on the one page depending on your needs.

Note that higher up in the scipt (not included here as this is just the get-credentials section) powergui is installed and is a requirement for this code below (as well as the "Add-PSSnapin Quest.ActiveRoles.ADManagement" line). Not sure what powergui does that's different but no one else could tell me and it works.

Subsitute your own domain name in the "domain_name" sections.

#Get credentials
$credential_ok = 0
while ($credential_ok -ne 1)
{
    $credential = get-credential
    $result = connect-qadservice -service *domain_name* -credential $credential
    [string]$result_string = $result.domain
    if ($result_string -eq "*domain_name*")
    {
        $credential_ok = 1
        #authenticated
    }
    else
    {
        #failed
    }     
}
$username = $credential.username 
$password = $credential.GetNetworkCredential().password 

$date = get-date
Add-Content "c:\lbin\Install_log.txt" "Successfully authenticated XP script as $username $date"
0
1

(yet) Another version:

param([string]$preloadServiceAccountUserName = "")

function HarvestCredentials()
{

        [System.Management.Automation.PSCredential]$credentialsOfCurrentUser = Get-Credential -Message "Please enter your username & password" -UserName $preloadServiceAccountUserName

        if ( $credentialsOfCurrentUser )
        {
            $credentialsOfCurrentUser = $credentialsOfCurrentUser
        }
        else
        {
            throw [System.ArgumentOutOfRangeException] "Gui credentials not entered correctly"          
        }

    Try
    {


        # see https://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.path(v=vs.110).aspx
        # validate the credentials are legitimate
        $validateCredentialsTest = (new-object System.DirectoryServices.DirectoryEntry ("WinNT://"+$credentialsOfCurrentUser.GetNetworkCredential().Domain), $credentialsOfCurrentUser.GetNetworkCredential().UserName, $credentialsOfCurrentUser.GetNetworkCredential().Password).psbase.name
        if ( $null -eq  $validateCredentialsTest)
        {
            throw [System.ArgumentOutOfRangeException] "Credentials are not valid.  ('" + $credentialsOfCurrentUser.GetNetworkCredential().Domain + '\' + $credentialsOfCurrentUser.GetNetworkCredential().UserName + "')"
        }
        else
        {
            $t = $host.ui.RawUI.ForegroundColor
            $host.ui.RawUI.ForegroundColor = "Magenta"
            Write-Output "GOOD CREDENTIALS"
            $host.ui.RawUI.ForegroundColor = $t
        }
    }
    Catch
    {

        $ErrorMessage = $_.Exception.Message
        $FailedItem = $_.Exception.ItemName
        $StackTrace = $_.Exception.StackTrace

        $t = $host.ui.RawUI.ForegroundColor
        $host.ui.RawUI.ForegroundColor = "Red"

        Write-Output "Exception - $ErrorMessage"
        Write-Output "Exception - $FailedItem"
        Write-Output "Exception - $StackTrace"

        $host.ui.RawUI.ForegroundColor = $t

        throw [System.ArgumentOutOfRangeException] "Attempt to create System.DirectoryServices.DirectoryEntry failed.  Most likely reason is that credentials are not valid."
    }

}


Try
{

    HarvestCredentials

}
Catch
{
    $ErrorMessage = $_.Exception.Message
    $FailedItem = $_.Exception.ItemName
    $StackTrace = $_.Exception.StackTrace

    $t = $host.ui.RawUI.ForegroundColor
    $host.ui.RawUI.ForegroundColor = "Red"

    Write-Output "Exception - " + $ErrorMessage
    Write-Output "Exception - " + $FailedItem
    Write-Output "Exception - " + $StackTrace

    $host.ui.RawUI.ForegroundColor = $t

    Break
}
Finally
{
    $Time=Get-Date
    Write-Output "Done - " + $Time
}

and

.\TestCredentials.ps1 -preloadServiceAccountUserName "mydomain\myusername"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .