PoshWSUS 2.0 released

Actually, it is now 2.0.1 as I have fixed a couple of bugs in the module. But yes, I have finally released version 2.0 of PoshWSUS on Codeplex. Please go out there to check out all of the new features and let me know what you think. If you see any bugs, please be sure to report them on the issues page of the project.

For anyone unfamiliar with PoshWSUS, it is a PowerShell module used to managing a WSUS Server.

I actually have some Hey, Scripting Guy! articles coming out the week of January 16th that will cover using PowerShell with WSUS and also talks about using PoshWSUS 2.0. So be sure to check them out!

List of commands:

  • Add-WSUSClientToGroup
  • Approve-WSUSUpdate
  • Connect-WSUSDatabaseServer
  • Connect-WSUSServer
  • Deny-WSUSUpdate
  • Disconnect-WSUSServer
  • Export-WSUSMetaData
  • Get-WSUSChildServer
  • Get-WSUSClient
  • Get-WSUSClientGroupMembership
  • Get-WSUSClientPerUpdate
  • Get-WSUSClientsInGroup
  • Get-WSUSCommand
  • Get-WSUSContentDownloadProgress
  • Get-WSUSCurrentUserRole
  • Get-WSUSDatabaseConfig
  • Get-WSUSDownstreamServer
  • Get-WSUSEmailConfig
  • Get-WSUSEvent
  • Get-WSUSGroup
  • Get-WSUSInstallableItem
  • Get-WSUSInstallApprovalRule
  • Get-WSUSServer
  • Get-WSUSStatus
  • Get-WSUSSubscription
  • Get-WSUSSyncEvent
  • Get-WSUSSyncHistory
  • Get-WSUSSyncProgress
  • Get-WSUSUpdate
  • Get-WSUSUpdateApproval
  • Get-WSUSUpdateCategory
  • Get-WSUSUpdateClassification
  • Get-WSUSUpdateFile
  • Get-WSUSUpdatePerClient
  • Get-WSUSUpdateSummary
  • Get-WSUSUpdateSummaryPerGroup
  • Get-WSUSUpdateSummaryPerClient
  • Import-WSUSMetaData
  • New-WSUSComputerScope
  • New-WSUSGroup
  • New-WSUSInstallApprovalRule
  • New-WSUSLocalPackage
  • New-WSUSUpdateScope
  • Remove-WSUSClient
  • Remove-WSUSClientFromGroup
  • Remove-WSUSGroup
  • Remove-WSUSInstallApprovalRule
  • Remove-WSUSUpdate
  • Reset-WSUSContent
  • Resume-WSUSDownload
  • Resume-WSUSUpdateDownload
  • Set-WSUSEmailConfig
  • Set-WSUSInstallApprovalRule
  • Start-WSUSCleanup
  • Start-WSUSInstallApprovalRule
  • Start-WSUSSync
  • Stop-WSUSDownload
  • Stop-WSUSSync
  • Stop-WSUSUpdateDownload
  • Test-WSUSDatabaseServerConnection
Posted in Modules, powershell, WSUS | Tagged , , , | 14 Comments

Use PowerCLI to Report Storage Paths in your ESXi Environment

Just something I wanted to share with the community…

One of our VMWare Administrators was working with VMWare vendor support in looking at some SAN connection issues and was requesting a report showing the following information: Host, Name, HBAName, Source, Target, LUN, Status, Path, PathSelectionPolicy

They could then use this information to further review the data and make some recommendations on our infrastructure. While you can view the data by looking at vSphere and going through a couple of different areas to pull the data, hand copying this data would be just painfully slow.

Here is where PowerShell and PowerCLI come into play. I was asked if I could put a script together that could pull all of the information and put it into a report of some sort. Putting the data into a report is very simple using Export-CSV, but gathering the data would be a little trickier as the required data requested was not in one place to just pull from.

Fortunately, after a little exploring I was able to locate all of the data and put together a short 41 line script that would pull this data and put it into a CSV file for the vendor to use.

A couple of things I added to the script was the use of Write-Progress to allow you to see where the script was at in its processing. Otherwise, you could be left wondering for an hour or so trying to figure out if the script was still running of was stuck somewhere.

image

In my case, the script took about an hour to run in one of my environments and produced a 11000+ row CSV file with the information seen below.

image

While I am by no means an expert with PowerCLI and am sure that there may be a better way of pulling this data, this still worked out like a champ and provided the vendor the useful information to decide where some changes could be made at. I would not call it a “finished” script as it lacks inline help and verbose logging, but feel free to take it and make any additions to it that you see fit. Hopefully you will find this script just as useful in your environment.

Remember to update the vCenter Server in the script to point to your server so the script works correctly!

image

Download (remove .doc from file)

Source Code

#Connect to vCenter Server
Connect-VIServer vCenterServer

#Get list of ESXi Hosts
$esxihosts = Get-VMHost
$i=0

$data = ForEach ($esxi in $esxihosts) {
    $i++
    Write-Progress -Activity "Scanning hosts" -Status ("Host: {0}" -f $esxi.Name) -PercentComplete ($i/$esxihosts.count*100) -Id 0
    $hbas = $esxi | Get-VMHostHba
    $j=0
    ForEach ($hba in $hbas) {
        $j++
        Write-Progress -Activity "Scanning HBAs" -Status ("HBA: {0}" -f $hba.Device) -PercentComplete ($j/$hbas.count*100) -Id 1
        $scsiluns = $hba | Get-ScsiLun
        $k=0
        ForEach ($scsilun in $scsiluns) {
            $k++
            Write-Progress -Activity "Scanning Luns" -Status ("Lun: {0}" -f $scsilun.CanonicalName) -PercentComplete ($k/$scsiluns.count*100) -Id 2
            $scsipaths = $scsilun | Get-Scsilunpath
            $l=0
            ForEach ($scsipath in $scsipaths) {
                $l++
                Write-Progress -Activity "Scanning Paths" -Status ("Path: {0}" -f $scsipath.Name) -PercentComplete ($l/$scsipaths.count*100) -Id 3
                New-Object PSObject -Property @{
                    Host = $esxi.name
                    HBAName = $scsilun.RuntimeName
                    PathSelectionPolicy = $scsilun.MultiPathPolicy
                    Status = $scsipath.state
                    Source = "{0}" -f ((("{0:x}" -f $hba.PortWorldWideName) -split '([a-f0-9]{2})' | where {$_}) -Join ":")
                    Target = $scsipath.SanId
                    LUN = (($scsilun.RunTimeName -Split "L")[1] -as [Int])
                    Path = $scsipath.LunPath
                }
            }
        }
    }
}

$data | Export-Csv -NoTypeInformation 'ESXiStorageInfo.csv'
Posted in PowerCLI, powershell, scripts, VMWare | Tagged , , , | 22 Comments

Quick Tip To Find Out What Your Background Jobs Are Doing

One of the great things about PowerShell is the use of background jobs to run long operations while still having access to your console, or running multiple jobs to gather your data that much quicker than doing a sequential approach. Rather than go into how cool this is and what not, I am going into a trick that I use to track the status of a running job.  For long running jobs, you might not have a good idea where the job is at in its progress. 

The trick to this is looking not at the job you usually see when you create your background job, as in this image:

image

This was just a simple line of code I used to keep the job running a little while.

Start-Job -ScriptBlock {start-sleep -seconds 10}

This is actually the parent job of a child job which is actually running the operation that you define either in a scriptblock or file.

To quote the msdn page:

The job does not perform any of the work. Each job has at least one child job because the child job performs the actual work. When you run a cmdlet so that the work is performed as a job, the cmdlet must add the job and the child jobs to a common repository, referred to as the job repository.

image

As you can see, there is the child job that is doing all of the work while the parent job patiently waits for it to complete. Now we might be so patient with our jobs, especially when we are trying to gauge when the job will finish. So how do we make this happen you ask? Simple, we use a technique that we already use (well, some of use at least): Write-Verbose logging in our code that will run in the background job. Looking deeper into the child job shows some interesting properties:

(Get-Job).childjobs | Select *

image

The properties I am talking about are Output,Error,Progress,Verbose,Debug and Warning.

Each one of these will display the appropriate data based on what you are using in your background job.  Lets dig deeper and actually see this in action by kicking off a longer job that provides some sort of output:

Start-Job -ScriptBlock {
    for ($i=0;$i -lt 100;$i++) {
        $VerbosePreference = 'continue'
        Write-Verbose ("[$(Get-Date)] - Iteration: {0}" -f $i)
        Start-Sleep -Seconds 5
    }
}

Wait a few seconds…

(Get-Job -Id 5).ChildJobs[0].Verbose

image

As you see, everything that you used for Write-Verbose is now displayed for you to track. You can actually take it a little bit further to only display the verbose output from the job:

(Get-Job -Id 5).ChildJobs[0].Verbose | Select -Expand Message

image

Just by adding a little bit of logging into your background job, you can track the status of it at any given moment when you think it might be hung up on something.  Given, my example was pretty simple, but you still get the idea of what you can do with this trick.

Lets look at the progress property and how we can use Write-Progress in a background job to track it as well. As you know, Write-Progress is a nice tool to use to track a running operation, but it also has great use in a background job as well.

Start-Job -ScriptBlock {
    for ($i=0;$i -lt 100;$i++) {
        $VerbosePreference = 'continue'
        Write-Progress -Activity "Progress" -Status ("Iteration: {0}" -f $i) -PercentComplete ($i)
        Start-Sleep -Seconds 5
    }
}

Wait a few seconds again…

(Get-Job -Id 7).ChildJobs[0].Progress

image

The objects that get returned when checking the progress property of the job is System.Management.Automation.ProgressRecord.  And if you just want the numbers of where it is at:

(Get-Job -Id 7).ChildJobs[0].Progress | Select -expand PercentComplete

image

 

Get-JobMessage Function

I wrote a simple function that can help to parse the jobs and provide you the specific type of data that you want to see, whether it is verbose, progress or errors. I could have probably done more with this, but decided to keep it as simple as possible due to time constraints. But if someone else wants to pick up on this and expand on it, then by all means go for it!

A couple screenshots of it in action.

Get-Job -id 5 | Get-JobMessage -ShowOutPut

image

Get-Job -id 7 | Get-JobMessage -ShowVerbose

image

Like I said, pretty simple but it gets the job done.

You can download the function below:

(Remove .Doc before using)

Posted in powershell | Tagged , , | 4 Comments

Querying UDP Ports with PowerShell: Part 2

In my previous post on working with UDP ports and PowerShell I showed you how you can set up listeners on both your local and remote systems to send and receive data through those ports.  While this is a great way to have some fun and potentially test whether the ports are indeed active on a system, it wasn’t really the best way at checking whether a UDP port is available on a remote system after some further checking.

For this post, I installed the Simple TCP/IP Services which gives me the QOTD (Quote of the Day), Character Generator, Daytime and Discard services each with a specific port that is opened up. I am mostly going to focus on the Character Generator service and its associated UDP port (19) as it gives you a set of 95 printable ASCII characters if the port is open. Some great information and better explanations about this can be found here and here.

The way that these services work is that you must first send a datagram to the port and then it will respond to you with a message in return. The problem that occurs using the method that I mention in my previous article is that when you first create the object with a specified port open, in this case 19 for Character Generator, the system will never get the message from the remote host. See below for the incorrect way to create the UDP object.

$udpobject = new-Object system.Net.Sockets.Udpclient(11000)

Now this is fine if you want the port opened up to receive traffic from another source, but based on my testing does not appear to play nice if you are sending data to a port and want to automatically receive data back from the remote port. Instead, I create my object like this:

$udpobject = new-Object system.Net.Sockets.Udpclient

From there I can continue to use the same code as before to make the connection to my remote system, DC1 and the remote port for Character Generator: 19 and also set a timeout for the receiving of the remote data to 1000 milliseconds (the default is set to 0 which means that it will continue to block access to your session until data is received).

$udpobject.Connect("dc1",19)
$udpobject.Client.ReceiveTimeout = 1000

If you wanted to have more asynchronous operations without the blocking, you can change perform the following change: (More info on Blocking here)

$udpobject.Client.Blocking = $False

I won’t be changing this to True in my example as I would rather wait for the received data before proceeding or let it timeout. The next step is to setup my string and convert it into a byte format so it can be sent to the remote port in hopes of getting a response.

$a = new-object system.text.asciiencoding
$byte = $a.GetBytes("TEST")

image

Looks like bytes to me.

Now to send the datagram to the remote port and also setup the remote endpoint that the UDP object will use to listen for.

[void]$udpobject.Send($byte,$byte.length)
$remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any,0)

Ok, so now that we have configured all of that stuff, the only thing left to do is tell the UDP object to wait until it receives the datagram from the remote host. It will do so based on the timeout property we set up on the client properties of the UDP object.

Try {
    $receivebytes = $udpobject.Receive([ref]$remoteendpoint)
} Catch {
    Write-Warning "$($Error[0])"
}

This is also where you will find out if the port is open on the remote system. If the port is not open, an error will be returned stating the an error like this:

image

You can set up some error handling to better catch this exception like I did in the code snippet above to get something like this:

image

But in my case, I know that this port is not only open, but willing to send out data on the condition that you first send data (which we did earlier). As soon as the object receives the data (or the timeout has been reached), it will stop the blocking and allow the code to continue processing the data if it has any. If there is data, we will need to translate it from bytes back to a string so we actually know what it is saying.

If ($receivebytes) {
    [string]$returndata = $a.GetString($receivebytes)
    $returndata
} Else {
    "No data received from {0} on port {1}" -f $Computername,$Port
}
$udpobject.Close()

Ok, lets see what we get from this…

image

Pretty cool, isn’t it? I was able to get the Character Generator service to respond back to me from UDP Port 19 with the data I was expecting (A lot of ASCII characters).

By the way, the Close() method is used to clean up after myself and close the connection.

Lets now check out the other ports covered by the Simple TCP/IP Services:

Daytime (Port 13)

image

Quote of the Day (Port 17)

image

Echo (Port 7)

image

As you can see, this is a pretty nice way to request information from UDP ports, but you should note that you will not always get a return message back from a UDP port, even if the port is actually open.

Test-Port Function

Being that this is a better way to test for open UDP ports, I updated my Test-Port function to reflect this change. The function is available from the following locations:

Script Repository

Poshcode

Quick demo of the updated Test-Port function

Test-Port -computer DC1 -port 7,8,13,17 -UDP -TCP

image

 

Full code available here (change to suit your needs)

$Computername = 'dc1'
$Port = 7
$udpobject = new-Object system.Net.Sockets.Udpclient
$udpobject.Connect($Computername,$Port)
$udpobject.Client.ReceiveTimeout = 1000
$a = new-object system.text.asciiencoding
$byte = $a.GetBytes("TEST")
[void]$udpobject.Send($byte,$byte.length)
$remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any,0)
Try {
    $receivebytes = $udpobject.Receive([ref]$remoteendpoint)
} Catch {
    Write-Warning "$($Error[0])"
}
If ($receivebytes) {
    [string]$returndata = $a.GetString($receivebytes)
    $returndata
} Else {
    "No data received from {0} on port {1}" -f $Computername,$Port
}
$udpobject.Close()
Posted in powershell, scripts | Tagged , , | 1 Comment

PowerShell Profile Removal GUI

In a previous article, I showed you how you can use PowerShell to remove local profiles from local and remote systems. A couple of downsides to using this was:

  1. This only worked on Vista and above Operating Systems
  2. You can only remove one profile at a time (this was mitigated Peter in the comments section)
  3. This is all done through the PowerShell console, no GUI to work with

After some work and a few delays, I have finally finished (for the most part) my update to my other profile removal script that includes support for XP and 2003 systems, allows for the removal of more than one system and also is a GUI!

Run the ProfileRemovalGUI.ps1 file to bring up the GUI as shown below.

image

 

It is recommended that you are running this as an Administrator also. Once the UI is up and running, simply type in a hostname and click connect.

I have tried to make this a pretty robust utility for locating and removing profiles. One thing I included is the ability to locate Temp. profiles that occasionally popup for one reason or another.

I was able to make this work for XP and 2003 systems where the Win32_UserProfile class doesn’t exist by performing a registry query for HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProfileList and enumerate through each SID folder and from there I can get the location of the profile folder via the ProfileImagePath key. To be sure that I cleanly remove the profile data from the system, I remove both the profile folder and the registry key. Code snippet below for the query:

    $basekey = "LocalMachine"
    $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($basekey,$Server)
    $SubKey = $registry.OpenSubKey("Software\Microsoft\Windows NT\CurrentVersion\ProfileList",$True)
    $Exclusions = "systemprofile","LocalService","NetworkService"  
    $Registrydata = ForEach ($key in $Subkey.GetSubKeyNames()) {
        $RegProfile = $registry.OpenSubKey("Software\Microsoft\Windows NT\CurrentVersion\ProfileList\$key",$True)
        $LocalPath = ($RegProfile.GetValue("ProfileImagePath") -replace "C:\\","\\$($Server)\C$\")
        $UserName = Split-Path $LocalPath -Leaf
        $regpath = ($RegProfile.name -replace "HKEY_LOCAL_MACHINE\\","")
        If ($Exclusions -notcontains $UserName) {
            New-Object PSObject -Property @{
                Username = $UserName
                LocalPath = $LocalPath
                SID = $key
                IsActive = "NA"
                RegistryPath = $regpath
                Type = 'Registry'
            }
        }
    

 

For the rest of the systems (Vista and above) I use the Win32_UserProfile WMI class to perform the queries and I then use Remove-WMIObject to remove the WMI path to each selected profile.

Lets make a connection to my Vista laptop to see what profiles I want to remove:

image

Here you see the username, sid, localpath to the profile folder and whether the profile is currently in use (IsActive) in which it cannot be removed. Note that the IsActive is only applicable to Vista and higher Operating Systems.

Lets remove the TestUser profile since I do not need it on my laptop.

image

As you can see, the TestUser profile has been removed now only laptop and the UI has been refreshed to show that only my profile (Boe) remains (Note: the Profile Count: has been updated since this screenshot and is now displaying properly).

Now on to a Windows 2003 Server to show the backwards compatibility of this utility:

image

Again, like the query for Vista and above operating systems, you get the same information with the exception of the IsActive column that only shows NA due to not being able to tell if the profile is currently in use.

The same process remains though to remove one or more profiles. Simply select as many profiles that you wish to remove and click Remove Profiles.

image

image

As you can see, profiles have been removed. The profiles have been removed both at the file/folder level and at the registry level.

Give it a shot and let me know what you think!

Updated: 9 Oct 2011 —
        Version: 1.2
            -> Added LastAccess column to UI
            -> Added autorefresh of list view after removal of profiles
            -> Added cancel button
            -> Fixed bug where some profile folders were not being included in registry or wmi queries but folders existed

Download

ProfileRemovalGUI.ps1

Posted in powershell, scripts | Tagged , , | 11 Comments