I have a Powershell command that for automation purposes in our monitoring system, I'm trying to write in one line.
Basically, we're checking multiple Hyper-V virtual machines to make sure they are replicating properly. The way we're doing this is checking the last replication date/time and comparing it to the current date/time. Here's the one-liner I have for checking one machine.
(new-timespan -start(get-vm termserv002| Measure-VMReplication | select -expand lastreplicationtime)).totalminutes
This works perfectly. However, we'd like to check ALL of the VMs on this Hyper-V box. I can do this without running the new-timespan by running this command -
get-vm | where {$_.replicationstate -ne "Disabled"} | Measure-VMReplication | select vmname, lastreplicationtime
This gives me every VM on the box and its last replication time.
Ultimately, what I'd like for output is the VMName and LastReplicationTime in minutes from current date/time. If I can do this on one line, homerun!
Any suggestions? No matter how I try to meld the two together, I just can't seem to get the syntax right (Or it's not possible.)
I can't test it but give this a try:
get-vm | where {$_.replicationstate -ne "Disabled"} | Measure-VMReplication |
select vmname, #{n="LastReplicationTime";e={ (new-timespan -start ($_.lastreplicationtime) -end (get-date)).TotalMinutes }}
Related
I need to make sure all computers are being restarted at least once a week, and I've been googling on it, and I found something, but it's... rather something simple, something... 1-timer... What I want is to make it a bit more advanced.
Here's the command I found online:
Get-WmiObject Win32_OperatingSystem -ComputerName <computername> : select cname, #(LABEL='LastBootUpTime' ;EXPRESSION=($_.ConverttoDateTime($_.lastbootuptime)))
(I'm not sure if sharing the links is a good idea here, but I just googled for "check when remote computer was last restarted" and clicked on a link on enterprisedaddy)
I do not know anything about PowerShell, so I'm asking for your help. Also, it may be useful not only to me, but to others as well...
Here's what I want:
Create the *.ps1 file (I can do that) and make it run it 24/7
Instead of copying all computers each time, I want to append new computers to the list, because otherwise, the list would be very, very long... and I don't want to be deleting a whole bunch of computers and making sure only 1 copy is left and deleting all other every day...
Export the list into either *.txt or (even better) *.csv
At the end of the day, the *.ps1 would create a new *.txt or *.csv file and start it all over. In a new file. Of course leaving the old one (or all others, starting day 2...) for the review...
I understand there's very little hope, but if you can help me - great. If not - well, it may take forever to google for it all, but in the end, I may be able to find it all myself... Although there is even less hope here...
To be fair, all I need is to see the last time the computers were restarted, and that's all, so if you know of an easier way - I'm ready to read about other ideas.
This is not a full answer, but should help you toward your goal. If I'm interpreting your question correctly you want to check the last boot up time daily and report machines that haven't rebooted in the last 7 days. That data should then be stored in a csv file.
So this is actually quite easy to do, and PowerShell is a great place to do it. However, you have to "develop" on top of what you've already discovered. Set goals and meet those goals.
A start might be something like:
$Today = Get-Date
$ReportDate = $Today.AddDays( -7 )
$OutputCsv = ("C:\Temp\" + $Today.ToString( "yyyy-MM-dd" ) + ".csv" )
$BootTimes =
Get-WmiObject Win32_OperatingSystem -ComputerName pyex06 |
Select-Object PSComputerName, #{ LABEL='LastBootUpTime' ;EXPRESSION={ $_.ConverttoDateTime($_.lastbootuptime) } }
$BootTime |
Where-Object{ $_.LastBootUpTime -lt $ReportDate } |
Export-Csv -Path $OutputCsv -NoTypeInformation
Note: I didn't test this just hammered it out quickly for demonstration.
What's happening:
Get the current date.
Use the current date to calculate the date boundry earlier than which you want to report on.
Use the data again to derive a string suitable for naming a somewhat unique output file.
Do the query, add the LastBootUpTime property. Note: I made several syntax corrections there.
Now run the results through a Where{} statement that will filter for boot times more than 7 days old, and export to a CSV file.
Again this is just a start. You will need to add an ability to work against more than one computer, You'll need to think about what your input is going to be for that. You will probably also want to add error handling as WMI (and later CIM) connections can and do fail.
An aside: Get-WMIObject is deprecated. It's been replaced by Get-CimInstance. GetCimInstance will return a LastBootUpTime property as an actual date, with no need to add the property. That command would look something like?
Get-CimInstance Win32_OperatingSystem -ComputerName ComputerName |
Select-Object PSComputerName,LastBootUpTime
All of these things are very well documented. In fact I'm sure somebody has even published a script doing exactly what you want. You have to have some idea of how building a larger script will progress and hopefully I've given you that much. It's a series of solving individual issues and putting those solutions together etc.
BTW, the WMI stuff is depreciated, and CIM is the new hotness going forward. Note that win using cross-platform PowerShell (PowerShell Core), the WMI cmdlets do not exist.
$PSVersionTable.PSVersion
# Results
<#
Major Minor Build Revision
----- ----- ----- --------
5 1 19041 1
#>
(Get-Command -Name '*wmi*').Count
# Results
<#
21
#>
$PSVersionTable.PSVersion
# Results
<#
Major Minor Patch PreReleaseLabel BuildLabel
----- ----- ----- --------------- ----------
7 0 3
#>
(Get-Command -Name '*wmi*').Count
# Results
<#
0
#>
Secondly, the command you posted is not syntactically correct and thus would never work.
Lastly, always do the basic stuff first to make sure you are getting what you'd expect before taking the next step.
Get-CimInstance -ClassName Win32_OperatingSystem |
Select-Object -Property '*'
# Results
<#
...
CSName : Lab01
...
Description :
InstallDate : 20-Jun-20 18:59:14
...
Distributed : False
LastBootUpTime : 13-Aug-20 01:00:42
LocalDateTime : 20-Aug-20 14:01:53
...
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
#>
Note, you can get the dates directly with no conversions needed, then format as needed.
Get-CimInstance -ClassName Win32_OperatingSystem |
Select-Object -Property CSName, LastBootUpTime,
#{
Name = 'TimeSpanSinceLastRestart'
Expression = {New-TimeSpan -Start $(Get-Date) -End $PSItem.LastBootUpTime }
}
# Results
<#
CSName LastBootUpTime TimeSpanSinceLastRestart
------ -------------- ------------------------
Lab01 13-Aug-20 01:00:45 -7.15:02:24.1130902
#>
As for this...
My question is how to make it run 24/7 and append new computer
... just put in in a scheduled task (on each host or on an admin workstation where you get computer names from AD or from a file) and export to either a CSV or a file using the -Append parameter.
(Get-ADComputer).Name |
ForEach{
Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $PSItem |
Select-Object -Property CSName, LastBootUpTime,
#{
Name = 'TimeSpanSinceLastRestart'
Expression = {New-TimeSpan -Start $(Get-Date) -End $PSItem.LastBootUpTime }
} |
Export-Csv -Path 'D:\Temp\SystemRebootReport.csv' -Append -NoTypeInformation -WhatIf
}
# Results
<#
What if: Performing the operation "Export-Csv" on target "D:\Temp\SystemRebootReport.csv".
#>
Just remove the -WhatIf to create the file.
Update
As for ...
[So how do I use this implicit remoting instead then?]
...this is all detailed in the Powershell Help files, see
about_Remote_Requirements - PowerShell | Microsoft Docs
'PowerShell implicit remoting active directory'
https://www.youtube.com/results?search_query=powershell+implicit+remoting+active+directory
https://www.itprotoday.com/powershell/powershell-implicit-remoting-never-install-module-again
TechNet Install RSAT for Windows 10 1809 and 1903 and 1909 -
automated
I have a pretty neat mess of batch/python scripts that install a program called MATRIS, followed by about 15 exe updates.
Around 11 of these updates open a window telling me the that the update was successful.
Now it would be really fun to run a batch or powershell script which closes all of these windows for me.
The last thing I tried was Get-Process | Where-Object {$_.Path -like "MatrisInstaller.APCIPLUS"} | Stop-Process -WhatIf
I wasn't sure if it was the name as read in task manager, or like the title of the window, but I tried both.
Please note that a couple of these are (32 bit) - I'm not sure if that would impact the script.
I was able to run tasklist followed by kill {PID} but PIDs change: I'm not sure how to script it.
Please reply if you need any clarification, I've historically been poor at wording my questions.
In your example, Path is pointing to the executable file on disk, so while possible to use (if it is consistent), it won't match the name you find in the processes tab of Task Manager. Typically, people will use the name as shown on the Details tab of Task manager. For example, with Outlook on my system, these three possibilities are:
Path: C:\Program Files\Microsoft Office\Office16\OUTLOOK.EXE
Processes tab: Microsoft Outlook
Details tab: outlook.exe
So, you need a command like this:
Get-Process | Where Name -eq 'Outlook' | Stop-Process
or, better:
Get-Process -Name 'Outlook' | Stop-Process
Note that PowerShell expects you to remove the '.exe' you see in Task manager.
EDIT: Additional technique
If you know the names of the processes, then you can simplify your script by doing something like this:
$processList = "Process1","Process2","Process3" # Add all process names to the list
$processList |
ForEach-Object {
Get-Process -Name $_ | Stop-Process
}
You were almost there, just need to change "Path" to "ProcessName" like so:
Get-Process | Where-Object {$_.ProcessName -like "MatrisInstaller.APCIPLUS"} | Stop-Process -WhatIf
Good Evening, I am developing a C# too, that is supposed to help users to control VMs on a server.
Now I have created the functions to create and erase Snapshots of these VMs and want to create a function to list all Snapshots of a VM including a view selected Snapshot parameter. With:
Invoke-Command -ComputerName ServerName -ScriptBlock { Get-VMSnapshot -VMName VMName |Select-Object}
I am able to get nearly all information that are requested but the total size of the Snapshot itself.
However I could get the size with this command:
Invoke-Command -ComputerName ServerName -ScriptBlock {Get-VM -VMName VMName | gci -r -Filter *.avhd*| select #{Name="MB";Expression={$_.Length / 1Mb}}, *}
Not to get the two results together I compared the creation time of these two with a tolerance of up to 10 seconds and took the closest one. (Primitive I know), but now it becomes even more complicated.
If I now use a Snapshot to set the VM to this state, the time of the File creation changes and so my previous method stops working completely.
Why I want to know is, is there a better way to compare these two results or even better, to get the Snapshot size without such primitive comparisons?
Thanks in advance for the help.
Below one-liners should suffice your need.
1)
Get-VM | Sort Name | Get-Snapshot | Where { $_.Name.Length -gt 0 } | Select VM,Name,Created,SizeMB
2)
Get-VM | Format-Table Name, #{Label="NumSnapshots";Expression={(Get-Snapshot -VM $_ | Measure-Object).Count}}, #{Label="TotalSnapShotSizeMB";Expression={(Get-Snapshot -VM $_ | Measure-Object -Sum SizeMB).Sum}}
Hope it helps.
get-eventlog -LogName system -ComputerName servername | where {$_.source -eq "user32"} | select -first 1 | Format-List
The above command does not return back to the prompt after displaying the desired result.
This happens only in the case of remote computers. If I use "localhost" it works just fine.
I think what you are running into is that Select-Object can kill the pipeline locally in PSv3, but it can't do that remotely. So you continue to get results until it is finished. Since your conditions mean that you will filter out all but one result, it seems like it is hanging.
Try removing the Select-Object (and maybe the Where-Object) to see how long it takes to run normally. You should also do more filtering in the remote call itself rather than filter after receiving the data.
Get-EventLog -LogName system -Source "user32" | select -first 1 | Format-List
I'm working on a small script that will get all the snapshots in a VM and remove all snapshots in the VM except for the 6 newest snapshots, based off the description.
Right now my code looks like this:
get-snapshot -vm "test" | sort -property description | remove-snapshot ?
I am using get-date to make the description for each VM be the date on which it was created, and want to remove all but the 6 newest snapshots. What am I missing with my script to accomplish this task?
I was thinking of using the -getchildren however I'm unable to figure out how to get it to where it would delete snapshots 7 and on.
A snapshot has a property called Created so you can sort on this property and skip the first 6. Test this in a test environment and remove the WhatIf switch to delete the snapshots.
Get-Snapshot -VM test |
Sort-Object Created |
Select-Object -Skip 6 |
Remove-Snapshot -Confirm:$false -WhatIf