Use PowerShell to remove local profiles

A couple of days ago at work, I was asked a question concerning whether a specific one-liner of code would work remotely. That one-liner was (Get-WMIObject Win32_UserProfile | ? {$_.localpath –like “*username).Delete()} I told him that, yes, in fact it would work.  After this, I began to think of a way to make a script that will allow a help desk person, desktop support admin or a server admin to run the script and have it prompt for a machine, rather local or remote to make a connection to and then present a list of user profiles and the option to remove a specified profile.

Overall, it is a fairly simplistic script with with a couple Do/Until loops for the selection processes and some Try/Catch  error checking (which in opinion, should always be included in any script that runs on PowerShell V2). I also make use of System.Management.Automation.Host.ChoiceDescription  class to present a choice at the end of the script on whether the user wishes to quit the script or re-scan and delete another profile.

OS Compatibility

A big gotcha with this script is that while it can be ran from any machine with PowerShell v2, it can only be ran against a client using Vista and above or a server running Windows 2008 and above.  Any other OS’s lower than this do not have the WMI class require for this to work and will fail.

Example

In this example, I have my profile (boe) on my laptop and another account (testuser) that I logged into to create the local profile folder.

Capture

I now call my script which asks for a computername, in which I type in the name and am then presented with 2 profiles, boe and testuser along with the option to quit the profile removal script. You will notice that it starts at 0, which is the beginning index of the array of user profiles.

Capture

I now select the testprofile to remove it.  Since it is labeled 0, I type in 0 and proceed to remove the profile. After a few seconds (your time will vary depending on the size of the profile), I get a confirmation that the profile has been successfully deleted.  There is then another prompt to either continue removing profiles or exit the script.

Capture

As you can see, the folder is now gone.

Capture

Really, that is all there is to it. This can be ran against a remote machine as well and it uses the Test-Connection cmdlet to verify the network access prior to making the WMI connection.

If you try to run this against a server that has an unsupported OS, then the script will throw an error and close.

Capture

Code

The script is available from the following locations:

Script Repository

PoshCode

<#   
.SYNOPSIS   
    Interactive menu that allows a user to connect to a local or remote computer and remove a local profile. 
.DESCRIPTION 
    Presents an interactive menu for user to first make a connection to a remote or local machine.  After making connection to the machine,  
    the user is presented with all of the local profiles and then is asked to make a selection of which profile to delete. This is only valid 
    on Windows Vista OS and above for clients and Windows 2008 and above for server OS.    
.NOTES   
    Name: Remove-LocalProfile 
    Author: Boe Prox 
    DateCreated: 26JAN2011       
.LINK   
    https://boeprox.wordpress.com
    http://msdn.microsoft.com/en-us/library/ee886409%28v=vs.85%29.aspx 
.EXAMPLE  
Remove-LocalProfile 
 
Description 
----------- 
Presents a text based menu for the user to interactively remove a local profile on local or remote machine.    
#>  
 
#Prompt for a computer to connect to 
$computer = Read-Host "Please enter a computer name" 
#Test network connection before making connection 
If ($computer -ne $Env:Computername) { 
    If (!(Test-Connection -comp $computer -count 1 -quiet)) { 
        Write-Warning "$computer is not accessible, please try a different computer or verify it is powered on." 
        Break 
        } 
    } 
Try {     
    #Verify that the OS Version is 6.0 and above, otherwise the script will fail 
    If ((Get-WmiObject -ComputerName $computer Win32_OperatingSystem -ea stop).Version -lt 6.0) { 
        Write-Warning "The Operating System of the computer is not supported.`nClient: Vista and above`nServer: Windows 2008 and above." 
        Break 
        } 
    } 
Catch { 
    Write-Warning "$($error[0])" 
    Break 
    }     
Do {     
#Gather all of the user profiles on computer 
Try { 
    [array]$users = Get-WmiObject -ComputerName $computer Win32_UserProfile -filter "LocalPath Like 'C:\\Users\\%'" -ea stop 
    } 
Catch { 
    Write-Warning "$($error[0]) " 
    Break 
    }     
#Cache the number of users 
$num_users = $users.count 
 
Write-Host -ForegroundColor Green "User profiles on $($computer):" 
 
    #Begin iterating through all of the accounts to display 
    For ($i=0;$i -lt $num_users; $i++) { 
        Write-Host -ForegroundColor Green "$($i): $(($users[$i].localpath).replace('C:\Users\',''))" 
        } 
    Write-Host -ForegroundColor Green "q: Quit" 
    #Prompt for user to select a profile to remove from computer 
    Do {     
        $account = Read-Host "Select a number to delete local profile or 'q' to quit" 
        #Find out if user selected to quit, otherwise answer is an integer 
        If ($account -NotLike "q*") { 
            $account = $account -as [int] 
            } 
        }         
    #Ensure that the selection is a number and within the valid range 
    Until (($account -lt $num_users -AND $account -match "\d") -OR $account -Like "q*") 
    If ($account -Like "q*") { 
        Break 
        } 
    Write-Host -ForegroundColor Yellow "Deleting profile: $(($users[$account].localpath).replace('C:\Users\',''))" 
    #Remove the local profile 
    ($users[$account]).Delete() 
    Write-Host -ForegroundColor Green "Profile:  $(($users[$account].localpath).replace('C:\Users\','')) has been deleted" 
 
    #Configure yes choice 
    $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Remove another profile." 
 
    #Configure no choice 
    $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No","Quit profile removal" 
 
    #Determine Values for Choice 
    $choice = [System.Management.Automation.Host.ChoiceDescription[]] @($yes,$no) 
 
    #Determine Default Selection 
    [int]$default = 0 
 
    #Present choice option to user 
    $userchoice = $host.ui.PromptforChoice("","Remove Another Profile?",$choice,$default) 
    } 
#If user selects No, then quit the script     
Until ($userchoice -eq 1)
Posted in powershell, scripts | Tagged , , | 26 Comments

WSUS Administrator Module to CodePlex as PoshWSUS

You heard it, I have finally set up a project over at CodePlex for my WSUS module.  The link to the project is here: http://poshwsus.codeplex.com/

Feel free to follow the project, add suggestions and note any issues/bugs that you find while using this module and I will do my best to knock them out.

Also, I am looking to publish my next version 1.1 of the module which will include 8 new commands and has some updated code as well on previous commands.

Posted in Modules, News, powershell, WSUS | Tagged , , , , | Leave a comment

Setting IIS log locations with PowerShell

In my previous post, I showed you how to query for the location of IIS log files using ADSI and connecting to remote systems.  In this post, I will show you some code that will allow you to set the log location for each website in IIS or all of the depending on your preference.

This script came about from a forum post in the Windows PowerShell forum in which a user wanted to change the IIS log locations. Basically you can specify 1 or more computers to run the script advanced function against. Also is the capability to specify a website as opposed to the default of all websites to change the location. The last parameter you need to provide is the new location of where the logs will go.  Using this along with my Get-IISLogLocation will allow you to easily locate and change the log locations.

PS C:\Users\boe> Get-IISLogLocation -computer dc1

Server                                  WebSite                                 LogLocation
——                                  ——-                                 ———–
dc1                                     {Default Web Site}                      {C:\temp}

PS C:\Users\boe> Set-IISLogLocation -computer dc1 -website “Default Web Site” -logdir “D:\logs”
Default Web Site: Log location set to D:\logs
PS C:\Users\boe> Get-IISLogLocation -computer dc1

Server                                  WebSite                                 LogLocation
——                                  ——-                                 ———–
dc1                                     {Default Web Site}                      {D:\logs}

This script also supports the use of –whatif and –confirm

Capture

You can also find this script out on the Script Repository and out at PoshCode.

Function Set-IISLogLocation {
<#
.SYNOPSIS
    This command will allow you to set the IIS log location on a server or multiple servers.
.DESCRIPTION
    This command will allow you to set the IIS log location on a server or multiple servers.
.PARAMETER computer
    Name of computer to set log location on
.PARAMETER logdir
    Location to set IIS logs to write to
.PARAMETER website
    Name of website to change the log location.
.NOTES
    Name: Set-IISLogLocation
    Author: Boe Prox
    DateCreated: 20Aug2010  

.LINK
    https://boeprox.wordpress.com
.EXAMPLE
    Set-IISLogLocation -computer <server> -logdir "D:\logs" 

Description
-----------
This command will change the IIS log locations for each website on the server.
.EXAMPLE
    Set-IISLogLocation -computer <server> -logdir "D:\logs" -website "Default Web Site" 

Description
-----------
This command will change the IIS log locations for only the Default Web Site on a server. 

#>
[cmdletbinding(
    SupportsShouldProcess = $True,
    DefaultParameterSetName = 'default',
    ConfirmImpact = 'low'
)]
param(
    [Parameter(
        Mandatory = $False,
        ParameterSetName = '',
        ValueFromPipeline = $True)]
        [string]$computer,
    [Parameter(
        Mandatory = $False,
        ParameterSetName = '',
        ValueFromPipeline = $False)]
        [string]$logdir,
    [Parameter(
        Mandatory = $False,
        ParameterSetName = 'site',
        ValueFromPipeline = $False)]
        [string]$website
)
Process {
    ForEach ($c in $Computer) { 

            If (Test-Connection -comp $c -count 1) { 

                $sites = [adsi]"IIS://$c/W3SVC"
                $children = $sites.children
                ForEach ($child in $children) {
                    Switch ($pscmdlet.ParameterSetName) {
                       "default" {
                                If ($child.KeyType -eq "IIsWebServer") {
                                If ($pscmdlet.ShouldProcess($($child.servercomment))) {
                                    $child.Put("LogFileDirectory",$logdir)
                                    $child.SetInfo()
                                    Write-Host -fore Green "$($child.servercomment): Log location set to $logdir"
                                    }
                                }
                            }
                        "site" {
                                If ($child.KeyType -eq "IIsWebServer" -AND $child.servercomment -eq $website) {
                                If ($pscmdlet.ShouldProcess($($child.servercomment))) {
                                    $child.Put("LogFileDirectory",$logdir)
                                    $child.SetInfo()
                                    Write-Host -fore Green "$($child.servercomment): Log location set to $logdir"
                                    }
                                }
                            }
                        }
                    }
            }
        }
    }
}
Posted in powershell, scripts | Tagged , , , | Leave a comment

Locating IIS Logs with PowerShell

It has been a while since I last posted here at my blog. Work, holidays that required travelling across a state, getting married and a honeymoon in Cozumel, Mexico have kept me plenty busy during the last month and a half.  But I am back and looking forward to a new post and a new year of learning and working with PowerShell!

This post is going to deal with finding the location of your IIS logs using an advanced function I wrote in response to a Windows PowerShell forum post last year where a user wanted to find a way to list out all of the locations of the log file for each site. While I did not use the IIS PowerShell snap-in to solve this issue, I was able to solve it using the ADSI type and connecting to IIS that way.  This is just one way of working with IIS and other ways include WMI, a snap-in and the IIS provider. Here is a link that deals with using both ADSI and WMI for IIS.

I chose to work with ADSI as it appeared to be the easiest way for me to accomplish what I wanted to do.  The function is pretty simple and just creates a custom object to store the data I need. In this case, the server name, website name and log file location.  This function can be run against 1 or more servers and it will return back the information required, which could be exported out to a CSV file if needed.

One thing I ran into was the script would not return back anything when ran from a Windows 7 workstations.  Turns out I did not have everything loaded that needed to be running thanks to this site.

Here is an example of me running it against a server of mine.

Untitled

Function Get-IISLogLocation { 
<#   
.SYNOPSIS   
    This function can be ran against a server or multiple servers to locate 
    the log file location for each web site configured in IIS. 
.DESCRIPTION 
    This function can be ran against a server or multiple servers to locate 
    the log file location for each web site configured in IIS.     
.PARAMETER computer 
    Name of computer to query log file location. 
.NOTES   
    Name: Get-IISLogLocation 
    Author: Boe Prox 
    DateCreated: 11Aug2010  
          
.LINK   
    https://boeprox.wordpress.com 
.EXAMPLE   
Get-IISLogLocation -computer 'server1' 
 
Description 
----------- 
This command will list the IIS log location for each website configured on 'Server1' 
           
#>  
[cmdletbinding( 
    SupportsShouldProcess = $True, 
    DefaultParameterSetName = 'computer', 
    ConfirmImpact = 'low' 
)] 
param( 
    [Parameter( 
        Mandatory = $False, 
        ParameterSetName = 'computer', 
        ValueFromPipeline = $True)] 
        [string[]]$computer       
) 
Begin { 
    $report = @() 
    } 
Process { 
    ForEach ($c in $Computer) { 
            Write-Verbose "Checking connection on $($c)"
 
            If (Test-Connection -comp $c -count 1) { 
                
                Write-Verbose "Making IIS connection to $($c)" 
                $sites = [adsi]"IIS://$c/W3SVC" 
                $children = $sites.children 
                ForEach ($child in $children) { 
                    Write-Verbose "Checking $child.servercomment" 
                    If ($child.KeyType -eq "IIsWebServer") { 
                        Write-Verbose "Found site"
                        $temp = "" | Select Server, WebSite, LogLocation 
                        $temp.Server = $c 
                        $temp.WebSite = $child.ServerComment 
                        $temp.LogLocation = $child.LogFileDirectory                            
                        $report += $temp  
                        }                                                             
                    } 
            }                 
        }  
    } 
End { 
    $report 
    } 
}
Posted in powershell, scripts | Tagged , , , | 13 Comments

Viewing Print Queue Statistics with PowerShell

Leveraging WMI, you can easily view and manipulate print queues on a print server via PowerShell.  You can do anything from building a Print Queue, deleting a print queue and cancelling all print jobs on an existing queue. You can also use a specific WMI class to view all print jobs that have been processed since the last reboot of a print server or the last service reset of the Print Spooler service on the print server.

While I will not go into how to build a print queue or set up a printer using PowerShell, these links here will show you some great examples on performing this. Instead, my focus in this blog post will show you how easily it is to view the statistics of each print queue on a print server or locally connected printer.

Print Server Statistics

Using the Win32_PerfFormattedData_Spooler_PrintQueue class, you can easily see what printer queues are being used and which ones are not.  The statistics reset themselves when the Print Spooler service is restarted. So if a print server is rebooted, all of the data is reset.  One thing I found while playing with this is that if the printer is not connected to the server or if the printer is turned off, then the statistics will not be reset even if the spooler service has been restarted.  The only way for the statistics to be reset is to power on the printer or reconnect it to the server and perform the spooler reset. Using the following one-liner, you can see which queues have been used along with any that might have had an error at some point in time.

Get-WMIObject Win32_PerfFormattedData_Spooler_PrintQueue |
 Select Name, @{Expression={$_.jobs};Label="CurrentJobs"}, TotalJobsPrinted, JobErrors

Untitled

As it shows, I have one job currently being processed and two jobs total have been sent through the print spooler.  Keep in mind that this is done from my laptop, so running this against a print server hosting many more printers will be more impressive.

Using this on a print server would be great to find old print queues that may remain even after a printer has long since been decommissioned or to see what printers are seeing the most usage.

Putting this into a excel spreadsheet is as simple as piping it into an Export-CSV command. You could even set up a scheduled job that checks each print server once a week and email the results to an individual or group for analysis.

Posted in powershell | Tagged , , | 9 Comments