Function execution error: Value cannot be null [duplicate] - powershell

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

Related

Problem with downloading language local experience file using Powershell script

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

Powershell Script: Wait-Process called with array of processes throws an exception due to invalid argument

I am trying to run multiple processes and then wait in the script for their execution to terminate. However, when calling $procs | Wait-Process an exception is thrown.
$SCRIPT_PATH = "path/to/Program.exe"
$procs = Get-ChildItem -Path $DIR | Foreach-Object {
if ($_ -like "Folder") {
# Do nothing
}
else {
$ARG1_VAR = "Directory\$($_.BaseName)"
$ARG2_VAR = "Directory\$($_.BaseName)\SubDirectory"
$ARG3_VAR = "Directory\$($_.BaseName)\SubSubDirectory"
if (Test-Path -Path $ARG1_VAR)
{
$ARG_LIST = #( "-arg1 $ARG1_VAR", "-arg2 $ARG2_VAR")
Start-Process -FilePath $SCRIPT_PATH -ArgumentList $ARG_LIST -PassThru -NoNewWindow
}
else
{
$ARG_LIST = #( "-arg1 $ARG1_VAR", "-arg3 $ARG3_VAR")
Start-Process -FilePath $SCRIPT_PATH -ArgumentList $ARG_LIST -PassThru -NoNewWindow
}
}
}
# ... Execute bunch of other code ...
# ...
# end of the script
$procs | Wait-Process
The exception says:
Wait-Process : The input object cannot be bound to any parameters of the command because the command does not accept pipeline inputs or the input and its properties do not match any of the parameters that accept pipeline input.
+ $procs | Wait-Process
+ CategoryInfo : InvalidArgument: (GPSObject) [Wait-Process], ParameterBindingException
+ FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.WaitProcessCommand
I have tried to output both string "$procs" as well as non-string $procs. The string "$procs" outputs System.Object[] whereas $procs lists correct content (process name, ID, etc.) of all started processes. Therefore, the variable $procs seems to be filled correctly. However, is actually System.Object[] the correct data type to be passed to $procs | Wait-Process ? Shouldn't the data type look something like System.Diagnostics.Process[] instead of System.Object[]?
I am using PowerShell V5.1.
Any help would be much appreciated! Thank you :)!
Pass the array directly as an argument instead of piping it, like this:
$v1 = Start-Process -FilePath 'cmd' -PassThru
$v2 = Start-Process -FilePath 'cmd' -PassThru
Wait-Process -InputObject #($v1,$v2)

PowerShell Function using ParamSets with a for loop

I put together a powershell function that schedules server reboots.
I'm stuck on adding a for loop to run the function for multiple servers using ParameterSetName.
Before adding the ParamSets, I just setup a function and called the function in the same script within a for loop which worked great.
Function ScheduleReboot{
[CmdletBinding(SupportsShouldProcess=$true,DefaultParameterSetName="ViewOnly")]
Param(
[Parameter(Mandatory=$false,ParameterSetName="KillSwitch")]
[Switch]$NukeJobs,
[Parameter(Mandatory=$true,
HelpMessage="ServerName goes here",ParameterSetName="FullRun")]
[string]$server,
[Parameter(Mandatory=$true,
HelpMessage="Enter a Date/Time 07-28-15 16:00 For July 28th, 2015 at 4:00 PM",
ParameterSetName="FullRun"
)]
[ValidatePattern('(?#Enter MM-dd-YY HH:MM 24hr clock)\d{2}-\d{2}-\d{2}\s\d{2}[:]\d{2}')]
$date,
[Parameter(Mandatory=$false,ParameterSetName="ViewOnly")]
[switch]$ViewOnly
) #"$server will reboot on $date"
Switch ($PSCmdlet.ParameterSetName){
"KillSwitch"{
if($env:PROCESSOR_ARCHITECTURE -eq "x86")
{
set-alias ps64 "$env:windir\sysnative\WindowsPowerShell\v1.0\powershell.exe"
$script2 = [ScriptBlock]::Create("IF(Test-Path -Path C:\Windows\System32\Tasks\Microsoft\Windows\PowerShell\ScheduledJobs\$server)
{Remove-Item -Force -Path C:\Windows\System32\Tasks\Microsoft\Windows\PowerShell\ScheduledJobs\$server}")
"Removing previous scheduled server reboot data"
ps64 -command $script2
} else {
Get-ScheduledJob | Unregister-ScheduledJob
}
}#End killswitch block.
"FullRun" {
[array]$servers = gc D:\Scripts\servers.txt
[array]$dates = gc D:\Scripts\dates.txt
if($dates.Length -eq $servers.Length)
{
"Input count matches"
for($i=0;$i -lt $servers.Length;$i++)
{
$day = $dates[$i]
$comp = $servers[$i]
$user = Get-Credential -UserName $env:USERNAME -Message "UserName/password for scheduled Reboot"
$trigger = New-JobTrigger -once -at $day
$script = [ScriptBlock]::Create("D:\Scripts\Scheduled-Reboot-Single.ps1 -server $server | Out-File -Force \\SysLogSvr\d$\scripts\$server-Reboot.log")
Register-ScheduledJob -Name $comp -Credential $user -Trigger $trigger -ScriptBlock $script
}#end for loop
} else {
$warn = "Server list contains " + $servers.Count + " items and Date list contains " + $dates.Count + " items, Please re-check!!!"
Write-Warning $warn
}
}#end fullrun block.
"ViewOnly" {
Get-ScheduledJob | Get-JobTrigger
Get-ScheduledJob | Select Name,Command | FT -AutoSize
}#end viewonly block.
}#End param switch
}#end function.
Make that 3 (or more) different functions. The parameter sets the way you are using it is over-complicating it and making it hard for you to debug it.
I would have made a Get-ScheduledReboot, Clear-ScheduledReboot, Set-ScheduledReboot collection of functions.
There are a few bugs in this script as posted that are causing you issues. The first is the nuke parameter set does not contain the server parameter. The other bug is the one that is throwing you off. You define a $server parameter above but in the function, you change gears and load from a text file. You are mixing $server, $servers and $comp variables around causing it to fail.
You should change your [string]$server to [string[]]$server or better yet, [string[]$ComputerName and foreach($node in $ComputerName){...} on the collection. Keep the text file loading outside that function.

Start-Sleep memory leak (or causing powershell memory leak)?

This question is a repost from https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/365ca396-400d-4401-bd7f-05d5d6c22cc8/powershells-startsleep-causes-memory-growth?forum=winserverpowershell
I did not get a working answer there so try if this forum can help. This is blocking my work.
Here is my two powershell scripts. The first one is to create an event source and the second one is to generate events using 4 threads. Start-Sleep is used in each thread to control the event generation rate. If I remove the Start-Sleep, then the powershell memory usage is constant, otherwise it grows fast until all system memory is used and the system becomes extremely slow.
Is this a known issue? Any workaround? Appreciate any clue.
# use this script to create channel and source if they does not exist.
$logName = "TestLog"
$sourceName = "TestSource"
New-EventLog -LogName $logName -Source $sourceName
# maximumSize's max is 4G.
Limit-EventLog -LogName $logName -OverflowAction OverWriteOlder -RetentionDays 30 -MaximumSize 3080000KB
Event generating script:
# use this script to generate events in TestLog channel.
Param(
[int]$sleepIntervalInMilliSeconds = 0
)
$eventGenScript = {
$logName = "TestLog"
$sourceName = "TestSource"
while($true) {
Write-EventLog -LogName $logName -Source $sourceName -Message "perfLog" -EventId 0 -EntryType information
Start-Sleep -ms $sleepIntervalInMilliSeconds
}
}
$threadCount = 4
for($i=0; $i -lt $threadCount; $i++)
{
Start-Job $eventGenScript
}
read-host "type a key to exit. You need to wait for some time for threads to exit."
It's not that Start-Sleep has a memory leak, you're calling it with an invalid Parameter (-ms) and the process memory of the jobs is filling up with error messages, because you keep calling the (invalid) statement in an infinite loop.
Demonstration:
PS C:\> Start-Sleep -ms 100
Start-Sleep : A parameter cannot be found that matches parameter name 'ms'.
At line:1 char:13
+ Start-Sleep -ms 100
+ ~~~
+ CategoryInfo : InvalidArgument: (:) [Start-Sleep], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.StartSleepCommand
Also, you're defining $sleepIntervalInMilliSeconds outside the scriptblock, but try to use it inside the scriptblock, which won't work, because the variable is undefined in the scope of the scriptblock. This is why your problem remained despite the correct advice you got on the Microsoft forums.
PS C:\> $ms = 100
PS C:\> $job = Start-Job -ScriptBlock { Start-Sleep -Milliseconds $ms }
PS C:\> $job | Wait-Job | Receive-Job
Cannot validate argument on parameter 'Milliseconds'. The argument is null,
empty, or an element of the argument collection contains a null value.
Supply a collection that does not contain any null values and then try the
command again.
+ CategoryInfo : InvalidData: (:) [Start-Sleep], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartSleepCommand
+ PSComputerName : localhost
You have three options to deal with this:
Define the variable inside the script block:
PS C:\> $job = Start-Job -ScriptBlock {
>> $ms = 100
>> $ms
>> Start-Sleep -Milliseconds $ms
>> }
>>
PS C:\> $job | Wait-Job | Receive-Job
100
Use the using scope modifier to get the local variable:
PS C:\> $ms = 100
PS C:\> $job = Start-Job -ScriptBlock {
>> $using:ms
>> Start-Sleep -Milliseconds $using:ms
>> }
>>
PS C:\> $job | Wait-Job | Receive-Job
100
Pass the variable into the scriptblock as an argument:
PS C:\> $ms = 100
PS C:\> $job = Start-Job -ScriptBlock {
>> param($ms)
>> $ms
>> Start-Sleep -Milliseconds $ms
>> } -ArgumentList $ms
>>
PS C:\> $job| Wait-Job | Receive-Job
100
Bottom line: replace
Start-Sleep -ms $sleepIntervalInMilliSeconds
with
Start-Sleep -Milliseconds $using:sleepIntervalInMilliSeconds
and the problem will disappear.

Capturing errors in this Powershell script

I have this test script to change the Administrator password on a list of servers.
I have set the script to log errors if the server can't be ping'd or account can't be found etc. However in addtion to this i'd like to capture any other errors that take place and also add those to the log file. I know you can use the "Try and Catch" for error handling but havn't had any luck so far.
Would someone be kind enough to show how to do it?
Here is the script
$date = Get-Date
$user = "Administrator"
$newpwd = "MyPassword"
$servers = gc C:\servers.txt
foreach ($server in $servers)
{
$ping = new-object System.Net.NetworkInformation.Ping
$Reply = $null
$Reply = $ping.send($server)
if($Reply.status -like 'Success')
{
$Admin=[adsi]("WinNT://" + $server + "/$user, user")
if($?)
{
$Admin.SetPassword($newpwd)
if($?)
{Add-Content -path C:\Audit\logs\servers-reset.csv -Value "$server, Succsess the $user password was changed. , $date"}
else
{Add-Content -path C:\Audit\logs\servers-reset.csv -Value "$server, Error: FAILED to change the password. , $date"}
}
else
{
Add-Content -path C:\Audit\logs\servers-reset.csv -Value "$server, Error: The $user user account was not found on the server. , $date"}
}
else
{
Add-Content -path C:\Audit\logs\servers-reset.csv -Value "$server, Error: Ping FAILED could not connect. , $date"
}
If you want to write exceptions to the log right after they were thrown, you could use a trap. Add something like this to you script:
trap [Exception] {
#Add message to log
Add-Content -Path test.csv -Value "$server, $($_.Exception.Message), $(Get-Date)"
#Continue script
continue;
}
That will log all exceptions (not all errors).
If you want all errors, you can access them using $Error. It's an arraylist containing every error during your sessions(script). The first item $Error[0] is the latest error. This however, is not something that fits directly into an csv file without formatting it.