Génération des MachineKeys .Net dans des environnements à répartition de charge / .Net MachineKeys generation for loadbalanced environments.

 

Go to English version

Les serveurs IIS de production sont souvent membres d’une ferme de serveurs Web (A la fois des raisons de performance et de fiabilité). Or dans le cadre d’applications ASP.Net il est obligatoire d’uniformiser les MachineKeys des applications sur l’ensemble de ces serveurs (je vous invite à consulter le liens ci-dessous si vous en ignorez la raison). Ce point est parfois oublié lors du passage en production. Je vous propose ici un script qui vous permettra de le faire (via notamment grâce à l’appel à la fonction Push-MachineKey et après avoir configuré correctement au moins un serveur “source” de la manière suivante (et pour toutes les applications concernées) : 

 

Untitled

  1. Décocher les deux cases à cocher sous “Validation Key”
  2. Décocher les deux cases à cocher sous “Decryption Key”
  3. Cliquer sur “Generate Keys”’
  4. Cliquer sur “Apply”

 

Une fois le serveur “source” correctement configuré, c’est lui qui servira de modèle pour la duplication des MachineKeys. Il suffit de lancer le script ci-dessous (depuis le serveur “source”) après avoir modifié la variable $TargetIISServers pour y mettre la liste des serveurs cibles (ie. les autres serveurs de la ferme Web).

Il existe d’autres fonctions présentes dans le script que je vais sommairement expliquer ci-après :

  • Get-MachineKey : Retourne la liste des MachinesKey locales sous forme de tableau
  • Set-MachineKey : Valorise les MachinesKey locales depuis un tableau passé en argument. Cette fonction supporte les switchs –whatif et –confirm (Risk Mitigation)
  • Push-MachineKey : Pousse les MachineKey locales sur les ordinateurs passées en argument (via du Remoting PowerShell)
  • Show-MachineKey : Affiche (et retourne si –passthru est spécifié) les machines key sur les machines passées en paramètres.
  • Export-MachineKey : Exporte la liste des MachinesKey locales dans le fichier CSV passé en argument
  • Import-MachineKey : Importe localement les MachinesKey depuis le fichier CSV passé en argument

 

Liens utiles :

 

Ce script est aussi disponible dans le TechNet Script Center : https://gallery.technet.microsoft.com/IIS-WebFarm-Machine-Key-d8c213de

Aller à la version française

IIS production servers are often members of a Web server farm (for performance and reliability reasons). However, in the context of ASP.Net applications, it is mandatory to standardize the MachineKeys of applications on all of these servers (I invite you to consult the links below if you do not know the reason). This point is sometimes forgotten in the production environment. I propose here a script that will allow you to do this (via the call to the Push-MachineKey function and after having correctly configured at least one "source" server in the following way (and for all the applications concerned) :

 

Untitled

  1. Uncheck the two checkboxes under “Validation Key”
  2. Uncheck the two checkboxes under “Decryption Key”
  3. Click on “Generate Keys”’
  4. Click on “Apply”

 

Once the "source" server is correctly configured, it will be the template for the duplication of the MachineKeys. Just run the script below (from the "source" server) after modifying the $TargetIISServers variable to set the list of target servers (ie. the other servers in the Web farm).

 

There are other functions present in the script that I will briefly explain hereafter:

  • Get-MachineKey: Returns the list of local MachinesKey as an array
  • Set-MachineKey: Sets the local MachinesKey from an array passed as argument. This function supports the -whatif and -confirm (Risk Mitigation)
  • Push-MachineKey: Pushes the local MachineKey on the computers passed as argument (via the PowerShell Remoting)
  • Show-MachineKey: Displays (and returns if -passthru is specified) the MachineKey on the computers passed as argument.
  • Export-MachineKey: Exports the list of the local MachinesKey in the CSV file passed as argument
  • Import-MachineKey: Locally imports the MachinesKey from the CSV file passed as argument

 

Useful links:

 

The script is also available in the TechNet Script Center : https://gallery.technet.microsoft.com/IIS-WebFarm-Machine-Key-d8c213de

 

 #requires -version 3 -RunAsAdministrator 
#requires -Modules WebAdministration

Import-Module -Name WebAdministration

#region function definitions
#For getting machine keys
function Get-MachineKey
{
    Import-Module -Name WebAdministration
    Write-Verbose -Message "Getting machine key configuration from $($env:COMPUTERNAME)"
    $MachineKey = Get-WebConfiguration -Filter 'system.web/machinekey' -Recurse |
    Select-Object -Property decryptionKey, decryption, validationKey, validation, PSPath, @{
        Name       = 'ComputerName'
        Expression = {
            $env:COMPUTERNAME
        }
    }
    return $MachineKey
}

#For setting machine keys
function Set-MachineKey
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $false)]
        [ValidateNotNullOrEmpty()]
        [array]$InputObject
    )
    begin
    {
        Import-Module -Name WebAdministration
    }
    process
    {
        foreach($CurrentInputObject in $InputObject)
        {
            $IISPath = $CurrentInputObject.PSPath -replace 'MACHINE/WEBROOT/APPHOST/', 'IIS:\sites\'
            if (Test-Path -Path $IISPath)
            {
                #Risk Mitigation : support of -whatif and -confirm
                If (($pscmdlet -eq $null) -or ($pscmdlet.ShouldProcess($CurrentInputObject.PSPath, 'Setting Machine Keys')))
                {
                    Write-Host -Object "[$env:Computername] Setting the decryptionKey to $($CurrentInputObject.decryptionKey) for $($CurrentInputObject.PSPath)"
                    Set-WebConfigurationProperty -Filter 'system.web/machinekey' -PSPath "$($CurrentInputObject.PSPath)" -name 'decryptionKey' -value $($CurrentInputObject.decryptionKey)
                    Write-Host -Object "[$env:Computername] Setting the Decryption Algorithm to $($CurrentInputObject.Decryption) for $($CurrentInputObject.PSPath)"
                    Set-WebConfigurationProperty -Filter 'system.web/machinekey' -PSPath "$($CurrentInputObject.PSPath)"  -name 'Decryption' -value $($CurrentInputObject.Decryption)
                    Write-Host -Object "[$env:Computername] Setting the validationKey to $($CurrentInputObject.validationKey) for $($CurrentInputObject.PSPath)"
                    Set-WebConfigurationProperty -Filter 'system.web/machinekey' -PSPath "$($CurrentInputObject.PSPath)" -name 'validationKey' -value $($CurrentInputObject.validationKey)
                    Write-Host -Object "[$env:Computername] Setting the validation Algorithm to $($CurrentInputObject.validation) for $($CurrentInputObject.PSPath)"
                    Set-WebConfigurationProperty -Filter 'system.web/machinekey' -PSPath "$($CurrentInputObject.PSPath)" -name 'validation' -value $($CurrentInputObject.validation)
                }
            }
            else
            {
                Write-Warning -Message "$IISPath doesn't exist on $($env:COMPUTERNAME)"
            }
        }
    }
    end
    {
    }
}

Function Push-MachineKey
{
    [CmdletBinding(PositionalBinding = $False, SupportsShouldProcess = $True)]
    Param(
        [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
        [ValidateNotNullOrEmpty()]
        [String[]]$ComputerName
    )
    begin
    {
        $LocalKeys = Get-MachineKey
    }
    process
    {
        Invoke-Command -ComputerName $ComputerName -ScriptBlock ${function:Set-MachineKey}  -ArgumentList (,$LocalKeys)
    }
    end
    {
    }
}


Function Show-MachineKey
{
    [CmdletBinding(PositionalBinding = $False, SupportsShouldProcess = $True)]
    Param(
        [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
        [ValidateNotNullOrEmpty()]
        [String[]]$ComputerName,

        [parameter(Mandatory=$false)]
        [switch]$PassThru
    )
    begin
    {
        $AllKeys = @()
    }
    process
    {
        $AllKeys+=Invoke-Command -ComputerName $ComputerName -ScriptBlock ${function:Get-MachineKey} | Select-Object -Property * -ExcludeProperty PSComputerName, RunSpaceId
    }
    end
    {
        if ($PassThru)
        {
            $AllKeys | Sort-Object PSPath, ComputerName | Out-GridView -Title 'Machine Keys across Web Servers' -PassThru
        }
        else
        {
            $AllKeys | Sort-Object PSPath, ComputerName | Out-GridView -Title 'Machine Keys across Web Servers'
        }
    }
}


function Export-MachineKey
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $True, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
                    Test-Path -Path (Split-Path -Path $_ -Parent) -PathType Container
        })]
        #CSV file full name
        
        [Alias('FilePath', 'Path')]
        [String]$FullName
    )
    Write-Verbose -Message "Exporting machine key configuration from $($env:COMPUTERNAME) to $CSVFile"
    Get-MachineKey | Export-Csv -Path $FullName -NoTypeInformation
}

#For importing machine keys
function Import-MachineKey
{
    [CmdletBinding(PositionalBinding = $True, SupportsShouldProcess = $True)]
    Param(
        [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
                    Test-Path -Path $_ -PathType Leaf
        })]
        #CSV file full name
        [Alias('FilePath', 'Path')]
        [String]$FullName
    )
    $LocalKeys = Import-Csv -Path $FullName
    Write-Verbose -Message "Importing machine key configuration from $(($LocalKeys | Select-Object -ExpandProperty ComputerName -Unique).ComputerName)"
    Set-MachineKey -InputObject $LocalKeys
}
#endregion

Clear-Host
# Getting the this script path
$CurrentScript = $MyInvocation.MyCommand.Path
# Getting the directory of this script
$CurrentDir = Split-Path -Path $CurrentScript -Parent
#CSV file for exporting/importing machine keys
$CSVFile = $CurrentScript.replace((Get-Item -Path $CurrentScript).Extension, '.csv')

#Exporting/Backing up local machine keys to a CSV file
#Export-MachineKey -Path $CSVFile -Verbose

#Importing and setting machine keys from a CSVFile to the local computer 
#Get-Item -Path $CSVFile | Import-MachineKey -Verbose #-WhatIf 

#Getting local machine keys
#$LocalKeys = Get-MachineKey
#Exporting/Backing up local machine keys to a CSV file
#$LocalKeys | Export-Csv -Path $CSVFile -NoTypeInformation

#Importing machine keys from a CSVFile
#$LocalKeys = Import-Csv -Path $CSVFile
#Setting machine keys the local computer 
#Set-MachineKey -InputObject $LocalKeys -whatif
#$LocalKeys | Set-MachineKey -whatif

$TargetIISServers="IIS002","DC001"
#Pushing/Duplicating local machine keys (after a manual setting on the source server) on the targeted computers
Push-MachineKey Push-MachineKey -ComputerName $TargetIISServers -Verbose


#Checking all machine keys across the web servers
$KeysAcrossWebFarm=Show-MachineKey -ComputerName $TargetIISServers -PassThru -Verbose
$KeysAcrossWebFarm