How to get consistent error messages in localized OSes using PowerShell

33 Views Asked by At

I am running the following code on an italian OS:

Rename-Computer -NewName 'NewCompName' -ErrorAction Stop -DomainCredential ($dcreds)

In several different cases I see these error messages:

  1. Impossibile rinominare il computer 'OldCompName' in 'NewCompName' a causa dell'eccezione seguente: L'account di riferimento è momentaneamente bloccato e non è consentito l'accesso.
  2. Rename-Computer : Impossibile rinominare il computer 'OldCompName' in 'NewCompName' a causa dell'eccezione seguente: Nome utente o password non corretta.
  3. Impossibile rinominare il computer 'OldCompName' in 'NewCompName' a causa dell'eccezione seguente: Account già esistente.
  4. Rename-Computer : Impossibile rinominare il computer 'OldCompName' in 'NewCompName' a causa dell'eccezione seguente: Accesso negato.
  5. Rename-Computer : Il computer 'OldCompName' con il nuovo nome 'NewCompName' verrà ignorato perché il nuovo nome è uguale a quello corrente.

If I prepend [Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US'; to the command above, I get the following errors (all of them are partially translated as you can see except the last one):

  1. Rename-Computer : Fail to rename computer 'OldCompName' to 'NewCompName' due to the following exception: L'account di riferimento è momentaneamente bloccato e non è consentito l'accesso.
  2. Rename-Computer : Fail to rename computer 'OldCompName' to 'NewCompName' due to the following exception: Nome utente o password non corretta.
  3. Rename-Computer : Fail to rename computer 'OldCompName' to 'NewCompName' due to the following exception: Account già esistente.
  4. Rename-Computer : Fail to rename computer 'OldCompName' to 'NewCompName' due to the following exception: Accesso negato.
  5. Rename-Computer : Skip computer 'OldCompName' with new name 'NewCompName' because the new name is the same as the current name.

Now my issue is that I am writing a script for which I need to handle different errors with different actions. My company has offices all around the World and we are dealing with OSes in more than a dozen different languages. The type of error matching I am doing is of this kind:

If ($_.Exception.Message -match 'The account already exists\.$') {...}

This is because all exceptions are of the kind [System.InvalidOperationException] (when catching typed exceptions) and even dissecting the exception I cannot find any reference to a numeric error code or else I can use through languages that is consistent and non-culture-specific.

Quite honestly I understand there are limitations when using MSDOS commands but in PS/.Net I expected that the developers have considered those challenges and given a way to work around them. I realize that the 2 different part of the errors come from different layers so the last part is likely surfacing from a stacked down call or something but it is in fact the second part that I care to address specifically all the various cases.

Any thoughts on how to deal with those various errors with different sections of code for each?

I have tried using [Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US' and also set LANG=en_US.UTF-8 none did the trick.

1

There are 1 best solutions below

0
mklement0 On
  • The fact that you're getting localized PowerShell error messages implies that you're using Windows PowerShell rather than PowerShell (Core) 7+, given that the latter has never been localized.

  • The parts of the error messages that remain in Italian - despite switching the UI culture to en-US (US English) with [Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US' - presumably came from the exception messages associated with .NET Framework APIs, which PowerShell only surfaces.

    • However, while built-in .NET Framework APIs respect an in-process UI-culture change too (unlike built-in .NET (Core) APIs, which seemingly aren't localized), this does not seem to extend to system error messages, i.e. those associated with the error codes returned from WinAPI functions - they remain in the language that is persistently configured for the current user.

    • In other words: You'd have to make en-US the current user's default UI language in order to see your error messages fully in English, e.g. with
      Set-WinUILanguageOverride en-US, but that is presumably undesired - and would also require logging off and back on for the change to take effect.

  • However, you may be able to bypass the issue in favor of a more robust solution that doesn't rely on matching the text of error/exception messages:

    • Inspect the .FullyQualifiedErrorId property of the [ErrorRecord] instances that PowerShell emits (which may or may not wrap .NET exceptions).

    • Its value is culture-invariant, and usually provides a specific clue to the nature of the error - though I don't know if it is specific enough in all cases of interest to you.

Here's a simple example, which uses the common -ErrorVariable parameter to capture (non-terminating) errors in a self-chosen variable for later inspection:

try {
  # Provoke an error by trying to rename to the current name.
  Rename-Computer $env:COMPUTERNAME -ErrorVariable err
} catch {
  # Turn statement-terminating errors into script-terminating (fatal) ones.
  throw
}

# Note: If no errors occurred, the body of this statement will not be entered.
switch -Regex (@($err.FullyQualifiedErrorId)) {
  '^NewNameIsOldName' { "New name is the same as the old one."  } # take specific action here
  # ... place other, error-specific branches here.
  Default { "FullyQualifiedErrorId: $_" } # print the error ID
}

In the example above, which uses a switch statement to evaluate the resulting errors, regex '^NewNameIsOldName' prefix-matches the full value of .FullyQualifiedErrorId, 'NewNameIsOldName,Microsoft.PowerShell.Commands.RenameComputerCommand' (of course, you're free to match by the full value too).