When changing the time on a remote server with PS (after turning off auto time sync), it keeps switching back to current time - remote-access

Summary:
I have 4 systems running Windows Server 2022. One system has the PS code and the other 3 are targets to run this code on via PS "Invoke-Command". The objective is to turn off the automatic time sync option on the remote system, then setting the date to the current date + 1 day. Then checking again on the date on those servers.
First, the script collects the list of servers:
$servers = Get-content -Path .\testservers.txt
Then, turns off the time sync option (via the only way I found: registry)
foreach ($server in $servers){
Write-host "Turning off time sync on $server - " -NoNewline -ForegroundColor Yellow
$scriptBlock = {
$props = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters\"
if ($props.Type -ne "NoSync"){
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" -Name Type -Value "NoSync"
}
}
$result = Invoke-command -ComputerName $server -ScriptBlock $scriptBlock
Write-Host "$result"
}
Then, I set the date
foreach ($server in $servers){
Write-host "Adding 1 day on $server - " -NoNewline -ForegroundColor Yellow
$scriptBlock = { Set-Date -Date ($Using:currentDateTime).AddDays(1) }
$result = Invoke-command -ComputerName $server -ScriptBlock $scriptBlock
Write-Host "$result"
}
Then after the date change, I recheck the date
foreach ($server in $servers){
Write-host "Date on server $server - " -NoNewline
$result = Invoke-command -ComputerName $server -ScriptBlock { Get-Date }
Write-Host "$result"
}
I often see at least one of them switched back by this point. If I do another loop to check the dates perhaps 30 seconds later, they've all reverted back to current date.
(Yes, I know the code can be more streamlined.)
Any ideas? Could this be a matter of the registry change or the time change being done in a PS session and only living within that session?

Related

PSCustomObject and overload method Where

I'm poking around a PSCustomObject and the PowerShell ISE auto complete showed me something interesting that I cannot seem to find more information about.
If you've got a variable containing PSCustomObject, there appears to be a not easily found method for filtering.
Would this method for filtering be faster than using the pipeline and Where-Object?
$CusomObject.Where({$_ -like '*hidden*'})
Using $CustomObject | Get-Member -Force does not list this overload. I found reference to this overload here but cannot seem to find much use of this in my searching.
This just appears to execute a script block and confirm the return is True. From limited testing the Where is faster (~1 second ) on a PS Object. Note: Stopwatch may be inaccurate. Where-Object has documentation and executes faster than the ForEach-Object. BeWhere There's bugs in PSv4.
Where Script Block
write-host "Started at $(get-date)"
$j = $i.Where({$_ -like "*"})
write-host "Ended at $(get-date)"
write-host "Total Elapsed Time: $($elapsed.Elapsed.ToString())"
00:00:00.0309266
ForEach-Object
$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
write-host "Started at $(get-date)"
$j = $i | %{$_ -like "*"}
write-host "Ended at $(get-date)"
write-host "Total Elapsed Time: $($elapsed.Elapsed.ToString())"
00:00:01.0555753
Where-Object
$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
write-host "Started at $(get-date)"
$j = $i | ?{$_ -like "*"}
write-host "Ended at $(get-date)"
write-host "Total Elapsed Time: $($elapsed.Elapsed.ToString())"
00:00:00.0348174

How to retrieve data from excel for ip ping..

I have been working on script that pulls ip addresses from a text file, and then pings them in a forever loop, if any are dead then the status output turn red, and also sends an email for alerting.
my script..
$hostnamestxt = "C:\ping_machines.txt"
$servers = get-content "$hostnamestxt"
$date = get-date
while ($true) {
$i++
Write-Host "-- Round $i Machines--" # -foregroundcolor black -backgroundcolor green
foreach($server in $servers){
if (test-Connection -ComputerName $server -Count 2 -Quiet )
{
write-host "$server is ONLINE" -foregroundcolor black -backgroundcolor green
}
else
{
$to = $server
switch ($to)
{
"192.168.252.37" {$device="RAPID"; break}
"192.168.252.39" {$device="Sash line Welder83"; break}
"192.168.252.40" {$device="Sash line Welder84"; break}
default {$device="error";exit}
}
write-host "$device, $server is OFFLINE/UNREACHABLE" -foregroundcolor black -backgroundcolor red
send-mailmessage -to "myemail" -subject "$device, $server is OFFLINE/UNREACHABLE at $date" -Body "$device, $server is OFFLINE/UNREACHABLE at $date"
}
}
}
the above works ok, but the issue i have is that when adding new devices to monitor, i have to add the ip in the txt file then also amend script and into the case statment.
what i would like to do, but not sure how to do is.. read the ip addresses from and excel spreadsheet, say from column A, and then the names will be in column B.
If any device is down it then translates the name from column B, and puts that into a variable to process later.
Any help much appreciated, as i am pretty much stuck at the moment.
Instead of specifying a text file, use a CSV. Then you can use the Import-CSV commandlet and it will turn the list into a collection of objects, which will have properties like IP and Name, depending on what you put in the list. You could even add something for criticality. I used your script and made a couple of really quick changes. Something like this should work.
CSV:
IP,Name
192.168.252.37,RAPID
...
Script:
$hostnamestxt = "C:\ping_machines.csv"
$servers = Import-Csv "$hostnamestxt"
$date = get-date
while ($true) {
$i++
Write-Host "-- Round $i Machines--" # -foregroundcolor black -backgroundcolor green
foreach($server in $servers){
$serverName = $server.Name
$serverAddress = $server.IP
if (test-Connection -ComputerName $serverAddress -Count 2 -Quiet )
{
write-host "$serverName is ONLINE" -foregroundcolor black -backgroundcolor green
}
else
{
write-host "$serverName, $serverAddress is OFFLINE/UNREACHABLE" -foregroundcolor black -backgroundcolor red
send-mailmessage -to "myemail" -subject "$serverName, $serverAddress is OFFLINE/UNREACHABLE at $date" -Body "$serverName, $serverAddress is OFFLINE/UNREACHABLE at $date"
}
}
}

Multi-threaded powershell script on single processor system

I have access to a single-core single-processor VM with which to do logging for my team. I have the following code:
$sb = {
Param($_)
if($_.CONTROLLER -ne ".xx" ){
$posIP = "10." + $_.IP + $_.CONTROLLER
if (Test-Connection -ComputerName $posIP -Count 1 -Quiet) {
$mapPath = "\\" + $posIP + "\c$"
net use $mapPath $password /user:$userName | Out-Null
if(Test-Path $mapPath$dataFile) {
[xml]$periods = Get-Content $mapPath$dataFile
$endDate = $periods.IndataDbf.ingredient.PeriodDetail.PeriodEndDate | select -last 1
$output = "$($_.STORE);$endDate" }
else {
$outPut = $_.STORE + ';' + "$dataFile Not Found" }
net use $mapPath /de | Out-Null
}
else {
$outPut = $_.STORE + ';' + "Map FAILED" }
Write-Output $OutPut
}
}
Import-Csv $inFile | ForEach-Object {
while ((Get-Job -State Running).Count -ge 100) {
Start-Sleep -Seconds 5;
}
Write-Output $_.STORE
Start-Job -Scriptblock $sb -ArgumentList $_ | Write-Verbose
Get-Job -State Completed -HasMoreData 1 | Receive-Job | Out-File -Append -FilePath $outLog
}
Get-Job | Wait-Job | Receive-Job | Out-File -Append -FilePath $outLog
Which runs well, but takes the same amount of time as running the same code without Start-Job and just a loop. However, the previous logging command used BATCH files and automatically opened a couple dozen child command windows to process data, then return, and it runs in under half the time. The code used is the same, so I don't understand why adding more threads didn't make the script run faster. Can anyone tell me why a BATCH file program with a couple dozen child windows runs so much faster with arguably the same code? Any why does the Start-Job command not improve the speed at all? I would think it would try to execute multiple threads simultaneously.
Because there is a lot of overhead when using start-job and whenever you use pipeline.
If you use runspaces instead it maybe faster.Take a look at http://newsqlblog.com/2012/05/22/concurrency-in-powershell-multi-threading-with-runspaces/

PowerShell Write to Same File Multiple Jobs

I have a script that pulls one line of data from a file on multiple servers. I have a single-threaded version that works just fine, but I want to get it to run faster. Since I only need one line of one file from each server, I'm sure I could run this in parallel. I pulled code from multiple places to get a multi-threaded script running, but when I try to get all the results to print to one output file, nothing prints. I wonder if anyone can look at my code to tell me why this same script, without the Jobs, works fine, but after adding jobs, it doesn't.
$sb = {
Param($computer, $fileName, $outLog)
net use "\\$computer\c$" **** /user:****
if(test-path \\$computer\c$\sc\$fileName){
[xml]$periods = Get-Content \\$computer\c$\sc\$fileName
$endDate = $periods.PeriodDetail | select -last 1
$output = "$computer;$endDate"
}
Else {
$output = "$computer;$fileName Not Found"
}
#Synchronize file usage
$mutex = new-object System.Threading.Mutex $false,'SomeUniqueName'
$mutex.WaitOne() > $null
#Write data to log
Out-File -Append -InputObject $output -FilePath $outLog
#Release file hold
$mutex.ReleaseMutex()
net use "\\$computer\c$" /de
}
foreach($computer in $computerName){
while ((Get-Job -State Running).Count -ge 20) {
Start-Sleep -Seconds 5;
}
Start-Job -Scriptblock $sb -ArgumentList $computer,$fileName,$outLog
}
Get-Job | Wait-Job | Receive-Job
Thank you for all the assistance. Here is the resulting code that works pretty well:
$sb = {
Param($computer, $fileName, $outLog)
net use "\\$computer\c$" $password /user:$userName | Out-Null
if(test-path \\$computer\c$\sc\$fileName){
[xml]$periods = Get-Content \\$computer\c$\sc\$fileName
$endDate = $periods.IndataDbf.ingredient.PeriodDetail.PeriodEndDate | select -last 1
$output = "$computer;$endDate"
}
Else {
$output = "$computer;$fileName Not Found"
}
Write-Output -InputObject $output
net use "\\$computer\c$" /de | Out-Null
}
foreach($computer in $computerName){
while ((Get-Job -State Running).Count -ge 20) {
Start-Sleep -Seconds 5;
}
Start-Job -Scriptblock $sb -ArgumentList $computer,$fileName,$outLog
}
Get-Job | Wait-Job | Receive-Job | Out-File -Append -FilePath $outLog
I'm thinking of doing another Get-Job right before the Start-Job, getting only jobs that are complete with more data, but I haven't tested it yet.

Powershell Multi-Threading Start-Job Help Please

I'm trying to use Start-Job to run a command to collect security logs from some servers.
I'm parsing a .ini file to get the list of servers, number of days etc.
#___Collect Logs from Servers___#
$servList = $iniContent["SERVERS"]["svr"]
$days = $iniContent["DAYS"]["days"]
$date = $(get-date -format ddMMyyyy)
$err = "Error Collecting $($logType) from $($server) or the Event Log is empty! | $(Get-Date -format g) "
$serv = $servList.Split(",")
foreach ($server in $serv){
$outfile = "D:\DCLogs\$($date)_$($server)_$logType.txt"
$ScriptBlock = cmd /c "D:\CollectLog\Dumpel.exe -f $($outFile) -l $($logType) -s $($server) -d $($days)"
Start-Job -ScriptBlock $ScriptBlock
Get-Job | Wait-Job
$file = Get-ChildItem D:\DCLogs -Filter "$($date)_$($server)*" -Name
$len = $file.length/1KB # Check LogFile Size
if ($len -eq 0){
$errCount = 1
write-output $err | Out-File $errLog -append
}
}
It's only starting one job at a time so I know I'm doing something wrong. If someone could please point out the problem I'd greatly appreciate it.
Thank you.
Amelia
Get-Job | Wait-Job in the loop, just serialize the jobs. You can use the loop to start the jobs and then use Get-Job | Wait-Job outside the loop.
Try to define your ScriptBlock using :
$ScriptBlock = {...}

Resources