Forcing PowerShell errors output in English on localized systems - powershell

I need to run some PowerShell scripts across various operating systems. Most of them are in English version, however, some are localized for example German, French, Spanish, etc. The problem is local system administrators mostly do not now PowerShell and in the case the script fails and throws an error at them, instead of reading it they just send screenshots of such error messages to me and if the cause to this error is not obvious I am stuck with typing it to g. translate to find out what is going on.
Is there a switch I can run the whole script or single command with or a parameter or any other way to force errors in PowerShell to be displayed in English instead of the language that is default for that particular machine?

You can change the pipeline thread's CurrrentUICulture like so:
[Threading.Thread]::CurrentThread.CurrentUICulture = 'fr-FR'; Get-Help Get-Process
I'm on an English system but before I executed the line above, I updated help like so:
Update-Help -UICulture fr-FR
With that, the Get-Help call above gave me French help on my English system. Note: if I put the call to Get-Help on a new line, it doesn't work. Confirmed that PowerShell resets the CurrentUICulture before the start of each pipeline which is why it works when the commands are in the same pipeline.
In your case, you would need to have folks install English help using:
Update-Help -UICulture en-US
And then execute your script like so:
[Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US'; .\myscript.ps1

[Threading.Thread]::CurrentThread.CurrentUICulture only affects to current one-liner, so you can use it for execution of single .ps1 file.
If you want to change messages to English throughout every command in a PowerShell window, you have to change the culture setting cached in PowerShell runtime with reflection like this:
# example: Set-PowerShellUICulture -Name "en-US"
function Set-PowerShellUICulture {
param([Parameter(Mandatory=$true)]
[string]$Name)
process {
$culture = [System.Globalization.CultureInfo]::CreateSpecificCulture($Name)
$assembly = [System.Reflection.Assembly]::Load("System.Management.Automation")
$type = $assembly.GetType("Microsoft.PowerShell.NativeCultureResolver")
$field = $type.GetField("m_uiCulture", [Reflection.BindingFlags]::NonPublic -bor [Reflection.BindingFlags]::Static)
$field.SetValue($null, $culture)
}
}
(from https://gist.github.com/sunnyone/7486486)

Related

Change Default Windows Sound With Powershell

I would like to find a way to change the windows default sound with powershell.
In vbs it is written like this:
WshShell.RegWrite "HKCU\AppEvents\Schemes\Apps\.Default\.Default\.Current\","c:\windows\media\horn.wav","REG_SZ"
I tried invoking the command but did not know the correct way to do it.
There are (at at least) two pretty simple ways to do this in PowerShell. The first is to use the WShell from within PowerShell (at least in Windows PowerShell 5.1 - unsure about Core):
$wshell = New-Object -ComObject WScript.Shell
$wshell.RegWrite("HKCU\AppEvents\Schemes\Apps\.Default\.Default\.Current\","c:\windows\media\horn.wav","REG_SZ")
The second is a more built-in way using PowerShell's Set-ItemProperty cmdlet - which should work in Core versions.
$SetItemProperty = #{
Path = "HKCU:\AppEvents\Schemes\Apps\.Default\.Default\.Current\"
Name = "(default)"
Value = "c:\windows\media\horn.wav"
}
Set-ItemPoperty #SetItemProperty
(Note: using the hashtable variable with the # symbol instead of the common $ symbol is called Splatting; it's effectively just matching up parameter names to values so I don't have to write out a really (horizontally) long command.)
Powershell does not have a native cmdlets for that, beside you would have to use C# to get that functionality in powershell.
Luckily, someone did the hardwork and put that in to a module, check the following repo :
https://github.com/frgnca/AudioDeviceCmdlets
There are detailed instructions on how to install the module, once you done that you can see all devices
Get-AudioDevice -List
Index : 1
Default : True
Type : Playback
Name : Speakers (2- Jabra EVOLVE LINK)
ID : {0.0.0.00000000}.{8c58263c-e6a0-4c7b-8e51-5231f04cbcb9}
Device : CoreAudioApi.MMDevice
From there you can see the current Default device and change it however you like.

Temporarily change powershell language to English?

I wrote some software that uses the output of system (powershell) commands, but did not foresee that the output would be different for languages other than English.
Is there a way to temporarily change the language in Powershell to English for just that one, single powershell session?
Notes
In case it is of significance, the particular powershell code I wish to run is netstat -n -a
I have come across some ways to change powershell language (e.g. here, here). But I want to be careful not to change it permanently! (that would be bad)
(a) For external programs such as netstat.exe, there is unfortunately no way (that I know of) to change the UI language in-session:
On Windows Server 2012 / Windows 8 and above, the Set-WinUILanguageOverride cmdlet allows you to (persistently) change the system-wide UI language for the current user, but that only takes effect in future logon sessions - that is, logging off and back on or a reboot are required.
As an aside: On Windows Server 2012 / Windows 8 and above, there is also the Set-Culture cmdlet, but its purpose is not to change the UI culture (display language), but only culture-specific settings such as date, number, and currency formats. It too changes the setting persistently for the current user, but only requires a new session (process) for the change to take effect.
(b) For PowerShell commands and .NET types, there is an in-session (non-persistent) solution - assuming the commands are culture-aware and come with localized strings:
Set [cultureinfo]::CurrentUICulture (temporarily) to the desired culture name (use [cultureinfo]::GetCultures('SpecificCultures') to see all predefined ones) ; e.g., [cultureinfo]::CurrentUICulture = 'en-US'
Complementarily, you may want to set [cultureinfo]::CurrentCulture (note the missing UI part) as well, which determines the culture-specific number, date, ... formatting.
In older versions of PowerShell / .NET, you'll have to set these properties on [System.Threading.Thread]::CurrentThread instead; e.g.,
[System.Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US'
See the bottom section for helper function Use-Culture that wraps this functionality for execution of code with a different culture temporarily in effect; here's an example call
with the culture-sensitive Get-LocalGroupMember cmdlet:
# Try with values other than "en-US", e.g. "fr-FR" to see localized
# values in the "ObjectClass" output column.
Use-Culture en-US { Get-LocalGroupMember Administrators }
An ad hoc example, if you don't want to define a helper function (only the UI culture is changed here):
& {
$prev=[cultureinfo]::CurrentUICulture
[cultureinfo]::CurrentUICulture='en-US'
Get-LocalGroupMember Administrators
[cultureinfo]::CurrentUICulture=$prev
}
Caveats:
PowerShell [Core] itself is not localized yet, as of v7.2.x; progress is being tracked in GitHub issue #666; however, the solution below does work with third-party modules that ship with localized messages and help content, as well as select Windows-specific modules that talk to platform APIs, such as the Microsoft.PowerShell.LocalAccounts module, whose Get-LocalGroupMember cmdlet was used in the example above.
Due to a bug in Windows PowerShell (PowerShell [Core] v6+ is not affected), in-session changes to [cultureinfo]::CurrentUICulture and [cultureinfo]::CurrentCulture are automatically reset at the command prompt, whenever a command finishes executing; however, for a given script the changes remain in effect for the entire script and its callees - see this answer.
Taking a step back:
I wrote some software that uses the output of system (powershell) commands, but did not foresee that the output would be different for languages other than English.
This is precisely why it's generally worth looking for PowerShell-native solutions as opposed to calling external programs:
Instead of having to parse - possibly localized - text, as with netstat.exe, for instance, PowerShell commands return objects whose properties you can robustly access in a culture-independent fashion.
Specifically, Mathias R. Jessen suggests looking at Get-NetTCPConnection as a PowerShell alternative to netstat.exe (available on Windows Server 2012 / Windows 8 and above).
Function Use-Culture's source code:
Note: The code was gratefully adapted from this venerable blog post; it is designed
# Runs a script block in the context of the specified culture, without changing
# the session's culture persistently.
# Handy for quickly testing the behavior of a command in the context of a different culture.
# Example:
# Use-Culture fr-FR { Get-Date }
function Use-Culture
{
param(
[Parameter(Mandatory)] [cultureinfo] $Culture,
[Parameter(Mandatory)] [scriptblock] $ScriptBlock
)
# Note: In Windows 10, a culture-info object can be created from *any* string.
# However, an identifier that does't refer to a *predefined* culture is
# reflected in .LCID containing 4096 (0x1000)
if ($Culture.LCID -eq 4096) { Throw "Unrecognized culture: $($Culture.DisplayName)" }
# Save the current culture / UI culture values.
$PrevCultures = [Threading.Thread]::CurrentThread.CurrentCulture, [Threading.Thread]::CurrentThread.CurrentUICulture
try {
# (Temporarily) set the culture and UI culture for the current thread.
[Threading.Thread]::CurrentThread.CurrentCulture = [Threading.Thread]::CurrentThread.CurrentUICulture = $Culture
# Now invoke the given code.
& $ScriptBlock
}
finally {
# Restore the previous culture / UI culture values.
[Threading.Thread]::CurrentThread.CurrentCulture = $PrevCultures[0]
[Threading.Thread]::CurrentThread.CurrentUICulture = $PrevCultures[1]
}
}
Original author of this code is #Scepticalist.
Run this from powershell console. It will change the culture to en-US for current session.
function Set-CultureWin([System.Globalization.CultureInfo] $culture) { [System.Threading.Thread]::CurrentThread.CurrentUICulture = $culture ; [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture } ; Set-CultureWin en-US ; [system.threading.thread]::currentthread.currentculture
Then you have to use the command Get-NetTCPConnection Instead of netstat. For its usage see https://learn.microsoft.com/en-us/powershell/module/nettcpip/get-nettcpconnection?view=win10-ps

Get output when using .run

I'm trying to run a program that will web scrape from Pastebin using PowerShell. I used the following code to do so:
Set Wshell = CreateObject("WScript.Shell")
Wshell.Run "%ComSpec% /c powershell & $result = Invoke-WebRequest
""https://pastebin.com/raw/wAhYB4UY"" & $result.content ", 0, True
$result.content will bring up everything I need from Pastebin. How can I transfer $result.content to a VBScript variable?
I know this is possible using the Exec() method as demonstrated here, but I can't use it because I want my code to stay hidden, which to my knowledge is not possible with Exec() (without having a window popping and closing)
I also don't want to use File I/O in Powershell because that can really complicate other things I want my program to do in the future; however, If absolutely no options are available, then I can use it.
EDIT: Some readers pointed out that my script only consists of running Powershell, so why not program my script in PowerShell? Well, not everything I am planning for this script to do can be done in PS. for example, I want my script to type some stuff outside of PS. I also want to wait until the user has pressed a certain key, in my case PrtSc (which will create a popup a message using MsgBox).
$Path = 'https://pastebin.com/raw/etc'
$Raw = Invoke-WebRequest $Path
$Raw.Content
What do you want to do with the data that using VBScript is the preferable tool?

How to format output in Posh Server (Powershell web server)?

I am currently trying the Powershell web server PoSH (http://poshserver.net/) for some administration reports. But i don't know how to format ouput.
From the start: i start the console with the default shortcut, with admin rights. I type Import-Module PoSHServer, then Start-PoSHServer. The web server starts, then i create a simple index.ps1 file, with just one line of code in the body section: $(command).
For example, i want to use the Get-Service Mpssvc command, but what i obtain is :
System.ServiceProcess.ServiceController
I try Get-Service MpsSvc | Select Name,Status. Output:
#{Name=MpsSvc; Status=Running}
Same thing for cmdlets Get-Process, i have an output with list of processes but it appears like this: System.Diagnostics.Process (AcroRd32) ...
However, some cmlets just like the Get-Date (used in the Posh demonstration web page) works fine and have a "normal" output.
I read the documentation, but there is no example which can help me for that.
How can i write powershell code to obtain a "clean" and console-like output?
I just downloaded and installed Posh-Server yesterday after reading this post.
If you want output to look like console inside a web-page you are probably looking at this from the wrong angle, you need to think string not console. Your code is supposed to be running inside of a here-string, in the example. So I got the hint here that the standard console formatter does not apply, posh-server will use whatever it wants to to turn your returned object into a STRING!. Your code output will get turned into a string using whatever formatter it deems applies unless you explicitly return a string - which the example script does correctly do. So try this on the console
get-process "power*" | out-string -width 80
And then try it in your posh-server script.
You probably really wanted this:
Get-Service MpsSvc | Select Name,Status | out-string -width 120
Hope that helps - I think the lack of examples in this project is a good thing because this is really a very simplistic web-server; lots of conceptual thinking required before you even start :) .

Execute a parameter passed into a powershell script as if it were a line in the script

I've got a wrapper powershell script that I'm hoping to use to automate a few things. It's pretty basic, and accepts a parameter that I want the script to run as if it were a line in the script. I absolutely cannot get it to work.
example:
param( [string[]] $p)
echo $p
# Adds the base cmdlets
Add-PSSnapin VMware.VimAutomation.Core
# Add the following if you want to do things with Update Manager
Add-PSSnapin VMware.VumAutomation
# This script adds some helper functions and sets the appearance. You can pick and choose parts of this file for a fully custom appearance.
. "C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-VIToolkitEnvironment.ps1"
$p
In the example above, I want $p to execute as if it were a line in the script. I know this isn't secure, and that's probably where the problem lies.
Here is how I try running the script and passing in a parameter for $p:
D:\ps\test>powershell -command "D:\ps\test\powershell_wrapper.ps1" 'Suspend-VM servername -Verbose -Confirm:$False'
How can I get my parameter of 'Suspend-VM servername -Verbose -Confirm:$False' to run inside my script? If I just include the value in the script instead of pass it in as a parameter it runs without any issues...
You can basically approach this two ways, depending on what your needs really are and how you want to structure your code.
Approach #1 - Invoke-Expression
Invoke-Expression basically allows you to treat a string like an expression and evaluate it. Consider the following trivial example:
Invoke-Expression '{"Hello World"}'
That will evaluate the string as if it were an expression typed in directly, and place the string "Hello World" on the pipeline. You could use that to take your string parameter and run it on-the-fly in your script.
Approach #2 - Using a ScriptBlock
PowerShell has a special data type called a ScriptBlock, where you can bind a script to a variable, and then invoke that script as part of your code. Again, here is a trivial example:
function Test-SB([ScriptBlock]$sb) {
$sb.Invoke()
}
Test-SB -sb {"Hello World"}
This example creates a function with a single parameter $sb that is of type ScriptBlock. Notice the parameter is bound to the actual chunk of code {"Hello World"}? That code is assigned to the $sb parameter, and then a call to the .Invoke method actually executes the code. You could adapt your code to take in a ScriptBlock and invoke it as part of your script.
Approach #3 - Updating your profile
OK, so I said there were two ways to approach it. There is actually a third... sort of... You could add the VMWare cmdlets to your $profile so they are always present and you don't need your wrapper to load in those libraries. Granted, this is a pretty big hammer - but it might make sense if this is the environment you are constantly working in. You could also just create a shortcut to PowerShell that runs a .ps1 on startup that includes those libraries and hangs around (this is what MS did with the SharePoint admin shell and several others). Take a look at this TechNet page to get more info on the $profile and if it can help you out:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb613488.aspx