Pedro Sales
Pedro Sales

Reputation: 553

handling net use error messages with powershell

I'm using a simple net use command to map a network drive

net use \\$HOSTIP $PASSWD /user:$UNAME

i must use net use instead of of New-PSDrive because the scripts runs for more then 400 machines in multiple instances and just wouldn't be doable. I want to filter the error message then net use return like

System error 64 has occurred.

or

System error 67 has occurred.

How can I do this?

Upvotes: 1

Views: 3299

Answers (3)

Jordan Riley
Jordan Riley

Reputation: 41

Following up on @jdr5ca`s Answer which will work and is good. I made some modifications to make it a little easier to read and understand.

First and foremost, I set some variables that are at the top of the script so that they can be quickly and easily modified.

Clear-Host # I use because of Visual Studio
$drivelettertouse = "Z" # or "*" for any.
$Server = "\\SERVER\"

I then kept the middle section pretty much the same excluding the Arguments. This was because, for my use, I do not need to use the Username and Password.

$pinfo = new-object System.Diagnostics.ProcessStartInfo
$pinfo.Filename = "net.exe"
$pinfo.UseShellExecute = $false
$pinfo.Arguments = @("use", "$($drivelettertouse):", "$($Server)")
$pinfo.redirectstandardError = $true
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.start() | out-null
$p.waitforexit()

Now, for the actual error-handling process. I did this differently. Not because his was not good, but because I find this way a little easier to read.

This will allow you to catch certain System Errors and either go to a function or set a variable for later use.

if($p.exitcode -ne 0){
    $err = $p.standardError.ReadToEnd()
    # List of Error Codes https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
    switch ($err)
    {
        {$_.contains("System error 3 has")} { $global:syserror=5; break}
        {$_.contains("System error 5 has")} { $global:syserror=5; break}
        {$_.contains("System error 15 has")} { $global:syserror=15; break}
        {$_.contains("System error 53")} { $global:syserror=53; break}
        {$_.contains("System error 55")} { $global:syserror=55; break}
        {$_.contains("System error 67")} { $global:syserror=67; break}
        {$_.contains("System error 85")} { $global:syserror=85; break}
        default {$global:syserror=9999; break}
    }
}

Additionally, I added if-else statements to manage those errors. The reason I am using If Else statements is because some of these will use more than one function for my use case. If you would also like to use the If Else method, here is how I used mine below, excluding my use.

if ($global:syserror -eq 5) {
    Write-Host "Access to $Server denied for user $env:USERNAME."
} elseif ($global:syserror -eq 15) {
    Write-Host "The system cannot find the drive specified."
} elseif ($global:syserror -eq 53) {
    Write-Host "The network path was not found."
} elseif ($global:syserror -eq 55) {
    Write-Host "The specified network resource or device is no longer available."
} elseif ($global:syserror -eq 67) {
    Write-Host "The network name cannot be found."
} elseif ($global:syserror -eq 85) {
    $inuseby = Get-PSDrive $drivelettertouse | Select -ExpandProperty DisplayRoot
    Write-Host "$($drivelettertouse):\ is already in use by $inuseby."
} elseif ($global:syserror -eq "9999") {
    Write-Host "Unknown Error."
    Write-Host $err
} 

I hope this helps anyone who needs it. :)

Full Code.

Clear-Host
$drivelettertouse = "Z"
$Server = "\\SERVER\" + $env:USERNAME

$pinfo = new-object System.Diagnostics.ProcessStartInfo
$pinfo.Filename = "net.exe"
$pinfo.UseShellExecute = $false
$pinfo.Arguments = @("use", "$($drivelettertouse):", "$($Server)")
$pinfo.redirectstandardError = $true
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.start() | out-null
$p.waitforexit()

if($p.exitcode -ne 0){
    $err = $p.standardError.ReadToEnd()
    # List of Error Codes https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
    switch ($err)
    {
        {$_.contains("System error 3 has")} { $global:syserror=5; break}
        {$_.contains("System error 5 has")} { $global:syserror=5; break}
        {$_.contains("System error 15 has")} { $global:syserror=15; break}
        {$_.contains("System error 53")} { $global:syserror=53; break}
        {$_.contains("System error 55")} { $global:syserror=55; break}
        {$_.contains("System error 67")} { $global:syserror=67; break}
        {$_.contains("System error 85")} { $global:syserror=85; break}
        default {$global:syserror=9999; break}
    }
}

if ($global:syserror -eq 5) {
    Write-Host "Access to $Server denied for user $env:USERNAME."
} elseif ($global:syserror -eq 15) {
    Write-Host "The system cannot find the drive specified."
} elseif ($global:syserror -eq 53) {
    Write-Host "The network path was not found."
} elseif ($global:syserror -eq 55) {
    Write-Host "The specified network resource or device is no longer available."
} elseif ($global:syserror -eq 67) {
    Write-Host "The network name cannot be found."
} elseif ($global:syserror -eq 85) {
    $inuseby = Get-PSDrive $drivelettertouse | Select -ExpandProperty DisplayRoot
    Write-Host "$($drivelettertouse):\ is already in use by $inuseby."
} elseif ($global:syserror -eq "9999") {
    Write-Host "Unknown Error."
    Write-Host $err
} 

Upvotes: 0

Paul
Paul

Reputation: 5871

You can do the following:

#set process startup info (redirect stderr)
$pinfo = new-object System.Diagnostics.ProcessStartInfo
$pinfo.Filename = "net.exe"
$pinfo.UseShellExecute = $false
$pinfo.Arguments = @("use","\\$($HOSTIP)","$($PASSWD)","/user:$($UNAME)")
$pinfo.redirectstandardError = $true
#start process and wait for it to exit
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.start() | out-null
$p.waitforexit()
#check the returncode
if($p.exitcode -ne 0){
    #rc != 0 so we grab the stderr output
    $err = $p.standardError.ReadToEnd()
    #first line of the output contains the string from your question, matching it against regex
    if($err[0] -match "System error ([0-9]*) has occurred"){
        #switching the error code
        switch($Matches[1]){
            64 {do-something64;break;}
            67 {do-something67;break;}
        }
    }
}

This should do the trick, although i cant make a statement about how performant it is, you will have to try. If the output can differ from the string you posted in your question you will have to write your own regexes to handle them.

Keep in mind that the output from net is localized so the regex in my example will not work on systems where the system language is not english.

Hope that helps

Upvotes: 2

woxxom
woxxom

Reputation: 73616

Use cmd to redirect stderr to stdout:

$log = cmd /c "2>&1" net use \\$HOSTIP $PASSWD /user:$UNAME

Now the variable contains an array of strings (2 with text and 2 empty):

System error 53 has occurred.

The network path was not found.

You can parse it:

$log = cmd /c "2>&1" net use \\$HOSTIP $PASSWD /user:$UNAME
if ($LASTEXITCODE -and $log -join "`n" -match '^.+?(\d+).+?\n+(.+)') {
    $errCode = [int]$matches[1]
    $errMessage = $matches[2]
}

Upvotes: 1

Related Questions