Thursday, February 28, 2013

How to retrieve ESX and local datastores with Powershell

I have been asked to individuate all the ESX that need being remediated as well as retrieve the capacity of their local storage to see if some of them might need larger disks to be bought.

There are a few hundreds ESX in that server farm, so I am going to show you how I used Powershell and PowerCLI cmdlets to retrieve exactly what I wanted.

The aim of my script is to return a custom Powershell object (aka PSObject) that contains all the needed properties for later analysis.

These properties are: the ESX name, model, version, build and all of its local datastores with their capacity. This last one in particular I retrieve it using the MultipleHostAccess property.

Here's the script I wrote:
  1. function Get-Esxinfo {  
  2. <#  
  3.   .SYNOPSIS  
  4.     This script returns information on one or more ESX.  
  5.    
  6.   .DESCRIPTION  
  7.     This script returns information on one or more ESX.  
  8.     You can run the script against one or more ESX.  
  9.     This script can display the information in a spreadsheet.  
  10.    
  11.   .PARAMETER Server  
  12.     Specify the vSphere servers on which you want to run this function.  
  13.    
  14.   .PARAMETER Name  
  15.     Specify the names of the hosts you want to retrieve.  
  16.     By default this script will retrieve all the ESX hosts connected to the current vCenter.  
  17.    
  18.   .PARAMETER Filepath  
  19.     Specifies the path to the CSV output file. The default is 'esxinfo.csv'. This file will be saved in the local temp folder.  
  20.       
  21.   .PARAMETER OpenFile  
  22.         Opens the output file with the program associated with CSV files.  
  23.            
  24.   .PARAMETER PassThru  
  25.         Returns the object.  
  26.    
  27.   .EXAMPLE  
  28.         Get-Esxinfo -server virtualcenter  
  29.         Gets information for all the ESX connected to VIserver named 'virtualcenter'.  
  30.           
  31.   .EXAMPLE  
  32.         Get-Esxinfo -server virtualcenter -name esx00*  
  33.         Gets information for all the ESX whose name starts with esx00* connected to VIserver named 'virtualcenter'.  
  34.   
  35.   .EXAMPLE  
  36.         $EsxServers = Get-Content ESXServers.txt  
  37.         $EsxServers | Get-Esxinfo -server virtualcenter -openfile -passthru  
  38.         Gets the information from all the ESX listed in ESXServers.txt  
  39.         Opens the output file with the program associated with CSV files.  
  40.         Returns the object.  
  41.   
  42.   .EXAMPLE  
  43.         Get-Esxinfo -server virtualcenter -verbose | format-table  
  44.         Gets information for all the ESX connected to VIserver named 'virtualcenter' and show the output in a table.  
  45.         Show verbose information.  
  46.   
  47.    .INPUTS  
  48.     System.String  
  49.     
  50.   .OUTPUTS  
  51.     PSObject  
  52.            
  53.   .NOTES  
  54.     Author: happysysadm.com  
  55.     Created: 27Avril2013  
  56.   
  57. #>  
  58.   
  59. #requires -version 2.0  
  60.   
  61. [CmdletBinding()]  
  62.   
  63. param(  
  64.     [Parameter(Mandatory=$true)]  
  65.     [string[]]$server,  
  66.       
  67.     [Parameter(Mandatory=$false,ValueFromPipeline=$True,ValueFromPipeLineByPropertyName=$True)]  
  68.     [string[]]$name="*",  
  69.       
  70.     [Parameter()]  
  71.     [ValidateScript({  
  72.         if((Test-Path -LiteralPath $_ -IsValid) -and ($_ -like '*.CSV'))  
  73.             {  
  74.             $true  
  75.             }  
  76.         else  
  77.             {  
  78.             throw ('The specified path "{0}" is invalid. Valid arguments include a path to a CSV file. Exiting.' -f $_)  
  79.         }  
  80.     })]  
  81.     [string]$filepath = (Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath "esxinfo.csv"),  
  82.   
  83.     [Parameter()]  
  84.     [switch]$OpenFile,  
  85.            
  86.     [Parameter()]  
  87.     [switch]$PassThru  
  88. )  
  89.   
  90. write-verbose "Adding VMware Powershell Snapin"  
  91. IF (-not (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) {  
  92. Add-PSSnapin VMware.VimAutomation.Core  
  93. }  
  94.   
  95. write-verbose "Connecting to vCenter"  
  96. try  
  97.     {  
  98.     Connect-VIServer $server  
  99.     }  
  100. catch  
  101.     {  
  102.     Write-Warning "An error occured.`n$_`nExiting..."  
  103.     break  
  104.     }  
  105.   
  106. write-verbose "Preparing the wrapper for the returned psobjects"  
  107. $esxarray = @()  
  108.   
  109. write-verbose  "Retrieving the ESX information and storing it in an array (can take a bit...)"  
  110. try  
  111.     {  
  112.     $esxsource = @(Get-VMHost -Name $name)  
  113.     }  
  114. catch  
  115.     {  
  116.     Write-Warning "An error occured.`n$_`nExiting..."  
  117.     break  
  118.     }  
  119.   
  120. write-verbose "Counting the number of ESX to show a progress bar"  
  121. $totalesx = ($esxsource).count  
  122.   
  123. write-verbose "Initialiasing a counter"  
  124. $a = 0  
  125.   
  126. foreach($esx in $esxsource)  
  127.     {  
  128.     write-verbose  "Creating a psobject and adding members to it"  
  129.     $esxobject = New-Object -typename psobject   
  130.     $esxobject | add-member -membertype noteproperty -name 'ESX Name' -Value $esx.name  
  131.     $esxobject | add-member -membertype noteproperty -name 'ESX Model' -value $esx.model  
  132.     $esxobject | add-member -membertype noteproperty -name 'ESX Version' -value $esx.version  
  133.     $esxobject | add-member -membertype noteproperty -name 'ESX Build' -value $esx.build  
  134.       
  135.     write-verbose  "Retrieving local datastores only, LUNs are excluded because they are not local hardware"  
  136.     $datastores = Get-Datastore -VMHost $esx | Get-View | ?{$_.Summary.MultipleHostAccess -match 'false'}  
  137.     $i = 1  
  138.     foreach($datastore in $datastores)  
  139.         {  
  140.         $esxobject | add-member -membertype noteproperty -name "ESX $i datastore name" -value $datastore.summary.name  
  141.         $esxobject | add-member -membertype noteproperty -Name "ESX $i datastore capacity GB" -value ($datastore.summary.capacity /1GB)  
  142.         $i++  
  143.         }  
  144.     write-verbose "Adding PSObject to array"  
  145.     $esxarray += $esxobject  
  146.       
  147.     write-verbose "Removing psobject to be reused"  
  148.     $esxobject = $null  
  149.     $a++  
  150.       
  151.     write-verbose "Writing a progress bar"  
  152.     Write-Progress -Id 1 -Activity ("Querying ESX") -PercentComplete ($a / $totalesx * 100) -Status ("Queried {0} ESX of {1}" -f $a$totalesx)  
  153.     }  
  154.   
  155. write-verbose "Going to write array content to $filepath"  
  156. try  
  157.     {  
  158.     $esxarray | Export-Csv -UseCulture -NoTypeInformation $filepath  
  159.     }  
  160. catch [System.IO.IOException]  
  161.     {  
  162.     Write-Warning "Unable to write to $filepath. File could be in use.`n$_`nExiting..."  
  163.     break  
  164.     }  
  165. catch  
  166.     {  
  167.     Write-Warning "An error occured.`n$_`nExiting..."  
  168.     }  
  169.       
  170. write-verbose "Opening file if asked so"  
  171. if ($OpenFile)  
  172.         {  
  173.         Invoke-Item -Path $FilePath  
  174.         }  
  175.   
  176. write-verbose "Returning psobject if asked so"  
  177. if ($PassThru)  
  178.     {  
  179.     $esxarray  
  180.     }  
  181.               
  182. }  
  183.   
  184. Get-Esxinfo -server virtualcenter -name myesx* -verbose -PassThru -OpenFile | ft  
The only mandatory parameter is the name of your vCenter, which is used for initial connection. Other optional parameters are the name of the ESX to retrieve (* is used in case you specify none), the name of the logfile (which will be saved in your temp folder), and the possibility to invoke the program associated with CSV files (-openfile).

As you can see I create two hashtables:
  • one for containing the output of the get-vmhost command, so that later queries can quickly be done against this object instead of waiting for powershell to query the vCenter database again and again;
  • one for containing every ESX object with its properties.

Interesting articles that explain the use of PSObject and that I suggest you to read are:

I would also like to point out the use I made of a progress bar. This is a nice Powershell feature which is often overlooked by scripters. I like the idea of having this pop-up bar, so I deeply suggest getting used to it because its syntax is not so diffcult.

Note that the appearance of the progress bar varies when it is used at the Powershell prompt or within the PowerGUI:

Powershell write-progress as it appears at the prompt

Powershell write-progress as it appears in PowerGUI
Be sociable, share!

Wednesday, February 27, 2013

Problem with SSL in VMware vCenter Update Manage 5

I am currently working for a new customer and went on installing the whole bundle of VCenter 5 and vSphere Update Manager 5 (VUM) and configuring them to update the hosts as I was used to. But, actually, when I started configuring my update baselines, I discovered that the download of patches in VUM failed for apparently no reason.

I was stuck for a moment on this problem because the settings I used for connection to the proxy appeared to work. To check this I used the 'Test-Connection' button in the Configuration tab of VUM and always got the same positive result:
The following URLs have been successfully accessed:

http://www.vmware.com
http://www.microsoft.com
What was very confusing in analyzing this problem was the fact that inside the VUM log (named vmware-vum-server-log4cpp and located under C:\ProgramData\VMware\VMware Update Manager\Logs on the VUM server) there were plenty of unrelated errors.

After a long digging, I traced it back to the following error, which was the first one in the logs:

[2013-02-19 13:36:11:648 'httpDownload' 10140 ERROR] [httpDownload, 732] Error 12175 from WinHttpSendRequest for url https://www.vmware.com/PatchManagementSystem/patchmanagement

which was followed by

[2013-02-19 13:36:11:648 'httpDownload' 10140 INFO] [httpDownload, 926] Retrying https://www.vmware.com/PatchManagementSystem/patchmanagement with proxy off
[2013-02-19 13:36:11:648 'httpDownload' 10140 INFO] [httpDownload, 556] Downloading https://www.vmware.com/PatchManagementSystem/patchmanagement
[2013-02-19 13:36:13:898 'httpDownload' 10140 ERROR] [httpDownload, 732] Error 12007 from WinHttpSendRequest for url https://www.vmware.com/PatchManagementSystem/patchmanagement

At this point it was clear to me that I was facing a proxy problem, so I went to talk to the network admins to see if there was some specific configuration in place at their company which could lead to this problem and bingo!, they said to me that they were using Zscaler, which, to my understanding, is an SSL inspection engine, whose role is to get the outgoing secured transaction (the one between your webclient and the proxy), analyze its content and only then separately setup another SSL session between the proxy and the destination website, which in our case is www.vmware.com.

The problem with this inspection engine is that the certificate with which Zscaler returns the information from the VMWare website is not recognised as trusted by the VUM server and the download is blocked.

Apparently this problem appeared with VUM version 5, where SSL certificate verification has been added for increased security.

The solution in my case was to disable this check by editing the following registry key:

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\VMware, Inc.\VMware Update Manager\SslVerifyDownloadCertificate

and setting its value to 0.

To completely solve this issue a restart of VUM service is required. This is quickly accomplished in Powershell with this one-liner:
gsv "vmware-ufad-vci" | Restart-Service
If you have an open VirtualCenter Client, you also have to re-enable the VUM plug-in with plug-in Manager.

I hope this help other people who might encounter this problem. If this posted helped you do not hesitate to +1.

Tuesday, February 19, 2013

Winter Scripting Games event 2

In this post I want to show you how I would have solved the second 2013 Winter Scripting Games event, whose statement was:

"Your organization is preparing to renew maintenance contracts on all of your server computers. You need to provide management with a report that details each server’s computer name, manufacturer, model, number of logical processors, and installed physical memory. Most of the servers do not have Windows PowerShell installed. None of the servers a separated from your client computer by a firewall. The server computer names are provided to you in a file named C:\Servers.txt, which contains one computer name per line.

Minimum Requirements

Optional Criteria
Suppress error messages if a computer cannot be contacted

Desired Output
ComputerName Model Manufacturer LogicalProcs PhysicalRAM
SERVER2 PowerEdge Dell 8 128 SERVER3 Proliant Compaq 2 4"

This is not a complex task, but it demands a good general knowledge of Powershell, especially if you are eager to solve it without Googling around for similar answers.

This is the script I would have posted:
get-wmiobject win32_computersystem -ComputerName (get-content C:\servers.txt) -erroraction silentlycontinue | format-table -property @{label='ComputerName';expression={$_.name}},Model,Manufacturer,@{label='LogicalProcs';expression={$_.NumberOfProcessors}},@{label='PhysicalRAM';expression={[Math]::Round($_.TotalPhysicalMemory/1GB)}}
As you can see I have kept it in a one-liner, because my opinion is that there is no need to write function for basic tasks which we are not going to perform again any soon. And over-doing is not a good habit, unless expressely required by the task.

The output looks like this:
ComputerName Model       Manufacturer LogicalProcs PhysicalRAM
------------ -----       ------------ ------------ -----------
server1      VMware V... VMware, Inc.            2           4
server2      VMware V... VMware, Inc.            1           4
server4      VMware V... VMware, Inc.            2           2
The computer 'server3' is missing from the list because it is powered off, but no error is showed because the optional criteria stated that we have to suppress error messages if it can't be contacted. I could have used Try Catch, but I choose to keep it simple as I feel this is what judges would prefer.

Feel free to comment!

For my solution for the first event, check here.

Monday, February 18, 2013

Now on Google+ and Facebook!

After getting into Twitter a few months ago, this weekend I have decided to give my blog a greater visibility by sharing my posts on Facebook and Google+. Here's the links to these new features of my blog:
This is exciting! I hope that my choice will improve my interaction with readers and followers of this blog, which is one of the main points I will focus on in 2013. So feel free to add me to your Google+ circles or to 'like it' on Facebook! 


For more information about me, check out my personal information page.

Do not hesitate to share!

Thursday, February 14, 2013

Using the new storage cmdlets in Windows 2012

Starting with Windows 2012 there are a bunch of new Powershell cmdlets for disk and partition management. Their use is pretty straightforward and they can easily replace Server Manager as the tool to format partitions, reset disks or get stats about disk usage.

Nonetheless it takes some time to find the correct parameter to bind them one another with pipes. That's the reason why I have decided to write a little sample script where, starting from a list of server disks, I go down to find the partitions and the volumes on each of them.

The three cmdlets I used for this script are get-disk, get-partition and get-volume. Others exist and, if you want to play with them, the complete list of these 84 cmdlets is either on Technet or on Powershell V3.0  Get-Command –Module Storage.

Here's the script:
foreach($disk in (get-disk))
{
Write-Host "`nIn Disk $($disk.number) there are the following partitions:"
foreach($partition in (Get-partition -DiskNumber $disk.number | ?{$_.driveletter -match "^[a-z]*$"}))
{
write-host "`tIn Partition $($partition.partitionnumber) there are the following volumes:" -nonewline
write-Host "`t`t$(get-volume -driveletter $partition.driveletter | ft -hidetableheaders -property driveletter,filesystem -autosize | out-string)" -nonewline
}
}
A sample output would be:

In Disk 0 there are the following partitions:
   In Partition 4 there are the following volumes:
      C NTFS

In Disk 1 there are the following partitions:
   In Partition 2 there are the following volumes:
      E NTFS
      F NTFS
      G REFS

I haven't tested it on servers with volumes that span one or more drive, so if you do, please report whether this script works or not.

Last thing to know is that for the moment PowerGUI, if you use it, is not aware of those cmdlets, and fails importing the Storage module.

As a side note, I have had some trouble filtering out from my get-partition query those drives that have no letter assigned (such as the hidden system restore partition). I ended up using the -match operator with regular expressions (regex) as brilliantly explained  here.

Monday, February 11, 2013

Get-DiskSizeInfo function

As some of you might have heard, the 2013 Powershell Scripting Games are approaching. As a warm up session, the Powershell community has prepared a Winter test which I failed to subscribe for. I have nevertheless decided to test myself against the first task, whose description is here:

"Winter Scripting Games 2013 - Practice Event #1

Sizing up the Disks

You have been asked to create a Windows PowerShell advanced function named Get-DiskSizeInfo. It must accept one or more computer names, and use WMI or CIM to query each computer. For each computer, it must display the percentage of free space, drive letter, total size in gigabytes, and free space in gigabytes. If a specified computer cannot be contacted, the function must log the computer name to C:\Errors.txt and display no error message.

Minimum Requirements:

Optional Criteria:

  • Display optional verbose output showing the computer name being contacted"
Here's the script I came up with. It is a pretty simple one, but knowledge of all main Powershell principles is required, i.e. parameterized functions, WMI queries, composite formatting, expressions and error handling. The behavior of my script can vary depending on the Powershell version you are using to execute it. In particular it is compatible with Powershell V2, but works better with Powershell V3, where some of the bugs of test-connection have been fixed.

Concerning test-connection, I decided to set the 'erroraction' parameter to 'stop' because my try() catch() construct was unable to detect the exception on unreachable hosts.
function Get-DiskSizeInfo{
<#
.DESCRIPTION
Check disk total size in GB, free space in GB and percent free space

.PARAMETER Computername
Specify computer name(s)

.EXAMPLE
Get-DiskSizeInfo

Get local drives free space

.EXAMPLE
Get-DiskSizeInfo -ComputerName computer1,computer2

Get remote computer[s] drive space

.EXAMPLE
get-DiskSizeInfo -verbose

Shows verbose output. The verbose switch is available since we use the [CmdletBinding()]
#>

[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach($Comp in $ComputerName)
{
try{
Write-Verbose -Message "ComputerName: $Comp - Testing connection..."   
Test-Connection $comp -ErrorAction stop | Out-Null
Write-Verbose -Message "ComputerName: $Comp - Connection successfull. Proceeding"   
$conn_OK = $True
}
catch{
Write-Verbose -Message "ComputerName: $Comp - Connection failed. Exiting."
add-content C:\Errors.txt $Computername
$conn_OK = $False
}
if($conn_OK)
{
Write-Verbose -Message "ComputerName: $Comp - Getting drive information..."
Get-WmiObject -Class Win32_volume -ComputerName $Comp |
ft DriveLetter,
@{Label="PercentFree";Expression= {"{0:p}" -f ($_.freespace / $_.capacity)}},
@{Label="Total size in GB";Expression= {"{0:N2}" -f ($_.capacity/1gb)}},
@{Label="Free space in GB";Expression= {"{0:N2}" -f ($_.freespace/1gb)}}
Write-Verbose -Message "ComputerName: $Comp - Ended retrieving drive information"
Write-Host " " #Add an empty line
}
}
}
get-DiskSizeInfo
If you have problems understanding this script, do not hesitate to contact me and I'll be glad to answer. Just keep in mind that this is my solution to the proposed problem and might not be as good as other's. Keep an eye in particular on the Scripting Guy's blog and on Powershell.org.

Thursday, February 7, 2013

Access denied to disk share on Windows 2012

One thing you may not know is that when you add a disk to a Windows 2012 virtual machine under vSphere 5, it gets added and tagged as removable. The consequence of this tag is that when you try to access that disk using \\servername\e$, a popup message appear:
"Windows cannot access \\servername\e$
You do not have permission to access \\servername\e$. Contact your network administrator to request access."

If you look on the security log of your Windows server: an audit failure should have appeared:
Log Name:      Security
Source:        Microsoft-Windows-Security-Auditing
Date:          07/02/2013 10:34:13
Event ID:      4656
Task Category: Removable Storage
Level:         Information
Keywords:      Audit Failure
User:          N/A
Computer:      server.domain.com
Description:
A handle to an object was requested.
What surprised me at first when I saw this message was the task category: Removable Storage. How comes that a new vmdk is seen as a removable disk by Windows? Well, the answer is that in ESXi 5.x, SCSI controllers are presented as removable devices to the VM, as you can see in the following screenshot:


After a lot of investigation, I have found two possible workarounds to this problem.

The first workaround is to disable a security policy in gpedit.msc under Computer Configuration\Windows Settings\Security Settings\Advanced Audit Policy Configuration\System Audit Policies\Object Access, because, as stated here, "in Windows Server 2012 or Windows 8, an audit event is generated each time a user attempts to copy, move, or save a resource to a removable storage device".

Microsoft justifies this choice in Windows 8 and Windows 2012 with this argument: "Many organizations are concerned about sensitive data being copied onto removable storage devices that are not controlled by their IT departments. Windows 7 and Windows Server 2008 R2 do not support auditing removable storage devices. As a result, enterprises lose the visibility of who accessed sensitive data after it has been copied to a removable storage device."

The short way to disable this auditing policy is to run the following command from an elevated prompt and then reboot:

auditpol /set /subcategory:"Removable Storage" /failure:disable

The second workaround is to disable hot plug for this virtual machine, by adding the configuration parameter devices.hotplug and setting it to false, as explained by VMWare at this link:


Adding this parameter requires the VM to be powered off, so it is currently an off-line fix.

Waiting for a real fix either by Microsoft or by VMWare, that's the best I could find.

Monday, February 4, 2013

Error enabling Data Collector Set on Windows 2012

Today I was trying to setup a Data Collector Set under performance monitor on a Windows 2012 system to monitor its disk activity. Once I had the Data Collector Set created and configured its schedule, I right-clicked on "Server Manager Performance Monitor" in the left pane and choose the "start" action.

When I did so an error message was shown saying: "when attempting to start the Data Collector Set the following system error occurred: The task is disabled".


It took some time to figure out what this message was referring to. At first I checked into the Services MMC, then I realized that the guilty object was the scheduled task named "Server Manager Performance Monitor" under "Task scheduler library / Microsoft / Windows / PLA".

I right clicked on it and choose "Enable". The problem went straight away and I was able to fire my data collection.


Hope this helps.
Related Posts Plugin for WordPress, Blogger...