How to delete old profiles with script - powershell

So I am trying to adapt this code I got from another post for the needs of my current organization. Please see below code:
$Admin = "Administrator","Public","Default","Administrator"
foreach($file in Get-ChildItem C:\Users\)
{
if ($file -in $Admin)
{
Write-Host = "`r`nUser account is" $file ". This is an Administrator Account, it will not be deleted."
}
else
{
Write-Host = "`r`nUser account is" $file ". Checking profiles age..."
$FileDate = (Get-item C:\Users\$file).CreationTime
Write-Host = $FileDate
$TestDate = (Get-Date).addDays(-30)
Write-Host = $TestDate
If ($FileDate -lt $TestDate)
{
Write-Host = "Since" $file "is older than 30 Days (" $FileDate ") it will be deleted."
$UserAccountPath = "C:\\Users\\$file"
$WMIQuery = "SELECT * FROM Win32_UserProfile WHERE localpath = '$UserAccountPath'"
$UserProfile = get-wmiobject win32_userprofile | where localpath -eq c:\\users\\$file
Remove-WmiObject -InputObject "$UserProfile"
}
else
{
Write-Host = "Since File is dated less than 30 days old (" $FileDate ") it will not need to be deleted."
}
}
}
After running this I get the following error:
Remove-WmiObject : Object reference not set to an instance of an object.
At line:28 char:17
+ Remove-WmiObject -InputObject "$UserProfile"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Remove-WmiObject], NullReferenceException
+ FullyQualifiedErrorId : System.NullReferenceException,Microsoft.PowerShell.Commands.RemoveWmiObject
Any help is appreciated.

Basically, the mistake is in this line:
$UserProfile = Get-WmiObject win32_userprofile | Where localpath -EQ c:\\users\\$file
$UserProfile is null, and will always be null, because the equality comparer -eq is looking for an exact match, to put it into perspective:
'C:\some\path' -eq 'C:\\some\\path' # => False
[pscustomobject]#{
localPath = 'C:\some\path'
} | Where-Object localPath -EQ 'C:\\some\\path' # => Null
To fix this, you can do the following:
# $UserAccountPath = "C:\\Users\\$file" // This line is not needed
#
# DirectoryInfo objects have a FullName property for their Absolute Path
$UserProfile = Get-WmiObject win32_userprofile | Where-Object localpath -EQ $file.FullName
Remove-WmiObject -InputObject $UserProfile
$UserProfile = Get-WmiObject win32_userprofile | Where-Object localpath -EQ $file.FullName
$UserProfile.Delete()

Related

I'm having issues with removing profiles in PowerShell

I have developed several scripts to remove unused profiles from machines with the exception of a few important accounts. Every script that I use always give me the same error when using the Remove-WmiObject, Remove-CimInstance, and .Delete(). I am certain that all of these scripts should work, but I receive the same error. Please see the below script and attached error. There is something wrong with my machine that is blocking me from using any of these functions. So my question: Is there anything I can do to get these functions to work for me or troubleshoot the issue? (I can attach other scripts and errors if needed)
Script:
$Admin = "Administrator","Public","Default","Administrator"
foreach($file in Get-ChildItem C:\Users\)
{
if ($file -in $Admin)
{
Write-Host = "`r`nUser account is" $file ". This is an Administrator Account, it will not be deleted."
}
else
{
Write-Host = "`r`nUser account is" $file ". Checking profiles age..."
$FileDate = (Get-item C:\Users\$file).CreationTime
Write-Host = $FileDate
$TestDate = (Get-Date).addDays(-30)
Write-Host = $TestDate
If ($FileDate -lt $TestDate)
{
Write-Host = "Since" $file "is older than 30 Days (" $FileDate ") it will be deleted."
$UserAccountPath = "C:\\Users\\$file"
$WMIQuery = "SELECT * FROM Win32_UserProfile WHERE localpath = '$UserAccountPath'"
$UserProfile = get-wmiobject win32_userprofile | Where-Object localpath -EQ $file.FullName
Remove-WmiObject -InputObject "$UserProfile"
}
else
{
Write-Host = "Since File is dated less than 30 days old (" $FileDate ") it will not need to be deleted."
}
}
}
Error:
Remove-WmiObject :
At line:28 char:17
+ Remove-WmiObject -InputObject "$UserProfile"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Remove-WmiObject], FileLoadException
+ FullyQualifiedErrorId : System.IO.FileLoadException,Microsoft.PowerShell.Commands.RemoveWmiObject
My past post might help for some background info: How to delete old profiles with script .
$sidToRemove = [System.Security.Principal.SecurityIdentifier]::new([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid, $null).ToString()
Get-WmiObject -Class Win32_UserProfile -Filter "SID = '$($sidToRemove)' AND SPECIAL = 'FALSE' AND LOADED = 'FALSE'" |
ForEach-Object { $_.Delete() }
And you can not remove public and default profiles as they are not profiles actually.

Duplicating result in Out-GridView [The property 'Installed' cannot be found on this object. Verify that the property exists and can be set.]?

The below script was working fine to search and check particular patches of installation information in the list of servers (Domain Controllers).
Clear-Host
$hotFixesID = #('KB4571694','KB4056890')
$computerList = Get-ADComputer -LDAPFilter "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))" | Where-Object { Test-Connection $_.Name -Count 1 -Quiet } | Select-Object -ExpandProperty Name
$properties = 'PSComputerName', 'HotFixID', 'Installed', 'Description', 'InstalledBy', 'InstalledOn', 'Exception'
$computerList | ForEach-Object {
Write-Host "Processing Server $($_) ..." -ForegroundColor Cyan
Foreach ($hotFixID in $hotFixesID) {
Write-Host "`tProcessing Server $($_) Hotfix ID $($hotFixID)..." -ForegroundColor Yellow
$out = $exception = $null
Try {
$out = Get-WmiObject -Query "Select * From Win32_QuickFixEngineering Where HotFixID='$($hotFixID)'" -ComputerName $_ -ErrorAction Stop | Select-Object -Property $properties
}
Catch {
$exception = $_.Exception.Message
}
If ($out) {
$out.Installed = $true
}
Else {
$out = '' | Select-Object -Property $properties
$out.PSComputerName = $_
$out.HotFixID = $hotFixID
If ($exception) {
$out.Exception = $exception
}
Else {
$out.Installed = $false
}
}
$out
}
} | OGV
How can I fix the minor issue where the Installed Hotfix result is duplicated?
Error:
The property 'Installed' cannot be found on this object. Verify that the property exists and can be set.
At line:17 char:13
+ $out.Installed = $true
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyAssignmentException
OK I found the script to be cumbersome and hard to debug. I've rewritten it with more consideration of what to output when the call doesn't fail but the KB is not installed.
$computerlist = Get-ADComputer -LDAPFilter "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))" | Select-Object -ExpandProperty Name
$computerList | ForEach-Object {
Write-Host "Processing Server $_ ..." -ForegroundColor Cyan
Foreach ($hotFixID in $hotFixesID) {
Write-Host "`tProcessing Server $_ Hotfix ID $hotFixID..." -ForegroundColor Yellow
Try
{
$result = Get-WmiObject -Query "Select * From Win32_QuickFixEngineering Where HotFixID='$hotFixID'" -ComputerName $_ -ErrorAction Stop
$exception = "No error"
if($result)
{
$installed = $true
$description = $result.description
$installedBy = $result.installedby
$installedOn = $result.installedon
}
else
{
$installed = $false
$description = "N/A"
$installedBy = "N/A"
$installedOn = "N/A"
}
}
Catch {
$exception = $_.Exception.Message
}
[PSCustomObject]#{
PSComputerName = $_
HotFixID = $hotFixID
Installed = $installed
Description = $description
InstalledBy = $installedBy
InstalledOn = $installedon
Exception = $exception
}
}
} | Out-GridView

PS | Deleting Local User Profile | Get-WmiObject -Query Not working

I am literally 1 Line away from getting this PS Task completed. Would one of you mind taking a look? This script simply checks the users directory, checks each local users creation date, and deletes it using WMIObject. Everything works up to line 34 (Marked it with ===> in Code block). Error being thrown on my WMI Query. I cannot find out why... Working with WMIExplorer it looks correct.
$Admin = "Administrator"
foreach($file in Get-ChildItem C:\Users\)
{
if ($file -in $Admin)
{
Write-Host = "`r`nUser account is" $file ". This is an Administrator Account, it will not be deleted."
}
else
{
Write-Host = "`r`nUser account is" $file ". Checking profiles age..."
$FileDate = (Get-item C:\Users\$file).CreationTime
Write-Host = $FileDate
$TestDate = (Get-Date).addDays(-30)
Write-Host = $TestDate
If ($FileDate -lt $TestDate)
{
Write-Host = "Since" $file "is older than 30 Days (" $FileDate ") it will be deleted."
$UserAccountPath = "C:\Users\$file"
====> $WMIQuery = "SELECT * FROM Win32_UserProfile WHERE localpath = $UserAccountPath"
$UserProfile = Get-WmiObject -Query $WMIQuery -ComputerName $Computer
Remove-WmiObject -InputObject $UserProfile
}
else
{
Write-Host = "Since File is dated less than 30 days old (" $FileDate ") it will not need to be deleted."
}
}
}
When it is run I get the following Error:
Get-WmiObject : Invalid query "SELECT * FROM Win32_UserProfile WHERE localpath = C:\Users\radconrm"
At C:\Users\AVC00\Documents\PowerShell\Profile Maintenance\TestCommands.ps1:18 char:13
+ $profile = (Get-WmiObject -Query $WMIQuery -ComputerName $Computer)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
Remove-WmiObject : Cannot bind argument to parameter 'InputObject' because it is null.
At C:\Users\AVC00\Documents\PowerShell\Profile Maintenance\TestCommands.ps1:19 char:31
+ Remove-WmiObject -InputObject $profile
+ ~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Remove-WmiObject], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.RemoveWmiObject
I look forward to your insight! Thank you for your time in advanced!
Double the backslashes. Add quotes to what localpath is equal to. It's like SQL.
$UserAccountPath = "C:\\Users\\$file"
$WMIQuery = "SELECT * FROM Win32_UserProfile WHERE localpath = '$UserAccountPath'"
$UserProfile = Get-WmiObject -Query $WMIQuery -ComputerName $Computer
You may find something like this easier:
get-wmiobject win32_userprofile | where localpath -eq c:\users\$file

Powershell - "Invalid assignment expression." error

I've got a couple things that i'm working on. One of them is sort of an import/export thing i found on here. but i'm getting the following error
PS C:\Users\joeblogs> C:\Users\joeblogs\Scripts\Copy user data.ps1 Invalid assignment expression. The left hand side of an assignment
operator needs to be something that can be assigned to like a variable
or a property. At C:\Users\delpillay\Documents\Scripts\Copy user
data.ps1:16 char:12
+ $username = <<<< gc env:userame
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : InvalidLeftHandSide
I don't know where to start and i'm not sure what to try...
Below is the code:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop",
#"Downloads",
"Favorites",
"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
######################################
$username = gc env:userame
$userprofile = gc env:userprofile
##$appData = gc env:localAPPDATA
###### Restore data section ######
if ([IO.Directory]::Exists($destination + "\" + $username + "\"))
{
$caption = "Choose Action";
$message = "A backup folder for $username already exists, would you like to restore the data to the local machine?";
$Yes = new-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Yes";
$No = new-Object System.Management.Automation.Host.ChoiceDescription "&No","No";
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($Yes,$No);
$answer = $host.ui.PromptForChoice($caption,$message,$choices,0)
if ($answer -eq 0)
{
write-host -ForegroundColor green "Restoring data to local machine for $username"
foreach ($f in $folder)
{
$currentLocalFolder = $userprofile + "\" + $f
$currentRemoteFolder = $destination + "\" + $username + "\" + $f
write-host -ForegroundColor cyan " $f..."
Copy-Item -ErrorAction silentlyContinue -recurse $currentRemoteFolder $userprofile
if ($f -eq "AppData\Local\Mozilla") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
if ($f -eq "AppData\Roaming\Mozilla") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
if ($f -eq "AppData\Local\Google") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
}
rename-item "$destination\$username" "$destination\$username.restored"
write-host -ForegroundColor green "Restore Complete!"
}
else
{
write-host -ForegroundColor yellow "Aborting process"
exit
}
}
###### Backup Data section ########
#else
{
Write-Host -ForegroundColor green "Outlook is about to close, save any unsaved emails then press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Get-Process | Where { $_.Name -Eq "OUTLOOK" } | Kill
write-host -ForegroundColor green "Backing up data from local machine for $username"
foreach ($f in $folder)
{
$currentLocalFolder = $userprofile + "\" + $f
$currentRemoteFolder = $destination + "\" + $username + "\" + $f
$currentFolderSize = (Get-ChildItem -ErrorAction silentlyContinue $currentLocalFolder -Recurse -Force | Measure-Object -ErrorAction silentlyContinue -Property Length -Sum ).Sum / 1MB
$currentFolderSizeRounded = [System.Math]::Round($currentFolderSize)
write-host -ForegroundColor cyan " $f... ($currentFolderSizeRounded MB)"
Copy-Item -ErrorAction silentlyContinue -recurse $currentLocalFolder $currentRemoteFolder
}
$oldStylePST = [IO.Directory]::GetFiles($appData + "\Microsoft\Outlook", "*.pst")
foreach($pst in $oldStylePST)
{
if ((test-path -path ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle")) -eq 0){new-item -type directory -path ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle") | out-null}
write-host -ForegroundColor yellow " $pst..."
Copy-Item $pst ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle")
}
write-host -ForegroundColor green "Backup complete!"
}
Few observations:
You are not commenting the Favourites and My Documents. If you want to use them then use comma separated directly.
Use this:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop","Favorites","My Documents"
#"Downloads",
#"Favorites",
#"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
You have missed the n in username:
$username = gc env:username
Donot use gc env:username. Instead directly access them like this below: Completely reframed:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop","Favorites","My Documents"
#"Downloads",
#"Favorites",
#"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
######################################
$username = $env:username
$userprofile = $env:userprofile
##$appData = gc env:localAPPDATA
These are the major things that have been fixed. Hope it helps you.

PowerShell - List all computers & which users are administrators

I am trying to create a powershell script that:
Finds all computers in the domain
Queries each computer to determine what groups/users are in the Administrators group
Lists each user/group/computer
The columns should be: Username, GroupName, ComputerName, DomainName
Here's where I am at so far, but I'm looking for some help parsing the WMI results in a consistent fashion, or per
Here's the script so far:
$auditdir = "c:\temp\"
$tempfile = $auditdir + "computers.csv"
$computers = (Get-ADComputer -Filter *)
$Table = #()
$Record = #{
"MachineName" = ""
"GroupName" = ""
"DomainName" = ""
"Username" = ""
}
Foreach ($computername in $computers){
$computername = $computername.name
$ADMINS = get-wmiobject -computername $computername -query "select * from win32_groupuser where GroupComponent=""Win32_Group.Domain='$computername',Name='administrators'""" | % {$_.partcomponent}
foreach ($ADMIN in $ADMINS){
Write-Host "Output current admin"
$admin
Write-Host "Clean up current admin"
$admin = ($admin | Out-String)
$admin = $admin -replace 'cimv2', '' -replace 'Win32','' -replace 'useraccount','' -replace 'root','' -replace $computername,'' -replace ',','' -replace "`n|`r" -replace $env:userdomain,'' -replace 'name','' -replace '=','' -replace ':','' -replace '_','' -replace '\\','' -replace '_','' -replace 'group','' -replace '""',''
$admin.substring(8)
$admin
$admin = $admin | %{$_.split('"')[3]}
$admin
Write-Host "done with current admin"
$Record."Machinename" = $computername
$Record."Groupname" = $admin
$Record."DomainName" = $env:USERDNSDOMAIN
$Record."UserName" = $admin.split("")[1]
$objRecord = New-Object PSObject -property $Record
$Table += $objrecord
}
$Table | Export-Csv $tempfile -NoTypeInformation
}
Can someone please help me get to the finish line?
Update 1:
With Mathias's help, I've cast the results of the WMI query to a WMI type and can access items easier.
$auditdir = "c:\temp\"
$tempfile = $auditdir + "computers.csv"
$computers = (Get-ADComputer -Filter *)
$Table = #()
$Record = #{
"ComputerName" = ""
"MemberName" = ""
"DomainName" = ""
"SID" = ""
}
Foreach ($computername in $computers){
$computername = $computername.name
$ADMINS = get-wmiobject -computername $computername -query "select * from win32_groupuser where GroupComponent=""Win32_Group.Domain='$computername',Name='administrators'""" | % {$_.partcomponent}
foreach ($ADMIN in $ADMINS){
$admin = [WMI]$admin
$admin
$Record."ComputerName" = $computername
$Record."MemberName" = $admin.name
$Record."DomainName" = $env:USERDNSDOMAIN
$Record."SID" = $admin.SID
$objRecord = New-Object PSObject -property $Record
$Table += $objrecord
}
$Table | Export-Csv $tempfile -NoTypeInformation
}
I seem to be running into an issue with domain admins group. It can't seem to be converted to WMI and then parsed.
Error is:
Cannot convert value "\\computername\root\cimv2:Win32_Group.Domain="domain",Name="Domain Admins"" to type
"System.Management.ManagementObject". Error: "Not found "
At C:\Users\f12admin\Desktop\computerlist.ps1:20 char:3
+ $admin = [WMI]$admin
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastToWMI