In embrace of sea and greenery of South-Memoirs from day 7, 8 and 9

IMG_20170630_085006

Munnar was the only location, where we were scheduled to stay for two nights means full one day for the place as the day next, we were to leave as early as possible to reach Bangalore in time.

When we woke up, it was raining but nevertheless, we got ready and then by 7.30 AM, we would have been on nearby restaurant for a heavy breakfast. After which we started driving after 8.30 AM. This time we had hired a guide to drive with us and show us around @ 500Rs.

IMG_20170630_085004

It continued to rain though we had hopes that it would settle till the time we reach to the viewpoints and luckily it happened that way.

Continue reading “In embrace of sea and greenery of South-Memoirs from day 7, 8 and 9”

Advertisements

In embrace of sea and greenery of South-Memoirs from day 5 and 6

DSCN9292

We all waited for morning to have more fun on beach, elders and kids alike. The first job in morning to get ready after bath etc and head to beach with sand castle tools.

DSCN9283

DSCN9319

We must have reached the beach by 7 AM. The lighthouse wasn’t gonna open before 10AM which means it wouldn’t cut into our schedule. It was all about the time on beach now and mild showers were worrying us.

Continue reading “In embrace of sea and greenery of South-Memoirs from day 5 and 6”

In embrace of sea and greenery of South-Memoirs from day 4

DSCN9147

What better feeling than to watch the sunrise at the last end of your country with 360 degree of sea view around and that too under a cloudy and less warm weather. Would admit we wouldn’t have preferred cloudy but a clear sky but every weather has its own texture and should be taken as such.

IMG_20170627_080156

Morning started with mild showers and we had to take umbrellas from the hotel. The excitement to walk towards the end of the country boundary was just something else.

IMG_20170627_060020

IMG_20170627_060556

Changing colors with each passing minute, the place is worth spending the entire day just sitting on that stone bridge.

DSCN9151

The view, the showers, people trying to sell sea shells and even tea vendors doing ferry on that small but not that crowded place, if it wasn’t about rains and specially kids, then we would have stayed much more longer there but then guarding kids on that place where both side it was heavy waves, our mind was asking us to move from there.

Continue reading “In embrace of sea and greenery of South-Memoirs from day 4”

In embrace of sea and greenery of South-Memoirs from day 3

Finally this was the day for which kids been waiting for. Sand castles on sea beach, but they had to wait little more as elders needed to visit Rameshwaram temple. No photos as cameras were not allowed in temples. We woke up early, taken bath, did Manidarshan, but going for “22 Kund snan” was kind of tough with kids around so we chosen the short route. By some 8.15 AM, we would have been free to drive again after checking out from hotel.

IMG_20170626_090914

We reached Danushkodi by 9 AM where we were stopped some 5-6 kms before by a check-post in name of “Road construction” which meant we weren’t allowed to go further at this weather at least. An impromptu parking spot it was from where pvt ferries used to take people to a certain point (Not the last end) on a cost from off-road route. It might look like just a line in maps, but including the shores, its around 500 mtr wide place which stretches around 7kms from north to south. It shares ONLY land border between Sri Lanka and India which is one of the smallest in world at 45 mtrs in length. (of course we didn’t made it to that point).

IMG_20170626_092739

This place is actually called as “Ghost Town” due to a tragedy back on 22 December, 1964 which wiped out roughly 1800 people including those 115 passengers on Pamban-Dhanushkodi passenger train. After that railways station was wiped out and the train never began its operations. A commission looked into possibility of new rail in 2010 and last year a road was constructed till this point.

Anyway… we went into history of the place… let’s come back to travelogue…

Continue reading “In embrace of sea and greenery of South-Memoirs from day 3”

In embrace of sea and greenery of South–Memoirs from day 1 and 2

The story is about the longest break I might have taken in last decade or so. The story is about a trip ranging close to 5700 kms, out of which 2200 kms was on roads driven by me in a car which was not my sweetheart Vista. The story is about a trip which gave first glimpses of sea to my kiddo. The story is about lush green mountains of Munnar. The story is about three families with four kids having fun together.

IMG_20170628_183759

This is just glimpse, a lot more to come from the repository of more than 20Gb of raw photos detailing moments from six individuals and four kids.

IMG_20170626_124447

A lengthy blog post for sure 

Continue reading “In embrace of sea and greenery of South–Memoirs from day 1 and 2”

WSUS and PowerShell: Declining and Deleting updates based on keywords

I am back again with just another write-up on WSUS in a very short time. Last time we talked about Reporting and Cleanup, this time its more into troubleshooting, which often requires one to find particular updates and nuke them out.

Yes! We are talking about those pesky Event IDs 364, which often mention about certain cab files and we System Admin bang their heads on walls to find out that which particular updates they belong to.

Let me give you an easy permanent way out via a Custom PowerShell module named PoshWSUS.

How to use that?

Just download the module, extract the folder named PoshWSUS and copy the same to PowerShell module location for your WSUS Server (I am assuming you have Windows 2008 or Windows 2012 though it works for older ones as well).

Ok. So where is this PowerShell Module Location?

Usually it is C:\Windows\System32\WindowsPowerShell\v1.0\Modules but to know for sure, can open PowerShell prompt and Type $PSHome, which should give you C:\Windows\System32\WindowsPowerShell\v1.0

Installed the module, now what to do?

You can make use of the below code:

Import-Module PoshWSUS

Connect-PoshWSUSServer –WsusServer  -port 8530

# In case you got some Windows 2003 machine which is connect over port 80 in place of 8530 then uncomment the below line and comment the one above this comment.

# Connect-PoshWSUSServer –WsusServer  -port 80 

Get-PoshWSUSUpdate | Get-PoshWSUSUpdateFile | export-csv -notype $env:userprofile\desktop\WSUSFiles.csv

This would give you all the update names, their corresponding files, their actual disk locations and then you can easily find out, which was the particular update, which is causing you Event ID 364. Once you know that its your choice that how to deal with that update, decline, clean and approve, download again or whatever you prefer.

All well? Nope! There might be still a trouble

There is a tricky scenario as well like the one I faced once and that is Local Update Publisher. Microsoft gives the way that one can push certain non-Microsoft updates via WSUS solution after packaging it in a certain format. Looks good but may be a huge trouble when things go wrong. Updates pushed by LUP don’t show up in GUI of WSUS console, so it gets tricky to decline or clean them out. PowerShell comes handy in such scenario as by that we can find updates by keywords and then decline or delete them. Here goes the code.

[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")


$WSUS = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer(,$false,8530) # Or port 80
$UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::Any
$updatescope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::All
$Updates = $WSUS.GetUpdates($UpdateScope)| where-object {$_.title -like "*7-Zip*"}
# In case, the purpose is just to list patches first where title contains 7-zip

$Updates.Title.ToString()

#In case, the purpose is just to decline those patches where 7-zip comes in title, then uncomment the below line 

#$Updates.Decline()

# Uncomment the line in case you need to delete the update files and remove patch from DB as well, then uncomment the below line

#$Updates | Foreach-OBject{$WSUS.DeleteUPdate($_.Id.UpdateId.Tostring()); WRITE-host $_.Title.ToString() deleted -ForeGroundColor RED}

Hope this helps. As you know, the health of WSUS can be checked via wsusutil checkhealth and appearance of Event ID 10000 and 10030 confirm that everything is well.

WSUS and PowerShell: Audit-Compliance Report and Automatic Cleanup

Its been long since I wrote anything on WSUS. As Windows world evolved, many of the troubleshooting steps might be bit out-dated now but WSUS functioning and basics still remain more or less same.

Today I wish to take another aspect of WSUS and talk about WSUS Reporting part and regular clean-up.

WSUS Reporting:

WSUS has a comprehensive reporting system built in and if Report Viewer installed over the server, one can variety of reports, but the same remains limited to a pre-set and also one server only. Let’s think of a scenario, where one might have more than dozens of WSUS servers catering thousands of machines and rather logging over each one or attaching them into one console, we want to have a consolidated report to see if patches getting installed well and also whether team is meeting compliance by install each single patch of (M-1) month.

Now what my PowerShell code does? It bring a report like below in csv format:

Sample

This would include how many patches are needed on which machines, when those machines last contacted their respective WSUS servers, what IP and OS do they have and which particular updates are pending on them and those pending updates were released when. Also it tells count of those updates which have been downloaded or pending download or pending reboot of machines to complete the installation.

I believe this pretty much covers what a system admin might need to get an overview of issues and to address them. Apart from this, what the script does is, it creates some more CSV files, one against each WSUS server mentioning that which particular updates were released between the period of 1st day of (M-2) month (if today is March, then we talking about January 1st) and Second Monday of (M-1) month means just one day before Patch Tuesday. Based on this data, one might check if any of the last month patches are missing on any of the servers. I have written a Excel Macro code around that as well but that still needs updating and make compatible for general usage so would post that at some later time.

Also notice, in code, there are two arrays of WSUS servers. Essential difference between the two are the connection lines. One connects over 8530 port while other connects on port 80, there might be SSL and non-SSL options as well, which can get handled by the second attribute passed depending on your scenario.


# Author : Nitish Kumar
# Performs an audit of WSUS
# Outputs the results to a text file. 
# version 1.2
# 21 May 2017

[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")

$firstdayofmonth = [datetime] ([string](get-date).AddMonths(-1).month + "/1/" + [string](get-date).year)
$DomainName = "."+ $env:USERDNSDOMAIN

# Create empty arrays to contain collected data.
$UpdateStatus = @()
$SummaryStatus = @()		

# For WSUS servers catering servers
$WSUSServers = ("XYZ","ABC")

$a0 = ($WSUSServers | measure).count
$b0 = 0

$thisDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$logFile = "WSUSAuditReports_$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).txt"
Start-Transcript -Path $thisDir\$logFile


ForEach ($WS1 in $WSUSServers) {		
        write-host "Working on $WS1 ..."	-foregroundcolor Green
        $b0 = $b0+1
		
    try {
        
		Try {
				$WSUS = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($WS1,$false,8530)
			}
		Catch {
				$WSUS = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($WS1,$false,80)
		}
		
		$ComputerScope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope
		$UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
		$UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::LatestRevisionApproved
		$updatescope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::All

		$ComputerTargetGroups = $WSUS.GetComputerTargetGroups() | Where {$_.Name -eq 'All Computers'}
		$MemberOfGroup = $WSUS.getComputerTargetGroup($ComputerTargetGroups.Id).GetComputerTargets()


        write-host "Connected and Fetching the data from $WS1 for all computers connecting to it..."	
        $Alldata = $WSUS.GetSummariesPerComputerTarget($updatescope, $computerscope)
        $a = ($Alldata | measure).count
        $b = 0

		write-host "Data recieved from $WS1 for all computers connecting to it..."	
        Foreach ($Object in $Alldata) {
			$b = $b+1
            write-host "Getting data from number $b of all $a computers connecting to $WS1 ($b0 of $a0)..."	-foregroundcolor Yellow
            Foreach ($object1 in $MemberOfGroup) {
				If ($object.computertargetid -match $object1.id) {

					$ComputerTargetToUpdate = $WSUS.GetComputerTargetByName($object1.FullDomainName)                    
					$NeededUpdate = $ComputerTargetToUpdate.GetUpdateInstallationInfoPerUpdate() | where {($_.UpdateApprovalAction -eq "install") -and (($_.UpdateInstallationState -eq "Downloaded") -or ($_.UpdateInstallationState -eq "Notinstalled") -or ($_.UpdateInstallationState -eq "Failed"))	}
					
					$FailedUpdateReport = @()
					$NeededUpdateReport = @()
					$NeededUpdateDateReport = @()

					if ($NeededUpdate -ne $null) {
						foreach ($Update in $NeededUpdate) {						
                                $NeededUpdateReport += ($WSUS.GetUpdate([Guid]$Update.updateid)).KnowledgebaseArticles
								$NeededUpdateDateReport += ($WSUS.GetUpdate([Guid]$Update.updateid)).ArrivalDate.ToString("dd/MM/yyyy ")	
						}
					}

                    $object1 | select -ExpandProperty FullDomainName                    
					$myObject1 = New-Object -TypeName PSObject
					$myObject1 | add-member -type Noteproperty -Name Server -Value (($object1 | select -ExpandProperty FullDomainName) -replace $DomainName, "")
					$myObject1 | add-member -type Noteproperty -Name NotInstalledCount -Value $object.NotInstalledCount
					$myObject1 | add-member -type Noteproperty -Name NotApplicable -Value $object.NotApplicableCount
					$myObject1 | add-member -type Noteproperty -Name DownloadedCount -Value $object.DownloadedCount
					$myObject1 | add-member -type Noteproperty -Name InstalledCount -Value $object.InstalledCount
					$myObject1 | add-member -type Noteproperty -Name InstalledPendingRebootCount -Value $object.InstalledPendingRebootCount
					$myObject1 | add-member -type Noteproperty -Name FailedCount -Value $object.FailedCount
					$myObject1 | add-member -type Noteproperty -Name NeededCount -Value ($NeededUpdate | measure).count
					$myObject1 | add-member -type Noteproperty -Name Needed -Value $NeededUpdateReport
					$myObject1 | add-member -type Noteproperty -Name LastSyncTime -Value $object1.LastSyncTime
					$myObject1 | add-member -type Noteproperty -Name IPAddress -Value $object1.IPAddress
					$myObject1 | add-member -type Noteproperty -Name OS -Value $object1.OSDescription
					$myObject1 | add-member -type Noteproperty -Name NeededDate -Value $NeededUpdateDateReport
					$SummaryStatus += $myObject1
				}
			}
		}

		$SummaryStatus | select-object server,NeededCount,LastSyncTime,InstalledPendingRebootCount,NotInstalledCount,DownloadedCount,InstalledCount,FailedCount,@{Name="KB Numbers"; Expression = {$_.Needed}},@{Name="Arrival Date"; Expression = {$_.NeededDate}},NotApplicable,IPAddress,OS|export-csv -notype $Env:Userprofile\desktop\AllServersStatus.csv
		
        write-host "Connected with $WS1 and finding patches for last month schedule .."	
        # Find patches from 1st day of (M-2) month to 2nd Monday of (M-1) month 
		$updatescope.FromArrivalDate = [datetime](get-date).Addmonths(-2).AddDays(-((Get-date).day-1))

		$updatescope.ToArrivalDate = [datetime](0..31 | % {$firstdayofmonth.adddays($_) } | ? {$_.dayofweek -like "Mon*"})[1]
        #[datetime](0..31 | % {$firstdayofmonth.adddays($_) } | ? {$_.dayofweek -like "Mon*"})[1]

        $file1 = "$env:userprofile\desktop\Currentmonthupdates_"+$WS1+".csv"
		$WSUS.GetSummariesPerUpdate($updatescope,$computerscope) |select-object @{L='UpdateTitle';E={($WSUS.GetUpdate([guid]$_.UpdateId)).Title}},@{L='Arrival Date';E={($WSUS.GetUpdate([guid]$_.UpdateId)).ArrivalDate}},@{L='KB Article';E={($WSUS.GetUpdate([guid]$_.UpdateId)).KnowledgebaseArticles}},@{L='NeededCount';E={($_.DownloadedCount+$_.NotInstalledCount)}},DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount | Export-csv -Notype $file1
		
        }
		catch [Exception] {
				write-host $_.Exception.GetType().FullName -foregroundcolor Red
				write-host $_.Exception.Message -foregroundcolor Red
                continue;
		}
		}
				
Stop-Transcript
notepad $logFile

Once the report task is done, let’s talk about one smaller one. Cleaning of WSUS not relevant files and Computers. WSUS itself provides a one click option for this but again, it doesn’t cater if you want to handle multiple servers at once nor gives you a text report of what happened in one pass. So, here goes the another script to solve that part.

Now this script considers that we are in a SCOM monitored (or may be some other tool) environment means we would get a few tickets when we run the clean-up as clean-up restarts the update service on each WSUS twice and also may trigger high CPU etc. The script stops before each WSUS server and takes a manual input from you while informing you that its going to work on which server and that server should be put into maintenance mode in SCOM before proceeding further. It logs everything into the same directory where the script is. The path can be changed in case needed.

Also if one wants to make it completely automated then read-host lines can be removed and also at the end of code, we can add some lines to ensure that the status of entire activity gets passed over email to relevant personnel.

# Author : Nitish Kumar
# Performs a cleanup of WSUS.
# Outputs the results to a text file.
# version 1.0
# 07 March 2017

[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") 

# For all WSUS servers in Environment
$WSUSNon2k3 = ("abc001","xyz001")
$WSUS2k3 = ("cba001","zyx001")

# In case need to check for some individual server or few particular servers
#$WSUSNon2k3 = ("abc001")
#$WSUS2k3 = ("")

$AllWSUSCount = ($WSUSNon2k3 | measure).count + ($WSUS2k3 | measure).count
$WorkingonWSUS = 0

$thisDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$logFile = "WSUSCleanupResults_$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).txt"
Start-Transcript -Path $thisDir\$logFile

$Reply = "@"

ForEach ($WS1 in $WSUSNon2k3) {
        $WorkingonWSUS = $WorkingonWSUS + 1
        $WS1

		Read-Host "Put $WS1 into maintenance mode. Once done,press Enter to continue ..." | Out-Null

        write-host "Working on $WS1 ($WorkingonWSUS of $AllWSUSCount) ..."	-foregroundcolor Green
try { 

    $WSUS = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($WS1,$false,8530)

	write-host "Connected with $WS1 ($WorkingonWSUS of $AllWSUSCount)and proceeding for cleanup ..."	-foregroundcolor Yellow
	write-host "Connected with $WS1 ($WorkingonWSUS of $AllWSUSCount)and proceeding for cleanup ..."

	$cleanupScope = new-object Microsoft.UpdateServices.Administration.CleanupScope; 

	$cleanupScope.CleanupObsoleteComputers = $true
	$cleanupScope.DeclineSupersededUpdates = $true
	$cleanupScope.DeclineExpiredUpdates         = $true
	$cleanupScope.CleanupObsoleteUpdates     = $true
	$cleanupScope.CleanupUnneededContentFiles = $true
	$cleanupScope.CompressUpdates                  = $true 

	$cleanupManager = $WSUS.GetCleanupManager();
	$cleanupManager.PerformCleanup($cleanupScope); 

	write-host "Cleaning done for $WS1 ($WorkingonWSUS of $AllWSUSCount) ..."	-foregroundcolor Yellow
}
catch [Exception] {

	write-host $_.Exception.GetType().FullName -foregroundcolor Red
	write-host $_.Exception.Message -foregroundcolor Red

	continue;
}
}

ForEach ($WS1 in $WSUS2k3) {
        $WorkingonWSUS = $WorkingonWSUS + 1
        $WS1

		Read-Host "Put $WS1 into maintenance mode. Once done,press Enter to continue ..." | Out-Null		

        write-host "Working on $WS1 ($WorkingonWSUS of $AllWSUSCount) ..."	-foregroundcolor Green

try { 

    $WSUS = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($WS1,$false,80)

	write-host "Connected with $WS1 ($WorkingonWSUS of $AllWSUSCount)and proceeding for cleanup ..."	-foregroundcolor Yellow

	$cleanupScope = new-object Microsoft.UpdateServices.Administration.CleanupScope; 

	$cleanupScope.CleanupObsoleteComputers = $true
	$cleanupScope.DeclineSupersededUpdates = $true
	$cleanupScope.DeclineExpiredUpdates         = $true
	$cleanupScope.CleanupObsoleteUpdates     = $true
	$cleanupScope.CleanupUnneededContentFiles = $true
	$cleanupScope.CompressUpdates                  = $true 

	$cleanupManager = $WSUS.GetCleanupManager();
	$cleanupManager.PerformCleanup($cleanupScope);
	write-host "Cleaning done for $WS1 ($WorkingonWSUS of $AllWSUSCount) ..."	-foregroundcolor Yellow

}
catch [Exception] {

	write-host $_.Exception.GetType().FullName -foregroundcolor Red
	write-host $_.Exception.Message -foregroundcolor Red

	continue;
}
}
Stop-Transcript
notepad $logFile

So these were my two cents. These scripts are still a work-in-progress and would improve based on feedbacks.