The time comes when a manager walks into your office and says "We're tightening the belt this year. One way we're looking to save money is to remove unused PCs from 1 area and move it into another area. Give me a list of all PCs that haven't been used in X days." You agree to do it without thinking as usual and think no big deal. After a few minutes of looking into it you then think "...ummm, what really does "unused" mean anyway?" Does "unused" mean users aren't working on it and just use it as a Facebook machine? Does "unused" mean X application isn't being executed on it or does it simply mean no keyboard or mouse activity at all?
After you grill the manager a little bit you find out that he wants all PCs that haven't had any kind of keyboard or mouse activity for 1 week. Fine. Game on.
I came across this awesome StackOverflow question which did 90% of the work for me.
Add-Type @'
using System; using System.Diagnostics;
using System.Runtime.InteropServices;namespace
PInvoke.Win32 {
public static class UserInput {
[DllImport("user32.dll", SetLastError=false)]
private static extern bool GetLastInputInfo(
ref LASTINPUTINFO plii
);
[StructLayout(LayoutKind.Sequential)]
private struct LASTINPUTINFO {
public uint cbSize;
public int dwTime;
}
public static DateTime LastInput {
get {
DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
return lastInput;
}
}
public static TimeSpan IdleTime {
get {
return DateTime.UtcNow.Subtract(LastInput);
}
}
public static int LastInputTicks {
get {
LASTINPUTINFO lii = new LASTINPUTINFO();
lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
GetLastInputInfo(ref lii);
return lii.dwTime;
}
}
}
}
'@"
$(([PInvoke.Win32.UserInput]::IdleTime).TotalMinutes)" | Add-Content -Path "\\SERVER\C$\FOLDER\$($env:COMPUTERNAME)-idletime.txt"
This is nearly exactly the same as the author example but I needed to modify it for a mass deployment. All I'm doing it executing the script on a workstation and getting the total idle time in minutes.
I'm then adding this to a file called PCNAME-idletime.txt on a server. I'm using SCCM so the deployment of the script was trivial.
After all workstations completed and I had a whole bunch of text files in the server's folder I then merged them all into 1 single CSV and voila!, the manager got what he wanted!
Get-ChildItem '\\SERVER\C$\FOLDER' | select @{n='PCName';e={$_.BaseName.Replace('-idletime.txt','')}},@{n='IdleTime';e={(Get-Content $_.FullName)}} | Export-Csv -NoTypeInformation -Append -Path CSVFILE.csv
This one-liner lists all the text files and using calculated properties creates a name to idle time reference which I then export out into a CSV. What I end up with is something like this:
PCName,Idletime PC1,345 PC2,123 PC3,0
I hope this helps the next time you want to get unused PCs!
Join the Jar Tippers on Patreon
It takes a lot of time to write detailed blog posts like this one. In a single-income family, this blog is one way I depend on to keep the lights on. I'd be eternally grateful if you could become a Patreon patron today!
Become a Patron!Subscribe to Adam the Automator
Get the latest posts delivered right to your inbox
Comments powered by Talkyard.