Verify Local Admin Password Change in Powershell

Viewed 214
$adminUser = [ADSI] "WinNT://$name/Administrator"
$adminUser.SetPassword($password)

Is there anyway to verify that these two lines of code were successful in changing the local admin password like say it returns a 1 or a 0 for success or failure? I can confirm that these two lines do work on my device, but the plan is to push my script out through group policy and want to create a log of whether the script was successful or not. Here is kind of what I am going for:

Code Snippet

2 Answers

So, as in my comment, as long as $ErrorActionPreference is set to Stop you script should be capturing the errors properly. In addition, you can use $_ to actually capture the error:

PS C:\> $admin.SetPassword('supers3cur3p4ssw0rd')
Exception calling "setpassword" with "1" argument(s): "Access is denied.
...
...

So, if we try with try {...} catch {...} you can actually get a properly formatted error with the details you want:

Note: You can also inspect the last error using $Error[0].

$ErrorActionPreference = 'Stop' # This should usually be at the top of your script.

$storedError = try
{
    $admin.SetPassword('supers3cur3p4ssw0rd')
}
catch
{
    [pscustomobject]@{
        TimeGenerated = [datetime]::Now
        Message = 'Failed to set password on {0}' -f $env:COMPUTERNAME
        Exception = $_.Exception.InnerException.Message.Trim()
    }
}

This would yield the following on my laptop:

PS C:\> $storedError

TimeGenerated        Message                           Exception        
-------------        -------                           ---------        
7/14/2021 7:30:09 PM Failed to set password on XXXXX   Access is denied.

This is how the ErrorRecord object looks:

PS C:\> $Error[0] | Select-Object *

PSMessageDetails      : 
Exception             : System.Management.Automation.MethodInvocationException: Exception calling "setpassword" with "1" argument(s): "Access is denied.
                        " ---> System.UnauthorizedAccessException: Access is denied.
                        
                           --- End of inner exception stack trace ---
                           at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
                           at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
                           at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
                           at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
TargetObject          : 
CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : CatchFromBaseAdapterMethodInvokeTI
ErrorDetails          : 
InvocationInfo        : System.Management.Automation.InvocationInfo
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}

Thanks for the help everyone! Here are the changes I made:

$ErrorActionPreference = 'Stop'

try 
{
    $adminUser = [ADSI] "WinNT://$name/Administrator"
    $adminUser.SetPassword($password)

    $message = "" + $date + " - Local admin password change success"
    $message | Out-File -FilePath $filePath -Append
}
catch
{
    $errorMessage = [pscustomobject]@{
        TimeGenerated = [datetime]::now
        Message = 'Failed to set Password on {0}' -f $env:COMPUTERNAME
        Exception = $_.Exception.InnerException.Message.Trim()
    }

    $errorMessage | Out-File -FilePath $filePath -Append
}

And here is what the output file looked like after a couple tests:

enter image description here


EDIT

$ErrorActionPreference = 'Stop'

# Since this block is running from a loop we can store all "Success" and
# "Failed" objects in a variable which then we can export.
$result = try 
{
    $adminUser = [ADSI] "WinNT://$name/Administrator"
    # Attempt to set password here
    $adminUser.SetPassword($password)

    # If setting password succeeded, create a "Success" object:
    [pscustomobject]@{
        TimeGenerated = [datetime]::now
        ComputerName = $env:COMPUTERNAME
        Status = 'Successfully Set Password'
        Exception = $null
    }  
}
catch
{
    # If setting password failed, create a "Failed" object
    # using the same properties as "Success" object:   
    [pscustomobject]@{
        TimeGenerated = [datetime]::now
        ComputerName = $env:COMPUTERNAME
        Status = 'Failed to Set Password'
        Exception = $_.Exception.InnerException.Message.Trim() # Catch the actual error here
    }
}

# Export the results here, first sort by Status
$result | Sort-Object Status, TimeGenerated |
Out-File -FilePath $filePath
# No need to append, this is far more efficient,
# appending to a file causes high amount of Disk I/O which
# would slow down your script.

This would yield a result like:


TimeGenerated        ComputerName Status                    Exception       
-------------        ------------ ------                    ---------       
7/15/2021 4:57:08 PM COMPUTER-01  Failed to Set Password    Access is denied
7/15/2021 4:57:08 PM COMPUTER-04  Failed to Set Password    Access is denied
7/15/2021 4:57:08 PM COMPUTER-08  Failed to Set Password    Access is denied
7/15/2021 4:57:08 PM COMPUTER-00  Successfully Set Password                 
7/15/2021 4:57:08 PM COMPUTER-02  Successfully Set Password                 
7/15/2021 4:57:08 PM COMPUTER-03  Successfully Set Password                 
7/15/2021 4:57:08 PM COMPUTER-05  Successfully Set Password                 
7/15/2021 4:57:08 PM COMPUTER-06  Successfully Set Password                 
7/15/2021 4:57:08 PM COMPUTER-07  Successfully Set Password                 
7/15/2021 4:57:08 PM COMPUTER-09  Successfully Set Password                 
7/15/2021 4:57:08 PM COMPUTER-10  Successfully Set Password                 
Related