Problem with downloading language local experience file using Powershell script - powershell

I use a fantastic script to download LXP files from microsoft and install them. This script works fine with languages like: en-us, pl-pl, zh-tw, zh-cn, fr-fr, ja-jp and maybe more BUT there is a strange issue with languages like italian (it-it), spanish (es-es) and portuguese (pt-pt). The script shows an error that the Invoke-WebRequest can't connect to the server and the rest of script fails because next variables are empty. Maybe sb will have an idea how to solve it.
There is a script I am talking about with es-es language:
# the language we want as new default
$language = "es-ES"
Start-Transcript -Path "$env:TEMP\LXP-SystemContext-Installer-$language.log" | Out-Null
# https://www.microsoft.com/de-de/p/deutsch-local-experience-pack/9p6ct0slw589
$applicationId = "9NWVGWLHPB1Z" # spanish
$webpage = Invoke-WebRequest -UseBasicParsing -Uri "https://bspmts.mp.microsoft.com/v1/public/catalog/Retail/Products/$applicationId/applockerdata"
$packageFamilyName = ($webpage | ConvertFrom-JSON).packageFamilyName
$skuId = 0016
$geoId = 217 # Spain
$inputLanguageID = "0c0a:0000040a" # es-es
# custom folder for temp scripts
"...creating custom temp script folder"
$scriptFolderPath = "$env:SystemDrive\ProgramData\CustomTempScripts"
New-Item -ItemType Directory -Force -Path $scriptFolderPath
"`n"
$languageXmlPath = $(Join-Path -Path $scriptFolderPath -ChildPath "MUI.xml")
# language xml definition for intl.cpl call to switch the language 'welcome screen' and 'new user' defaults
$languageXml = #"
<gs:GlobalizationServices xmlns:gs="urn:longhornGlobalizationUnattend">
<!-- user list -->
<gs:UserList>
<gs:User UserID="Current" CopySettingsToDefaultUserAcct="true" CopySettingsToSystemAcct="true"/>
</gs:UserList>
<!-- GeoID -->
<gs:LocationPreferences>
<gs:GeoID Value="$geoId"/>
</gs:LocationPreferences>
<!-- UI Language Preferences -->
<gs:MUILanguagePreferences>
<gs:MUILanguage Value="$language"/>
</gs:MUILanguagePreferences>
<!-- system locale -->
<gs:SystemLocale Name="$language"/>
<!-- input preferences -->
<gs:InputPreferences>
<gs:InputLanguageID Action="add" ID="$inputLanguageID" Default="true"/>
</gs:InputPreferences>
<!-- user locale -->
<gs:UserLocale>
<gs:Locale Name="$language" SetAsCurrent="true" ResetAllSettings="false"/>
</gs:UserLocale>
</gs:GlobalizationServices>
"#
$userConfigScriptPath = $(Join-Path -Path $scriptFolderPath -ChildPath "UserConfig.ps1")
# we could encode the complete script to prevent the escaping of $, but I found it easier to maintain
# to not encode. I do not have to decode/encode all the time for modifications.
$userConfigScript = #"
`$language = "$language"
Start-Transcript -Path "`$env:TEMP\LXP-UserSession-Config-`$language.log" | Out-Null
`$geoId = $geoId
"explicitly register the LXP in current user session (Add-AppxPackage -Register ...)"
`$appxLxpPath = (Get-AppxPackage | Where-Object Name -Like *LanguageExperiencePack`$language).InstallLocation
Add-AppxPackage -Register -Path "`$appxLxpPath\AppxManifest.xml" -DisableDevelopmentMode
# important for regional change like date and time...
"Set-WinUILanguageOverride = `$language"
Set-WinUILanguageOverride -Language `$language
"Set-WinUserLanguageList = `$language"
#Set-WinUserLanguageList `$language -Force
# changed handling due to new knowledge :-)
# https://oliverkieselbach.com/2021/01/13/company-portal-stuck-in-a-different-language/
`$OldList = Get-WinUserLanguageList
`$UserLanguageList = New-WinUserLanguageList -Language `$language
`$UserLanguageList += `$OldList | where { `$_.LanguageTag -ne `$language }
"Setting new user language list:"
`$UserLanguageList | select LanguageTag
""
"Set-WinUserLanguageList -LanguageList ..."
Set-WinUserLanguageList -LanguageList `$UserLanguageList -Force
"Set-WinSystemLocale = `$language"
Set-WinSystemLocale -SystemLocale `$language
"Set-Culture = `$language"
Set-Culture -CultureInfo `$language
"Set-WinHomeLocation = `$geoId"
Set-WinHomeLocation -GeoId `$geoId
Stop-Transcript -Verbose
"#
Set-WinUserLanguageList -LanguageList $language -Force
$userConfigScriptHiddenStarterPath = $(Join-Path -Path $scriptFolderPath -ChildPath "UserConfigHiddenStarter.vbs")
$userConfigScriptHiddenStarter = #"
sCmd = "powershell.exe -ex bypass -file $userConfigScriptPath"
Set oShell = CreateObject("WScript.Shell")
oShell.Run sCmd,0,true
"#
"...set reg key: BlockCleanupOfUnusedPreinstalledLangPacks = 1"
& REG add "HKLM\Software\Policies\Microsoft\Control Panel\International" /v BlockCleanupOfUnusedPreinstalledLangPacks /t REG_DWORD /d 1 /f /reg:64
"`n"
$packageName = "Microsoft.LanguageExperiencePack$language"
# check for installed Language Experience Pack (LXP), maybe already installed?
$status = $(Get-AppxPackage -AllUsers -Name $packageName).Status
# create CIM session here as we need it for Store updates as well
$session = New-CimSession
try {
if ($status -ne "Ok")
{
try {
$namespaceName = "root\cimv2\mdm\dmmap"
$omaUri = "./Vendor/MSFT/EnterpriseModernAppManagement/AppInstallation"
$newInstance = New-Object Microsoft.Management.Infrastructure.CimInstance "MDM_EnterpriseModernAppManagement_AppInstallation01_01", $namespaceName
$property = [Microsoft.Management.Infrastructure.CimProperty]::Create("ParentID", $omaUri, "string", "Key")
$newInstance.CimInstanceProperties.Add($property)
$property = [Microsoft.Management.Infrastructure.CimProperty]::Create("InstanceID", $packageFamilyName, "String", "Key")
$newInstance.CimInstanceProperties.Add($property)
$flags = 0
$paramValue = [Security.SecurityElement]::Escape($('<Application id="{0}" flags="{1}" skuid="{2}"/>' -f $applicationId, $flags, $skuId))
$params = New-Object Microsoft.Management.Infrastructure.CimMethodParametersCollection
$param = [Microsoft.Management.Infrastructure.CimMethodParameter]::Create("param", $paramValue, "String", "In")
$params.Add($param)
# we create the MDM instance and trigger the StoreInstallMethod to finally download the LXP
$instance = $session.CreateInstance($namespaceName, $newInstance)
$result = $session.InvokeMethod($namespaceName, $instance, "StoreInstallMethod", $params)
"...Language Experience Pack install process triggered via MDM/StoreInstall method"
}
catch [Exception] {
write-host $_ | out-string
$exitcode = 1
}
}
if (($result.ReturnValue.Value -eq 0) -or ($status -eq "Ok")) {
if ($status -ne "Ok")
{
"...busy wait until language pack found, max 15 min."
$counter=0
do {
Start-Sleep 10
$counter++
# check for installed Language Experience Pack (LXP)
$status = $(Get-AppxPackage -AllUsers -Name $packageName).Status
} while ($status -ne "Ok" -and $counter -ne 90) # 90x10s sleep => 900s => 15 min. max wait time!
}
# print some LXP package details for the log
Get-AppxPackage -AllUsers -Name $packageName
if ($status -eq "Ok") {
"...found Microsoft.LanguageExperiencePack$language with Status=Ok"
"...trigger install for language FOD packages"
# add Windows capabilities / FODs to avoid UAC dialog after restart
# Parameter -Online will reach out to Windows Update to get the latest correct source files
Get-WindowsCapability -Online | Where-Object Name -ilike "Language.*~~~$($language)~*" | ForEach-Object {
if ($_.State -ine "Installed") {
"Adding windows capability '$($_.Name)'..."
$_ | Add-WindowsCapability -Online | Out-Null
}
else {
"Windows capability '$($_.Name)' is already installed.`n"
}
}
"`n"
"...trigger language change for current user session via ScheduledTask = LXP-UserSession-Config-$language"
Out-File -FilePath $userConfigScriptPath -InputObject $userConfigScript -Encoding ascii
Out-File -FilePath $userConfigScriptHiddenStarterPath -InputObject $userConfigScriptHiddenStarter -Encoding ascii
# REMARK: usag of wscript as hidden starter may be blocked because of security restrictions like AppLocker, ASR, etc...
# switch to PowerShell if this represents a problem in your environment.
$taskName = "LXP-UserSession-Config-$language"
$action = New-ScheduledTaskAction -Execute "wscript.exe" -Argument "$userConfigScriptHiddenStarterPath"
$trigger = New-ScheduledTaskTrigger -AtLogOn
$principal = New-ScheduledTaskPrincipal -UserId (Get-CimInstance –ClassName Win32_ComputerSystem | Select-Object -expand UserName)
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries
$task = New-ScheduledTask -Action $action -Trigger $trigger -Principal $principal -Settings $settings
Register-ScheduledTask $taskName -InputObject $task
Start-ScheduledTask -TaskName $taskName
Start-Sleep -Seconds 30
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
# trigger 'LanguageComponentsInstaller\ReconcileLanguageResources' otherwise 'Windows Settings' need a long time to change finally
"...trigger ScheduledTask = LanguageComponentsInstaller\ReconcileLanguageResources"
Start-ScheduledTask -TaskName "\Microsoft\Windows\LanguageComponentsInstaller\ReconcileLanguageResources"
Start-Sleep 10
# change 'welcome screen' and 'new user' language defaults
"...trigger language change for welcome screen and new user defaults"
Out-File -FilePath $languageXmlPath -InputObject $languageXml -Encoding ascii
# check eventlog 'Microsoft-Windows-Internationl/Operational' for troubleshooting
& $env:SystemRoot\System32\control.exe "intl.cpl,,/f:`"$languageXmlPath`""
# trigger store updates, there might be new app versions due to the language change
"...trigger MS Store updates for app updates"
Get-CimInstance -Namespace "root\cimv2\mdm\dmmap" -ClassName "MDM_EnterpriseModernAppManagement_AppManagement01" | Invoke-CimMethod -MethodName "UpdateScanMethod"
$exitcode = 0
}
}
else {
$exitcode = 1
}
"...cleanup and finish"
try {
$session.DeleteInstance($namespaceName, $instance) | Out-Null
Remove-CimSession -CimSession $session
} catch {}
Remove-Item -Path $scriptFolderPath -Force -Recurse -ErrorAction SilentlyContinue
}
catch [Exception] {
# maybe a left over to clean, but prevent aditional errors and set exitcode
try {
$session.DeleteInstance($namespaceName, $instance) | Out-Null
} catch {}
$exitcode = 1
}
if ($exitcode -eq 0) {
$installed = 1
}
else {
$installed = 0
}
# IME app install runs by default in 32-bit so we write explicitly to 64-bit registry
& REG add "HKLM\Software\MyIntuneApps" /v "SetLanguage-$language" /t REG_DWORD /d $installed /f /reg:64 | Out-Null
Stop-Transcript -Verbose
exit $exitcode
I tried to find my mistakes in ID etc also checking if the requested language is available (and it is!). I tried to run the invoke-webrequest alone and it also works.
The error msg:
PS>TerminatingError(Invoke-WebRequest): "Unable to connect to remote server"
Invoke-WebRequest : Unable to connect to remote server
At C:\Windows\IMECache\ffa6ea3b-604e-41c3-98b9-dd30e2f9c714_1\languagechange.ps1:47 char:12
+ $webpage = Invoke-WebRequest -UseBasicParsing -Uri "https://bspmts.mp ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebExcep
tion
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Invoke-WebRequest : Unable to connect to remote server
At C:\Windows\IMECache\ffa6ea3b-604e-41c3-98b9-dd30e2f9c714_1\languagechange.ps1:47 char:12
+ $webpage = Invoke-WebRequest -UseBasicParsing -Uri "https://bspmts.mp ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebExc
eption
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
ConvertFrom-Json : Cannot bind argument to parameter 'InputObject' because it is null.
At C:\Windows\IMECache\ffa6ea3b-604e-41c3-98b9-dd30e2f9c714_1\languagechange.ps1:48 char:34
+ $packageFamilyName = ($webpage | ConvertFrom-JSON).packageFamilyName
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [ConvertFrom-Json], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ConvertFromJs
onCommand
ConvertFrom-Json : Cannot bind argument to parameter 'InputObject' because it is null.
At C:\Windows\IMECache\ffa6ea3b-604e-41c3-98b9-dd30e2f9c714_1\languagechange.ps1:48 char:34
+ $packageFamilyName = ($webpage | ConvertFrom-JSON).packageFamilyName
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [ConvertFrom-Json], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ConvertFrom
JsonCommand

Related

specified cast is not valid Powershell when changing automatically detect settings and auto config script for LAN settings in Internet Properties

I'm trying to enable and disable the automatically detect settings and automatic configuration script for LAN settings in Internet Explorer(IE). The script is working fine on windows 7 32,64 bit, Windows 10 32,64 bit but on windows 8 64bit it's throwing error specified cast is not valid when disabling the automatically detect settings and enabling/disabling automatic configuration script for LAN settings.
[string]$ProxyRegRoot = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings'
#this setting is affected by multiple options, so fetch once here
[string]$DefaultConnectionSettingsPath = (Join-Path $ProxyRegRoot 'Connections')
[byte[]]$DefaultConnectionSettings = Get-ItemProperty -Path $DefaultConnectionSettingsPath -Name 'DefaultConnectionSettings' | Select-Object -ExpandProperty 'DefaultConnectionSettings'
[byte]$MaskProxyEnabled = 2
[byte]$MaskUseAutomaticConfigurationScript = 4
[byte]$MaskAutomaticDetect = 8
[int]$ProxyConnectionSettingIndex = 8
#Auto-ConfigScript for LAN Settings
function Auto-ConfigScript {
#region config script
$UseAutomaticConfigurationScriptInput = Read-Host "Enable Automatic Configuration Script [y]Yes [n]No"
if ($UseAutomaticConfigurationScriptInput -eq "yes" -or $UseAutomaticConfigurationScriptInput -eq "y") {
$UseAutomaticConfigurationScript = $true
$UseAutomaticConfigurationScriptAddress = Read-Host "Enter Address "
Set-ItemProperty -Path $ProxyRegRoot -Name AutoConfigURL -Value $UseAutomaticConfigurationScriptAddress
$DefaultConnectionSettings[$ProxyConnectionSettingIndex] = $DefaultConnectionSettings[$ProxyConnectionSettingIndex] -bor $MaskUseAutomaticConfigurationScript
}
else {
$UseAutomaticConfigurationScript = $false
$DefaultConnectionSettings[$ProxyConnectionSettingIndex] = $DefaultConnectionSettings[$ProxyConnectionSettingIndex] -band (-bnot $MaskUseAutomaticConfigurationScript)
}
#endregion
#persist the updates made above
Set-ItemProperty -Path $DefaultConnectionSettingsPath -Name 'DefaultConnectionSettings' -Value $DefaultConnectionSettings
}
function Auto-DetectSettings {
#region auto detect
$AutomaticDetect = Read-Host "Enable Automatically detect settings [y]Yes [n]No"
if ($AutomaticDetect -eq $true -or $AutomaticDetect -eq "yes" -or $AutomaticDetect -eq "y") {
Set-ItemProperty -Path $ProxyRegRoot -Name AutoDetect -Value 1
$DefaultConnectionSettings[$ProxyConnectionSettingIndex] = $DefaultConnectionSettings[$ProxyConnectionSettingIndex] -bor $MaskAutomaticDetect
}
else {
Set-ItemProperty -Path $ProxyRegRoot -Name AutoDetect -Value 0
$DefaultConnectionSettings[$ProxyConnectionSettingIndex] = $DefaultConnectionSettings[$ProxyConnectionSettingIndex] -band (-bnot $MaskAutomaticDetect)
}
#endregion
#persist the updates made above
Set-ItemProperty -Path $DefaultConnectionSettingsPath -Name 'DefaultConnectionSettings' -Value $DefaultConnectionSettings
}
Error Traceback
Specified cast is not valid.
At C:\Users\afs.prithvi\Downloads\Internet Properties Win7 32-64 bit working.ps1:23 char:9
+ $DefaultConnectionSettings[$ProxyConnectionSettingIndex] = $DefaultConne ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], InvalidCastException
+ FullyQualifiedErrorId : System.InvalidCastException
Update
error is occuring at line number 23

Powershell will not start or stop Windows service with nssm

i have a script made in powershell and i am using nssm to create as a service to be executed every "x" time, however when starting the service it generates error and does not execute.
I have full administrator rights and I even tried to run PowerShell as an administrator without success.
If I run the script directly it works, however using nssm it is not working.
The error that happens is this:
Start-Service: Service 'nice (nice)' start failed.
At C: \ Program Files \ NICE Systems \ nssm.ps1: 10 char: 14
Start-Service <<<< $ serviceName
CategoryInfo: OpenError: (System.ServiceProcess.ServiceController: ServiceController) [Start-Service],
ServiceCommandException
FullyQualifiedErrorId: StartServiceFailed, Microsoft.PowerShell.Commands.StartServiceCommand
nssm.ps1
$nssm = (Get-Command nssm.exe).Definition
$serviceName = 'nice'
$powershell = (Get-Command powershell.exe).Definition
$scriptPath = 'C:\Program Files\NICE Systems\script_delecao.ps1'
$arguments = '-ExecutionPolicy Bypass -NoProfile -File "{0}"' -f $scriptPath
& $nssm install $serviceName $powershell $arguments
& $nssm status $serviceName
Start-Service $serviceName
Get-Service $serviceName
script_delecao.ps1
$logPath = "C:\Program Files\NICE Systems\Logs\*\Archive\*"
# -------------------------------------------------------------------------------------------
# SET $NDAYS WITH THE NUMBER OF DAYS TO KEEP IN LOG FOLDER.
$nDays = 180
# -------------------------------------------------------------------------------------------
# SET $EXTENSIONS WITH THE FILE EXTENSION TO DELETE.
# YOU CAN COMBINE MORE THAN ONE EXTENSION: "*.LOG, *.TXT,"
$Extensions = "*.log*"
# -------------------------------------------------------------------------------------------
# PAY ATTENTION! IF YOU COMBINE MORE THAN ONE LOG PATH AND EXTENSIONS,
# MAKE SURE THAT YOU ARE NOT REMOVING FILES THAT CANNOT BE DELETED
# -------------------------------------------------------------------------------------------
$PathDelete = "C:\Program Files\NICE Systems\Delecoes"
while ($true) {
If(!(test-path $PathDelete))
{
New-Item -ItemType Directory -Force -Path $PathDelete
}
$LogDate = (Get-Date).ToString("dd_MM_yyyy")
$DateTime = (Get-Date).ToString("yyy-MM-ddThh:mm:ss")
$Files = Get-Childitem $LogPath -Include $Extensions -Recurse | Where `
{$_.LastWriteTime -le (Get-Date).AddDays(-$nDays)}
foreach ($File in $Files)
{
if ($File -ne $NULL)
{
$Log = $DateTime + " - O arquivo " + $File + " foi deletado "
$Log | Out-File -Append $PathDelete\DeleteLogFile_$LogDate.log
Remove-Item $File.FullName| out-null
}
}
# Add a sleep at the end of the loop to prevent the script from eating
# too much CPU time
$Log = $DateTime + " FINAL DO ARQUIVO "
$Log | Out-File -Append $PathDelete\DeleteLogFile_$LogDate.log
Start-Sleep -Seconds 300
}
I believe I have a similar scenario where I cannot back-up Bamboo file system while it's running. My back-up executes from a rundeck server via Remote PowerShell, and even though the user has local admin rights it cannot stop and start services using NSSM. So I use this function to run the command elevated
ELEVAT "nssm stop bamboo"
tar --exclude=./logs --exclude=./temp --exclude=*.log --exclude=*.jar --verbose -czf E:\dropfolder\bamboo-home.tar.gz --directory=E:\bamboo-home .
ELEVAT "nssm start bamboo"
the function itself...
function ELEVAT ($command) {
$scriptBlock = [scriptblock]::Create($command)
configuration elevated {
Import-DscResource -ModuleName 'PSDesiredStateConfiguration'
Set-StrictMode -Off
Node localhost {
Script execute {
SetScript = $scriptBlock
TestScript = {
if (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Verbose "Verified Elevated Session"
return $false
} else {
Write-Verbose "Not an Elevated Session!"
exit 9996
}
}
GetScript = { return #{ 'Result' = 'RUN' } }
}
}
}
$mof = elevated
Start-DscConfiguration ./elevated -Wait -Verbose -Force
if ( $error ) { Write-Host "[ELEVAT][WARN] `$Error[] = $Error" ; $Error.clear()
}
}

Issues running a Powershell script without running a VB script first

I was looking for a solution to pin a shortcut or program to the task in win 10 with PS. I found Pin program to taskbar using PS in Windows 10. The VB Script works,
If WScript.Arguments.Count < 1 Then WScript.Quit
'----------------------------------------------------------------------
Set objFSO = CreateObject("Scripting.FileSystemObject")
objFile = WScript.Arguments.Item(0)
sKey1 = "HKCU\Software\Classes\*\shell\{:}\\"
sKey2 = Replace(sKey1, "\\", "\ExplorerCommandHandler")
'----------------------------------------------------------------------
With WScript.CreateObject("WScript.Shell")
KeyValue = .RegRead("HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer" & _
"\CommandStore\shell\Windows.taskbarpin\ExplorerCommandHandler")
.RegWrite sKey2, KeyValue, "REG_SZ"
With WScript.CreateObject("Shell.Application")
With .Namespace(objFSO.GetParentFolderName(objFile))
With .ParseName(objFSO.GetFileName(objFile))
.InvokeVerb("{:}")
End With
End With
End With
.Run("Reg.exe delete """ & Replace(sKey1, "\\", "") & """ /F"), 0, True
End With
'----------------------------------------------------------------------
I can invoke VB script from PS but a helpful person converted the script to PS
Param($Target)
$KeyPath1 = "HKCU:\SOFTWARE\Classes"
$KeyPath2 = "*"
$KeyPath3 = "shell"
$KeyPath4 = "{:}"
$ValueName = "ExplorerCommandHandler"
$ValueData =
(Get-ItemProperty `
("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\" + `
"CommandStore\shell\Windows.taskbarpin")
).ExplorerCommandHandler
$Key2 = (Get-Item $KeyPath1).OpenSubKey($KeyPath2, $true)
$Key3 = $Key2.CreateSubKey($KeyPath3, $true)
$Key4 = $Key3.CreateSubKey($KeyPath4, $true)
$Key4.SetValue($ValueName, $ValueData)
$Shell = New-Object -ComObject "Shell.Application"
$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)
$Item = $Folder.ParseName((Get-Item $Target).Name)
$Item.InvokeVerb("{:}")
$Key3.DeleteSubKey($KeyPath4)
if ($Key3.SubKeyCount -eq 0 -and $Key3.ValueCount -eq 0) {
$Key2.DeleteSubKey($KeyPath3)
}
However this PS script will not run unless the VB script has been ran at least one time. Is there a way to make the PS script work without having to run the VB script?
The error I get when trying to run the PS script without running the VB script at least once before it:
You cannot call a method on a null-valued expression.
At \\server\Utilities\TaskbarPin.ps1:41 char:5
+ $Key3 = $Key2.CreateSubKey($KeyPath3, $true)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At \\server\Utilities\TaskbarPin.ps1:42 char:5
+ $Key4 = $Key3.CreateSubKey($KeyPath4, $true)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At \\server\Utilities\TaskbarPin.ps1:43 char:5
+ $Key4.SetValue($KeyValue, $ValueData)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At \\server\Utilities\TaskbarPin.ps1:50 char:5
+ $Key3.DeleteSubKey($KeyPath4)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
I do not get an error after using the VB script once to do the task.
You should not have been impacted by this this way.
The code works as designed, but you have to call the path the exe fully.
I just converted it to a function and it is successful with no other dependencies.
Function Add-AppToTaskbar
{
[cmdletbinding()]
Param
(
[string]$Target
)
$KeyPath1 = "HKCU:\SOFTWARE\Classes"
$KeyPath2 = "*"
$KeyPath3 = "shell"
$KeyPath4 = "{:}"
$ValueName = "ExplorerCommandHandler"
$ValueData =
(Get-ItemProperty `
("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\" + `
"CommandStore\shell\Windows.taskbarpin")
).ExplorerCommandHandler
$Key2 = (Get-Item $KeyPath1).OpenSubKey($KeyPath2, $true)
$Key3 = $Key2.CreateSubKey($KeyPath3, $true)
$Key4 = $Key3.CreateSubKey($KeyPath4, $true)
$Key4.SetValue($ValueName, $ValueData)
$Shell = New-Object -ComObject "Shell.Application"
$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)
$Item = $Folder.ParseName((Get-Item $Target).Name)
$Item.InvokeVerb("{:}")
$Key3.DeleteSubKey($KeyPath4)
if ($Key3.SubKeyCount -eq 0 -and $Key3.ValueCount -eq 0)
{$Key2.DeleteSubKey($KeyPath3)}
}
Add-AppToTaskbar -Target 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
BTW, these pinned things live in two places on your system:
Here:
$env:USERPROFILE\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar
Registry:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband
Both are required.
Update based on OP's comment
I just ran this locally and remotely, both are successful. See results below.
The local host I am using - WS2012R2 set as a workstation role
I don't have any W10 systems in my lab. The earlier test was on a local W10 host.
Executed in the console host, ISE and VSCode.
PS C:\Windows\system32> $env:COMPUTERNAME
LabWS01
# PS Version
PS C:\Windows\system32> $PSVersionTable
Name Value
---- -----
PSVersion 4.0
WSManStackVersion 3.0
SerializationVersion 1.1.0.1
CLRVersion 4.0.30319.42000
BuildVersion 6.3.9600.18968
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion 2.2
# the current user profile pinned location filtered for notepad*
PS C:\Windows\system32> Get-ChildItem -Path "$env:USERPROFILE\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Notepad*"
# Tested path to remote share
PS C:\Windows\system32> Test-path -Path '\\Server\ShareName\Add-AppToTaskbar.ps1'
True
# Ran the script from that remote share
PS C:\Windows\system32> \\Server\ShareName\Add-AppToTaskbar.ps1 'c:\windows\notepad.exe'
or this way...
Start-process -FilePath Powershell -ArgumentList '\\Server\ShareName\Add-AppToTaskbar.ps1 -Target C:\Windows\notepad.exe'
# Review pinned item location, filtered for notepad*
PS C:\Windows\system32> Get-ChildItem -Path "$env:USERPROFILE\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Notepad*"
Directory: C:\Users\Labuser001\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 8/9/2018 8:48 PM 791 Notepad.lnk
Shortcut shows pinned to taskbar.
So, this sounds environmental on your side. Now you can pin apps using GPO if this issue continues.
I've modified your function so that selectively pins or unpins items to the taskbar. Previously, the problem is that the pin command was not exclusive, it would unpin the application if it was already pinned. With further detection of what was pinned in a binary registry value, it has been possible to determine that the item has already been pinned and it will not attempt to pin the item twice.
Set-AppPinTaskbarCsv is a function that was customized for our environment, I only include it as an example only, if someone wanted to roll this out in a login script to ensure the users have all of the apps they need pinned, it would need a good deal of modification and simplification. It has some functions which are not included that check group membership and reformat strings to expand variables, which are not required. After pinning the applications, it displays more reliably if explorer is restarted, and the Csv function will restart explorer if any items are pinned.
Function Set-PinTaskbar {
Param (
[parameter(Mandatory=$True, HelpMessage="Target item to pin")]
[ValidateNotNullOrEmpty()]
[string] $Target
,
[Parameter(Mandatory=$False, HelpMessage="Target item to unpin")]
[switch]$Unpin
)
If (!(Test-Path $Target)) {
Write-Warning "$Target does not exist"
Break
}
$Reg = #{}
$Reg.Key1 = "*"
$Reg.Key2 = "shell"
$Reg.Key3 = "{:}"
$Reg.Value = "ExplorerCommandHandler"
$Reg.Data = (Get-ItemProperty ("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\Windows.taskbarpin")).ExplorerCommandHandler
$Reg.Path1 = "HKCU:\SOFTWARE\Classes"
$Reg.Path2 = Join-Path $Reg.Path1 $Reg.Key1
$Reg.Path3 = Join-Path $Reg.Path2 $Reg.Key2
$Reg.Path4 = Join-Path $Reg.Path3 $Reg.Key3
If (!(Test-Path -LiteralPath $Reg.Path2)) {
New-Item -ItemType Directory -Path $Reg.Path1 -Name [System.Management.Automation.WildcardPattern]::Escape($Reg.Key1)
}
If (!(Test-Path -LiteralPath $Reg.Path3)) {
New-Item -ItemType Directory -Path ([System.Management.Automation.WildcardPattern]::Escape($Reg.Path2)) -Name $Reg.Key2
}
If (!(Test-Path -LiteralPath $Reg.Path4)) {
New-Item -ItemType Directory -Path ([System.Management.Automation.WildcardPattern]::Escape($Reg.Path3)) -Name $Reg.Key3
}
Set-ItemProperty -Path ([System.Management.Automation.WildcardPattern]::Escape($Reg.Path4)) -Name $Reg.Value -Value $Reg.Data
$Shell = New-Object -ComObject "Shell.Application"
$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)
$Item = $Folder.ParseName((Get-Item $Target).Name)
# Registry key where the pinned items are located
$RegistryKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband"
# Binary registry value where the pinned items are located
$RegistryValue = "FavoritesResolve"
# Gets the contents into an ASCII format
$CurrentPinsProperty = ([system.text.encoding]::ASCII.GetString((Get-ItemProperty -Path $RegistryKey -Name $RegistryValue | Select-Object -ExpandProperty $RegistryValue)))
# Filters the results for only the characters that we are looking for, so that the search will function
[string]$CurrentPinsResults = $CurrentPinsProperty -Replace '[^\x20-\x2f^\x30-\x3a\x41-\x5c\x61-\x7F]+', ''
# Globally Unique Identifiers for common system folders, to replace in the pin results
$Guid = #{}
$Guid.FOLDERID_ProgramFilesX86 = #{
"ID" = "{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}"
"Path" = ${env:ProgramFiles(x86)}
}
$Guid.FOLDERID_ProgramFilesX64 = #{
"ID" = "{6D809377-6AF0-444b-8957-A3773F02200E}"
"Path" = $env:ProgramFiles
}
$Guid.FOLDERID_ProgramFiles = #{
"ID" = "{905e63b6-c1bf-494e-b29c-65b732d3d21a}"
"Path" = $env:ProgramFiles
}
$Guid.FOLDERID_System = #{
"ID" = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}"
"Path" = Join-Path $env:WINDIR "System32"
}
$Guid.FOLDERID_Windows = #{
"ID" = "{F38BF404-1D43-42F2-9305-67DE0B28FC23}"
"Path" = $env:WINDIR
}
ForEach ($GuidEntry in $Guid.Keys) {
$CurrentPinsResults = $CurrentPinsResults -replace $Guid.$GuidEntry.ID,$Guid.$GuidEntry.Path
}
$Split = $CurrentPinsResults -split ('C:')
$SplitOutput = #()
# Process each path entry, remove invalid characters, test to determine if the path is valid
ForEach ($Entry in $Split) {
If ($Entry.Substring(0,1) -eq '\') {
# Get a list of invalid path characters
$InvalidPathCharsRegEx = [IO.Path]::GetInvalidPathChars() -join ''
$InvalidPathChars = "[{0}]" -f [RegEx]::Escape($InvalidPathCharsRegEx)
$EntryProcessedPhase1 = "C:" + ($Entry -replace $InvalidPathChars)
$EntryProcessedPhase2 = $null
# Remove characters from the path until it is resolvable
ForEach ($Position in $EntryProcessedPhase1.Length .. 1) {
If (Test-Path $EntryProcessedPhase1.Substring(0,$Position)) {
$EntryProcessedPhase2 = $EntryProcessedPhase1.Substring(0,$Position)
Break
}
}
# If the path resolves, add it to the array of paths
If ($EntryProcessedPhase2) {
$SplitOutput += $EntryProcessedPhase2
}
}
}
$PinnedItems = #()
$Shell = New-Object -ComObject WScript.Shell
ForEach ($Path in $SplitOutput) {
# Determines if the entry in the registry is a link in the standard folder, if it is, resolve the path of the shortcut and add it to the array of pinnned items
If ((Split-Path $Path) -eq (Join-Path $env:USERPROFILE "AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar")) {
$Shell.CreateShortcut($Path).TargetPath
$PinnedItems += $Shell.CreateShortcut($Path).TargetPath
}
Else {
# If the link or executable is not in the taskbar folder, add it directly
$PinnedItems += $Path
}
}
# Unpin if the application is pinned
If ($Unpin.IsPresent) {
If ($PinnedItems -contains $Target) {
$Item.InvokeVerb("{:}")
Write-Host "Unpinning application $Target"
}
}
Else {
# Only pin the application if it hasn't been pinned
If ($PinnedItems -notcontains $Target) {
$Item.InvokeVerb("{:}")
Write-Host "Pinning application $Target"
}
}
# Remove the registry key and subkeys required to pin the application
If (Test-Path $Reg.Path3) {
Remove-Item -LiteralPath $Reg.Path3 -Recurse
}
}
Function Set-PinTaskbarCsv {
Param (
[Parameter(Mandatory=$true)]
$PinHashTable
,
[Parameter(Mandatory=$true)]
$UnpinHashTable
)
$Organization = "LIHC"
$RootRegistry = "HKCU:\Software\" + $Organization
$RootRegistryPinned = Join-Path $RootRegistry "Pinned"
# Unpin applications from taskbar
ForEach ($Entry in $UnpinHashTable.Keys) {
$Location = Format-VariablesString -String $UnpinHashTable.$Entry.Location
Add-Log "Taskbar app unpinned" $Location
Set-PinTaskbar -Target $Location -Unpin
}
# Pin applications to taskbar
$Groups = #("Group1","Group2","Group3","Group4","Group5")
ForEach ($Entry in $PinHashTable.Keys) {
$Entry
$Location = Format-VariablesString -String $PinHashTable.$Entry.Location
$ToTaskbar = [string]::IsNullOrWhiteSpace($PinHashTable.$Entry.Group1)
ForEach ($Group in $Groups) {
If (!([string]::IsNullOrWhiteSpace($PinHashTable.$Entry.$Group))) {
$ToTaskbar = (Get-UserGroups -Username $env:USERNAME -Group $PinHashTable.$Entry.$Group) -or $ToTaskbar
}
}
If (!([string]::IsNullOrWhiteSpace($PinHashTable.$Entry.TestPath))) {
$ToTaskbar = ((Test-Path $PinHashTable.$Entry.TestPath) -or (Test-Path $PinHashTable.$Entry.TestPath2)) -and $true
}
If ($ToTaskbar -and (Test-Path $Location) -and (!(Get-ItemProperty $RootRegistryPinned $Location -ErrorAction SilentlyContinue))) {
#Set-AppPinTaskbar -Application $Location
Set-PinTaskbar -Target $Location
Add-Log "Taskbar app Pinned" $Location
New-ItemProperty -Path $RootRegistryPinned -Name $Location 2>&1 > $null
$Status = $true
}
}
If ($Status) {
Get-Process -Name explorer | Stop-Process
Start-Process -FilePath explorer.exe
}
}

Function execution error: Value cannot be null [duplicate]

This question already has answers here:
How do I pass multiple parameters into a function in PowerShell?
(15 answers)
Closed 7 years ago.
I have created a PowerShell script that is triggered via Task Scheduler when a windows event occur.
The script is expected to receive eventID, eventRecordId and channel and gets event detail from Get-WinEvent and display the detail via a pop up.
Param( $eventID, $eventRecordId, $channel)
#$ErrorActionPreference="SilentlyContinue"
Stop-Transcript | out-null
$ErrorActionPreference = "Continue"
Start-Transcript -path C:\temp\output.txt -append
function PopUPEventInfo ( $eventID, $eventRecordId, $channel)
{
Add-Content -Value $eventID -Path C:\users\CFNLocalAdmin\data.txt
Add-Content -Value $eventRecordId -Path C:\users\CFNLocalAdmin\data.txt
Add-Content -Value $channel -Path C:\users\CFNLocalAdmin\data.txt
if($eventID -eq 7036)
{
$events = Get-WinEvent -LogName 'System' -FilterXPath "<QueryList><Query Path='System'><Select >*[System[(EventRecordID=8478)]]</Select></Query></QueryList>"
foreach($event in $events)
{
$eventParams=$event.ToXml()
$eventXml = [xml]$event.ToXml()
#Write-Host "Attempting parsing xml event"
$SysTime=$eventXml.Event.System.TimeCreated.SystemTime
#Write-Host $SysTime
$ProviderName=$eventXml.Event.System.Provider.Name
#Write-Host $ProviderName
$ServiceName=""
$ServiceStatus=""
$ServiceName= $eventXml.Event.EventData.Data[0].'#text'
$ServiceStatus=$eventXml.Event.EventData.Data[1].'#text'
$popipObj = New-Object -ComObject wscript.shell
$popipObj.popup("RecordID: "+$eventRecordId +", Channel :"+$channel+"Event Timestamp: "+$ServiceName +": "+$ServiceStatus)
}
}
}
PopUPEventInfo $eventID, $eventRecordId, $channel
The line
$events = Get-WinEvent -LogName 'System' -FilterXPath "<QueryList><Query Path='System'><Select >*[System[(EventRecordID=8478)]]</Select></Query></QueryList>"
works fine but when I replace constants with variables
$events = Get-WinEvent -LogName $channel -FilterXPath "<QueryList><Query Path='$channel'><Select >*[System[(EventRecordID=$eventRecordId)]]</Select></Query></QueryList>"
I get the following error.
TerminatingError(Get-WinEvent): "Value cannot be null.
Parameter name: collection"
Get-WinEvent : Value cannot be null.
Parameter name: collection
At C:\Users\CFNLocalAdmin\test.ps1:26 char:11
+ $events = Get-WinEvent -LogName $channel -FilterXPath "<QueryList><Query Path='S ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-WinEvent], ArgumentNullException
+ FullyQualifiedErrorId : System.ArgumentNullException,Microsoft.PowerShell.Commands.GetWinEventCommand
I am not sure what I am doing wrong. I can see the values of $eventID, $eventRecordId and $channel getting written to the data file but why the Get-WinEvent is giving null exception
I appreciate if anyone point me to the right direction
Most likely cause for the error is $channel being null. You are saying that you can see that it's not null because it's written to a file but I'm sceptical. Perhaps you are seeing values there from previous runs.
If you by any chance have a Cyrillic keyboard, or if you copied your script from a Cyrillic source, make sure that c, a and e in the $channel are not Cyrillic с, а and е. Powershell can stomach them happily, but $channel and $сhаnnеl are two different variables, even if you can't see that.
$channel = 'Hello'
$сhаnnеl = 'Again'
Write-Host $channel
Write-Host $сhаnnеl
Gives this output:
PS C:\WINDOWS\system32> $channel = 'Hello'
PS C:\WINDOWS\system32> $сhаnnеl = 'Again'
PS C:\WINDOWS\system32> Write-Host $channel
Hello
PS C:\WINDOWS\system32> Write-Host $сhаnnеl
Again
PS C:\WINDOWS\system32>
The issue that you are experiencing is that you are calling the function incorrectly. You have placed commas between your parameters when you call it, making them an array, so you are effectively passing all three variables to your first parameter.
PopUPEventInfo $eventID, $eventRecordId, $channel
Is effectively seen as this:
PopUPEventInfo -EventId #($eventID, $eventRecordId, $channel) -EventRecordId $null -Channel $null
If you simply remove the commas the command should work as expected.
PopUPEventInfo $eventID $eventRecordId $channel
Or fully:
PopUPEventInfo -EventId $eventID -EventRecordId $eventRecordId -Channel $channel

Upgraded to PS3, System.Net.WebClient not working on some machines

I am running in a lab environment and need to automate about 50 machines. I am trying to recover an .xml wireless network profile from a server then install it. This command is being sent from 1 server to the 50 clients.
I recently reimaged my some of my clients and upgraded from PS2 to PS3 and now my Download script is not working anymore.
It does work fine on my PS2 workstations. I am assuming that it might be a permission thing, but I'm not sure. ThrustedHosts is set to * and Script execution policy is set to Unrestricted.
Here's a snippet and the error:
function InstallProfile(){
clear
$fonction =
#'
param($profileName)
$File = "c:\profiles\profile.xml"
$webclient = New-Object System.Net.WebClient
$webclient.Proxy = $NULL
$ftp = "ftp://anonymous:anonymous#192.168.2.200/profiles/$profileName"
$uri = New-Object System.Uri($ftp)
Write-Host (hostname)
$webclient.DownloadFile($uri, $File)
write-host (hostname) (netsh wlan add profile filename="c:\profiles\profile.xml")
'#
$profileName = Read-Host "Enter the profile name(XML file must be present in c:\share\profiles\)"
ExecCmd -fonction $fonction -argument $profileName
func_done
}
#
function ExecCmd
{
param(
$fonction,
$argument
)
$PingTest = RetrieveStatus
$results = #{}
$results = $PingTest.up
$results | sort -uniq | out-Null
$fonctionSB = ConvertTo-ScriptBlock($fonction)
foreach($result in $results)
{
$os = "Windows"
try{
$session = New-PSSession -ComputerName $result.address -Credential $credentials -EA stop
}
catch{
$os = "Not Windows"
}
if($os -eq "Windows"){
Invoke-Command $result.address -ScriptBlock $fonctionSB -Arg $argument -Credential $credentials
Get-PSSession | Remove-PSSession
}
else{
Write-Host $result.address "does not support Powershell commands"
}
}
}
And the error:
Exception calling "DownloadFile" with "2" argument(s): "An exception occurred during a WebClient request."
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : WebException
+ PSComputerName : 192.168.2.110
I found a workaround,
I added a try/catch for the webclient.Download, which works for PS2. In the catch portion, I replaced the command with Invoke-WebRequest $uri -OutFile $File
Works fine for PS2 and PS3 this way!