Remove Local User Profiles (Cleanly) over 7 days - powershell

Goal - To run this script on a Task Schedule at 00:30 daily, remove local user profiles over 7 days cleanly (Windows 8.1 won't just let you delete the user folder). Preserve local user profiles under 7 days.
Currently the script I have just seems to delete all profiles once run.
I have tried playing with -gt and -lt but am confusing myself into double negatives etc!
Get-WmiObject -Class Win32_UserProfile | Where {
(!$_.Special) -and
($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(7))
} | Remove-WmiObject
Expected result - local user profiles over 7 days deleted. Less than 7 days preserved.
Actual - Removes all local user profiles on that machine.

this is one way to do the selection. it uses a more intuitive way to test for age ... folks often get the -gt/-lt test reversed with dates, so i used age in days for the testing. also, the CIM-* cmdlets return datetime objects instead of the filetime objects that the WMI-* cmdlets give you.
$Today = (Get-Date).Date
$MaxDaysOld = 7
Get-CimInstance -ClassName Win32_UserProfile |
Where-Object {
-not $_.Special -and
($Today - $_.LastUseTime).Days -gt $MaxDaysOld
}
you can pipe that to Remove-CimInstance to remove the indicated profile. [grin]

Related

Powershell - check for last time computers were restarted

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

Deleting Local Accounts Only Deletes Half In Powershell

I am creating hundreds of local accounts for testing purposes. The problem is cleanup, when I run the command to delete all the temp accounts, it only deletes half of them. "bca" is the account prefix, short for "bulk created account"
I run the count (everything done as admin)
Get-LocalUser | ? {$_.Name -like "bca*"} | Measure-Object
And I get count of 20
So I run the delete
Get-LocalUser | ? {$_.Name -like "bca*"} | Remove-LocalUser
Then I do the count again. Expecting 0. I get 10
I repeat the deletion, do the count, get 5, then 2, then 1 then finally 0.
UPDATE:
I was able to delete them all using the following command, from a comment below
foreach($user in Get-LocalUser -Name "bca*") { Remove-LocalUser $user}
BUT I am still curious why the original didn't work

Powershell Get-EventLog hangs on RemoteComputer

The following runs fine on local computer, but when I put -ComputerName "myRemoteName", it hangs and doesn't return anything even after about 5 minutes; but program still seems to be running.
Is it trying to return a large packet of data across the "wire"?
In theory, I should have under 10 errors on the remote computer in the last 2 hours.
$getEventLog = Get-EventLog -log application -ComputerName "myRemoteName" -after ((get-date).addMinutes($minutes*-1)) -EntryType Error
Write-Host Get-Eventlog completed
# list of events to exclude (based on text found in the message)
$getEventLogFiltered = $getEventLog | Where-Object {$_.Message -notlike 'Monitis*' -and $_.Message -notlike '*MQQueueDepthMonitor.exe*' -and $_.Message -notlike '*The local computer may not have the necessary registry*' }
#to only select certain columns, use Select-Object -Property and list the property/columns
$getEventLogColumns = $getEventLogFiltered | Select-Object -Property TimeGenerated,Source,Message,EntryType,MachineName,EventID
$tableFragment = $getEventLogColumns | ConvertTo-Html -fragment
Write-Host "HTML-Table Built"
Code after that builds an email and sends it...
I've seen other posts that suggest switching to Get-WinEvents, but I think that would take me an hour or two to rewrite (due to my lack of experience with Powershell); what I have above is working fine on local computers.
Updates 03/04/2014 13:40 CT:
Running with $minutes = 120 ran 14.5 minutes.
Running with $minutes = 1 ran 12.5 minutes.
Conclusion, changing the range of $minutes doesn't really seem to effect the response time; both are slow.
After parameter is not very well designed, it prints all records it suppose to, but when it reaches set date it stills scans till the end of even log file despite the fact, that there is nothing to print (at least it looks so). I've used Where-object filter and .CompareTo() method to prints logs which are after set date (in my case a day before current date).
#Sets yesterday date (script will get all records after that date)
$YDate = (Get-Date).AddDays(-1)
#Gets all event logs from Security log where event id represents successful logon and records where generated after prepared date (current date - 24 hours)
$YestardayLogons = Get-EventLog -ComputerName $ServerName -LogName Security |
WHERE { ($_.EventId -eq '528') -and ($_.TimeGenerated.CompareTo($YDate) -eq '1') }
It seems that I was wrong, even with Where-Object filter it still scans like in case of -after parametr (I was just testing on different, freshly build machine and this is why it finished so quickly).
Additional research showed however that break function could be useful, so what I did was:
Get-EventLog -ComputerName $ServerName -LogName Security | WHERE { ($_.EventID -eq '528')} | ForEach-Object {
$_
if ($_.TimeGenerated.CompareTo($YDate) -lt 1) { Break}
}
It prints all event logs and when it hits event log older than (in my case 24 hours) break kicks in and stops get-eventlog cmdlet.
It is not the most pretty solution but it seems to be working fine so far.

Powershell - Getting Last Log Off Date and Time of a computer

I'm not very good with powershell so here goes.
I'm having trouble with displaying the last logoff Date and time of a computer. So far I have:
$Logoff = GWMI -Comp $strComputer -Cl Win32_NetworkLoginProfile |
foreach-object {Write-Host "Last Logoff: "$_.Lastlogoff}
This gives me a list of I guess logoff dates and time. This seemed ok so I tried to convert the output using ConvertToDateTime to get a readable date/time but I don't now how to get it to work when a selection of datetimes are sent back. I've tried:
$Logoff = GWMI -Comp $strComputer -Cl Win32_NetworkLoginProfile |
foreach-object {Write-Host "Last Logoff: "ConvertToDateTime($_)}
but as you can guess this didn't work. Can someone point me in the right direction? Maybe I'm going about this wrong and I should be looking at a different way of getting last logoff/logoff details
Another way of achieving the same result as Ansgar's suggested command:
Get-EventLog -ComputerName $Computer -LogName 'Security' -InstanceId 4634 -newest 1 | Select-object TimeGenerated
On my computer, there was a big difference in time taken to retrieve the result.
You could read the most recent logoff event from the computers' eventlogs:
Get-EventLog -Computer $strComputer "Security" `
| ? { $_.EventId -eq 4634 } `
| sort -Desc TimeGenerated `
| select -First 1 TimeGenerated
Note that reading the Security eventlog requires admin privileges. Also, reading the entire eventlog may require significant amounts of time, so you may want to restrict the processed events by date (-After (Get-Date).AddDays(-1)) or by number (-Newest 500).
This is a better working script over here.
Note that it shows full User Logon/Logoff, but does not show "idle" times.
User Logon/Logoff Information using Powershell

PowerShell Script to query and delete print jobs older than "x" days

I started putting this PowerShell Script together, the hope would be to replace some tasks that are currently carried out manually
I'm using the
get-Date.AddDays()
function
I'm using the ISE to build the script and in testing I get output if I single out the 'starttime' property, but this seems to be a catch all because the values all come up null, ideally I'd like to use the 'timesubmitted' property, but the date seems to output in an odd that I don't think is being read correctly because my queries with 'timesubmitted' always come up empty
it comes out in this format, if you do an open query
20120416030836.778000-420
here's what I have so far.
disregard the | 'format-table' function that's just so I can see if I'm getting the desired output
#Clears Old Print Jobs on Specified server
#Sets Execution Policy for Script to run
Set-ExecutionPolicy RemoteSigned -Force
#establishes variable for cutoff date
$d = Get-Date
$old = $d.AddDays(-4)
#Queries WMI and retrieves print jobs
Get-WmiObject -class win32_printjob -namespace "root\CIMV2" | where-object {$_.timesubmitted -lt
"$old"} | ft caption,document,jobid,jobstatus,owner,timesubmitted
In PowerShell every WMI instance has a ScriptMethod that you can use to convert the dates from WMI format to .NET format:
Get-WmiObject Win32_PrintJob |
Where-Object { $_.ConvertToDateTime($_.TimeSubmitted) -lt $old } |
Foreach-Object { $_.Delete() }
Just an update in case anyone is looking in 2021.
This command/syntax worked for me in 2008 R2 (PowerShell version 2.0) (I was able to piece this together from this page, as well as others).
Finds all jobs over 30 minutes and deletes them:
Get-wmiobject win32_printjob
| Where-Object {[System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeSubmitted) -lt (Get-Date).addminutes(-30)}
| ForEach-Object { $_.delete() }*