Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

. $PSScriptRoot\..\..\..\..\..\Shared\ScriptBlockFunctions\RemotePipelineHandlerFunctions.ps1

<#
.DESCRIPTION
This function converts specific .NET or PowerShell objects into a custom PSCustomObject instance.
By copying selected properties onto a simple PSCustomObject, it helps avoid serialization
problems that can occur when remoting or persisting complex framework types.
#>
function ConvertTo-PSObject {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[object]$ObjectToConvert,

[Parameter(Mandatory = $true)]
[string[]]$ObjectTypeToConvert,

[string[]]$PropertiesToSkip,

[int]$DefaultDepthValue = 5
)

begin {
Write-Verbose "Calling: $($MyInvocation.MyCommand)"
if ((Get-PSCallStack)[1].Command -ne "ConvertTo-PSObject") {
$Script:ConvertToPSObjectDepth = 0
}
$newPSObject = New-Object PSCustomObject
}
process {
# The properties on the object that we want to pull out and place on a custom PS Object.
# This should help with serialization issues.
$objectGetTypeFullName = $ObjectToConvert.GetType().FullName
$doNotConvert = $true

foreach ($type in $ObjectTypeToConvert) {
# This is intended to handle both ways that the wildcard can be setup and used.
if ($type -like $objectGetTypeFullName -or
$objectGetTypeFullName -like $type) {
$doNotConvert = $false
}
}

# $Pester should only be set when we are doing Pester testing.
if ($doNotConvert -and
$Script:Pester -eq $false) {
Write-Verbose "Object '$($ObjectToConvert.GetType().FullName)' is not one of these types to convert: $([string]::Join(", ", $ObjectTypeToConvert))."
return
}

$properties = ($ObjectToConvert |
Get-Member |
Where-Object {
$_.MemberType -ne "Method" -and
$_.MemberType -ne "ParameterizedProperty" -and
$_.MemberType -ne "CodeMethod" -and
$_.MemberType -ne "ScriptMethod"
}).Name |
Where-Object { $PropertiesToSkip -notcontains $_ }
Write-Verbose "Going to include the following properties to the object: $([string]::Join(", ", $properties))"

foreach ($prop in $properties) {
# If the property is an array, we need to handle this differently.
# If the property is one of the types, Call this function again. Otherwise, add it as is.
if ($ObjectToConvert.$prop -is [array]) {
Write-Verbose "$prop is an Array on this object."
$list = New-Object System.Collections.Generic.List[object]

foreach ($entry in $ObjectToConvert.$prop) {
if ($null -ne $entry) {
# Make this easier by just calling the method again, just need to add the type object that it is, just in case we are dealing with a list of a different type.
$Script:ConvertToPSObjectDepth++
$value = $null
$params = @{
ObjectToConvert = $entry
ObjectTypeToConvert = $ObjectTypeToConvert + ($entry.GetType().FullName)
PropertiesToSkip = $PropertiesToSkip
DefaultDepthValue = $DefaultDepthValue
}
ConvertTo-PSObject @params | Invoke-RemotePipelineHandler -Result ([ref]$value)
$list.Add($value)
} else {
# Add the empty list.
$list.Add($entry)
}
}

$newPSObject | Add-Member -MemberType NoteProperty -Name $prop -Value $list
} elseif ($null -ne $ObjectToConvert.$prop -and
[string]::Empty -ne $ObjectToConvert.$prop -and
$null -ne ($ObjectTypeToConvert | Where-Object { $ObjectToConvert.$prop.GetType().FullName -like $_ })) {

if ($Script:ConvertToPSObjectDepth -gt $DefaultDepthValue) {
Write-Verbose "Unable to convert this attribute property, as we are too deep in the object."
$newPSObject | Add-Member -MemberType NoteProperty -Name $prop -Value "--ERROR TOO DEEP--"
} else {
Write-Verbose "Going to call ConvertTo-PSObject for $prop property to expand"
$Script:ConvertToPSObjectDepth++
$value = $null
$params = @{
ObjectToConvert = ($ObjectToConvert.$prop)
ObjectTypeToConvert = $ObjectTypeToConvert
PropertiesToSkip = $PropertiesToSkip
DefaultDepthValue = $DefaultDepthValue
}
ConvertTo-PSObject @params | Invoke-RemotePipelineHandler -Result ([ref]$value)
$newPSObject | Add-Member -MemberType NoteProperty -Name $prop -Value $value
}
} else {
Write-Verbose "Adding property: $prop"
$newPSObject | Add-Member -MemberType NoteProperty -Name $prop -Value ($ObjectToConvert.$prop)
}
}
}
end {
$Script:ConvertToPSObjectDepth--
return $newPSObject
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

. $PSScriptRoot\ConvertTo-PSObject.ps1

function Get-IISWebApplication {
try {
$webApplications = Get-WebApplication
Expand Down Expand Up @@ -59,9 +61,29 @@ function Get-IISWebApplication {
Write-Verbose "Failed to process additional context for: $($webApplication.ItemXPath). Exception: $($_.Exception)"
}

# Convert the object to prevent serialization issues
$convertedObject = $null
$params = @{
ObjectToConvert = $webApplication
ObjectTypeToConvert = "Microsoft.IIs.PowerShell.Framework*"
PropertiesToSkip = @("ChildElements", "Attributes", "Schema", "ConfigurationPathType", "ElementTagName", "Methods")
}
try {
ConvertTo-PSObject @params | Invoke-RemotePipelineHandler -Result ([ref]$convertedObject)
} catch {
Write-Verbose "Failed to convert the object. Inner Exception: $_"
Invoke-CatchActions
}

if ($null -eq $convertedObject -and
$null -ne $webApplication) {
Write-Verbose "ConvertTo-PSObject failed to return an object. Using the default object."
$convertedObject = $webApplication
}

$returnList.Add([PSCustomObject]@{
FriendlyName = $friendlyName
Path = $webApplication.Path
Path = $convertedObject.Path
ConfigurationFileInfo = ([PSCustomObject]@{
Valid = $validWebConfig
Location = $configurationFilePath
Expand All @@ -70,18 +92,18 @@ function Get-IISWebApplication {
LinkedConfigurationLine = $linkedConfigurationLine
LinkedConfigurationFilePath = $linkedConfigurationFilePath
})
ApplicationPool = $webApplication.applicationPool
EnabledProtocols = $webApplication.enabledProtocols
ServiceAutoStartEnabled = $webApplication.serviceAutoStartEnabled
ServiceAutoStartProvider = $webApplication.serviceAutoStartProvider
PreloadEnabled = $webApplication.preloadEnabled
PreviouslyEnabledProtocols = $webApplication.previouslyEnabledProtocols
ServiceAutoStartMode = $webApplication.serviceAutoStartMode
VirtualDirectoryDefaults = $webApplication.virtualDirectoryDefaults
Collection = $webApplication.Collection
Location = $webApplication.Location
ItemXPath = $webApplication.ItemXPath
PhysicalPath = $webApplication.PhysicalPath.Replace("%windir%", $env:windir).Replace("%SystemDrive%", $env:SystemDrive)
ApplicationPool = $convertedObject.applicationPool
EnabledProtocols = $convertedObject.enabledProtocols
ServiceAutoStartEnabled = $convertedObject.serviceAutoStartEnabled
ServiceAutoStartProvider = $convertedObject.serviceAutoStartProvider
PreloadEnabled = $convertedObject.preloadEnabled
PreviouslyEnabledProtocols = $convertedObject.previouslyEnabledProtocols
ServiceAutoStartMode = $convertedObject.serviceAutoStartMode
VirtualDirectoryDefaults = $convertedObject.virtualDirectoryDefaults
Collection = $convertedObject.Collection
Location = $convertedObject.Location
ItemXPath = $convertedObject.ItemXPath
PhysicalPath = $convertedObject.PhysicalPath.Replace("%windir%", $env:windir).Replace("%SystemDrive%", $env:SystemDrive)
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

. $PSScriptRoot\ConvertTo-PSObject.ps1

function Get-IISWebSite {
[CmdletBinding()]
param()
Expand All @@ -18,8 +20,35 @@ function Get-IISWebSite {

foreach ($site in $webSites) {
Write-Verbose "Working on Site: $($site.Name)"
$siteBindings = $bindings |
[array]$siteBindings = $bindings |
Where-Object { $_.ItemXPath -like "*@name='$($site.name)' and @id='$($site.id)'*" }

# Convert the object to prevent serialization issues
$siteBindingsConvertedList = New-Object System.Collections.Generic.List[object]

foreach ($binding in $siteBindings) {
$convertedSiteBindings = $null
$params = @{
ObjectToConvert = $binding
ObjectTypeToConvert = "Microsoft.IIs.PowerShell.Framework*"
PropertiesToSkip = @("ChildElements", "Attributes", "Schema", "ConfigurationPathType", "ElementTagName", "Methods")
}

try {
ConvertTo-PSObject @params | Invoke-RemotePipelineHandler -Result ([ref]$convertedSiteBindings)
} catch {
Write-Verbose "Failed to convert the object. Inner Exception: $_"
Invoke-CatchActions
}

if ($null -eq $convertedSiteBindings -and $null -ne $binding) {
Write-Verbose "ConvertTo-PSObject failed to return an object for Site Bindings. Using old object."
$convertedSiteBindings = $binding
}

$siteBindingsConvertedList.Add($convertedSiteBindings)
}

# Logic should be consistent for all ways we call Get-WebConfigFile
try {
$configurationFilePath = (Get-WebConfigFile "IIS:\Sites\$($site.Name)").FullName
Expand Down Expand Up @@ -134,23 +163,44 @@ function Get-IISWebSite {
$physicalPath = $site.physicalPath.Replace("%windir%", $env:windir).Replace("%SystemDrive%", $env:SystemDrive)
}

# Convert the object to prevent serialization issues
$convertedObject = $null
$params = @{
ObjectToConvert = $site
ObjectTypeToConvert = "Microsoft.IIs.PowerShell.Framework*"
PropertiesToSkip = @("ChildElements", "Attributes", "Schema", "ConfigurationPathType", "ElementTagName", "Methods")
}

try {
ConvertTo-PSObject @params | Invoke-RemotePipelineHandler -Result ([ref]$convertedObject)
} catch {
Write-Verbose "Failed to convert the object. Inner Exception: $_"
Invoke-CatchActions
}

if ($null -eq $convertedObject -and
$null -ne $site) {
Write-Verbose "ConvertTo-PSObject failed to return an object. Using the default object."
$convertedObject = $site
}

$returnList.Add([PSCustomObject]@{
Name = $site.Name
Id = $site.Id
State = $site.State
Bindings = $siteBindings
Limits = $site.Limits
LogFile = $site.logFile
TraceFailedRequestsLogging = $site.traceFailedRequestsLogging
Name = $convertedObject.Name
Id = $convertedObject.Id
State = $convertedObject.State
Bindings = $siteBindingsConvertedList
Limits = $convertedObject.Limits
LogFile = $convertedObject.logFile
TraceFailedRequestsLogging = $convertedObject.traceFailedRequestsLogging
Hsts = [PSCustomObject]@{
NativeHstsSettings = $site.hsts
NativeHstsSettings = $convertedObject.hsts
HstsViaCustomHeader = $customHeaderHstsObj
}
ApplicationDefaults = $site.applicationDefaults
VirtualDirectoryDefaults = $site.virtualDirectoryDefaults
Collection = $site.collection
ApplicationPool = $site.applicationPool
EnabledProtocols = $site.enabledProtocols
ApplicationDefaults = $convertedObject.applicationDefaults
VirtualDirectoryDefaults = $convertedObject.virtualDirectoryDefaults
Collection = $convertedObject.collection
ApplicationPool = $convertedObject.applicationPool
EnabledProtocols = $convertedObject.enabledProtocols
PhysicalPath = $physicalPath
ConfigurationFileInfo = [PSCustomObject]@{
Location = $configurationFilePath
Expand Down