I am creating a script which should delete profiles which haven't been used in the last 180 days in Windows systems.
Following is my code.
$profiles = Get-WMIObject -Class Win32_UserProfile | Where {
(!$_.Special) -and
($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-1))
}
$count = $profiles | Measure-Object | select -ExpandProperty Count
if ($count -eq 0) {
Write-Output "No Profiles found for Deletion"
exit
}
Write-Output "Number of profiles : $count"
$profiledeleted = #()
$profileerror = #()
foreach ($profile in $profiles) {
$profile | Remove-WmiObject
if ($?) {
$profiledeleted += $profile.Localpath
continue
} else {
$profileerror += $profile.Localpath
}
}
if ($profiledeleted.Length -gt 0) {
Write-Output "Localpath of removed profiles"
$profiledeleted
}
if ($profileerror.Length -gt 0) {
Write-Output "Error While removing below profiles"
$profileerror
}
The code should run with the system account but I am getting the following error.
Remove-WmiObject :
At C:\Users\Admin\GRT-2687_deleteunuseduserprofiles.ps1:22 char:32
+ $profile | Remove-WmiObject <<<<
+ CategoryInfo : InvalidOperation: (:) [Remove-WmiObject], COMException
+ FullyQualifiedErrorId : RemoveWMICOMException,Microsoft.PowerShell.Commands.RemoveWmiObject
Edit : It is deleting all the data except the folder C:\Users\Username.
Related
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.
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()
I am trying to modify a script that used to work when executed on each of the servers manually to generate the DFS replication then send an email, so it can be executed once from one location then remotely connect to each and every server.
Invoke-Command:
$ComputerName = Get-DfsrMember | Select-Object -ExpandProperty ComputerName -Unique | Sort-Object
$ComputerName | ForEach-Object {
Try {
$session = New-PSSession -ComputerName $_ -ErrorAction Stop
Invoke-Command -ErrorAction Stop -Session $session -ScriptBlock {
## Script body starts here....
Write-Host "Processing on server $($_) `n" -ForegroundColor Yellow
Param (
[String[]]$ReplicationGroupList = ("")
)
....
}
}
Catch [System.UnauthorizedAccessException] {
Write-Warning "You do not have the proper access to this system!"
Break
}
Catch [System.Runtime.InteropServices.COMException] {
Write-Warning "Communications Exception occurred!"
Break
}
}
This is the error I have received:
The term 'Param' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:7 char:9
+ Invoke-Command -ErrorAction Stop -Session $session -ScriptBlo ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Param:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : PRDDC01-VM
The actual Powershell script that used to be working, but must be manually executed in each of the server RDP session:
Param (
[String[]]$ReplicationGroupList = ("")
)
$RGroups = Get-WmiObject -Namespace "root\MicrosoftDFS" -Query "SELECT * FROM DfsrReplicationGroupConfig"
#If replication groups specified, use only those.
if ($ReplicationGroupList) {
$SelectedRGroups = #()
foreach ($ReplicationGroup IN $ReplicationGroupList) {
$SelectedRGroups += $rgroups | Where-Object { $_.ReplicationGroupName -eq $ReplicationGroup }
}
if ($SelectedRGroups.count -eq 0) {
Write-Error "None of the group names specified were found, exiting"
exit
}
else {
$RGroups = $SelectedRGroups
}
}
$ComputerName = $ENV:ComputerName
$Succ = 0
$Warn = 0
$Err = 0
Start-Transcript -path "$([Environment]::GetFolderPath("Desktop"))\dfsr1.txt"
foreach ($Group in $RGroups) {
$RGFoldersWMIQ = "SELECT * FROM DfsrReplicatedFolderConfig WHERE ReplicationGroupGUID='" + $Group.ReplicationGroupGUID + "'"
$RGFolders = Get-WmiObject -Namespace "root\MicrosoftDFS" -Query $RGFoldersWMIQ
$RGConnectionsWMIQ = "SELECT * FROM DfsrConnectionConfig WHERE ReplicationGroupGUID='" + $Group.ReplicationGroupGUID + "'"
$RGConnections = Get-WmiObject -Namespace "root\MicrosoftDFS" -Query $RGConnectionsWMIQ
foreach ($Connection in $RGConnections) {
$ConnectionName = $Connection.PartnerName#.Trim()
if ($Connection.Enabled -eq $True) {
if (((New-Object System.Net.NetworkInformation.ping).send("$ConnectionName")).Status -eq "Success") {
foreach ($Folder in $RGFolders) {
$RGName = $Group.ReplicationGroupName
$RFName = $Folder.ReplicatedFolderName
if ($Connection.Inbound -eq $True) {
$SendingMember = $ConnectionName
$ReceivingMember = $ComputerName
$Direction = "inbound"
}
else {
$SendingMember = $ComputerName
$ReceivingMember = $ConnectionName
$Direction = "outbound"
}
$BLCommand = "dfsrdiag Backlog /RGName:'" + $RGName + "' /RFName:'" + $RFName + "' /SendingMember:" + $SendingMember + " /ReceivingMember:" + $ReceivingMember
$Backlog = Invoke-Expression -Command $BLCommand
$BackLogFilecount = 0
foreach ($item in $Backlog) {
if ($item -ilike "*Backlog File count*") {
$BacklogFileCount = [int]$Item.Split(":")[1].Trim()
}
}
if ($BacklogFileCount -eq 0) {
$Color = "white"
$Succ = $Succ + 1
}
elseif ($BacklogFilecount -lt 10) {
$Color = "yellow"
$Warn = $Warn + 1
}
else {
$Color = "red"
$Err = $Err + 1
}
Write-Output "$BacklogFileCount files in backlog $SendingMember->$ReceivingMember for $RGName"
} # Closing iterate through all folders
} # Closing If replies to ping
} # Closing If Connection enabled
} # Closing iteration through all connections
} # Closing iteration through all groups
Write-Output "$Succ successful, $Warn warnings and $Err errors from $($Succ+$Warn+$Err) replications."
Stop-Transcript
$file = "$([Environment]::GetFolderPath("Desktop"))\dfsr1.txt"
get-content $file |
Select-Object -Skip 18 |
set-content "$file-temp"
Move-Item "$file-temp" $file -Force
$emailrecipients = "boss#it.com";
$emailbody = Get-Content -Path "$([Environment]::GetFolderPath("Desktop"))\dfsr1.txt" -Raw
Send-MailMessage -to $emailrecipients -smtpserver smtp.domain.COM -from "$env:COMPUTERNAME#$env:userdnsdomain" -subject "DFSR Report for $(get-date -format dd/MM/yyyy) from $env:COMPUTERNAME" -body $emailbody;
Remove-Item "$([Environment]::GetFolderPath("Desktop"))\dfsr1.txt"
Theo is correct, the Param() code needs to be first thing in the script block. You can find more information about script blocks here: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7
enter image description here
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
$count
#$farm = Get-SPFarm
$siteList = Get-SPSite -Limit 1
$serviceContext = Get-SPServiceContext($siteList[0])
$profileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($servicecontext)
$profiles = $profileManager.GetEnumerator()
foreach ($hi in $profiles) {
if([string]::IsNullOrEmpty($hi["AboutMe"].Value) -eq $false){
write-host "Acount Name:"$hi.AccountName"|AboutMe:"$hi["AboutMe"].Value
$count++
}
}
write-host $count
I keep getting errors with the foreachloop, the following error. When I type SP-Farm, I get everything back so it can't be an access issue?
UserProfileDBCache_WCFLogging :: ProfileDBCacheServiceClient.GetUserData threw exception: Access is denied.
At line:8 char:10
+ foreach ($hi in $profiles) {
+ ~~~
+ CategoryInfo : OperationStopped: (:) [], UserProfileApplicationNotAvailableException
+ FullyQualifiedErrorId : Microsoft.Office.Server.UserProfiles.UserProfileApplicationNotAvailableException
The following PowerShell for your reference.
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
$count
$siteList = Get-SPSite -Limit 1
$serviceContext = Get-SPServiceContext($siteList[0])
$profileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext)
$profiles = $profileManager.GetEnumerator()
foreach ($profile in $profiles) {
if([string]::IsNullOrEmpty($profile["AboutMe"].Value) -eq $false){
write-host "Acount Name:"$profile.AccountName"|AboutMe:"$profile["AboutMe"].Value
$count++
}
}
write-host $count
i have created a powershell script to run image validation remotely and when any of the service found in stopped state this will fail the script.I am getting below error while failing the script.Is there anyway so that I can hide these error from the report?
+ Invoke-Command -ScriptBlock {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], RuntimeException
+ FullyQualifiedErrorId : ScriptHalted
This is the script i used to fail when the service is in stopped state.
$EMService = get-wmiobject win32_service | where-object {($_.Name -eq 'HP12cAgent') -or ($_.Name -eq 'HPagent12c2Agent') -or ($_.Name -eq 'HPagent10gAgent') -or ($_.Name -eq 'FarmEM10gAgent') -or ($_.Name -eq 'FarmEM11gAgent')} | format-list name | Out-String
$Servicename = $EMService.Split(":")[1].Trim()
$EMStatus1 = get-wmiobject win32_service | where-object {$_.Name -eq $Servicename} | format-list state | Out-String
$ServiceStatus = $EMStatus1.Split(":")[1].Trim()
if ($Servicename -eq $null)
{
$Servicename = "Unavailable"
}
else
{
$Servicename = "$Servicename"
}
if ($ServiceStatus -eq "Stopped")
{
throw
}
Else
{
exit 0
}
You can always try wrapping stuff in a try/catch block.
try { some powershell stuff }
catch { Write-Host $_ ; or do nothing }