mercredi 22 novembre 2017

Using Powershell to group by the Hard Disks Used Space from multiple Servers

Sometime, you may want to retrieve the Hard Disks Used Space from one or multiple Servers to build up a report (or something else!). In this case, the Drive ID does not really need to be shown as we want to group it up.

Here's what the Output looks like:

TotalCapacity TotalUsedSpace Server    
------------- -------------- ------    
571,7 Gb      228,3 Gb       SRV-SAPCS001
161,7 Gb      17,9 Gb        SRV-IIS001
109,7 Gb      15,4 Gb        SRV-IIS002

Here's the script:

"srv-sapcs001.katalykt.lan", "srv-iis001.katalykt.lan", "srv-iis002.katalykt.lan" |

    ForEach-Object {Get-WmiObject Win32_LogicalDisk -ComputerName $_ -Filter "Drivetype=3"} |

        Select-Object SystemName, Size, @{Label = "Used Space";Expression = {$_.Size - $_.FreeSpace}} |

            Group-Object SystemName |

                ForEach-Object {

                    New-Object PSObject -Property @{

                        Server = $_.Group.SystemName[0]

                        TotalUsedSpace = ("{0:N1} Gb" -f (($_.Group."Used Space" | Measure-Object -Sum).Sum / 1Gb))

                        TotalCapacity = ("{0:N1} Gb" -f (($_.Group.Size | Measure-Object -Sum).Sum / 1Gb))

                    }

                }

vendredi 21 avril 2017

Using Iptables to create a NAT/DMZ

Iptables is a powerful Firewall which can be used to create and maintain a simple or complex infrastructure.

In this case, we will be using the NAT feature of Iptables in order to make an Internal IIS Web Server communicate with a DMZ IIS Web Server.


Let's assume that we have a Web Service running on the IIS Web Server located in the DMZ that needs to be queried by the Internal Web Server by TCP/HTTP.

As our DMZ subnet is isolated, the Internal Web Server cannot communicate with the DMZ Web Server. This is where NAT comes handy.

As a result, if our Internal Web Server wants to communicate with the DMZ Web Server it will have to use the back-end Firewall's Internal interface eth0 which will translate the address to the DMZ via it's eth1 interface. In short, the NAT allows us to hide the DMZ Subnet which cannot be routed and which should remain in a controlled isolation.

Of course in this case, we will only be performing NAT for queries which come to our back-end Firewall on HTTP/TCP port 80.

So let's get started!


Enabling IPv4 Forwarding


The first thing is to ensure that IPv4 forwarding is enabled on our Firewall, the following command will output it's status (note that I'm using a Debian 8 Jessie in here):

sysctl net.ipv4.ip_forward

If it's already enabled, then everything's fine, otherwise you will have to either enable it on the fly (temporary) or make it persistent


Temporary change:


The temporary way is quite straightforward but will not survive a reboot

echo 1 > /proc/sys/net/ipv4/ip_forward


Persistent change:


On the other hand the persistent way requires the editing of the /etc/sysctl.conf file

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1

After changing that value we refresh our kernel with the new configuration file

sysctl -p /etc/sysctl.conf



Adding the NAT rule


The next step is to actually add the NAT rule to the back-end Iptables Firewall. This rule will need to be present in both PREROUTING and POSTROUTING chains.

iptables -t nat -A PREROUTING -i eth0 -p tcp -d 10.10.10.200 --dport 80 -j DNAT --to-destination 192.168.200.200:80
iptables -t nat -A POSTROUTING -o eth1 -p tcp -s 10.10.10.210 --dport 80 -j SNAT --to-source 192.168.200.199

Our PREROUTING rule will make sure that packets coming to our back-end Firewall eth0 interface (10.10.10.200) on port 80 (TCP/HTTP) will be translated toward the DMZ Web Server (192.168.200.200) on port 80

The POSTROUTING chain will alter the source of the outgoing package coming from the Internal Web Server (10.10.10.210) on port 80 and will substitute it's source with the back-end Firewall's eth1 IP (192.168.200.199)

This is it for the NAT rule, next we have to allow it.



Allowing the NAT rule


So we do have the NAT rule loaded on the Iptables but it will not work yet as long as we do not add it to the FORWARD chain (that is, of course, if your default policy is to drop all packets!)

iptables -A FORWARD -p tcp -i eth0 -s 10.10.10.210 -d 192.168.200.200 -o eth1 --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -p tcp -i eth1 -s 192.168.200.200 -d 10.10.10.210 -o eth0 --sport 80 -m state --state ESTABLISHED -j ACCEPT

The first FORWARD rule will allow new and established TCP/HTTP connections coming from the Internal Web Server to the DMZ Web Server while the second one will allow the reversed scenario but only with established connections.


That's about it, the NAT is now working exclusively between the Internal Web Server and the DMZ Web Server.



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 {

        

    }

}