ebcli/controllers/migrate_scripts/arr_configuration_importer_script.ps1 (221 lines of code) (raw):

<# .SYNOPSIS Imports Application Request Routing (ARR) configuration from XML files. .DESCRIPTION Handles the import of ARR configuration settings, including backup of current configuration and type-safe import of new settings. .NOTES Requires: - WebAdministration module - Administrative privileges - Configuration files named arr_config_[section].xml #> . "$PSScriptRoot\\ebdeploy_utils.ps1" if (-not [Environment]::Is64BitProcess) { Write-HostWithTimestamp "Restarting in 64-bit PowerShell" $scriptPath = $MyInvocation.MyCommand.Path $args = "-ExecutionPolicy unrestricted -NonInteractive -NoProfile -File `"$scriptPath`"" Start-Process "$env:windir\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe" -ArgumentList $args -Wait -NoNewWindow exit } Import-Module WebAdministration function Export-ARRConfig { param ( [Parameter(Mandatory=$false)] [string]$configPath ) <# .SYNOPSIS Exports current ARR configuration to XML files. .DESCRIPTION Exports modified (non-default) settings for proxy, rewrite, and caching configurations to separate XML files. .PARAMETER configPath Optional base path for output files. Files will be named {configPath}-{section}.xml .NOTES - Only exports modified settings (different from defaults) - Creates separate files for each configuration section #> # Get the proxy configuration $configSections = @( "system.webServer/proxy", "system.webServer/rewrite", "system.webServer/caching" ) $outputPath = "" try { foreach ($section in $configSections) { $sectionName = $section.Split('/')[-1] if ([string]::IsNullOrEmpty($configPath)) { $outputPath = ".\\arr_config_$sectionName.xml" } else { $outputPath = "$configPath-$sectionName.xml" } $proxyConfig = Get-WebConfiguration -Filter $section # Filter attributes that have been modified from defaults $modifiedAttributes = $proxyConfig.Attributes | Where-Object { -not $_.IsInheritedFromDefaultValue } # Build XML string $xmlContent = "<proxy" foreach ($attr in $modifiedAttributes) { $xmlContent += " $($attr.Name)=`"$($attr.Value)`"" } $xmlContent += " />" # Save to file $xmlContent | Out-File -FilePath $outputPath -Force Write-HostWithTimestamp "Backed up '$section' to $outputPath" } } catch { Write-ErrorWithTimestamp "Failed to export ARR configuration: $_" throw } Write-HostWithTimestamp "Current Automatic Request Routing (ARR) configuration exported to $($configPath)*" } function Get-SectionAttributeTypeMappings { <# .SYNOPSIS Retrieves type information for IIS configuration section attributes. .DESCRIPTION Maps each attribute in a specified IIS configuration section to its corresponding .NET type. Used to ensure type-safe configuration imports by determining the correct type for each property value. .PARAMETER SectionPrefix The IIS configuration section path (e.g., "system.webServer/proxy") .OUTPUTS System.Collections.Hashtable A hashtable where: - Keys are attribute names from the configuration section - Values are .NET type names (e.g., "Boolean", "Int32", "String") .EXAMPLE $typeMappings = Get-SectionAttributeTypeMappings "system.webServer/proxy" # Returns hashtable like: # @{ # "enabled" = "Boolean" # "timeout" = "TimeSpan" # "httpVersion" = "String" # } .NOTES - Uses MACHINE/WEBROOT/APPHOST configuration path - Defaults to "String" type for null values - Supports standard IIS configuration types: * Boolean * Int32 * Int64 * Double * TimeSpan * String #> param( [Parameter(Mandatory=$true)] [string]$SectionPrefix ) # Get the proxy section properties $sectionConfig = Get-WebConfiguration "$SectionPrefix" -pspath 'MACHINE/WEBROOT/APPHOST' # Loop through each attribute and determine its type $propertyMappings = @{} foreach ($attr in $sectionConfig.Attributes) { $propName = $attr.Name $propValue = $sectionConfig.$propName $propType = if ($propValue -ne $null) { $propValue.GetType().Name } else { "String" } # Store in hashtable $propertyMappings[$propName] = $propType } return $propertyMappings } function Import-ARRConfig { <# .SYNOPSIS Imports ARR configuration from XML files. .DESCRIPTION Reads configuration from XML files and applies settings to IIS, handling proper type conversion and validation. .NOTES - Creates backup before import - Handles type conversion for various configuration values - Logs all changes with detailed output - Provides backup restoration information on failure #> $configSections = @( "system.webServer/proxy", "system.webServer/rewrite", "system.webServer/caching" ) $configurationExists = $false foreach ($section in $configSections) { $sectionName = $section.Split('/')[-1] $outputPath = "C:\\staging\\ebmigrateScripts\\arr_config_$sectionName.xml" if (Test-Path $outputPath) { $configurationExists = $true break } } if (! $configurationExists) { Write-HostWithTimestamp "No Automatic Request Routing configuration found." return } try { # Create backup of current state $backupPath = "arr-backup" Export-ARRConfig -configPath $backupPath Write-HostWithTimestamp "Applying new ARR configuration" $i = 1 foreach ($section in $configSections) { $sectionName = $section.Split('/')[-1] $outputPath = "C:\\staging\\ebmigrateScripts\\arr_config_$sectionName.xml" Write-HostWithTimestamp "Handling $sectionName at $outputPath" if (! $(Test-Path $outputPath)) { Write-HostWithTimestamp " $($i; $i++). $outputPath doesn't exist. No relevant configuration for $sectionName present." continue } [xml]$config = Get-Content $outputPath $proxyNode = $config.proxy $attributes = $proxyNode.Attributes if ([string]::IsNullOrEmpty($attributes)) { Write-HostWithTimestamp " $($i; $i++). $section -> {}" continue } $propertyTypeMappings = Get-SectionAttributeTypeMappings $section foreach ($attr in $proxyNode.Attributes) { $propName = $attr.Name $propValue = $attr.Value if ($propertyTypeMappings.ContainsKey($propName)) { $expectedType = $propertyTypeMappings[$propName] # Convert based on expected type switch ($expectedType) { "Boolean" { $propValue = [System.Boolean]::Parse($propValue) } "Int32" { $propValue = [int]$propValue } "Int64" { $propValue = [long]$propValue } "Double" { $propValue = [double]$propValue } "TimeSpan" { $propValue = [System.TimeSpan]::Parse($propValue) } "String" { $propValue = [string]$propValue } default { Write-Host "Warning: Unknown type $expectedType for $propName. Using string."; $propValue = [string]$propValue } } Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' ` -filter $section ` -name $propName ` -value $propValue } else { Write-WarningWithTimestamp "Ignoring unknown type for $propName from section $section" } } $output = @" $($i; $i++). $section -> { $($proxyNode.Attributes | ForEach-Object { " $($_.Name): $($_.Value)" } | Out-String) } "@ Write-HostWithTimestamp $output } } catch { Write-ErrorWithTimestamp "Failed to import ARR configuration: $_" if (![string]::IsNullOrEmpty($backupPath)) { if (Test-Path $backupPath) { Write-HostWithTimestamp "Backup is available at: $backupPath*" } } throw } } Import-ARRConfig