Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Error Deleting Profiles On Remote Server

Status
Not open for further replies.
Aug 7, 2006
578
US
Hello,
I've gotten to the point where I could use some help.

I've written a script to remove profiles from our Citrix servers. (Our User Profile Management is suppose to remove the profiles when the users log out, but that doesn't always happen.)

I'm pretty sure the script is working because it did remove a couple of users. However, for the majority I'm getting an exception error. See below where it removed the "Builder" profile, but failed on paige. The users are not logged in. At the time this script was run, there were NO users logged into the server, remotely.

Code:
Checking APP-SP-5. Please wait...
C:\Users\Builder.IQTEST-SP-53
C:\Users\paige14064
Exception calling "Delete" with "0" argument(s): ""
At U:\Exchange_Scripts\Remove_BackupTemp_Citrix_Profiles.ps1:71 char:17
+              $user.delete <<<< ()
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

Here is my script in case it helps. It's a bit messy, because I've been doing a lot of troubleshooting.

Code:
##-----------------------------------------------------------------------------------------------------##
##  MAIN SCRIPT
##-----------------------------------------------------------------------------------------------------##
## Include function library
. .\PPC_Function_Library.ps1

PPC-Add-Snapins
#Import-Module PSTerminalServices

## User search prompt variables
$All = new-object System.Management.Automation.Host.ChoiceDescription "&All"
$User = new-object System.Management.Automation.Host.ChoiceDescription "&Single User"
$search_choices = [System.Management.Automation.Host.ChoiceDescription[]] ($All, $User)
$search_title = ""

## set variable to check all servers
$list = 2

## Array of servers
#$serverlist = PPC-Get-Citrix-Servers $list
$serverlist = @("APP-SP-5")


$exclude_list = @("*Administrator*","*classic*","*svcpvs*")

$script_message = "Do you want the script to remove all temporary and backup profiles or just those for a specific user?"
$script_answer = $host.ui.PromptForChoice($search_title,$script_message,$search_choices,1)

if ($script_answer)
	{
	 [string]$solo_user = PPC-Get-User
	 $solo_user = $solo_user.trim()
	 $username = "*"+$solo_user+"*"
	}

foreach ($server in $serverlist)
	{
	 write-host "Checking $server. Please wait..." -foregroundcolor "darkcyan"
	 $users = Get-WmiObject Win32_UserProfile -ComputerName $server -Filter "Loaded='FALSE' And Special='FALSE' AND LocalPath != NULL"
	 if ($script_answer)
		{
		 $temp = @()
		 $temp = $users
		 $users = @()
		 foreach ($t in $temp)
			{
			 if ($t.localpath -like $username)
				{$users += $t}
			}
		}
		
	 foreach ($user in $users)
		{
		 $skip = $FALSE
		 foreach ($exclude in $exclude_list)
			{
			 if ($user.localpath -like $exclude)
				{
				 $skip = $TRUE
				 break
				}
			}
			
		 if (!$skip)
			{
			 $path = $user.localpath
			 write-host $path -foregroundcolor "DarkYellow"
			 $user.delete()
			}
		}
	}
	
write-host "";""
write-host "End of Script"
write-host "";""


I've checked about all I can think of to check. Any assistance is appreciated.
Thanks.


Light travels faster than sound. That's why some people appear bright until you hear them speak.
 
I've switched to using delprof2 to remove the profiles. I think I might have it running.


Light travels faster than sound. That's why some people appear bright until you hear them speak.
 
Code:
if (!$skip)
			{
			 $path = $user.localpath
			 write-host $path -foregroundcolor "DarkYellow"
			 [highlight #FCE94F]Remove-Item $path[/highlight]
			}

This will remove the directory, however you will still have the issue of the registry keys existing.

I hope that helps.

Regards,

Mark

No trees were harmed in posting this message, however a significant number of electrons were terribly inconvenienced.

Check out my scripting solutions at
Work SMARTER not HARDER.
 
Mark,
Thanks for the reply.

I tried to remove the orphaned user directories that way, but I ran into a couple of errors. The first was that some of the files/folders were set as hidden, system, or read-only. The second was that some of the directories had a circular reference (recursive linking??) to the Application Data folder (under c:\users\<username>\Appdata\Local\). So when I used the -recurse & -force switches with the remove-item cmdlet it would blow up.

I downloaded the delprof2 utility and that works well for deleting the profiles that are not in use.

I had to do a lot of trial and error, but I think I've come up with something that works. In case it might help someone else out, here is my script:
(All commands beginning with "PPC-" are functions I've written and are loaded with the PPC_Function_Library. I've tried to explain in the documentation what information they get/return)


Code:
##-----------------------------------------------------------------------------------------------------##
##  MAIN SCRIPT
##-----------------------------------------------------------------------------------------------------##

## Include function library
. .\PPC_Function_Library.ps1

## Load our standard list of snap-ins & modules.  I think the only one 
## used in this script is the Active Directory module:  Import-Module ActiveDirectory
PPC-Add-Snapins

## User search prompt variables
$All = new-object System.Management.Automation.Host.ChoiceDescription "&All"
$User = new-object System.Management.Automation.Host.ChoiceDescription "&Single User"
$search_choices = [System.Management.Automation.Host.ChoiceDescription[]] ($All, $User)
$search_title = ""

## set variable to check all servers
$list = 2

$home_dir = "C:\Users"

## Array of server(s)
#$serverlist = PPC-Get-Citrix-Servers $list ## Returns a list of all of our Citrix servers
$serverlist = @("APP-ID-108") ## Run against one server for testing

## Accounts to exclude from deletion
$exclude_list = @("*Administrator*","*Classic*","*svcpvs*","ctx_*","*citrixadmin*","*public*","*default*")

## Excluded usernames, will be used to skip home directories 
$excludes = @()
foreach ($exclude in $exclude_list)
	{$excludes += $exclude.trim("*")}

## Excluded profiles; to be used as a delprof2 parameter
$dont_delete = ""
foreach ($exclude in $exclude_list)
	{
	 if ($exclude.substring(0,1) -eq "*")
		{$profile = $exclude.substring(1,($exclude.length-1))}
	 else
		{$profile = $exclude}
	 $profile = $profile.trim()
	 $dont_delete = $dont_delete+"/ed:$profile "
	}
$dont_delete = $dont_delete.trim()

## Find out what to delete; a specific user or all profiles/directories not currently loaded
write-host "";""
$single_user_message = "Do you want the script to remove all temporary and backup profiles or just those for a specific user?"
$single_user_answer = $host.ui.PromptForChoice($search_title,$single_user_message,$search_choices,1)

## Get the samaccountname of the user if single user was selected
if ($single_user_answer)
	{
	 [string]$solo_user = PPC-Get-User  ## Searches AD for user
	 $solo_user = $solo_user.trim()
	 ## Add wildcards to samaccountname
	 $user_name = "*"+$solo_user+"*"
	}
else
	{$user_name = ""}

## Check all servers in serverlist for profiles and orphaned directories to remove
foreach ($server in $serverlist)
	{
	 write-host ""
	 write-host "Checking $server. Please wait..." -foregroundcolor "cyan"
	 ## Get all normal user profile that are not currently loaded
	 $all_users = Get-WmiObject Win32_UserProfile -ComputerName $server -Filter "Loaded='FALSE' AND Special='FALSE' AND LocalPath != NULL"

	 ## Remove requested user(s)' profiles and create a list of names to be passed to $orphandir_scriptblock through invoke-command parameter
	 [array]$remove_users = @()
	 if ($single_user_answer)
		{		 
		 foreach ($user in $all_users)
			{
			 [string]$path = $user.localpath
			 if ($path -like $user_name)
				{
				 $remove_users += $user
				 $name_loc = $path.lastindexof("\")+1
				 $dot = $path.indexof(".")
				 if ($dot -gt 0)
					{$search_name = ($path.substring(($name_loc),($dot-($name_loc)))+"*")}
				 else
					{$search_name = ($path.substring($name_loc,($path.length-$name_loc))+"*")}
				 write-host "Removing unused profiles that match $search_name..." -foregroundcolor "darkcyan"
				 Invoke-Expression "& .\DelProf2.exe /c:$server /id:$search_name /q"
				}
			}
		}
	 else
		{
		 write-host "Removing unused user profiles..." -foregroundcolor "darkcyan"
		 $remove_users = $all_users
		 Invoke-Expression "& .\DelProf2.exe /c:$server $dont_delete /q"
		}
		
	 ## Remove orphaned user directories
	 $orphandir_scriptblock =
		{
		 Param($home_dir,$server,$single_user_answer,$excludes,$user_name)
		 $tab = "	"
		 if ($user_name.length -gt 0)
			{
			 $star = $user_name.indexof("*")
			 if ($star -eq 0)
				{$user_name = $user_name.substring(1,($user_name.length -1))}
			}

		 $purge_path = "C:\Purge"
		 $keep_paths = @()

		 ## Get all remaining profiles; unwanted profiles should have been removed earlier in the script
		 [array]$keep_users = Get-WmiObject Win32_UserProfile -ComputerName $server
		 
		 ## Put all the localpaths, associated with the remaining profiles, into an array of "paths to keep"
		 foreach ($user in $keep_users)
			{
			 if ($user.localpath -like ($home_dir+"\*"))
				{$keep_paths += $user.localpath}
			}
		 
		 ## Add the exclusions into the array of paths to keep
		 foreach ($exclude in $excludes)
			{
			 $new = $home_dir+"\"+$exclude
			 foreach ($path in $keep_paths)
				{
				 if ($path -ne $new)
					{$keep_paths += $new}
				}
			}
		 
		 ## Create the purge folder if it doesn't already exist; used for robocopy command later in script
		 $purge_found = Test-Path $purge_path
		 if (!($purge_found))
			{New-Item $purge_path -Type directory > $NULL}

		 ## Get all user directories
		 [array]$folders = Get-ChildItem $home_dir
		 foreach ($folder in $folders)
			{
			 $delete = $TRUE
			 ## Verify the folder name isn't empty
			 if ($folder.name.length -gt 0)
				{
				 if (($single_user_answer) -AND ($folder.name -NOTLIKE $user_name))
					{$delete = $FALSE}
				 else
					{
					 $home_directory = $home_dir +"\"+$folder.name
					 
					 if(!($single_user_answer))
						{
						 ## Check if any of the paths listed in $keep_paths matches the current folder
						 foreach ($lp in $keep_paths)
							{
							 ## The path was found, so don't delete
							 if ($lp -eq $home_directory)
								{
								 write-host $tab -nonewline
								 write-host "Excluding: $home_directory" -foregroundcolor "darkcyan"
								 $delete = $FALSE
								 break
								}
							}
						}
					}
				}
			 ## Folder name is empty, skip it
			 else
				{$delete = $FALSE}
			
			 ## Directory not found in $keep_paths, it can be deleted
			 if ($delete)
				{
				 ## Take ownership of the user directory so it can be manipulated and deleted
				 takeown /f $home_directory /a /r /d Y
				 
				 ## There may be a circular reference to "Application Data" in this folder
				 $bad_dir = $home_directory +"\AppData\Local"

				 ## Run RoboCopy to clear out recursive linking
				 robocopy.exe c:\purge $bad_dir /e /nfl /ndl /njh /njs /purge /nocopy 

				 ## Remove hidden, system, and read-only attributes on user's directory
				 $contents = Get-ChildItem -Path $home_directory -Force
				 foreach ($content in $contents)
					{
					 if ($content.Attributes -match "Hidden")
						{$content.attributes = $content.attributes -bxor [System.IO.FileAttributes]::Hidden}
					 if ($content.Attributes -match "ReadOnly")
						{$content.attributes = $content.attributes -bxor [System.IO.FileAttributes]::ReadOnly}
					 if ($content.isreadonly -eq $TRUE)
						{$content.isreadonly = $FALSE}
					 if ($content.Attributes -match "System")
						{$content.attributes = $content.attributes -bxor [System.IO.FileAttributes]::System}
					}
				 $removefolder = [System.IO.Path]::GetFullPath($home_directory)
				 Remove-Item $removefolder -Recurse -Force
				}
			}
		}
	 write-host "Removing orphaned user directories..." -foregroundcolor "darkcyan"
	 Invoke-Command -ComputerName $server -ScriptBlock $orphandir_scriptblock -ArgumentList $home_dir,$server,$single_user_answer,$excludes,$user_name
	}
	
write-host "";""
write-host "End of Script"
write-host "";""






Light travels faster than sound. That's why some people appear bright until you hear them speak.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top