Processing native command output in a script

I’m having some inconsitant issues with processing “net use” output from within a script.

Background: I have a script which reads in a list of servers, then attaches to them, reads some data, then detaches and moves to the next. There is a dot-sourced file, functions.ps1, which contians the function ConnectToTarget(). The offending part of the code is:

function ConnectToTarget()
...
      LogMessage "Connecting to server as `"$($credential.UserName)`""
      "" | & net use "\\$($computer.DnsName)" "/u:$($credential.UserName)" $credential.GetNetworkCredential().Password /n 2>&1 | foreach {
         #
         # Remove embedded newline chars from the output that shows up sometimes
         #
         DebugMessage "`$_.ToString() =`"$($_.ToString().Replace("`r`n","[\r\n]"))`""
         $CommandOutput = $_.ToString().Replace("`r`n","")
         DebugMessage "`$CommandOutput =`"$CommandOutput`""
         if ($CommandOutput -ne "")
         {
            #
            # Keep the most recent message in case we need to save it in the computer object
            #
            $LastNativeOutput = "netuse: $CommandOutput"
            DebugMessage "`$LastNativeOutput =`"$LastNativeOutput`""
            LogNativeOutput $LastNativeOutput
         }
      }
      DebugMessage "`$LASTEXITCODE = $LASTEXITCODE"
      if ($LASTEXITCODE -ne 0)
      {
         DebugMessage "Setting connection status to Failed"
         $Computer.ErrorMessage = $LastNativeOutput
         if ([string]::IsNullOrEmpty($Computer.ErrorMessage))
         {
            $Computer.ErrorMessage = "netuse: Unknown error.  LASTEXITCODE = $LASTEXITCODE"
         }
         $computer.ConnectionStatus = "Failed"
      }

I have one server in the list which generates the following output to the log:

Connecting to server as “XXXXXXXXXX”

$LASTEXITCODE = 2

Setting connection status to Failed

Exiting functions::ConnectToTarget()

*** netuse: Unknown error. LASTEXITCODE = 2

Here, I’m not seeing any of the “net use” output in the foreach. However, if I run the (virtually) same code interactively, it works as expected:

$a = @()
$comp = “Not-Important”
“” | & net use “\$comp” “/u:XXXXXXXX” XXXXXXXX /n 2>&1 | foreach {
#
# Remove embedded newline chars from the output that shows up sometimes
#
write-host “$_.ToString() =”$($_.ToString().Replace(“rn”,“[\r\n]”))"" $CommandOutput = $_.ToString().Replace("rn","") write-host "$CommandOutput ="$CommandOutput""
if ($CommandOutput -ne “”)
{
#
# Keep the most recent message in case we need to save it in the computer object
#
$LastNativeOutput = “netuse: $CommandOutput”
write-host “$LastNativeOutput =”$LastNativeOutput`“”
}
}

$.ToString() = “System error 1789 has occurred.”
$CommandOutput = “System error 1789 has occurred.”
$LastNativeOutput = “netuse: System error 1789 has occurred.”
$
.ToString() = “[\r\n]The trust relationship between this workstation and the primary domain failed.[\r\n][\r\n]”
$CommandOutput = “The trust relationship between this workstation and the primary domain failed.”
$LastNativeOutput = “netuse: The trust relationship between this workstation and the primary domain failed.”

Any thoughts as to why the same code behaves differnetly in a script vs. when run interactively?

My first question has to be - do you really need net use to achieve your goal?
What data are you reading?

Can’t you read directly using PowerShell or WMI?

Parsing text output from legacy utilities is a fraught business as you’ve discovered

Given the error message you are receiving when running interactively could it be due to the account you are using not authenticating because of the broken trust with the domain whereas you are using a cached credential when logging on interactively?

Richard,

Thanks for the reply. In this instance, I’m use net use to establish a connection using credentials (different than the running user) to read registry values. I haven’t found a good alternative for ‘net use’ to accomplish that.

As to the error, yes, I understand why I cannot connect to the server, but thats not the point of the question. For all other server connections, the code write the command output to the log:

Connecting to server as “********”

$_.ToString() = “The command completed successfully.”

$CommandOutput = “The command completed successfully.”

$LastNativeOutput = “netuse: The command completed successfully.”

netuse: The command completed successfully.

$_.ToString() = “”

$CommandOutput = “”

I’m just trying to figure out what I’ve misse din this code that would cause it to behave differently in a script vs. interactively.

if all you want is registry values you can do that with WMI
for instance to read a string (REG_SZ) registry value

$HKLM = [uint32]2147483650
$key = “SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine”
$value = “PSCompatibleVersion”
Invoke-WmiMethod -Class StdRegprov -Name GetStringValue -ArgumentList $hklm, $key, $value

the data is returned in the svalue property

Invoke-WmiMethod takes a computername & credential

Richard,

Thanks again, but that doesn’t actually address the question about why the code works differently in different environments. However, I’ve found the cause. In the ConnectToTarget() function, I set the ErrorActionPreference to SilentlyContinue. This apparently suppresses the output of the “net” command if the return code is non-zero. I was thrown because we were receiving “Command completed successfully” output from net.exe. I expected that the EAP variable only affected cmdlets and expressesion, but apparently not.

That said, I’ll look into the Invoke-Wmi* cmdlet, but I’ve seen nasty performance hits in the past with WMI, so I tend to avoid it if I can. Across a few servers, the performance hit is not too bad, but these scripts process throusands of serves, and event short delays add up.

Also, one other function we perform is to pull a config file from the remote servers for parsing and analysis. Again, the id used to access the servers is not the id running the script, so we need to connect to the target server as a configured id, then pull the file back for analysis. Without a “net use” in place, is there another method I’ve missed for pulling a file back using different credentials?

Thanks again for the input…