Vérifiez si la saisie du mot de passe utilisateur est valide dans le script Powershell

30

Je travaille avec un script Powershell qui ajoute des tâches planifiées aux systèmes de notre domaine. Lorsque j'exécute ce script, il me demandera mon mot de passe. Parfois, je tape du doigt sur le mot de passe et le processus démarre, ce qui bloque mon compte. Existe-t-il un moyen de vérifier mes informations d'identification pour s'assurer que ce que j'ai tapé sera validé avec le domaine?

Je voudrais trouver un moyen d'interroger le contrôleur de domaine. J'ai fait quelques recherches sur Google et je devrais pouvoir faire une requête WMI et intercepter une erreur. J'aimerais éviter ce style de validation si possible.

Des idées? Merci d'avance.

Doltknuckle
la source

Réponses:

26

J'ai ceci dans ma bibliothèque:

$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"
}
Jim B
la source
1
Si je ne me trompe pas, cela finirait par envoyer le mot de passe en texte brut sur le réseau, non? Si c'est le cas, ai-je raison de supposer que ce AccountManagement.PrincipalContext.ValidateCredentials()n'est pas le cas (si vous fournissez une chaîne de sécurité pour le mot de passe)?
Code Jockey du
Pourquoi n'utilisez-vous pas le ActiveDirectorymodule pour effectuer votre requête LDAP?
Kolob Canyon
Il y a 6 ans, il n'y avait pas de module Active Directory
Jim B
Ce script est également utile dans les situations où vous ne pouvez pas installer les modules AD PowerShell pour une raison ou une autre.
Dodzi Dzakuma
16

C'est ce que j'ai utilisé dans le passé; il est censé fonctionner pour les comptes de machines locales et le «répertoire d'application», mais jusqu'à présent, je ne l'ai utilisé qu'avec succès avec les informations d'identification AD:

    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)
    }
}
jbsmith
la source
J'aimerais entendre si quelqu'un le remarque - je crois que lorsque j'utilise ValidateCredentials () de cette manière avec un mot de passe incorrect, il semble déclencher deux (2) tentatives de mot de passe incorrectes - je ne peux pas contrôler le nombre de tentatives seuil sur notre domaine, et c'est bas, donc je préfère ne pas avoir deux mauvaises tentatives quand je fais un seul appel ... est-ce que quelqu'un peut voir ça aussi?
Code Jockey
Utilisez-vous le format domaine \ utilisateur ou UPN (utilisateur @ domaine)? Je ne suis pas en mesure de reproduire cela, mais l'URL suivante décrit un problème similaire: social.msdn.microsoft.com/Forums/vstudio/en-US/…
jbsmith
Vous devriez pouvoir simplement passer $contextl'argument au constructeur. PowerShell convertira automatiquement les chaînes en une énumération. Mieux encore, faites simplement [System.DirectoryServices.AccountManagement.ContextType]le type de $context. Aussi, pourquoi utilisez-vous beginet processici? Le pipeline semble être une façon étrange d'utiliser cette fonction.
jpmc26
@ jpmc26: la saisie du $contextparamètre [System.DirectoryServices.AccountManagement.ContextType]n'est pas une option, car l'assembly contenant n'est pas chargé tant que le corps de la fonction n'est pas exécuté; l'utilisation du pipeline est utile si vous souhaitez valider plusieurs informations d'identification.
mklement
@mklement Il n'y a aucune raison pour que l' Add-Typeappel ne puisse pas être déplacé en dehors de la fonction, avant l'exécution de sa définition. J'hésite à ce qu'un Add-Typeappel soit exécuté de manière inconditionnelle à plusieurs reprises à l'intérieur de la fonction, même si elle est déjà chargée, de toute façon. La validation simultanée de plusieurs informations d'identification semble être une situation étrange en premier lieu. Dans les rares cas, c'est ce que vous voulez, vous pouvez facilement envelopper l'appel ForEach-Object, donc je ne vois pas de raison de compliquer la fonction avec.
jpmc26
1

J'ai trouvé cet article utile, mais il n'a pas résolu mon problème car j'essayais de l'exécuter à partir d'un script avec le compte d'administrateur local connecté. Il ne semble pas fonctionner en tant qu'administrateur local (uniquement lorsque vous êtes connecté en tant qu'utilisateur de domaine).

Cependant, j'ai finalement réussi à obtenir une solution de travail et comme c'était tellement difficile, je pensais que je la partagerais ici pour que toute autre personne ayant ce problème ait la réponse ici. Les deux réponses sur une seule page en fonction de vos besoins.

Notez que plus haut dans le scipt (non inclus ici car il ne s'agit que de la section get-credentials) powergui est installé et est une exigence pour ce code ci-dessous (ainsi que la ligne "Add-PSSnapin Quest.ActiveRoles.ADManagement"). Je ne sais pas quel powergui fait, c'est différent, mais personne d'autre ne pourrait me le dire et cela fonctionne.

Substituez votre propre nom de domaine dans les sections "nom_domaine".

#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"
Michael
la source
1

(encore) Une autre 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
}

et

.\TestCredentials.ps1 -preloadServiceAccountUserName "mydomain\myusername"
granadaCoder
la source