mercredi 7 décembre 2016

Setting up or Updating SNMP via Powershell on a Windows Server 2012 R2

A little while ago, I had to write a Powershell function to update the SNMP settings from a bunch of servers running Windows 2012 R2 Server.

The following cases had to be handled:
 - The SNMP feature could be missing or present
 - The SNMP settings could be existing (Managers / Communities)
 - The SNMP settings could be wiped depending on the servers (Managers / Communities)

So here's what I came up with. Note that we rely on WSMan to make things easier when we're dealing with more than one server and that a new community is always added as Read-Only.

Function Set-SNMPProperties {

    #requires -version 3


<#

    .SYNOPSIS

            This function will install SNMP if needed and configure the Permitted Managers along with

            the desired communities in read-only via WSMan

    .DESCRIPTION

            This function will install SNMP if needed and configure the Permitted Managers along with

            the desired communities in read-only via WSMan

    .PARAMETER  Computer

            Specifies which computer(s) will be processed

    .PARAMETER  SNMPPermittedManager

            Specifies which Managers are allowed

    .PARAMETER  SNMPCommunity

            Specifies which Communities are allowed

    .PARAMETER  SNMPClear

            Specifies whether the existing Managers/Communities should be cleared or not

    .EXAMPLE

            Set-SNMPProperties -Computer "lab-fs001.katalykt.lan" `

                -SNMPPermittedManager "lab-centreon001.katalykt.lan" `

                -SNMPCommunity "katalyktRO"


            PSComputerName           SNMPManagers                   SNMPCommunities

            --------------           ------------                   ---------------

            lab-fs001.katalykt.lan   lab-centreon001.katalykt.lan   katalyktRO

    .EXAMPLE

            Set-SNMPProperties -Computer "lab-fs001.katalykt.lan", "lab-fs002.katalykt.lan" `

                -SNMPPermittedManager "lab-centreon001.katalykt.lan", "lab-centreon002.katalykt.lan" `

                -SNMPCommunity "katalyktRO", "labRO"


            PSComputerName           SNMPManagers                                         SNMPCommunities

            --------------           ------------                                         ---------------

            lab-fs001.katalykt.lan   {lab-centreon001.katalykt.lan, lab-centreon002...}   {katalyktRO, labRO}

            lab-fs002.katalykt.lan   {lab-centreon001.katalykt.lan, lab-centreon002...}   {katalyktRO, labRO}

    .EXAMPLE

            # Assuming the CSV contains one column with the title "Computer"

            Import-Csv -Path D:\Scripts\PSv3\Set-SNMPProperties.csv | ForEach-Object {$_.Computer} | `

                Set-SNMPProperties -SNMPPermittedManager "lab-centreon001.katalykt.lan" `

                    -SNMPCommunity "katalyktRO" `

                    -SNMPClear


            PSComputerName           SNMPManagers                   SNMPCommunities

            --------------           ------------                   ---------------

            lab-fs001.katalykt.lan   lab-centreon001.katalykt.lan   katalyktRO

            lab-fs002.katalykt.lan   lab-centreon001.katalykt.lan   katalyktRO

            lab-fs003.katalykt.lan   lab-centreon001.katalykt.lan   katalyktRO

            lab-fs004.katalykt.lan   lab-centreon001.katalykt.lan   katalyktRO

    .NOTES

            NAME:     Set-SNMPProperties

            AUTHOR:   ROULEAU Benjamin

            LASTEDIT: 2016-11-17

    .LINKS

            http://katalykt.blogspot.fr/

#>

    [CmdletBinding()]

    PARAM(

        [Parameter(

            Mandatory,

            ValueFromPipeline,

            ValueFromPipelineByPropertyName)]

        $Computer,

        

        $SNMPPermittedManager,


        $SNMPCommunity,


        [switch]$SNMPClear

    )


    BEGIN {


    }


    PROCESS {

        $Computer | ForEach-Object {

            Try {

                Write-Verbose -Message "[PROCESS - Set-SNMPProperties] Processing computer [$_]"


                If (Test-WSMan -ComputerName $_ -ErrorAction SilentlyContinue) {

                    Invoke-Command -ComputerName $_ -ErrorAction Stop -ErrorVariable ErrInvoke -ScriptBlock {

                        $VerbosePreference = $Using:VerbosePreference

                        

                        # Install the SNMP Service with the WMI provider if needed

                        If ((Get-WindowsFeature -Name "SNMP-Service").Installed -eq "True") {

                            Write-Verbose -Message "[PROCESS - Set-SNMPProperties] The SNMP-Service Windows Feature is already installed"

                        } Else {

                            Write-Verbose -Message "[PROCESS - Set-SNMPProperties] Installing the SNMP-Service Windows Feature"

                            

                            Get-WindowsFeature -Name "SNMP-Service" | Add-WindowsFeature -IncludeAllSubFeature -IncludeManagementTools

                        }


                        # Clear the existing managers/communities if specified

                        If ($SNMPClear) {

                            Write-Verbose -Message "[PROCESS - Set-SNMPProperties] Clearing up the existing Managers and Communities"


                            "PermittedManagers", "ValidCommunities" | ForEach-Object {

                                $Subkey = $_

                                (Get-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\$Subkey").GetValueNames() |  ForEach-Object {

                                    Write-Verbose -Message "[PROCESS - Set-SNMPProperties] Clearing up existing registry record: HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\$Subkey\$_"


                                    Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\$Subkey" -Name $_

                                }

                            }

                        }


                        # Check if the given permitted managers are defined or not

                        Write-Verbose -Message "[PROCESS - Set-SNMPProperties] Checking up Permitted Managers..."


                        $LocalManagers = $using:SNMPPermittedManager

                        If ($LocalManagers) {

                            $PermittedManagers = Get-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\PermittedManagers"

                            $Index = ($PermittedManagers.ValueCount)+1

    

                            $PermittedManagers.GetValueNames() | ForEach-Object {

                                # If this manager is located within the manager array, we remove it from it

                                $Item = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\PermittedManagers" -Name $_).$_

                            

                                If ($Item -in $LocalManagers) {

                                    Write-Verbose -Message "[PROCESS - Set-SNMPProperties] Skipping Manager $Item since it's already present"

                                    $LocalManagers = $LocalManagers | Where-Object {$_ -ne $Item}

                                }

                            }


                            # Add the missing managers if needed

                            $LocalManagers | ForEach-Object {

                                Write-Verbose -Message "[PROCESS - Set-SNMPProperties] Adding Manager $_ in the Permitted list with Index $Index"


                                New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\PermittedManagers" -Name $Index -Value $_

                                $Index++

                            }

                        }


                        # Check if the community exists, if not we add it

                        Write-Verbose -Message "[PROCESS - Set-SNMPProperties] Checking up the communities..."


                        $using:SNMPCommunity | ForEach-Object {

                            If (-not(Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ValidCommunities" -Name $_ -ErrorAction SilentlyContinue)) {

                                Write-Verbose -Message "[PROCESS - Set-SNMPProperties] Adding Community $_"


                                New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ValidCommunities" -Name $_ -Value 4 -PropertyType DWORD

                            } Else {

                                Write-Verbose -Message "[PROCESS - Set-SNMPProperties] Skipping Community $_ since it's already present"

                            }

                        }


                        # Return the new SNMP settings for this host

                        New-Object -TypeName PSObject -Property @{

                            SNMPCommunities = (Get-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ValidCommunities").GetValueNames()

                            SNMPManagers = (Get-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\PermittedManagers").GetValueNames() | ForEach-Object {(Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\PermittedManagers" -Name $_).$_}

                        }

                    }

                } Else {

                    Write-Warning -Message "[PROCESS - Set-SNMPProperties] Computer [$_] could not be contacted via WSMan"

                }

            } Catch {

                If ($ErrInvoke) { Write-Warning -Message ("[PROCESS - Set-SNMPProperties] Invoke-Command failed for computer [{0}] with error {1}" -f $Error[0].OriginInfo, $Error[0].Exception.Message) }

            }

        } | Select PSComputerName, SNMPManagers, SNMPCommunities

    }


    END {


    }

}

mercredi 21 septembre 2016

Deploying an High-Availability Domain-Based DFS Namespace with Powershell on Windows Server 2012 R2

As I'm often doing some tests in my lab environment, I try to automate as many things as possible.

So here's a little Powershell script that can be used to deploy an High Availability Domain-Based DFS Namespace on a Windows Server 2012 R2. The UI result can be seen below.




In this lab's case I've been using the following servers to create the root namespace:

  • lab-dc001: This is the Windows Server 2012 R2 Domain Controller
  • lab-dfs001: This is the first DFS Server
  • lab-dfs002: This is the second DFS Server

Both DFS servers are members of my katalykt.lan domain.

Note that the script can be executed on any of our DFS servers and that we rely on PS/CIM sessions to execute some remote commands (i.e Folders creation, SMB Shares)

# Parameters

$DFS_Root = "lab-dfs001"

$DFS_Secondaries = "lab-dfs002"

$DFS_Domain = "katalykt.lan"

$DFS_Namespace = "root"

$DFS_SmbPath = "E:\DFS\root"

$DFS_Servers = $DFS_Root, $DFS_Secondaries

# Install the DFS Namespace & Replication features

$DFS_Servers | ForEach-Object {Install-WindowsFeature -ComputerName $_ -Name FS-DFS-Namespace,FS-DFS-Replication -IncludeManagementTools}

# Create the Local Folders

$Sessions_DFS = New-PSSession -ComputerName $DFS_Servers

Invoke-Command -Session $Sessions_DFS -Script { New-Item $args[0] –Type Directory } -ArgumentList $DFS_SmbPath

$Sessions_DFS | Remove-PSSession

# Create the Local Shares

$CIMs_DFS = New-CimSession -ComputerName $DFS_Servers

New-SmbShare –Name $DFS_Namespace –Path $DFS_SmbPath -CimSession $CIMs_DFS

$CIMs_DFS | Remove-CimSession

# Create the main DFS Namespace root

New-DfsnRoot -TargetPath ("\\{0}\{1}" -f $DFS_Root, $DFS_Namespace) -Type DomainV2 -Path ("\\{0}\{1}" -f $DFS_Domain, $DFS_Namespace)

# Add additional targets to our DFS Namespace

$DFS_Secondaries | ForEach-Object {

    New-DfsnRootTarget -Path ("\\{0}\{1}" -f $DFS_Domain, $DFS_Namespace) -TargetPath ("\\{0}\{1}" -f $_, $DFS_Namespace)

}

mardi 28 juin 2016

How to create a report on the VM Guest Disks in VMware vCenter via Powershell




A while ago, I was asked to build up a report about the Free and Occupied space by the VM in our vCenter Infrastructure.

The task itself wasn't that complicated, but the tricky part was mostly about the presentation. So I came up with a simple HTML design which lists the following properties:

  • The VM name
  • The Drive name
  • The Total capacity on the drive
  • The Free space on the drive
  • The Occupation percentage shown along with a progress bar

Once executed, the results are parsed in a easy-to-read way as seen below!



The script may be called normally or by using a piped format:

"vc001.katalykt.lan", "vc002.katalykt.lan" |

    Get-VMGuestDiskCapacity -Verbose |

    Sort-Object -Property Name |

    Export-VMGuestDiskCapacity -Path d:\reports -Verbose



Note that the script uses two profile variables for the mailing ($Profile_Mailing and $Profile_SMTP) which may be defined as follows in your powershell profile (you may also adapt the script to pass it as a variable!):

# Local System Mailing Definition

$_Profile_Mailing = New-Object -TypeName PSObject -Property @{

    From = "Powershell Reporter<posh@noreply.com>"

    ToSysAdm = "SYSTEM REPORT <SYSTEM-REPORT@katalykt.lan>"

}


# Profile - Global RO - System Mailing

New-Variable -Name Profile_Mailing -Value $_Profile_Mailing -Scope Global -Option ReadOnly


# Profile - Global RO - SMTP

New-Variable -Name SMTP_Host -Value "smtp.katalykt.lan" -Scope Global -Option ReadOnly



In my environment, the script isn't being executed with an account that allows a direct connection to the vCenter. In this case I am using some Read-Only credentials stored within my Powershell Profile. You may want to use the -Credentials switch with the Get-VMGuestDiskCapacity function, thus giving us a command like:

"vc001.katalykt.lan", "vc002.katalykt.lan" |

    Get-VMGuestDiskCapacity -Credentials $SYSTEM_Accounts.vCenterReader -Verbose |

    Sort-Object -Property Name |

    Export-VMGuestDiskCapacity -Path d:\reports -Verbose


The credentials may be defined as follows:

# Local System Operators Definition

$_SYSTEM_Accounts = New-Object -TypeName PSObject -Property @{

    vCenterReader = New-Object System.Management.Automation.PSCredential ("KATALYKT\srv-vcreader", ("654d1116743f0423413b16050a5345MgB8AGAFAARgBFAFEAZABwAGEALcAdABNkAMQBowBmAEEATQB4AGADgANwB1AHcAPQA9AHwAMQBjADUAMQBmADMANQAzAGYAMgAwAGEAOAAzADcAZQBlADkAMgA4ADMAZQOAA1AGMANQA4AGIAYwAyAGUANAA5ADMAZABlADMAZQAyADYABjAGYANAA4ADkAYwBkADkAMQDgAMwAzADcAA3ADMAZQBmADIANwA4AMAA1ADIAMgA=" | ConvertTo-SecureString -Key (1..16)))

}


# Profile - Global RO - System Operators

New-Variable -Name SYSTEM_Accounts -Value $_SYSTEM_Accounts -Scope Global -Option ReadOnly



So here's the script!

#Requires -Version 3.0


Function Export-VMGuestDiskCapacity {

<#

    .SYNOPSIS

            This function will export the output from Get-VMGuestDiskCapacity

    .DESCRIPTION

            This function will export the output from Get-VMGuestDiskCapacity via Mail or/and in an HTML report

    .PARAMETER  VM

            The VM status in an object-format, pipeline is supported

    .PARAMETER  Path

            (Optional) The Folder Path used to store the HTML report in

    .PARAMETER  AsMail

            (Optional) Send the report by Mail if present

    .EXAMPLE

            $VM = Get-VMGuestDiskCapacity -vCenter vc001.katalykt.lan

            Export-VMGuestDiskCapacity -VM $VM

    .EXAMPLE

            Get-VMGuestDiskCapacity -vCenter vc001.katalykt.lan | Export-VMGuestDiskCapacity -Path d:\reports

    .EXAMPLE

            Get-VMGuestDiskCapacity -vCenter vc001.katalykt.lan | Export-VMGuestDiskCapacity -AsMail

    .EXAMPLE

            "vc001.katalykt.lan", "vc002.katalykt.lan" | Get-VMGuestDiskCapacity | Export-VMGuestDiskCapacity -Path d:\reports -AsMail

    .PROFILES

            [System.Object]$Profile_Mailing

            [System.String]$Profile_SMTP

    .NOTES

            NAME:     Export-VMGuestDiskCapacity

            AUTHOR:   ROULEAU Benjamin

            LASTEDIT: 2015-08-21

#>

    [CmdletBinding()]

    PARAM(

        [Parameter(

            Mandatory,

            ValueFromPipeline,

            ValueFromPipelineByPropertyName)]

        $VM,


        [ValidateScript({Test-Path -Path $_ -PathType Container})]

        $Path,


        [Switch]$AsMail

    )


    BEGIN {

        Write-Verbose -Message "[BEGIN - Export-VMGuestDiskCapacity] Attempting to export the desired output"


        Function Get-ProgressColoration {

        <#

            .SYNOPSIS

                    This function will return a color according to an Hue/Saturation/Lightness Value

            .DESCRIPTION

                    This function will return a color according to an Hue/Saturation/Lightness Value

            .PARAMETER  HSLValue

                    The HSL Value on a scale from 0 to 1

            .PARAMETER  ConvertToRGB

                    (Optional) Define whether the output should be returned in a RGB format or not

            .PARAMETER  Saturation

                    (Optional) The Saturation Value on a scale from 0 to 1

            .PARAMETER  Lightness

                    (Optional) The Lightness Value on a scale from 0 to 1

            .EXAMPLE

                    Get-ProgressColoration -HSLValue 0.5

                    

                    hsl(60.0,100%,50%)

            .EXAMPLE

                    Get-ProgressColoration -HSLValue 0.2 -Saturation 0.7 -Lightness 0.4

                    

                    hsl(96.0,70%,40%)

            .EXAMPLE

                    Get-ProgressColoration -HSLValue 0.5 -ConvertToRGB

                    

                    rgb(255,255,0)

            .NOTES

                    NAME:     Get-ProgressColoration

                    AUTHOR:   ROULEAU Benjamin

                    LASTEDIT: 2015-08-21

        #>

            [CmdletBinding()]

            PARAM(

                [Parameter(Mandatory)]

                [ValidateRange(0,1.00)]

                [Decimal]$HSLValue,

                

                [Switch]$ConvertToRGB,


                $Saturation = 1,


                $Lightness = 0.5

            )


            BEGIN {

                Write-Verbose -Message "[BEGIN - Get-ProgressColoration] Getting a desired color for value $Value"

            }


            PROCESS {

                # Return either the HSL or the RGB format

                If ($ConvertToRGB) {

                    $Hue = ((1-$HSLValue)*120).ToString() -replace ',', '.'

                    "rgb({0})" -f ((Convert-HSLToRGB -Hue ($Hue/360) -Saturation $Saturation -Lightness $Lightness) -join ',')

                } Else {

                    "hsl({0},{1}%,{2}%)" -f ((((1-$HSLValue)*120).ToString() -replace ',', '.'), [System.Math]::Round($Saturation * 100), [System.Math]::Round($Lightness * 100))

                }

            }


            END {

                

            }

        }


        Function Convert-HSLToRGB {

        <#

            .SYNOPSIS

                    This function will convert a Hue/Saturation/Lightness Value to a Red/Green/Blue format

            .DESCRIPTION

                    This function will convert a Hue/Saturation/Lightness Value to a Red/Green/Blue format

            .PARAMETER  Hue

                    The Hue Value on a scale from 0 to 1

            .PARAMETER  Saturation

                    The Saturation Value on a scale from 0 to 1

            .PARAMETER  Lightness

                    The Lightness Value on a scale from 0 to 1

            .EXAMPLE

                    Convert-HSLToRGB -Hue 0.166666666666667 -Saturation 1 -Lightness 0.5

                    

                    255, 255, 0

            .NOTES

                    NAME:     Convert-HSLToRGB

                    AUTHOR:   ROULEAU Benjamin

                    LASTEDIT: 2015-08-21

            .LINKS

                    https://en.wikipedia.org/wiki/HSL_color_space

                    http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c

        #>

            [CmdletBinding()]

            PARAM(

                [Parameter(Mandatory)]

                $Hue,


                [Parameter(Mandatory)]

                $Saturation,


                [Parameter(Mandatory)]

                $Lightness

            )


            BEGIN {

                Write-Verbose -Message "[BEGIN - Convert-HSLToRGB] Converting HSL: $Hue, $Saturation, $Lightness to RGB"

                

                Function Convert-HueToRGB {

                <#

                    .SYNOPSIS

                            This function will convert a Hue value to either a Red, Green or Blue value

                    .DESCRIPTION

                            This function will convert a Hue value to either a Red, Green or Blue value

                    .PARAMETER  p

                            The temporary 2 value

                    .PARAMETER  q

                            The temporary 1 value

                    .PARAMETER  t

                            The temporary color (R, G or B)

                    .EXAMPLE

                            Convert-HSLToRGB -Hue 0.166666666666667 -Saturation 1 -Lightness 0.5

                    

                            255, 255, 0

                    .NOTES

                            NAME:     Convert-HueToRGB

                            AUTHOR:   ROULEAU Benjamin

                            LASTEDIT: 2015-08-21

                    .LINKS

                            https://en.wikipedia.org/wiki/HSL_color_space

                            http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c

                            http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/

                #>

                    [CmdletBinding()]

                    PARAM(

                        [Parameter(Mandatory)]

                        $p,


                        [Parameter(Mandatory)]

                        $q,


                        [Parameter(Mandatory)]

                        $t

                    )


                    BEGIN {


                    }


                    PROCESS {

                        If ($t -lt 0) { $t += 1 }

                        If ($t -gt 1) { $t -= 1 }

                        If ($t -lt 1/6) { return (($p + ($q - $p)) * 6 * $t) }

                        If ($t -lt 1/2) { return $q }

                        If ($t -lt 2/3) { return (($p + ($q - $p)) * (2/3 - $t) * 6) }

                        return $p

                    }


                    END {


                    }

                }

            }


            PROCESS {

                # If there's no saturation then it's a shade of grey.

                If ($Saturation -eq 0) {

                    $Red = $Green = $Blue = $Lightness

                } Else {

                    If ($Lightness -lt 0.5) {

                        $q = $Lightness * (1 + $Saturation)

                    } Else {

                        $q = $Lightness + $Saturation - ($Lightness *$Saturation)

                    }


                    $p = 2 * $Lightness -$q


                    $Red = Convert-HueToRGB -p $p -q $q -t ($Hue + 1/3)

                    $Green = Convert-HueToRGB -p $p -q $q -t ($Hue)

                    $Blue = Convert-HueToRGB -p $p -q $q -t ($Hue - 1/3)

                }


                # Round it up

                $Red = [System.Math]::Round($Red * 255)

                $Green = [System.Math]::Round($Green * 255)

                $Blue = [System.Math]::Round($Blue * 255)


                Write-Verbose -Message "[BEGIN - Convert-HSLToRGB] Returning RGB: $Red, $Green, $Blue"


                @($Red, $Green, $Blue)

            }


            END {

                

            }

        }


        $HTML_BorderThinColor = "#e5e5e5"

        $HTML_BorderThickColor = "#6690bc"

        $InitDate = Get-Date -Format "dd/MM/yyyy HH:mm:ss"

    }


    PROCESS {

        # Build the output

        $HTML_BorderColor = $HTML_BorderThickColor

        If ($VM.Disks) {

            $HTML_Disks = ""


            $VM.Disks | Sort-Object DiskPath | ForEach-Object {

                # Parse properly

                If ($HTML_Disks -ne "") {

                    $TR_OpenTag = "<tr>"

                } Else {

                    $TR_OpenTag = ""

                }


                # Coloration scale

                $ScaleRatio = 1

                If ($_.PercentFull -lt 95) { $ScaleRatio = 1.3 }



                $HTML_Disks += '

                  {5}

                    <td style="border-right: 1px solid {7}; border-top: 1px solid {6}; padding-left: 4px;"><span id="pbcontainer">{0}</span></td>

                    <td width="80px" id="rowlight" style="border-top: 1px solid {6};">{1} GB</td>

                    <td width="80px" id="rowlight" style="border-top: 1px solid {6};">{2} GB</td>

                    <td width="50px" id="rowlight" style="border-top: 1px solid {6};">{3} %</td>

                    <td style="border-top: 1px solid {6};">

                        <div class="percentbar" style="width:100px; ">

                          <div style="background-color: {4};width:{3}px;"></div>

                        </div>

                    </td>

                  </tr>

                ' -f $_.DiskPath, $_.Capacity, $_.Freespace, $_.PercentFull, (Get-ProgressColoration -HSLValue (($_.PercentFull / 100) / $ScaleRatio) -ConvertToRGB), $TR_OpenTag, $HTML_BorderColor, $HTML_BorderThinColor

                

                # The next borders are lighter than the first one

                $HTML_BorderColor = $HTML_BorderThinColor

            }


            $HTML_Content += '

            <tr>

              <td width="300px" rowspan="{0}" id="rowhead">{1}</td>

              {2}

            

            ' -f $VM.Disks.Count, $VM.Name, $HTML_Disks, $HTML_BorderThickColor

        } Else {

            $HTML_Content += '

            <tr>

              <td width="300px" id="rowhead">{0}</td>

              <td colspan="5" id="rowoff">{1}</td>

            </tr>

            ' -f $VM.Name, $VM.Status, $HTML_BorderThickColor

        }

    }


    END {

        $HTML_Export = @"

        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

        <html xmlns="http://www.w3.org/1999/xhtml">

          <head>

            <title>VMware Disk Report</title>

            <style type="text/css">

              body {

                width: 900px;

                margin: 0 auto;

                padding-top: 15px;

              }

          

              #reporting {

                border: 1px solid #6690BC;

                border-collapse:collapse;

                font: 12px Arial, Helvetica, sans-serif;

                margin: 0 auto;

              }

          

              #reporting th { background-color: #6690BC; color: #ffffff; }


              #reporting tr { vertical-align: top; }    


              #subheaders {

                font-weight: bold;

                padding-left: 4px;

                background-color: #A8C8E9;

              }


              #rowlight {

                border-right: 1px solid #e5e5e5;

                padding-left: 4px;

              }


              #rowhead {

                background-color: #D3E6FA;

                border-right: 1px solid #6690BC;

                border-top: 1px solid #6690bc;

                padding-left: 4px;

              }


              #rowoff {

                padding-left: 4px;

                border-top: 1px solid #6690bc;

                background-color: #CCCCCC;

              }


              #pbcontainer {

                display:block;

                float:left;

                overflow: hidden;

                text-overflow:ellipsis;

                max-width:80px;

                width:80px;

              }

          

              .percentbar { background:#CCCCCC; border:1px solid #666666; height:10px; }

              .percentbar div { background: #28B8C0; height: 10px; }

            </style>

          </head>

      

          <body>

            <table id="reporting">

              <tr>

                <th>Virtual Machine</th>

                <th colspan="5">Guest Disk Properties</th>

              </tr>


              <tr>

                <td width="300px" id="subheaders"></td>

                <td width="60px" id="subheaders">Path</td>

                <td width="80px" id="subheaders">Capacity</td>

                <td width="80px" id="subheaders">Free Space</td>

                <td width="150px" colspan="2" id="subheaders">Occupation</td>

              </tr>


              $HTML_Content

            </table>


            <br /><br />

          </body>

        </html>

"@

        

        # Output the HTML in a file if specified

        If ($Path) {

            $HTML_Export | Out-File (Join-Path -Path $Path -ChildPath ("vCenter-GuestDisks-Report-{0}.html" -f (Get-Date -Format "yyyyMMdd_HHmmss")))

        }


        # Send the report via Mail if specified

        If ($AsMail) {

            Send-MailMessage `

                -To $Profile_Mailing.ToSysAdm `

                -From $Profile_Mailing.From `

                -Subject "vCenter Report: $InitDate" `

                -Body $HTML_Export `

                -BodyAsHtml `

                -SmtpServer $Profile_SMTP

        }

    }

}


Function Get-VMGuestDiskCapacity {

<#

    .SYNOPSIS

            This function will retrieve the Guest Disk capacity of all VMs present in a vCenter

    .DESCRIPTION

            This function will retrieve the Guest Disk capacity of all VMs present in a vCenter

            as long as the Guest Tools are present in the VM

    .PARAMETER  vCenter

            The vCenter server, pipeline is supported

    .PARAMETER  Credentials

            (Optional) The Credentials used to connect to the vCenter(s), the current user is used

            if nothing is specified

    .EXAMPLE

            Get-VMGuestDiskCapacity -vCenter vc001.katalykt.lan


            Status         VCServer              Name             Disks                                      

            ------         --------              ----             -----                                      

            Powered On     vc001.katalykt.lan    srv-iis001       {@{Capacity=10; PercentFull=92; DiskPath=...

            Powered On     vc001.katalykt.lan    srv-iis002       {@{Capacity=19,9; PercentFull=71; DiskPat...

    .EXAMPLE

            "vc001.katalykt.lan", "vc002.katalykt.lan" | Get-VMGuestDiskCapacity


            Status         VCServer              Name             Disks                                      

            ------         --------              ----             -----                                      

            Powered On     vc001.katalykt.lan    srv-iis001       {@{Capacity=10; PercentFull=92; DiskPath=...

            Powered On     vc001.katalykt.lan    srv-iis002       {@{Capacity=19,9; PercentFull=71; DiskPat...

            Powered On     vc002.katalykt.lan    srv-sql001       {@{Capacity=120; PercentFull=65; DiskPat...

    .SNAPIN

           VMware.VimAutomation.Core

    .NOTES

            NAME:     Export-VMGuestDiskCapacity

            AUTHOR:   ROULEAU Benjamin

            LASTEDIT: 2015-08-21

#>

    [CmdletBinding()]

    PARAM(

        [Parameter(

            Mandatory,

            ValueFromPipeline,

            ValueFromPipelineByPropertyName)]

        [String]$vCenter,


        $Credentials

    )


    BEGIN {

        Try {

            # Attempt to load the VIM Snapin

            If ( (Get-PSSnapin -Name "VMware.VimAutomation.Core" -errorAction SilentlyContinue) -eq $null ) {

                Write-Verbose -Message "[BEGIN - Get-VMGuestDiskCapacity] Attempting to load the VMware.VimAutomation.Core Snapin"

                Add-PsSnapin "VMware.VimAutomation.Core" -ErrorAction Stop -ErrorVariable ErrSnapin

            }

        } Catch {

            If ($ErrSnapin) { Write-Warning -Message ("[BEGIN - Get-VMGuestDiskCapacity] An error has occured during the PSSnapin import: {0}" -f $Error[0].Exception.Message) }

            exit

        }

    }


    PROCESS {

        Try {

            $vCenter | ForEach-Object {

                $vCenterHost = $_


                If ($Credentials) {

                    Write-Verbose -Message "[PROCESS - Get-VMGuestDiskCapacity] Attempting to connect to the vCenter host: $vCenterHost with the given credentials"

                    $VCServer = Connect-VIServer -Server $vCenterHost -Credential $Credentials -ErrorAction SilentlyContinue -ErrorVariable ErrVCConnect

                } Else {

                    Write-Verbose -Message "[PROCESS - Get-VMGuestDiskCapacity] Attempting to connect to the vCenter host: $vCenterHost with the current user"

                    $VCServer = Connect-VIServer -Server $vCenterHost -ErrorAction SilentlyContinue -ErrorVariable ErrVCConnect

                }


                # If we're connected to the vCenter, we retrieve the list of it's VMs

                If ($VCServer) {

                    Get-View -Server $VCServer -ViewType VirtualMachine | Where-Object {-not $_.Config.Template} | Sort-Object Name | ForEach-Object {

                        $VM = New-Object -TypeName PSObject -Property @{

                            Name = $_.Name

                            VCServer = $vCenterHost

                            Status = ""

                        }

            

                        Write-Verbose -Message ("[PROCESS - Get-VMGuestDiskCapacity] Processing Virtual Machine '{0}'" -f $VM.Name)


                        If ($_.Summary.Runtime.PowerState -eq "poweredOn") {

                            If ($_.Summary.Guest.ToolsRunningStatus -eq "guestToolsRunning") {

                                # The VM is online and it has VM Tools running. Retrieve the disks capacity

                                $VM.Status = "Powered On"


                                $VM | Add-Member -Name Disks -MemberType NoteProperty -Value ($_.Guest.Disk | ForEach-Object {

                                    New-Object -TypeName PSObject -Property @{

                                        DiskPath = $_.DiskPath

                                        Capacity = [math]::Round($_.Capacity/ 1GB, 2)

                                        FreeSpace = [math]::Round($_.FreeSpace / 1GB, 2)

                                        PercentFull = (100 - [math]::Round(([math]::Round($_.FreeSpace / 1GB, 2) * 100) / [math]::Round($_.Capacity/ 1GB, 2)))

                                    }

                                })

                    

                            } Else {

                                Write-Warning -Message ("[PROCESS - Get-VMGuestDiskCapacity] VM Tools on Virtual Machine '{0}' does not appear to be running" -f $VM.Name)

                                $VM.Status = "VM Tools missing"

                            }

                        } Else {

                            Write-Warning -Message ("[PROCESS - Get-VMGuestDiskCapacity] Virtual Machine '{0}' is not powered on" -f $VM.Name)

                            

                            $VM.Status = "Not Powered On"


                            # Just in case, it could managed by SRM?

                            If ($_.Config.ManagedBy.ExtensionKey -eq "com.vmware.vcDr") { $VM.Status = "Managed by Site Recovery Manager" }

                        }


                        $VM

                    }


                    Write-Verbose -Message "[PROCESS - Get-VMGuestDiskCapacity] Removing the vCenter connection from host $_"

                    Disconnect-VIServer $VCServer -Force -Confirm:$false | Out-Null

                } Else {

                    Write-Warning -Message "[PROCESS - Get-VMGuestDiskCapacity] vCenter connection is not established"

                }

            }

        } Catch {

            If ($ErrVCConnect) { Write-Warning -Message ("[PROCESS - Get-VMGuestDiskCapacity] Cannot connect to the given vCenter: {0}" -f $Error[0].Exception.Message) }

            Write-Warning $Error[0].Exception.Message

        }

    }


    END {

        

    }

}