diff --git a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 index 6def1b06a..982b5fcfa 100644 --- a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 +++ b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 @@ -2,19 +2,91 @@ Import-Module -Name "$PSScriptRoot\..\Helper.psm1" # Localized messages -data LocalizedData -{ +data LocalizedData { # culture="en-US" ConvertFrom-StringData -StringData @' - NoWebAdministrationModule = Please ensure that WebAdministration module is installed. - SettingValue = Changing default value '{0}' to '{1}' - ValueOk = Default value '{0}' is already '{1}' - VerboseGetTargetResource = Get-TargetResource has been run. + ErrorAppCmdNonZeroExitCode = AppCmd.exe has exited with error code "{0}". + VerboseGetTargetResource = Get-TargetResource has been run. + + ErrorAppPoolDefaultsNotFound = Application pool defaults element could not be located. + VerboseAppPoolDefaultsFound = Application pool defaults was found. + VerbosePropertyNotInDesiredState = The "{0}" property of application pool defaults does not match the desired state. + VerboseCredentialToBeCleared = Custom account credentials of application pool defaults need to be cleared because the "identityType" property is not set to "SpecificUser". + VerboseCredentialToBeIgnored = The "Credential" property is only valid when the "identityType" property is set to "SpecificUser". + VerboseResourceInDesiredState = The target resource is already in the desired state. No action is required. + VerboseResourceNotInDesiredState = The target resource is not in the desired state. + VerboseSetProperty = Setting the "{0}" property of application pool defaults". + VerboseClearCredential = Clearing custom account credentials of application pool defaults because the "identityType" property is not set to "SpecificUser". + VerboseRestartScheduleValueAdd = Adding value "{0}" to the "restartSchedule" collection of application pool defaults. + VerboseRestartScheduleValueRemove = Removing value "{0}" from the "restartSchedule" collection of application pool defaults. '@ } -function Get-TargetResource -{ +# Writable properties except Credential. +data PropertyData { + @( + # General + @{Name = 'autoStart'; Path = 'autoStart'} + @{Name = 'CLRConfigFile'; Path = 'CLRConfigFile'} + @{Name = 'enable32BitAppOnWin64'; Path = 'enable32BitAppOnWin64'} + @{Name = 'enableConfigurationOverride'; Path = 'enableConfigurationOverride'} + @{Name = 'managedPipelineMode'; Path = 'managedPipelineMode'} + @{Name = 'managedRuntimeLoader'; Path = 'managedRuntimeLoader'} + @{Name = 'managedRuntimeVersion'; Path = 'managedRuntimeVersion'} + @{Name = 'passAnonymousToken'; Path = 'passAnonymousToken'} + @{Name = 'startMode'; Path = 'startMode'} + @{Name = 'queueLength'; Path = 'queueLength'} + + # CPU + @{Name = 'cpuAction'; Path = 'cpu.action'} + @{Name = 'cpuLimit'; Path = 'cpu.limit'} + @{Name = 'cpuResetInterval'; Path = 'cpu.resetInterval'} + @{Name = 'cpuSmpAffinitized'; Path = 'cpu.smpAffinitized'} + @{Name = 'cpuSmpProcessorAffinityMask'; Path = 'cpu.smpProcessorAffinityMask'} + @{Name = 'cpuSmpProcessorAffinityMask2'; Path = 'cpu.smpProcessorAffinityMask2'} + + # Process Model + @{Name = 'identityType'; Path = 'processModel.identityType'} + @{Name = 'idleTimeout'; Path = 'processModel.idleTimeout'} + @{Name = 'idleTimeoutAction'; Path = 'processModel.idleTimeoutAction'} + @{Name = 'loadUserProfile'; Path = 'processModel.loadUserProfile'} + @{Name = 'logEventOnProcessModel'; Path = 'processModel.logEventOnProcessModel'} + @{Name = 'logonType'; Path = 'processModel.logonType'} + @{Name = 'manualGroupMembership'; Path = 'processModel.manualGroupMembership'} + @{Name = 'maxProcesses'; Path = 'processModel.maxProcesses'} + @{Name = 'pingingEnabled'; Path = 'processModel.pingingEnabled'} + @{Name = 'pingInterval'; Path = 'processModel.pingInterval'} + @{Name = 'pingResponseTime'; Path = 'processModel.pingResponseTime'} + @{Name = 'setProfileEnvironment'; Path = 'processModel.setProfileEnvironment'} + @{Name = 'shutdownTimeLimit'; Path = 'processModel.shutdownTimeLimit'} + @{Name = 'startupTimeLimit'; Path = 'processModel.startupTimeLimit'} + + # Process Orphaning + @{Name = 'orphanActionExe'; Path = 'failure.orphanActionExe'} + @{Name = 'orphanActionParams'; Path = 'failure.orphanActionParams'} + @{Name = 'orphanWorkerProcess'; Path = 'failure.orphanWorkerProcess'} + + # Rapid-Fail Protection + @{Name = 'loadBalancerCapabilities'; Path = 'failure.loadBalancerCapabilities'} + @{Name = 'rapidFailProtection'; Path = 'failure.rapidFailProtection'} + @{Name = 'rapidFailProtectionInterval'; Path = 'failure.rapidFailProtectionInterval'} + @{Name = 'rapidFailProtectionMaxCrashes'; Path = 'failure.rapidFailProtectionMaxCrashes'} + @{Name = 'autoShutdownExe'; Path = 'failure.autoShutdownExe'} + @{Name = 'autoShutdownParams'; Path = 'failure.autoShutdownParams'} + + # Recycling + @{Name = 'disallowOverlappingRotation'; Path = 'recycling.disallowOverlappingRotation'} + @{Name = 'disallowRotationOnConfigChange'; Path = 'recycling.disallowRotationOnConfigChange'} + @{Name = 'logEventOnRecycle'; Path = 'recycling.logEventOnRecycle'} + @{Name = 'restartMemoryLimit'; Path = 'recycling.periodicRestart.memory'} + @{Name = 'restartPrivateMemoryLimit'; Path = 'recycling.periodicRestart.privateMemory'} + @{Name = 'restartRequestsLimit'; Path = 'recycling.periodicRestart.requests'} + @{Name = 'restartTimeLimit'; Path = 'recycling.periodicRestart.time'} + @{Name = 'restartSchedule'; Path = 'recycling.periodicRestart.schedule'} + ) +} + +function Get-TargetResource { <# .SYNOPSIS This will return a hashtable of results @@ -34,14 +106,46 @@ function Get-TargetResource Write-Verbose -Message $LocalizedData.VerboseGetTargetResource - return @{ - ManagedRuntimeVersion = (Get-Value -Path '' -Name 'managedRuntimeVersion') - IdentityType = (Get-Value -Path 'processModel' -Name 'identityType') + # XPath -Filter is case-sensitive. Use Where-Object to get the target application pool by name. + $appPool = Get-AppPoolDefault + + $cimCredential = $null + if ($appPool.processModel.identityType -eq 'SpecificUser') { + $cimCredential = New-CimInstance -ClientOnly ` + -ClassName MSFT_Credential ` + -Namespace root/microsoft/windows/DesiredStateConfiguration ` + -Property @{ + UserName = [String]$appPool.processModel.userName + Password = [String]$appPool.processModel.password + } + } + + + $returnValue = @{ + Credential = $cimCredential } + + $PropertyData.Where( + { + $_.Name -ne 'restartSchedule' + } + ).ForEach( + { + $property = Get-Property -Object $appPool -PropertyName $_.Path + $returnValue.Add($_.Name, $property) + } + ) + + $restartScheduleCurrent = [String[]]@( + @($appPool.recycling.periodicRestart.schedule.Collection).ForEach('value') + ) + + $returnValue.Add('restartSchedule', $restartScheduleCurrent) + + return $returnValue } -function Set-TargetResource -{ +function Set-TargetResource { <# .SYNOPSIS This will set the desired state @@ -57,24 +161,287 @@ function Set-TargetResource $ApplyTo, [Parameter()] - [ValidateSet('','v2.0','v4.0')] + [ValidateSet('', 'v2.0', 'v4.0')] [System.String] $ManagedRuntimeVersion, - [Parameter()] - [ValidateSet('ApplicationPoolIdentity','LocalService','LocalSystem','NetworkService')] - [System.String] - $IdentityType + + [Boolean] $autoStart, + + [String] $CLRConfigFile, + + [Boolean] $enable32BitAppOnWin64, + + [Boolean] $enableConfigurationOverride, + + [ValidateSet('Integrated', 'Classic')] + [String] $managedPipelineMode, + + [String] $managedRuntimeLoader, + + [Boolean] $passAnonymousToken, + + [ValidateSet('OnDemand', 'AlwaysRunning')] + [String] $startMode, + + [ValidateRange(10, 65535)] + [UInt32] $queueLength, + + [ValidateSet('NoAction', 'KillW3wp', 'Throttle', 'ThrottleUnderLoad')] + [String] $cpuAction, + + [ValidateRange(0, 100000)] + [UInt32] $cpuLimit, + + [ValidateScript( { + ([ValidateRange(0, 1440)]$valueInMinutes = [TimeSpan]::Parse($_).TotalMinutes); $? + })] + [String] $cpuResetInterval, + + [Boolean] $cpuSmpAffinitized, + + [UInt32] $cpuSmpProcessorAffinityMask, + + [UInt32] $cpuSmpProcessorAffinityMask2, + + [ValidateSet( + 'ApplicationPoolIdentity', 'LocalService', 'LocalSystem', + 'NetworkService', 'SpecificUser' + )] + [String] $identityType, + + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential, + + [ValidateScript( { + ([ValidateRange(0, 43200)]$valueInMinutes = [TimeSpan]::Parse($_).TotalMinutes); $? + })] + [String] $idleTimeout, + + [ValidateSet('Terminate', 'Suspend')] + [String] $idleTimeoutAction, + + [Boolean] $loadUserProfile, + + [String] $logEventOnProcessModel, + + [ValidateSet('LogonBatch', 'LogonService')] + [String] $logonType, + + [Boolean] $manualGroupMembership, + + [ValidateRange(0, 2147483647)] + [UInt32] $maxProcesses, + + [Boolean] $pingingEnabled, + + [ValidateScript( { + ([ValidateRange(1, 4294967)]$valueInSeconds = [TimeSpan]::Parse($_).TotalSeconds); $? + })] + [String] $pingInterval, + + [ValidateScript( { + ([ValidateRange(1, 4294967)]$valueInSeconds = [TimeSpan]::Parse($_).TotalSeconds); $? + })] + [String] $pingResponseTime, + + [Boolean] $setProfileEnvironment, + + [ValidateScript( { + ([ValidateRange(1, 4294967)]$valueInSeconds = [TimeSpan]::Parse($_).TotalSeconds); $? + })] + [String] $shutdownTimeLimit, + + [ValidateScript( { + ([ValidateRange(1, 4294967)]$valueInSeconds = [TimeSpan]::Parse($_).TotalSeconds); $? + })] + [String] $startupTimeLimit, + + [String] $orphanActionExe, + + [String] $orphanActionParams, + + [Boolean] $orphanWorkerProcess, + + [ValidateSet('HttpLevel', 'TcpLevel')] + [String] $loadBalancerCapabilities, + + [Boolean] $rapidFailProtection, + + [ValidateScript( { + ([ValidateRange(1, 144000)]$valueInMinutes = [TimeSpan]::Parse($_).TotalMinutes); $? + })] + [String] $rapidFailProtectionInterval, + + [ValidateRange(0, 2147483647)] + [UInt32] $rapidFailProtectionMaxCrashes, + + [String] $autoShutdownExe, + + [String] $autoShutdownParams, + + [Boolean] $disallowOverlappingRotation, + + [Boolean] $disallowRotationOnConfigChange, + + [String] $logEventOnRecycle, + + [UInt32] $restartMemoryLimit, + + [UInt32] $restartPrivateMemoryLimit, + + [UInt32] $restartRequestsLimit, + + [ValidateScript( { + ([ValidateRange(0, 432000)]$valueInMinutes = [TimeSpan]::Parse($_).TotalMinutes); $? + })] + [String] $restartTimeLimit, + + [ValidateScript( { + ($_ -eq '') -or + (& { + ([ValidateRange(0, 86399)]$valueInSeconds = [TimeSpan]::Parse($_).TotalSeconds); $? + }) + })] + [String[]] $restartSchedule ) Assert-Module + + $appPool = Get-AppPoolDefault + + # Set Application Pool Properties + + $PropertyData.Where( + { + ($_.Name -in $PSBoundParameters.Keys) -and + ($_.Name -notin @('restartSchedule')) + } + ).ForEach( + { + $propertyName = $_.Name + $propertyPath = $_.Path + $property = Get-Property -Object $appPool -PropertyName $propertyPath + + if ( + $PSBoundParameters[$propertyName] -ne $property + ) { + Write-Verbose -Message ( + $LocalizedData['VerboseSetProperty'] -f $propertyName + ) + + Invoke-AppCmd -ArgumentList ( + '/{0}:{1}' -f "applicationPoolDefaults.$($propertyPath)", $PSBoundParameters[$propertyName] + ) + } + } + ) + + if ($PSBoundParameters.ContainsKey('Credential')) { + if ($PSBoundParameters['identityType'] -eq 'SpecificUser') { + if ($appPool.processModel.userName -ne $Credential.UserName) { + Write-Verbose -Message ( + $LocalizedData['VerboseSetProperty'] -f 'Credential (userName)' + ) + + Invoke-AppCmd -ArgumentList ( + '/applicationPoolDefaults.processModel.userName:{0}' -f $Credential.UserName + ) + } + + $clearTextPassword = $Credential.GetNetworkCredential().Password + + if ($appPool.processModel.password -cne $clearTextPassword) { + Write-Verbose -Message ( + $LocalizedData['VerboseSetProperty'] -f 'Credential (password)' + ) + + Invoke-AppCmd -ArgumentList ( + '/applicationPoolDefaults.processModel.password:{0}' -f $clearTextPassword + ) + } + } + else { + Write-Verbose -Message ($LocalizedData['VerboseCredentialToBeIgnored']) + } + } - Set-Value -Path '' -Name 'managedRuntimeVersion' -NewValue $ManagedRuntimeVersion - Set-Value -Path 'processModel' -Name 'identityType' -NewValue $IdentityType + # Ensure userName and password are cleared if identityType isn't set to SpecificUser. + if ( + ( + ( + ($PSBoundParameters.ContainsKey('identityType') -eq $true) -and + ($PSBoundParameters['identityType'] -ne 'SpecificUser') + ) -or + ( + ($PSBoundParameters.ContainsKey('identityType') -eq $false) -and + ($appPool.processModel.identityType -ne 'SpecificUser') + ) + ) -and + ( + ([String]::IsNullOrEmpty($appPool.processModel.userName) -eq $false) -or + ([String]::IsNullOrEmpty($appPool.processModel.password) -eq $false) + ) + ) { + Write-Verbose -Message ($LocalizedData['VerboseClearCredential']) + + Invoke-AppCmd -ArgumentList '/applicationPoolDefaults.processModel.userName:' + Invoke-AppCmd -ArgumentList '/applicationPoolDefaults.processModel.password:' + } + + if ($PSBoundParameters.ContainsKey('restartSchedule')) { + # Normalize the restartSchedule array values. + $restartScheduleDesired = [String[]]@( + $restartSchedule.Where( + { + $_ -ne '' + } + ).ForEach( + { + [TimeSpan]::Parse($_).ToString('hh\:mm\:ss') + } + ) | + Select-Object -Unique + ) + + $restartScheduleCurrent = [String[]]@( + @($appPool.recycling.periodicRestart.schedule.Collection).ForEach('value') + ) + + Compare-Object -ReferenceObject $restartScheduleDesired ` + -DifferenceObject $restartScheduleCurrent | + ForEach-Object -Process { + + # Add value + if ($_.SideIndicator -eq '<=') { + Write-Verbose -Message ( + $LocalizedData['VerboseRestartScheduleValueAdd'] -f + $_.InputObject + ) + + Invoke-AppCmd -ArgumentList ( + "/+applicationPoolDefaults.recycling.periodicRestart.schedule.[value='{0}']" -f $_.InputObject + ) + } + # Remove value + else { + Write-Verbose -Message ( + $LocalizedData['VerboseRestartScheduleValueRemove'] -f + $_.InputObject + ) + + Invoke-AppCmd -ArgumentList ( + "/-applicationPoolDefaults.recycling.periodicRestart.schedule.[value='{0}']" -f $_.InputObject + ) + } + + } + } + } -function Test-TargetResource -{ +function Test-TargetResource { <# .SYNOPSIS This tests the desired state. If the state is not correct it will return $false. @@ -92,151 +459,353 @@ function Test-TargetResource $ApplyTo, [Parameter()] - [ValidateSet('','v2.0','v4.0')] + [ValidateSet('', 'v2.0', 'v4.0')] [System.String] $ManagedRuntimeVersion, - [Parameter()] - [ValidateSet('ApplicationPoolIdentity','LocalService','LocalSystem','NetworkService')] - [System.String] - $IdentityType + [Boolean] $autoStart, + + [String] $CLRConfigFile, + + [Boolean] $enable32BitAppOnWin64, + + [Boolean] $enableConfigurationOverride, + + [ValidateSet('Integrated', 'Classic')] + [String] $managedPipelineMode, + + [String] $managedRuntimeLoader, + + [Boolean] $passAnonymousToken, + + [ValidateSet('OnDemand', 'AlwaysRunning')] + [String] $startMode, + + [ValidateRange(10, 65535)] + [UInt32] $queueLength, + + [ValidateSet('NoAction', 'KillW3wp', 'Throttle', 'ThrottleUnderLoad')] + [String] $cpuAction, + + [ValidateRange(0, 100000)] + [UInt32] $cpuLimit, + + [ValidateScript( { + ([ValidateRange(0, 1440)]$valueInMinutes = [TimeSpan]::Parse($_).TotalMinutes); $? + })] + [String] $cpuResetInterval, + + [Boolean] $cpuSmpAffinitized, + + [UInt32] $cpuSmpProcessorAffinityMask, + + [UInt32] $cpuSmpProcessorAffinityMask2, + + [ValidateSet( + 'ApplicationPoolIdentity', 'LocalService', 'LocalSystem', + 'NetworkService', 'SpecificUser' + )] + [String] $identityType, + + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential, + + [ValidateScript( { + ([ValidateRange(0, 43200)]$valueInMinutes = [TimeSpan]::Parse($_).TotalMinutes); $? + })] + [String] $idleTimeout, + + [ValidateSet('Terminate', 'Suspend')] + [String] $idleTimeoutAction, + + [Boolean] $loadUserProfile, + + [String] $logEventOnProcessModel, + + [ValidateSet('LogonBatch', 'LogonService')] + [String] $logonType, + + [Boolean] $manualGroupMembership, + + [ValidateRange(0, 2147483647)] + [UInt32] $maxProcesses, + + [Boolean] $pingingEnabled, + + [ValidateScript( { + ([ValidateRange(1, 4294967)]$valueInSeconds = [TimeSpan]::Parse($_).TotalSeconds); $? + })] + [String] $pingInterval, + + [ValidateScript( { + ([ValidateRange(1, 4294967)]$valueInSeconds = [TimeSpan]::Parse($_).TotalSeconds); $? + })] + [String] $pingResponseTime, + + [Boolean] $setProfileEnvironment, + + [ValidateScript( { + ([ValidateRange(1, 4294967)]$valueInSeconds = [TimeSpan]::Parse($_).TotalSeconds); $? + })] + [String] $shutdownTimeLimit, + + [ValidateScript( { + ([ValidateRange(1, 4294967)]$valueInSeconds = [TimeSpan]::Parse($_).TotalSeconds); $? + })] + [String] $startupTimeLimit, + + [String] $orphanActionExe, + + [String] $orphanActionParams, + + [Boolean] $orphanWorkerProcess, + + [ValidateSet('HttpLevel', 'TcpLevel')] + [String] $loadBalancerCapabilities, + + [Boolean] $rapidFailProtection, + + [ValidateScript( { + ([ValidateRange(1, 144000)]$valueInMinutes = [TimeSpan]::Parse($_).TotalMinutes); $? + })] + [String] $rapidFailProtectionInterval, + + [ValidateRange(0, 2147483647)] + [UInt32] $rapidFailProtectionMaxCrashes, + + [String] $autoShutdownExe, + + [String] $autoShutdownParams, + + [Boolean] $disallowOverlappingRotation, + + [Boolean] $disallowRotationOnConfigChange, + + [String] $logEventOnRecycle, + + [UInt32] $restartMemoryLimit, + + [UInt32] $restartPrivateMemoryLimit, + + [UInt32] $restartRequestsLimit, + + [ValidateScript( { + ([ValidateRange(0, 432000)]$valueInMinutes = [TimeSpan]::Parse($_).TotalMinutes); $? + })] + [String] $restartTimeLimit, + + [ValidateScript( { + ($_ -eq '') -or + (& { + ([ValidateRange(0, 86399)]$valueInSeconds = [TimeSpan]::Parse($_).TotalSeconds); $? + }) + })] + [String[]] $restartSchedule ) Assert-Module - if (-not((Confirm-Value -Path '' ` - -Name 'managedRuntimeVersion' ` - -NewValue $ManagedRuntimeVersion))) - { - return $false + $inDesiredState = $true + + $appPool = Get-AppPoolDefault + + $PropertyData.Where( + { + ($_.Name -in $PSBoundParameters.Keys) -and + ($_.Name -ne 'restartSchedule') + } + ).ForEach( + { + $propertyName = $_.Name + $propertyPath = $_.Path + $property = Get-Property -Object $appPool -PropertyName $propertyPath + + if ( + $PSBoundParameters[$propertyName] -ne $property + ) { + Write-Verbose -Message ( + $LocalizedData['VerbosePropertyNotInDesiredState'] -f $propertyName + ) + + $inDesiredState = $false + } + } + ) + + + if ($PSBoundParameters.ContainsKey('Credential')) { + if ($PSBoundParameters['identityType'] -eq 'SpecificUser') { + if ($appPool.processModel.userName -ne $Credential.UserName) { + Write-Verbose -Message ( + $LocalizedData['VerbosePropertyNotInDesiredState'] -f + 'Credential (userName)' + ) + + $inDesiredState = $false + } + + $clearTextPassword = $Credential.GetNetworkCredential().Password + + if ($appPool.processModel.password -cne $clearTextPassword) { + Write-Verbose -Message ( + $LocalizedData['VerbosePropertyNotInDesiredState'] -f + 'Credential (password)' + ) + + $inDesiredState = $false + } + } + else { + Write-Verbose -Message ($LocalizedData['VerboseCredentialToBeIgnored']) + } + } + + # Ensure userName and password are cleared if identityType isn't set to SpecificUser. + if ( + ( + ( + ($PSBoundParameters.ContainsKey('identityType') -eq $true) -and + ($PSBoundParameters['identityType'] -ne 'SpecificUser') + ) -or + ( + ($PSBoundParameters.ContainsKey('identityType') -eq $false) -and + ($appPool.processModel.identityType -ne 'SpecificUser') + ) + ) -and + ( + ([String]::IsNullOrEmpty($appPool.processModel.userName) -eq $false) -or + ([String]::IsNullOrEmpty($appPool.processModel.password) -eq $false) + ) + ) { + Write-Verbose -Message ($LocalizedData['VerboseCredentialToBeCleared']) + + $inDesiredState = $false } - if (-not((Confirm-Value -Path 'processModel' ` - -Name 'identityType' ` - -NewValue $IdentityType))) - { - return $false + if ($PSBoundParameters.ContainsKey('restartSchedule')) { + # Normalize the restartSchedule array values. + $restartScheduleDesired = [String[]]@( + $restartSchedule.Where( + { + $_ -ne '' + } + ).ForEach( + { + [TimeSpan]::Parse($_).ToString('hh\:mm\:ss') + } + ) | + Select-Object -Unique + ) + + $restartScheduleCurrent = [String[]]@( + @($appPool.recycling.periodicRestart.schedule.Collection).ForEach('value') + ) + + if ( + Compare-Object -ReferenceObject $restartScheduleDesired ` + -DifferenceObject $restartScheduleCurrent + ) { + Write-Verbose -Message ( + $LocalizedData['VerbosePropertyNotInDesiredState'] -f 'restartSchedule' + ) + + $inDesiredState = $false + } } - return $true + + if ($inDesiredState -eq $true) { + Write-Verbose -Message ($LocalizedData['VerboseResourceInDesiredState']) + } + else { + Write-Verbose -Message ($LocalizedData['VerboseResourceNotInDesiredState']) + } + + return $inDesiredState } #region Helper Functions -function Confirm-Value -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param + +function Get-Property { + param ( - [Parameter(Mandatory = $true)] - [AllowEmptyString()] - [System.String] - $Path, + [object] $Object, + [string] $PropertyName) - [Parameter(Mandatory = $true)] - [System.String] - $Name, + $parts = $PropertyName.Split('.') + $firstPart = $parts[0] - [Parameter()] - [System.String] - $NewValue - ) - - if (-not($NewValue)) - { - # if no new value was specified, we assume this value is okay. - return $true - } + $value = $Object.$firstPart + if ($parts.Count -gt 1) { + $newParts = @() + 1..($parts.Count - 1) | ForEach-Object { + $newParts += $parts[$_] + } - $existingValue = Get-Value -Path $Path -Name $Name - if ($existingValue -ne $NewValue) - { - return $false + $newName = ($newParts -join '.') + return Get-Property -Object $value -PropertyName $newName } - else - { - $relPath = $Path + '/' + $Name - Write-Verbose($LocalizedData.ValueOk -f $relPath,$NewValue); - return $true + else { + return $value } -} +} + +<# + .SYNOPSIS + Runs appcmd.exe - if there's an error then the application will terminate + + .PARAMETER ArgumentList + Optional list of string arguments to be passed into appcmd.exe -function Set-Value -{ +#> +function Invoke-AppCmd { [CmdletBinding()] param ( - [Parameter(Mandatory = $true)] - [AllowEmptyString()] - [System.String] - $Path, - - [Parameter(Mandatory = $true)] - [System.String] - $Name, - - [Parameter()] - [System.String] - $NewValue + [String[]] $ArgumentList ) - # if the variable doesn't exist, the user doesn't want to change this value - if (-not($NewValue)) - { - return - } + <# + This is a local preference for the function which will terminate + the program if there's an error invoking appcmd.exe + #> + $ErrorActionPreference = 'Stop' - $existingValue = Get-Value -Path $Path -Name $Name - if ($existingValue -ne $NewValue) - { - if ($Path -ne '') - { - $Path = '/' + $Path - } + $appcmdFilePath = "$env:SystemRoot\System32\inetsrv\appcmd.exe" + $allArguments = @("set", "config", "-section:system.applicationHost/applicationPools") + $ArgumentList + ("/commit:apphost") + + # Write-Verbose -Message "calling $($appcmdFilePath) $($allArguments)" + $appcmdResult = $(& $appcmdFilePath $allArguments) + Write-Verbose -Message $($appcmdResult).ToString() - Set-WebConfigurationProperty ` - -PSPath 'MACHINE/WEBROOT/APPHOST' ` - -Filter "system.applicationHost/applicationPools/applicationPoolDefaults$Path" ` - -Name $Name ` - -Value "$NewValue" - - $relPath = $Path + '/' + $Name - Write-Verbose($LocalizedData.SettingValue -f $relPath,$NewValue); + if ($LASTEXITCODE -ne 0) { + $errorMessage = $LocalizedData['ErrorAppCmdNonZeroExitCode'] -f $LASTEXITCODE + + New-TerminatingError -ErrorId 'ErrorAppCmdNonZeroExitCode' ` + -ErrorMessage $errorMessage ` + -ErrorCategory 'InvalidResult' } } -function Get-Value -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [AllowEmptyString()] - [System.String] - $Path, - [Parameter(Mandatory = $true)] - [System.String] - $Name - ) +function Get-AppPoolDefault { + # XPath -Filter is case-sensitive. Use Where-Object to get the target application pool by name. + $appPool = Get-WebConfiguration ` + -PSPath 'MACHINE/WEBROOT/APPHOST' ` + -Filter '/system.applicationHost/applicationPools/applicationPoolDefaults' + - if ($Path -ne '') - { - $Path = '/' + $Path + if ($null -eq $appPool) { + New-TerminatingError -ErrorId 'ErrorAppPoolDefaultsNotFound' ` + -ErrorMessage $LocalizedData['ErrorAppPoolDefaultsNotFound'] ` + -ErrorCategory 'InvalidResult' } - $result = Get-WebConfigurationProperty ` - -PSPath 'MACHINE/WEBROOT/APPHOST' ` - -Filter "system.applicationHost/applicationPools/applicationPoolDefaults$Path" ` - -Name $Name + Write-Verbose -Message ($LocalizedData['VerboseAppPoolDefaultsFound']) - if ($result -is [Microsoft.IIs.PowerShell.Framework.ConfigurationAttribute]) - { - return $result.Value - } else { - return $result - } + return $appPool } #endregion diff --git a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.schema.mof b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.schema.mof index 99b2f22c9..47e1a9d79 100644 --- a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.schema.mof +++ b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.schema.mof @@ -3,6 +3,54 @@ class MSFT_xWebAppPoolDefaults : OMI_BaseResource { [Key, Description("Dummy value because we need a key, always 'Machine'"), ValueMap{"Machine"}, Values{"Machine"}] string ApplyTo; - [write, Description("applicationPools/applicationPoolDefaults/managedRuntimeVersion"), ValueMap{"","v2.0","v4.0"}, Values{"","v2.0","v4.0"}] string ManagedRuntimeVersion; - [write, Description("applicationPools/applicationPoolDefaults/processModel/identityType"), ValueMap{"ApplicationPoolIdentity","LocalService","LocalSystem","NetworkService"}, Values{"ApplicationPoolIdentity","LocalService","LocalSystem","NetworkService"}] string IdentityType; + + [Write, Description("When set to true, indicates to the World Wide Web Publishing Service (W3SVC) that the application pool should be automatically started when it is created or when IIS is started.")] Boolean autoStart; + [Write, Description("Indicates the .NET configuration file for the application pool.")] String CLRConfigFile; + [Write, Description("When set to true, enables a 32-bit application to run on a computer that runs a 64-bit version of Windows.")] Boolean enable32BitAppOnWin64; + [Write, Description("When set to true, indicates that delegated settings in Web.config files will processed for applications within this application pool. When set to false, all settings in Web.config files will be ignored for this application pool.")] Boolean enableConfigurationOverride; + [Write, Description("Indicates the request-processing mode that is used to process requests for managed content. The values that are allowed for this property are: Integrated, Classic."), ValueMap{"Integrated","Classic"},Values{"Integrated","Classic"}] String managedPipelineMode; + [Write, Description("Indicates the managed loader to use for pre-loading the application pool.")] String managedRuntimeLoader; + [Write, Description("Indicates the CLR version to be used by the application pool. The values that are allowed for this property are: v4.0, v2.0, and ''."), ValueMap{"v4.0","v2.0",""},Values{"v4.0","v2.0",""}] String managedRuntimeVersion; + [Write, Description("When set to true, the Windows Process Activation Service (WAS) creates and passes a token for the built-in IUSR anonymous user account to the Anonymous authentication module. The Anonymous authentication module uses the token to impersonate the built-in account. When this property is set to false, the token will not be passed.")] Boolean passAnonymousToken; + [Write, Description("Indicates the startup type for the application pool. The values that are allowed for this property are: OnDemand, AlwaysRunning."), ValueMap{"OnDemand","AlwaysRunning"},Values{"OnDemand","AlwaysRunning"}] String startMode; + [Write, Description("Indicates the maximum number of requests that HTTP.sys will queue for the application pool. The value must be a valid integer between 10 and 65535.")] UInt32 queueLength; + [Write, Description("Configures the action that IIS takes when a worker process exceeds its configured CPU limit. The values that are allowed for this property are: NoAction, KillW3wp, Throttle, and ThrottleUnderLoad."), ValueMap{"NoAction","KillW3wp","Throttle","ThrottleUnderLoad"},Values{"NoAction","KillW3wp","Throttle","ThrottleUnderLoad"}] String cpuAction; + [Write, Description("Configures the maximum percentage of CPU time (in 1/1000ths of one percent) that the worker processes in the application pool are allowed to consume over a period of time as indicated by the cpuResetInterval property. The value must be a valid integer between 0 and 100000.")] UInt32 cpuLimit; + [Write, Description("Indicates the reset period (in minutes) for CPU monitoring and throttling limits on the application pool. The value must be a string representation of a TimeSpan value. The valid range (in minutes) is 0 to 1440. Setting the value of this property to 0 disables CPU monitoring.")] String cpuResetInterval; + [Write, Description("Indicates whether a particular worker process assigned to the application pool should also be assigned to a given CPU.")] Boolean cpuSmpAffinitized; + [Write, Description("Indicates the hexadecimal processor mask for multi-processor computers, which indicates to which CPU the worker processes in the application pool should be bound. Before this property takes effect, the cpuSmpAffinitized property must be set to true for the application pool. The value must be a valid integer between 0 and 4294967295.")] UInt32 cpuSmpProcessorAffinityMask; + [Write, Description("Indicates the high-order DWORD hexadecimal processor mask for 64-bit multi-processor computers, which indicates to which CPU the worker processes in the application pool should be bound. Before this property takes effect, the cpuSmpAffinitized property must be set to true for the application pool. The value must be a valid integer between 0 and 4294967295.")] UInt32 cpuSmpProcessorAffinityMask2; + [Write, Description("Indicates the account identity under which the application pool runs. The values that are allowed for this property are: ApplicationPoolIdentity, LocalService, LocalSystem, NetworkService, and SpecificUser."), ValueMap{"ApplicationPoolIdentity","LocalService","LocalSystem","NetworkService","SpecificUser"}, Values{"ApplicationPoolIdentity","LocalService","LocalSystem","NetworkService","SpecificUser"}] String identityType; + [Write, Description("Indicates the custom account crededentials. This property is only valid when the identityType property is set to SpecificUser."), EmbeddedInstance("MSFT_Credential")] String Credential; + [Write, Description("Indicates the amount of time (in minutes) a worker process will remain idle before it shuts down. The value must be a string representation of a TimeSpan value and must be less than the restartTimeLimit property value. The valid range (in minutes) is 0 to 43200.")] String idleTimeout; + [Write, Description("Indicates the action to perform when the idle timeout duration has been reached. The values that are allowed for this property are: Terminate, Suspend."), ValueMap{"Terminate","Suspend"}, Values{"Terminate","Suspend"}] String idleTimeoutAction; + [Write, Description("Indicates whether IIS loads the user profile for the application pool identity.")] Boolean loadUserProfile; + [Write, Description("Indicates that IIS should generate an event log entry for each occurrence of the specified process model events.")] String logEventOnProcessModel; + [Write, Description("Indicates the logon type for the process identity. The values that are allowed for this property are: LogonBatch, LogonService."), ValueMap{"LogonBatch","LogonService"},Values{"LogonBatch","LogonService"}] String logonType; + [Write, Description("Indicates whether the IIS_IUSRS group Security Identifier (SID) is added to the worker process token.")] Boolean manualGroupMembership; + [Write, Description("Indicates the maximum number of worker processes that would be used for the application pool. The value must be a valid integer between 0 and 2147483647.")] UInt32 maxProcesses; + [Write, Description("Indicates whether pinging (health monitoring) is enabled for the worker process(es) serving this application pool.")] Boolean pingingEnabled; + [Write, Description("Indicates the period of time (in seconds) between health monitoring pings sent to the worker process(es) serving this application pool. The value must be a string representation of a TimeSpan value. The valid range (in seconds) is 1 to 4294967.")] String pingInterval; + [Write, Description("Indicates the maximum time (in seconds) that a worker process is given to respond to a health monitoring ping. The value must be a string representation of a TimeSpan value. The valid range (in seconds) is 1 to 4294967.")] String pingResponseTime; + [Write, Description("Indicates the environment to be set based on the user profile for the new process.")] Boolean setProfileEnvironment; + [Write, Description("Indicates the period of time (in seconds) a worker process is given to finish processing requests and shut down. The value must be a string representation of a TimeSpan value. The valid range (in seconds) is 1 to 4294967.")] String shutdownTimeLimit; + [Write, Description("Indicates the period of time (in seconds) a worker process is given to start up and initialize. The value must be a string representation of a TimeSpan value. The valid range (in seconds) is 1 to 4294967.")] String startupTimeLimit; + [Write, Description("Indicates an executable to run when a worker process is orphaned.")] String orphanActionExe; + [Write, Description("Indicates parameters for the executable that is specified in the orphanActionExe property.")] String orphanActionParams; + [Write, Description("Indicates whether to assign a worker process to an orphan state instead of terminating it when the application pool fails. If true, an unresponsive worker process will be orphaned instead of terminated.")] Boolean orphanWorkerProcess; + [Write, Description("Indicates the response behavior of a service when it is unavailable. The values that are allowed for this property are: HttpLevel, TcpLevel. If set to HttpLevel and the application pool is stopped, HTTP.sys will return HTTP 503 error. If set to TcpLevel, HTTP.sys will reset the connection."), ValueMap{"HttpLevel","TcpLevel"},Values{"HttpLevel","TcpLevel"}] String loadBalancerCapabilities; + [Write, Description("Indicates whether rapid-fail protection is enabled. If true, the application pool is shut down if there are a specified number of worker process crashes within a specified time period.")] Boolean rapidFailProtection; + [Write, Description("Indicates the time interval (in minutes) during which the specified number of worker process crashes must occur before the application pool is shut down by rapid-fail protection. The value must be a string representation of a TimeSpan value. The valid range (in minutes) is 1 to 144000.")] String rapidFailProtectionInterval; + [Write, Description("Indicates the maximum number of worker process crashes permitted before the application pool is shut down by rapid-fail protection. The value must be a valid integer between 0 and 2147483647.")] UInt32 rapidFailProtectionMaxCrashes; + [Write, Description("Indicates an executable to run when the application pool is shut down by rapid-fail protection.")] String autoShutdownExe; + [Write, Description("Indicates parameters for the executable that is specified in the autoShutdownExe property.")] String autoShutdownParams; + [Write, Description("Indicates whether the W3SVC service should start another worker process to replace the existing worker process while that process is shutting down. If true, the application pool recycle will happen such that the existing worker process exits before another worker process is created.")] Boolean disallowOverlappingRotation; + [Write, Description("Indicates whether the W3SVC service should rotate worker processes in the application pool when the configuration has changed. If true, the application pool will not recycle when its configuration is changed.")] Boolean disallowRotationOnConfigChange; + [Write, Description("Indicates that IIS should generate an event log entry for each occurrence of the specified recycling events.")] String logEventOnRecycle; + [Write, Description("Indicates the maximum amount of virtual memory (in KB) a worker process can consume before causing the application pool to recycle. The value must be a valid integer between 0 and 4294967295. A value of 0 means there is no limit.")] UInt32 restartMemoryLimit; + [Write, Description("Indicates the maximum amount of private memory (in KB) a worker process can consume before causing the application pool to recycle. The value must be a valid integer between 0 and 4294967295. A value of 0 means there is no limit.")] UInt32 restartPrivateMemoryLimit; + [Write, Description("Indicates the maximum number of requests the application pool can process before it is recycled. The value must be a valid integer between 0 and 4294967295. A value of 0 means the application pool can process an unlimited number of requests.")] UInt32 restartRequestsLimit; + [Write, Description("Indicates the period of time (in minutes) after which the application pool will recycle. The value must be a string representation of a TimeSpan value. The valid range (in minutes) is 0 to 432000. A value of 0 means the application pool does not recycle on a regular interval.")] String restartTimeLimit; + [Write, Description("Indicates a set of specific local times, in 24 hour format, when the application pool is recycled. The value must be an array of string representations of TimeSpan values. TimeSpan values must be between 00:00:00 and 23:59:59 seconds inclusive, with a granularity of 60 seconds. Setting the value of this property to '' disables the schedule.")] String restartSchedule[]; + }; diff --git a/Examples/Sample_xWebAppPoolDefaults.ps1 b/Examples/Sample_xWebAppPoolDefaults.ps1 index 6eca34e0b..b1b67dfc1 100644 --- a/Examples/Sample_xWebAppPoolDefaults.ps1 +++ b/Examples/Sample_xWebAppPoolDefaults.ps1 @@ -21,9 +21,56 @@ Configuration Sample_xWebAppPoolDefaults # Configures the application pool defaults. xWebAppPoolDefaults PoolDefaults { - ApplyTo = 'Machine' - ManagedRuntimeVersion = 'v4.0' - IdentityType = 'ApplicationPoolIdentity' + ApplyTo = 'Machine' + autoStart = $true + CLRConfigFile = '' + enable32BitAppOnWin64 = $false + enableConfigurationOverride = $true + managedPipelineMode = 'Integrated' + managedRuntimeLoader = 'webengine4.dll' + managedRuntimeVersion = 'v4.0' + passAnonymousToken = $true + startMode = 'OnDemand' + queueLength = 1000 + cpuAction = 'NoAction' + cpuLimit = 90000 + cpuResetInterval = (New-TimeSpan -Minutes 5).ToString() + cpuSmpAffinitized = $false + cpuSmpProcessorAffinityMask = 4294967295 + cpuSmpProcessorAffinityMask2 = 4294967295 + identityType = 'ApplicationPoolIdentity' + idleTimeout = (New-TimeSpan -Minutes 20).ToString() + idleTimeoutAction = 'Terminate' + loadUserProfile = $true + logEventOnProcessModel = 'IdleTimeout' + logonType = 'LogonBatch' + manualGroupMembership = $false + maxProcesses = 1 + pingingEnabled = $true + pingInterval = (New-TimeSpan -Seconds 30).ToString() + pingResponseTime = (New-TimeSpan -Seconds 90).ToString() + setProfileEnvironment = $false + shutdownTimeLimit = (New-TimeSpan -Seconds 90).ToString() + startupTimeLimit = (New-TimeSpan -Seconds 90).ToString() + orphanActionExe = '' + orphanActionParams = '' + orphanWorkerProcess = $false + loadBalancerCapabilities = 'HttpLevel' + rapidFailProtection = $true + rapidFailProtectionInterval = (New-TimeSpan -Minutes 5).ToString() + rapidFailProtectionMaxCrashes = 5 + autoShutdownExe = '' + autoShutdownParams = '' + disallowOverlappingRotation = $false + disallowRotationOnConfigChange = $false + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + restartMemoryLimit = 0 + restartPrivateMemoryLimit = 0 + restartRequestsLimit = 0 + restartTimeLimit = (New-TimeSpan -Minutes 1440).ToString() + restartSchedule = @('00:00:00', '08:00:00', '16:00:00') } + + } } diff --git a/README.md b/README.md index 8d8e99255..2e271aed7 100644 --- a/README.md +++ b/README.md @@ -242,8 +242,90 @@ Please check out common DSC Resources [contributing guidelines](https://github.c ### xWebAppPoolDefaults * **ApplyTo**: Required Key value, always **Machine** -* **ManagedRuntimeVersion**: CLR Version {v2.0|v4.0|} empty string for unmanaged. -* **ApplicationPoolIdentity**: {ApplicationPoolIdentity | LocalService | LocalSystem | NetworkService} +* **autoStart** : When set to `$true`, indicates to the World Wide Web Publishing Service (W3SVC) that the application pool should be automatically started when it is created or when IIS is started. +* **CLRConfigFile** : Indicates the .NET configuration file for the application pool. +* **enable32BitAppOnWin64** : When set to `$true`, enables a 32-bit application to run on a computer that runs a 64-bit version of Windows. +* **enableConfigurationOverride** : When set to `$true`, indicates that delegated settings in Web.config files will be processed for applications within this application pool. + When set to `$false`, all settings in Web.config files will be ignored for this application pool. +* **managedPipelineMode** : Indicates the request-processing mode that is used to process requests for managed content. The values that are allowed for this property are: `Integrated`, `Classic`. +* **managedRuntimeLoader** : Indicates the managed loader to use for pre-loading the application pool. +* **managedRuntimeVersion** : Indicates the CLR version to be used by the application pool. The values that are allowed for this property are: `v4.0`, `v2.0`, and `""`. +* **passAnonymousToken** : When set to `$true`, the Windows Process Activation Service (WAS) creates and passes a token for the built-in IUSR anonymous user account to the Anonymous authentication module. + The Anonymous authentication module uses the token to impersonate the built-in account. When this property is set to `$false`, the token will not be passed. +* **startMode** : Indicates the startup type for the application pool. The values that are allowed for this property are: `OnDemand`, `AlwaysRunning`. +* **queueLength** : Indicates the maximum number of requests that HTTP.sys will queue for the application pool. The value must be a valid integer between `10` and `65535`. +* **cpuAction** : Configures the action that IIS takes when a worker process exceeds its configured CPU limit. + The values that are allowed for this property are: `NoAction`, `KillW3wp`, `Throttle`, and `ThrottleUnderLoad`. +* **cpuLimit** : Configures the maximum percentage of CPU time (in 1/1000ths of one percent) that the worker processes in the application pool are allowed to consume over a period of time as indicated by the **cpuResetInterval** property. + The value must be a valid integer between `0` and `100000`. +* **cpuResetInterval** : Indicates the reset period (in minutes) for CPU monitoring and throttling limits on the application pool. + The value must be a string representation of a TimeSpan value. The valid range (in minutes) is `0` to `1440`. + Setting the value of this property to `00:00:00` disables CPU monitoring. +* **cpuSmpAffinitized** : Indicates whether a particular worker process assigned to the application pool should also be assigned to a given CPU. +* **cpuSmpProcessorAffinityMask** : Indicates the hexadecimal processor mask for multi-processor computers, which indicates to which CPU the worker processes in the application pool should be bound. + Before this property takes effect, the **cpuSmpAffinitized** property must be set to `$true` for the application pool. + The value must be a valid integer between `0` and `4294967295`. +* **cpuSmpProcessorAffinityMask2** : Indicates the high-order DWORD hexadecimal processor mask for 64-bit multi-processor computers, which indicates to which CPU the worker processes in the application pool should be bound. + Before this property takes effect, the **cpuSmpAffinitized** property must be set to `$true` for the application pool. + The value must be a valid integer between `0` and `4294967295`. +* **identityType** : Indicates the account identity under which the application pool runs. + The values that are allowed for this property are: `ApplicationPoolIdentity`, `LocalService`, `LocalSystem`, `NetworkService`, and `SpecificUser`. +* **Credential** : Indicates the custom account crededentials. This property is only valid when the **identityType** property is set to `SpecificUser`. +* **idleTimeout** : Indicates the amount of time (in minutes) a worker process will remain idle before it shuts down. + The value must be a string representation of a TimeSpan value and must be less than the **restartTimeLimit** property value. The valid range (in minutes) is `0` to `43200`. +* **idleTimeoutAction** : Indicates the action to perform when the idle timeout duration has been reached. + The values that are allowed for this property are: `Terminate`, `Suspend`. +* **loadUserProfile** : Indicates whether IIS loads the user profile for the application pool identity. +* **logEventOnProcessModel** : Indicates that IIS should generate an event log entry for each occurrence of the specified process model events. +* **logonType** : Indicates the logon type for the process identity. The values that are allowed for this property are: `LogonBatch`, `LogonService`. +* **manualGroupMembership** : Indicates whether the IIS_IUSRS group Security Identifier (SID) is added to the worker process token. +* **maxProcesses** : Indicates the maximum number of worker processes that would be used for the application pool. + The value must be a valid integer between `0` and `2147483647`. +* **pingingEnabled** : Indicates whether pinging (health monitoring) is enabled for the worker process(es) serving this application pool. +* **pingInterval** : Indicates the period of time (in seconds) between health monitoring pings sent to the worker process(es) serving this application pool. + The value must be a string representation of a TimeSpan value. The valid range (in seconds) is `1` to `4294967`. +* **pingResponseTime** : Indicates the maximum time (in seconds) that a worker process is given to respond to a health monitoring ping. + The value must be a string representation of a TimeSpan value. The valid range (in seconds) is `1` to `4294967`. +* **setProfileEnvironment** : Indicates the environment to be set based on the user profile for the new process. +* **shutdownTimeLimit** : Indicates the period of time (in seconds) a worker process is given to finish processing requests and shut down. + The value must be a string representation of a TimeSpan value. The valid range (in seconds) is `1` to `4294967`. +* **startupTimeLimit** : Indicates the period of time (in seconds) a worker process is given to start up and initialize. + The value must be a string representation of a TimeSpan value. The valid range (in seconds) is `1` to `4294967`. +* **orphanActionExe** : Indicates an executable to run when a worker process is orphaned. +* **orphanActionParams** : Indicates parameters for the executable that is specified in the **orphanActionExe** property. +* **orphanWorkerProcess** : Indicates whether to assign a worker process to an orphan state instead of terminating it when the application pool fails. + If `$true`, an unresponsive worker process will be orphaned instead of terminated. +* **loadBalancerCapabilities** : Indicates the response behavior of a service when it is unavailable. The values that are allowed for this property are: `HttpLevel`, `TcpLevel`. + If set to `HttpLevel` and the application pool is stopped, HTTP.sys will return HTTP 503 error. If set to `TcpLevel`, HTTP.sys will reset the connection. +* **rapidFailProtection** : Indicates whether rapid-fail protection is enabled. + If `$true`, the application pool is shut down if there are a specified number of worker process crashes within a specified time period. +* **rapidFailProtectionInterval** : Indicates the time interval (in minutes) during which the specified number of worker process crashes must occur before the application pool is shut down by rapid-fail protection. + The value must be a string representation of a TimeSpan value. The valid range (in minutes) is `1` to `144000`. +* **rapidFailProtectionMaxCrashes** : Indicates the maximum number of worker process crashes permitted before the application pool is shut down by rapid-fail protection. + The value must be a valid integer between `0` and `2147483647`. +* **autoShutdownExe** : Indicates an executable to run when the application pool is shut down by rapid-fail protection. +* **autoShutdownParams** : Indicates parameters for the executable that is specified in the **autoShutdownExe** property. +* **disallowOverlappingRotation** : Indicates whether the W3SVC service should start another worker process to replace the existing worker process while that process is shutting down. + If `$true`, the application pool recycle will happen such that the existing worker process exits before another worker process is created. +* **disallowRotationOnConfigChange** : Indicates whether the W3SVC service should rotate worker processes in the application pool when the configuration has changed. + If `$true`, the application pool will not recycle when its configuration is changed. +* **logEventOnRecycle** : Indicates that IIS should generate an event log entry for each occurrence of the specified recycling events. +* **restartMemoryLimit** : Indicates the maximum amount of virtual memory (in KB) a worker process can consume before causing the application pool to recycle. + The value must be a valid integer between `0` and `4294967295`. + A value of `0` means there is no limit. +* **restartPrivateMemoryLimit** : Indicates the maximum amount of private memory (in KB) a worker process can consume before causing the application pool to recycle. + The value must be a valid integer between `0` and `4294967295`. + A value of `0` means there is no limit. +* **restartRequestsLimit** : Indicates the maximum number of requests the application pool can process before it is recycled. + The value must be a valid integer between `0` and `4294967295`. + A value of `0` means the application pool can process an unlimited number of requests. +* **restartTimeLimit** : Indicates the period of time (in minutes) after which the application pool will recycle. + The value must be a string representation of a TimeSpan value. The valid range (in minutes) is `0` to `432000`. + A value of `00:00:00` means the application pool does not recycle on a regular interval. +* **restartSchedule** : Indicates a set of specific local times, in 24 hour format, when the application pool is recycled. + The value must be an array of string representations of TimeSpan values. + TimeSpan values must be between `00:00:00` and `23:59:59` seconds inclusive, with a granularity of 60 seconds. + Setting the value of this property to `""` disables the schedule. ### xWebSiteDefaults @@ -257,6 +339,9 @@ Please check out common DSC Resources [contributing guidelines](https://github.c ## Versions ### Unreleased +* Added missing settings for **xWebAppPoolDefaults**. Fixes #105. + +* Added missing settings for **xWebAppPoolDefaults**. Fixes #105. ### 1.20.0.0 @@ -1080,9 +1165,54 @@ configuration Sample_IISServerDefaults xWebAppPoolDefaults PoolDefaults { - ApplyTo = 'Machine' - ManagedRuntimeVersion = 'v4.0' - IdentityType = 'ApplicationPoolIdentity' + ApplyTo = 'Machine' + autoStart = $true + CLRConfigFile = '' + enable32BitAppOnWin64 = $false + enableConfigurationOverride = $true + managedPipelineMode = 'Integrated' + managedRuntimeLoader = 'webengine4.dll' + managedRuntimeVersion = 'v4.0' + passAnonymousToken = $true + startMode = 'OnDemand' + queueLength = 1000 + cpuAction = 'NoAction' + cpuLimit = 90000 + cpuResetInterval = (New-TimeSpan -Minutes 5).ToString() + cpuSmpAffinitized = $false + cpuSmpProcessorAffinityMask = 4294967295 + cpuSmpProcessorAffinityMask2 = 4294967295 + identityType = 'ApplicationPoolIdentity' + idleTimeout = (New-TimeSpan -Minutes 20).ToString() + idleTimeoutAction = 'Terminate' + loadUserProfile = $true + logEventOnProcessModel = 'IdleTimeout' + logonType = 'LogonBatch' + manualGroupMembership = $false + maxProcesses = 1 + pingingEnabled = $true + pingInterval = (New-TimeSpan -Seconds 30).ToString() + pingResponseTime = (New-TimeSpan -Seconds 90).ToString() + setProfileEnvironment = $false + shutdownTimeLimit = (New-TimeSpan -Seconds 90).ToString() + startupTimeLimit = (New-TimeSpan -Seconds 90).ToString() + orphanActionExe = '' + orphanActionParams = '' + orphanWorkerProcess = $false + loadBalancerCapabilities = 'HttpLevel' + rapidFailProtection = $true + rapidFailProtectionInterval = (New-TimeSpan -Minutes 5).ToString() + rapidFailProtectionMaxCrashes = 5 + autoShutdownExe = '' + autoShutdownParams = '' + disallowOverlappingRotation = $false + disallowRotationOnConfigChange = $false + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + restartMemoryLimit = 0 + restartPrivateMemoryLimit = 0 + restartRequestsLimit = 0 + restartTimeLimit = (New-TimeSpan -Minutes 1440).ToString() + restartSchedule = @('00:00:00', '08:00:00', '16:00:00') } } } diff --git a/Tests/Integration/MSFT_xWebAppPoolDefaults.Integration.Tests.ps1 b/Tests/Integration/MSFT_xWebAppPoolDefaults.Integration.Tests.ps1 index 3b6e4d551..cee617cc1 100644 --- a/Tests/Integration/MSFT_xWebAppPoolDefaults.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xWebAppPoolDefaults.Integration.Tests.ps1 @@ -1,6 +1,7 @@ +#requires -Version 4.0 -$script:DSCModuleName = 'xWebAdministration' -$script:DSCResourceName = 'MSFT_xWebAppPoolDefaults' +$script:DSCModuleName = 'xWebAdministration' +$script:DSCResourceName = 'MSFT_xWebAppPoolDefaults' #region HEADER @@ -16,148 +17,98 @@ Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\ $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $script:DSCModuleName ` -DSCResourceName $script:DSCResourceName ` - -TestType Integration + -TestType Integration + #endregion -[string] $tempName = "$($script:DSCResourceName)_" + (Get-Date).ToString("yyyyMMdd_HHmmss") +# Test Setup +if ((Get-Service -Name 'W3SVC').Status -ne 'Running') +{ + Start-Service -Name 'W3SVC' +} + +$tempBackupName = "$($script:DSCResourceName)_$(Get-Date -Format 'yyyyMMdd_HHmmss')" # Using try/finally to always cleanup even if something awful happens. + try { - #region Integration Tests + # Create configuration backup + + Backup-WebConfiguration -Name $tempBackupName | Out-Null - # some constants - [string]$constPsPath = 'MACHINE/WEBROOT/APPHOST' - [string]$constAPDFilter = 'system.applicationHost/applicationPools/applicationPoolDefaults' - [string]$constSiteFilter = 'system.applicationHost/sites/' + #region Integration Tests $ConfigFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:DSCResourceName).config.ps1" . $ConfigFile - $null = Backup-WebConfiguration -Name $tempName + Describe "$($script:DSCResourceName)_Integration" { - function Get-SiteValue([string]$path,[string]$name) - { - return (Get-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/sites/$path" -name $name).value - } + #region Default Tests - Describe "$($script:DSCResourceName)_Integration" { - #region DEFAULT TESTS - It 'Should compile without throwing' { + It 'Should be able to compile and apply without throwing' { { - Invoke-Expression -Command "$($script:DSCResourceName)_Config -OutputPath `$TestDrive" - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force - } | Should not throw + Invoke-Expression -Command ( + '{0}_Config -OutputPath $TestDrive -ConfigurationData $ConfigData -ErrorAction Stop' -f + $script:DSCResourceName + ) + + Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Force -Wait -Verbose + } | Should Not Throw } It 'Should be able to call Get-DscConfiguration without throwing' { - { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should Not throw + { + Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should Not Throw } + #endregion - It 'Changing ManagedRuntimeVersion' { + It 'Should have set the resource and all the parameters should match' { + + $currentConfiguration = Get-DscConfiguration + + foreach ($parameter in $TestParameters.GetEnumerator()) { - # get the current value - [string] $originalValue = (Get-WebConfigurationProperty -pspath $constPsPath -filter $constAPDFilter -name managedRuntimeVersion) + Write-Verbose -Message "The $($parameter.Name) property should be set." - # We are using environment variables here, because a inline PowerShell variable was empty after executing Start-DscConfiguration + if ($parameter.Name -eq 'Credential') + { + $appPoolDefaults = Get-WebConfiguration -Filter '/system.applicationHost/applicationPools/applicationPoolDefaults' + + $appPoolDefaults.processModel.userName | + Should Be $TestParameters['Credential'].UserName - # change the value to something else - if ($originalValue -eq 'v4.0') + $appPoolDefaults.processModel.password | + Should Be $TestParameters['Credential'].GetNetworkCredential().Password + } + elseif ($parameter.Name -eq 'ApplyTo') { - $env:PesterManagedRuntimeVersion = 'v2.0' + # ignored. } else { - $env:PesterManagedRuntimeVersion = 'v4.0' + $currentConfiguration."$($parameter.Name)" | + Should Be $TestParameters[$parameter.Name] } - - Invoke-Expression -Command "$($script:DSCResourceName)_ManagedRuntimeVersion -OutputPath `$TestDrive" - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force - } | should not throw - - # get the configured value again - $changedValue = (Get-WebConfigurationProperty -pspath $constPsPath -filter $constAPDFilter -name managedRuntimeVersion).Value - - # compare it to the one we just tried to set. - $changedValue | should be $env:PesterManagedRuntimeVersion - } - - It 'Changing IdentityType' { - # get the current value - [string] $originalValue = (Get-WebConfigurationProperty ` - -PSPath $constPsPath ` - -Filter $constAPDFilter/processModel ` - -Name identityType) - - if ($originalValue -eq 'ApplicationPoolIdentity') - { - $env:PesterApplicationPoolIdentity = 'LocalService' - } - else - { - $env:PesterApplicationPoolIdentity = 'ApplicationPoolIdentity' } - # Compile the MOF File - { - Invoke-Expression -Command "$($script:DSCResourceName)_AppPoolIdentityType -OutputPath `$TestDrive" - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force - } | Should not throw - - $changedValue = (Get-WebConfigurationProperty -PSPath $constPsPath -Filter $constAPDFilter/processModel -Name identityType) - - $changedValue | Should Be $env:PesterApplicationPoolIdentity } - - It 'Changing LogFormat' { - [string] $originalValue = Get-SiteValue 'logFile' 'logFormat' - - if ($originalValue -eq 'W3C') - { - $env:PesterLogFormat = 'IIS' - } - else - { - $env:PesterLogFormat = 'W3C' - } - - # Compile the MOF File - { - Invoke-Expression -Command "$($script:DSCResourceName)_LogFormat -OutputPath `$TestDrive" - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force - } | Should not throw - - $changedValue = Get-SiteValue 'logFile' 'logFormat' - - $changedValue | Should Be $env:PesterALogFormat - } - - It 'Changing Default AppPool' { - # get the current value - - [string] $originalValue = Get-SiteValue 'applicationDefaults' 'applicationPool' - - $env:PesterDefaultPool = 'DefaultAppPool' - # Compile the MOF File - { - Invoke-Expression -Command "$($script:DSCResourceName)_DefaultPool -OutputPath `$TestDrive" - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force - } | Should not throw - - $changedValue = Get-SiteValue 'applicationDefaults' 'applicationPool' - $changedValue | should be $env:PesterDefaultPool + It 'Actual configuration should match the desired configuration' { + Test-DscConfiguration -Verbose | Should Be $true } } + #endregion } finally { #region FOOTER - Restore-WebConfiguration -Name $tempName - Remove-WebConfigurationBackup -Name $tempName + Restore-WebConfiguration -Name $tempBackupName + Remove-WebConfigurationBackup -Name $tempBackupName Restore-TestEnvironment -TestEnvironment $TestEnvironment #endregion diff --git a/Tests/Integration/MSFT_xWebAppPoolDefaults.config.ps1 b/Tests/Integration/MSFT_xWebAppPoolDefaults.config.ps1 index c804256ae..32c1aaad2 100644 --- a/Tests/Integration/MSFT_xWebAppPoolDefaults.config.ps1 +++ b/Tests/Integration/MSFT_xWebAppPoolDefaults.config.ps1 @@ -1,60 +1,137 @@ -[string] $constPsPath = 'MACHINE/WEBROOT/APPHOST' -[string] $constAPDFilter = 'system.applicationHost/applicationPools/applicationPoolDefaults' -[string] $constSiteFilter = 'system.applicationHost/sites/' +#requires -Version 4.0 -[string] $originalValue = (Get-WebConfigurationProperty -pspath $constPsPath -filter $constAPDFilter -name managedRuntimeVersion).Value +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssigments', '')] +param () -configuration MSFT_xWebAppPoolDefaults_Config -{ - Import-DscResource -ModuleName xWebAdministration - - xWebAppPoolDefaults PoolDefaults - { - ApplyTo = 'Machine' - ManagedRuntimeVersion = $originalValue - } +$ConfigData = @{ + AllNodes = @( + @{ + NodeName = '*' + PSDscAllowPlainTextPassword = $true + } + @{ + NodeName = 'localhost' + } + ) } -configuration MSFT_xWebAppPoolDefaults_ManagedRuntimeVersion -{ - Import-DscResource -ModuleName xWebAdministration +$TestCredential = New-Object -TypeName PSCredential -ArgumentList ( + 'CONTOSO\JDoe', + ('5t6y7u8i' | ConvertTo-SecureString -AsPlainText -Force) +) - xWebAppPoolDefaults PoolDefaults - { - ApplyTo = 'Machine' - ManagedRuntimeVersion = $env:PesterManagedRuntimeVersion - } +$TestParameters = [Ordered]@{ + applyTo = 'Machine' + autoStart = $false + CLRConfigFile = 'C:\inetpub\temp\aspnet.config' + enable32BitAppOnWin64 = $true + enableConfigurationOverride = $false + managedPipelineMode = 'Classic' + managedRuntimeLoader = '' + managedRuntimeVersion = 'v2.0' + passAnonymousToken = $false + startMode = 'AlwaysRunning' + queueLength = 2000 + cpuAction = 'KillW3wp' + cpuLimit = 90000 + cpuResetInterval = '00:10:00' + cpuSmpAffinitized = $true + cpuSmpProcessorAffinityMask = 1 + cpuSmpProcessorAffinityMask2 = 1 + identityType = 'SpecificUser' + Credential = $TestCredential + idleTimeout = '00:15:00' + idleTimeoutAction = 'Suspend' + loadUserProfile = $false + logEventOnProcessModel = '' + logonType = 'LogonService' + manualGroupMembership = $true + maxProcesses = 2 + pingingEnabled = $false + pingInterval = '00:01:00' + pingResponseTime = '00:02:00' + setProfileEnvironment = $true + shutdownTimeLimit = '00:02:00' + startupTimeLimit = '00:02:00' + orphanActionExe = 'C:\inetpub\temp\orphanAction.exe' + orphanActionParams = '/orphanActionParam1' + orphanWorkerProcess = $true + loadBalancerCapabilities = 'TcpLevel' + rapidFailProtection = $false + rapidFailProtectionInterval = '00:10:00' + rapidFailProtectionMaxCrashes = 10 + autoShutdownExe = 'C:\inetpub\temp\autoShutdown.exe' + autoShutdownParams = '/autoShutdownParam1' + disallowOverlappingRotation = $true + disallowRotationOnConfigChange = $true + logEventOnRecycle = 'Time,Memory,PrivateMemory' + restartMemoryLimit = 1048576 + restartPrivateMemoryLimit = 1048576 + restartRequestsLimit = 1000 + restartTimeLimit = '2.10:00:00' + restartSchedule = @('05:00:00', '21:00:00') } -configuration MSFT_xWebAppPoolDefaults_AppPoolIdentityType +Configuration MSFT_xWebAppPoolDefaults_Config { Import-DscResource -ModuleName xWebAdministration - xWebAppPoolDefaults PoolDefaults + Node $AllNodes.NodeName { - ApplyTo = 'Machine' - IdentityType = $env:PesterApplicationPoolIdentity + xWebAppPoolDefaults Defaults + { + ApplyTo = $TestParameters.applyTo + autoStart = $TestParameters.autoStart + CLRConfigFile = $TestParameters.CLRConfigFile + enable32BitAppOnWin64 = $TestParameters.enable32BitAppOnWin64 + enableConfigurationOverride = $TestParameters.enableConfigurationOverride + managedPipelineMode = $TestParameters.managedPipelineMode + managedRuntimeLoader = $TestParameters.managedRuntimeLoader + managedRuntimeVersion = $TestParameters.managedRuntimeVersion + passAnonymousToken = $TestParameters.passAnonymousToken + startMode = $TestParameters.startMode + queueLength = $TestParameters.queueLength + cpuAction = $TestParameters.cpuAction + cpuLimit = $TestParameters.cpuLimit + cpuResetInterval = $TestParameters.cpuResetInterval + cpuSmpAffinitized = $TestParameters.cpuSmpAffinitized + cpuSmpProcessorAffinityMask = $TestParameters.cpuSmpProcessorAffinityMask + cpuSmpProcessorAffinityMask2 = $TestParameters.cpuSmpProcessorAffinityMask2 + identityType = $TestParameters.identityType + Credential = $TestParameters.Credential + idleTimeout = $TestParameters.idleTimeout + idleTimeoutAction = $TestParameters.idleTimeoutAction + loadUserProfile = $TestParameters.loadUserProfile + logEventOnProcessModel = $TestParameters.logEventOnProcessModel + logonType = $TestParameters.logonType + manualGroupMembership = $TestParameters.manualGroupMembership + maxProcesses = $TestParameters.maxProcesses + pingingEnabled = $TestParameters.pingingEnabled + pingInterval = $TestParameters.pingInterval + pingResponseTime = $TestParameters.pingResponseTime + setProfileEnvironment = $TestParameters.setProfileEnvironment + shutdownTimeLimit = $TestParameters.shutdownTimeLimit + startupTimeLimit = $TestParameters.startupTimeLimit + orphanActionExe = $TestParameters.orphanActionExe + orphanActionParams = $TestParameters.orphanActionParams + orphanWorkerProcess = $TestParameters.orphanWorkerProcess + loadBalancerCapabilities = $TestParameters.loadBalancerCapabilities + rapidFailProtection = $TestParameters.rapidFailProtection + rapidFailProtectionInterval = $TestParameters.rapidFailProtectionInterval + rapidFailProtectionMaxCrashes = $TestParameters.rapidFailProtectionMaxCrashes + autoShutdownExe = $TestParameters.autoShutdownExe + autoShutdownParams = $TestParameters.autoShutdownParams + disallowOverlappingRotation = $TestParameters.disallowOverlappingRotation + disallowRotationOnConfigChange = $TestParameters.disallowRotationOnConfigChange + logEventOnRecycle = $TestParameters.logEventOnRecycle + restartMemoryLimit = $TestParameters.restartMemoryLimit + restartPrivateMemoryLimit = $TestParameters.restartPrivateMemoryLimit + restartRequestsLimit = $TestParameters.restartRequestsLimit + restartTimeLimit = $TestParameters.restartTimeLimit + restartSchedule = $TestParameters.restartSchedule + } } } -configuration MSFT_xWebAppPoolDefaults_LogFormat -{ - Import-DscResource -ModuleName xWebAdministration - xWebSiteDefaults LogFormat - { - ApplyTo = 'Machine' - LogFormat = $env:PesterLogFormat - } -} - -configuration MSFT_xWebAppPoolDefaults_DefaultPool -{ - Import-DscResource -ModuleName xWebAdministration - - xWebSiteDefaults DefaultPool - { - ApplyTo = 'Machine' - DefaultApplicationPool = $env:PesterDefaultPool - } -} diff --git a/Tests/Unit/MSFT_xWebAppPoolDefaults.Tests.ps1 b/Tests/Unit/MSFT_xWebAppPoolDefaults.Tests.ps1 index c6161a95f..5fe29e006 100644 --- a/Tests/Unit/MSFT_xWebAppPoolDefaults.Tests.ps1 +++ b/Tests/Unit/MSFT_xWebAppPoolDefaults.Tests.ps1 @@ -1,194 +1,2836 @@ -#region HEADER +#requires -Version 4.0 + +# Suppressing this rule because IIS requires PlainText for one of the functions used in this test +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param () $script:DSCModuleName = 'xWebAdministration' $script:DSCResourceName = 'MSFT_xWebAppPoolDefaults' -# Unit Test Template Version: 1.2.1 +#region HEADER $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) { - & git @('clone','https://github.com/PowerShell/DscResource.Tests.git', ` - (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests')) + & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) } -Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path ` - -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force +Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force +Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'Tests\MockWebAdministrationWindowsFeature.psm1') $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $script:DSCModuleName ` -DSCResourceName $script:DSCResourceName ` -TestType Unit - -#endregion HEADER - -function Invoke-TestSetup { -} - -function Invoke-TestCleanup { - Restore-TestEnvironment -TestEnvironment $TestEnvironment -} +#endregion # Begin Testing try { - Invoke-TestSetup + #region Pester Tests InModuleScope $script:DSCResourceName { Describe "$($script:DSCResourceName)\Get-TargetResource" { - Context 'Get application pool defaults' { + Mock Assert-Module + + Context 'Application pool defaults' { + $mockAppPoolDefaults = @{ + autoStart = $true + CLRConfigFile = '' + enable32BitAppOnWin64 = $false + enableConfigurationOverride = $true + managedPipelineMode = 'Integrated' + managedRuntimeLoader = 'webengine4.dll' managedRuntimeVersion = 'v4.0' + passAnonymousToken = $true + startMode = 'OnDemand' + queueLength = 1000 + cpu = @{ + action = 'NoAction' + limit = 0 + resetInterval = '00:05:00' + smpAffinitized = $false + smpProcessorAffinityMask = 4294967295 + smpProcessorAffinityMask2 = 4294967295 + } processModel = @{ identityType = 'SpecificUser' + idleTimeout = '00:20:00' + idleTimeoutAction = 'Terminate' + loadUserProfile = $true + logEventOnProcessModel = 'IdleTimeout' + logonType = 'LogonBatch' + manualGroupMembership = $false + maxProcesses = 1 + password = 'P@$$w0rd' + pingingEnabled = $true + pingInterval = '00:00:30' + pingResponseTime = '00:01:30' + setProfileEnvironment = $false + shutdownTimeLimit = '00:01:30' + startupTimeLimit = '00:01:30' + userName = 'CONTOSO\JDoe' + } + failure = @{ + orphanActionExe = '' + orphanActionParams = '' + orphanWorkerProcess = $false + loadBalancerCapabilities = 'HttpLevel' + rapidFailProtection = $true + rapidFailProtectionInterval = '00:05:00' + rapidFailProtectionMaxCrashes = 5 + autoShutdownExe = '' + autoShutdownParams = '' + } + recycling = @{ + disallowOverlappingRotation = $false + disallowRotationOnConfigChange = $false + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + periodicRestart = @{ + memory = 0 + privateMemory = 0 + requests = 0 + time = '1.05:00:00' + schedule = @{ + Collection = @( + @{value = '04:00:00'} + @{value = '08:00:00'} + ) + } + } } } - Mock Get-WebConfigurationProperty -MockWith { - $path = $Filter.Replace('system.applicationHost/applicationPools/applicationPoolDefaults', '') + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} - if ([System.String]::IsNullOrEmpty($path)) { - return $mockAppPoolDefaults[$Name] - } else { - $path = $path.Replace('/', '') - return $mockAppPoolDefaults[$path][$Name] - } + $result = Get-TargetResource -ApplyTo 'Machine' + + It 'Should return the autoStart property' { + $result.autoStart | Should Be $mockAppPoolDefaults.autoStart } - $result = Get-TargetResource -ApplyTo 'Machine' + It 'Should return the CLRConfigFile property' { + $result.CLRConfigFile | Should Be $mockAppPoolDefaults.CLRConfigFile + } + + It 'Should return the enable32BitAppOnWin64 property' { + $result.enable32BitAppOnWin64 | Should Be $mockAppPoolDefaults.enable32BitAppOnWin64 + } + + It 'Should return the enableConfigurationOverride property' { + $result.enableConfigurationOverride | Should Be $mockAppPoolDefaults.enableConfigurationOverride + } + + It 'Should return the managedPipelineMode property' { + $result.managedPipelineMode | Should Be $mockAppPoolDefaults.managedPipelineMode + } + + It 'Should return the managedRuntimeLoader property' { + $result.managedRuntimeLoader | Should Be $mockAppPoolDefaults.managedRuntimeLoader + } + + It 'Should return the managedRuntimeVersion property' { + $result.managedRuntimeVersion | Should Be $mockAppPoolDefaults.managedRuntimeVersion + } + + It 'Should return the passAnonymousToken property' { + $result.passAnonymousToken | Should Be $mockAppPoolDefaults.passAnonymousToken + } + + It 'Should return the startMode property' { + $result.startMode | Should Be $mockAppPoolDefaults.startMode + } + + It 'Should return the queueLength property' { + $result.queueLength | Should Be $mockAppPoolDefaults.queueLength + } + + It 'Should return the cpuAction property' { + $result.cpuAction | Should Be $mockAppPoolDefaults.cpu.action + } + + It 'Should return the cpuLimit property' { + $result.cpuLimit | Should Be $mockAppPoolDefaults.cpu.limit + } + + It 'Should return the cpuResetInterval property' { + $result.cpuResetInterval | Should Be $mockAppPoolDefaults.cpu.resetInterval + } + + It 'Should return the cpuSmpAffinitized property' { + $result.cpuSmpAffinitized | Should Be $mockAppPoolDefaults.cpu.smpAffinitized + } + + It 'Should return the cpuSmpProcessorAffinityMask property' { + $result.cpuSmpProcessorAffinityMask | Should Be $mockAppPoolDefaults.cpu.smpProcessorAffinityMask + } + + It 'Should return the cpuSmpProcessorAffinityMask2 property' { + $result.cpuSmpProcessorAffinityMask2 | Should Be $mockAppPoolDefaults.cpu.smpProcessorAffinityMask2 + } + + It 'Should return the identityType property' { + $result.identityType | Should Be $mockAppPoolDefaults.processModel.identityType + } + + It 'Should return the Credential (userName) property' { + # Get-DscConfiguration returns MSFT_Credential with empty UserName + $result.Credential.userName | Should Be $mockAppPoolDefaults.processModel.userName + } + + It 'Should return the Credential (password) property' { + # Get-DscConfiguration returns MSFT_Credential with empty Password + $result.Credential.Password | Should Be $mockAppPoolDefaults.processModel.password + } + + It 'Should return the idleTimeout property' { + $result.idleTimeout | Should Be $mockAppPoolDefaults.processModel.idleTimeout + } + + It 'Should return the idleTimeoutAction property' { + $result.idleTimeoutAction | Should Be $mockAppPoolDefaults.processModel.idleTimeoutAction + } + + It 'Should return the loadUserProfile property' { + $result.loadUserProfile | Should Be $mockAppPoolDefaults.processModel.loadUserProfile + } + + It 'Should return the logonType property' { + $result.logonType | Should Be $mockAppPoolDefaults.processModel.logonType + } + + It 'Should return the logEventOnProcessModel property' { + $result.logEventOnProcessModel | Should Be $mockAppPoolDefaults.processModel.logEventOnProcessModel + } + + It 'Should return the manualGroupMembership property' { + $result.manualGroupMembership | Should Be $mockAppPoolDefaults.processModel.manualGroupMembership + } + + It 'Should return the maxProcesses property' { + $result.maxProcesses | Should Be $mockAppPoolDefaults.processModel.maxProcesses + } + + It 'Should return the pingingEnabled property' { + $result.pingingEnabled | Should Be $mockAppPoolDefaults.processModel.pingingEnabled + } + + It 'Should return the pingInterval property' { + $result.pingInterval | Should Be $mockAppPoolDefaults.processModel.pingInterval + } + + It 'Should return the pingResponseTime property' { + $result.pingResponseTime | Should Be $mockAppPoolDefaults.processModel.pingResponseTime + } + + It 'Should return the setProfileEnvironment property' { + $result.setProfileEnvironment | Should Be $mockAppPoolDefaults.processModel.setProfileEnvironment + } + + It 'Should return the shutdownTimeLimit property' { + $result.shutdownTimeLimit | Should Be $mockAppPoolDefaults.processModel.shutdownTimeLimit + } + + It 'Should return the startupTimeLimit property' { + $result.startupTimeLimit | Should Be $mockAppPoolDefaults.processModel.startupTimeLimit + } + + It 'Should return the orphanActionExe property' { + $result.orphanActionExe | Should Be $mockAppPoolDefaults.failure.orphanActionExe + } + + It 'Should return the orphanActionParams property' { + $result.orphanActionParams | Should Be $mockAppPoolDefaults.failure.orphanActionParams + } + + It 'Should return the orphanWorkerProcess property' { + $result.orphanWorkerProcess | Should Be $mockAppPoolDefaults.failure.orphanWorkerProcess + } + + It 'Should return the loadBalancerCapabilities property' { + $result.loadBalancerCapabilities | Should Be $mockAppPoolDefaults.failure.loadBalancerCapabilities + } + + It 'Should return the rapidFailProtection property' { + $result.rapidFailProtection | Should Be $mockAppPoolDefaults.failure.rapidFailProtection + } + + It 'Should return the rapidFailProtectionInterval property' { + $result.rapidFailProtectionInterval | Should Be $mockAppPoolDefaults.failure.rapidFailProtectionInterval + } + + It 'Should return the rapidFailProtectionMaxCrashes property' { + $result.rapidFailProtectionMaxCrashes | Should Be $mockAppPoolDefaults.failure.rapidFailProtectionMaxCrashes + } + + It 'Should return the autoShutdownExe property' { + $result.autoShutdownExe | Should Be $mockAppPoolDefaults.failure.autoShutdownExe + } + + It 'Should return the autoShutdownParams property' { + $result.autoShutdownParams | Should Be $mockAppPoolDefaults.failure.autoShutdownParams + } + + It 'Should return the disallowOverlappingRotation property' { + $result.disallowOverlappingRotation | Should Be $mockAppPoolDefaults.recycling.disallowOverlappingRotation + } + + It 'Should return the disallowRotationOnConfigChange property' { + $result.disallowRotationOnConfigChange | Should Be $mockAppPoolDefaults.recycling.disallowRotationOnConfigChange + } + + It 'Should return the logEventOnRecycle property' { + $result.logEventOnRecycle | Should Be $mockAppPoolDefaults.recycling.logEventOnRecycle + } + + It 'Should return the restartMemoryLimit property' { + $result.restartMemoryLimit | Should Be $mockAppPoolDefaults.recycling.periodicRestart.memory + } + + It 'Should return the restartPrivateMemoryLimit property' { + $result.restartPrivateMemoryLimit | Should Be $mockAppPoolDefaults.recycling.periodicRestart.privateMemory + } + + It 'Should return the restartRequestsLimit property' { + $result.restartRequestsLimit | Should Be $mockAppPoolDefaults.recycling.periodicRestart.requests + } - It 'Should return managedRuntimeVersion' { - $result.managedRuntimeVersion | ` - Should Be $mockAppPoolDefaults.managedRuntimeVersion + It 'Should return the restartTimeLimit property' { + $result.restartTimeLimit | Should Be $mockAppPoolDefaults.recycling.periodicRestart.time } - It 'Should return processModel\identityType' { - $result.identityType | ` - Should Be $mockAppPoolDefaults.processModel.identityType + It 'Should return the restartSchedule property' { + + $restartScheduleValues = [String[]]@( + @($mockAppPoolDefaults.recycling.periodicRestart.schedule.Collection).ForEach('value') + ) + + $compareSplat = @{ + ReferenceObject = [String[]]@($result.restartSchedule) + DifferenceObject = $restartScheduleValues + ExcludeDifferent = $true + IncludeEqual = $true + } + + $compareResult = Compare-Object @compareSplat + + $compareResult.Count -eq $restartScheduleValues.Count | Should Be $true + } + } + } - Describe "$($script:DSCResourceName)\Test-TargetResource" { + Describe "how '$($script:DSCResourceName)\Test-TargetResource' responds" { + + Mock Assert-Module + + Context 'Test target resource with no property specified' { - $mockAppPoolDefaults = @{ - managedRuntimeVersion = 'v4.0' - processModel = @{ - identityType = 'NetworkService' + $mockAppPoolDefaults = @{ } - } - Mock Get-WebConfigurationProperty -MockWith { - $path = $Filter.Replace('system.applicationHost/applicationPools/applicationPoolDefaults', '') + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} - if ([System.String]::IsNullOrEmpty($path)) { - return $mockAppPoolDefaults[$Name] - } else { - $path = $path.Replace('/', '') - return $mockAppPoolDefaults[$path][$Name] + It 'Should return True' { + Test-TargetResource -ApplyTo 'Machine' | + Should Be $true } + } - Context 'Application pool defaults correct' { - $result = Test-TargetResource -ApplyTo 'Machine' ` - -ManagedRuntimeVersion 'v4.0' ` - -IdentityType 'NetworkService' + Context 'All the properties match the desired state' { + + $mockAppPoolDefaults = @{ + autoStart = $true + CLRConfigFile = '' + enable32BitAppOnWin64 = $false + enableConfigurationOverride = $true + managedPipelineMode = 'Integrated' + managedRuntimeLoader = 'webengine4.dll' + managedRuntimeVersion = 'v4.0' + passAnonymousToken = $true + startMode = 'OnDemand' + queueLength = 1000 + cpu = @{ + action = 'NoAction' + limit = 0 + resetInterval = '00:05:00' + smpAffinitized = $false + smpProcessorAffinityMask = 4294967295 + smpProcessorAffinityMask2 = 4294967295 + } + processModel = @{ + identityType = 'SpecificUser' + idleTimeout = '00:20:00' + idleTimeoutAction = 'Terminate' + loadUserProfile = $true + logEventOnProcessModel = 'IdleTimeout' + logonType = 'LogonBatch' + manualGroupMembership = $false + maxProcesses = 1 + password = 'P@$$w0rD' + pingingEnabled = $true + pingInterval = '00:00:30' + pingResponseTime = '00:01:30' + setProfileEnvironment = $false + shutdownTimeLimit = '00:01:30' + startupTimeLimit = '00:01:30' + userName = 'CONTOSO\JDoe' + } + failure = @{ + orphanActionExe = '' + orphanActionParams = '' + orphanWorkerProcess = $false + loadBalancerCapabilities = 'HttpLevel' + rapidFailProtection = $true + rapidFailProtectionInterval = '00:05:00' + rapidFailProtectionMaxCrashes = 5 + autoShutdownExe = '' + autoShutdownParams = '' + } + recycling = @{ + disallowOverlappingRotation = $false + disallowRotationOnConfigChange = $false + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + periodicRestart = @{ + memory = 0 + privateMemory = 0 + requests = 0 + time = '1.05:00:00' + schedule = @{ + Collection = @( + @{value = '04:00:00'} + @{value = '08:00:00'} + ) + } + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $mockUserName = $mockAppPoolDefaults.processModel.userName + $mockPassword = $mockAppPoolDefaults.processModel.password | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName PSCredential -ArgumentList $mockUserName, $mockPassword + + $mockRestartSchedule = [String[]]@( + @($mockAppPoolDefaults.recycling.periodicRestart.schedule.Collection).ForEach('value') + ) + + $testParamsSplat = @{ + autoStart = $mockAppPoolDefaults.autoStart + CLRConfigFile = $mockAppPoolDefaults.CLRConfigFile + enable32BitAppOnWin64 = $mockAppPoolDefaults.enable32BitAppOnWin64 + enableConfigurationOverride = $mockAppPoolDefaults.enableConfigurationOverride + managedPipelineMode = $mockAppPoolDefaults.managedPipelineMode + managedRuntimeLoader = $mockAppPoolDefaults.managedRuntimeLoader + managedRuntimeVersion = $mockAppPoolDefaults.managedRuntimeVersion + passAnonymousToken = $mockAppPoolDefaults.passAnonymousToken + startMode = $mockAppPoolDefaults.startMode + queueLength = $mockAppPoolDefaults.queueLength + cpuAction = $mockAppPoolDefaults.cpu.action + cpuLimit = $mockAppPoolDefaults.cpu.limit + cpuResetInterval = $mockAppPoolDefaults.cpu.resetInterval + cpuSmpAffinitized = $mockAppPoolDefaults.cpu.smpAffinitized + cpuSmpProcessorAffinityMask = $mockAppPoolDefaults.cpu.smpProcessorAffinityMask + cpuSmpProcessorAffinityMask2 = $mockAppPoolDefaults.cpu.smpProcessorAffinityMask2 + identityType = $mockAppPoolDefaults.processModel.identityType + Credential = $mockCredential + idleTimeout = $mockAppPoolDefaults.processModel.idleTimeout + idleTimeoutAction = $mockAppPoolDefaults.processModel.idleTimeoutAction + loadUserProfile = $mockAppPoolDefaults.processModel.loadUserProfile + logEventOnProcessModel = $mockAppPoolDefaults.processModel.logEventOnProcessModel + logonType = $mockAppPoolDefaults.processModel.logonType + manualGroupMembership = $mockAppPoolDefaults.processModel.manualGroupMembership + maxProcesses = $mockAppPoolDefaults.processModel.maxProcesses + pingingEnabled = $mockAppPoolDefaults.processModel.pingingEnabled + pingInterval = $mockAppPoolDefaults.processModel.pingInterval + pingResponseTime = $mockAppPoolDefaults.processModel.pingResponseTime + setProfileEnvironment = $mockAppPoolDefaults.processModel.setProfileEnvironment + shutdownTimeLimit = $mockAppPoolDefaults.processModel.shutdownTimeLimit + startupTimeLimit = $mockAppPoolDefaults.processModel.startupTimeLimit + orphanActionExe = $mockAppPoolDefaults.failure.orphanActionExe + orphanActionParams = $mockAppPoolDefaults.failure.orphanActionParams + orphanWorkerProcess = $mockAppPoolDefaults.failure.orphanWorkerProcess + loadBalancerCapabilities = $mockAppPoolDefaults.failure.loadBalancerCapabilities + rapidFailProtection = $mockAppPoolDefaults.failure.rapidFailProtection + rapidFailProtectionInterval = $mockAppPoolDefaults.failure.rapidFailProtectionInterval + rapidFailProtectionMaxCrashes = $mockAppPoolDefaults.failure.rapidFailProtectionMaxCrashes + autoShutdownExe = $mockAppPoolDefaults.failure.autoShutdownExe + autoShutdownParams = $mockAppPoolDefaults.failure.autoShutdownParams + disallowOverlappingRotation = $mockAppPoolDefaults.recycling.disallowOverlappingRotation + disallowRotationOnConfigChange = $mockAppPoolDefaults.recycling.disallowRotationOnConfigChange + logEventOnRecycle = $mockAppPoolDefaults.recycling.logEventOnRecycle + restartMemoryLimit = $mockAppPoolDefaults.recycling.periodicRestart.memory + restartPrivateMemoryLimit = $mockAppPoolDefaults.recycling.periodicRestart.privateMemory + restartRequestsLimit = $mockAppPoolDefaults.recycling.periodicRestart.requests + restartTimeLimit = $mockAppPoolDefaults.recycling.periodicRestart.time + restartSchedule = $mockRestartSchedule + } It 'Should return True' { - $result | Should Be $true + Test-TargetResource -ApplyTo Machine @testParamsSplat | + Should Be $true } + } - Context 'Application pool different managedRuntimeVersion' { - $result = Test-TargetResource -ApplyTo 'Machine' ` - -ManagedRuntimeVersion 'v2.0' ` - -IdentityType 'NetworkService' + Context 'Test the autoStart property' { + + $mockAppPoolDefaults = @{ + autoStart = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -autoStart $true | + Should Be $true + } - It 'Should return False' { - $result | Should Be $false + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -autoStart $false | + Should Be $false } + } - Context 'Application pool different processModel/@identityType' { - $result = Test-TargetResource -ApplyTo 'Machine' ` - -ManagedRuntimeVersion 'v4.0' ` - -IdentityType 'LocalSystem' + Context 'Test the CLRConfigFile property' { + + $mockAppPoolDefaults = @{ + CLRConfigFile = '' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -CLRConfigFile '' | + Should Be $true + } - It 'Should return False' { - $result | Should Be $false + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -CLRConfigFile 'C:\inetpub\temp\aspnet.config' | + Should Be $false } + } - Context 'Application pool no value for managedRuntimeVersion' { - $result = Test-TargetResource -ApplyTo 'Machine' ` - -IdentityType 'NetworkService' + Context 'Test the enable32BitAppOnWin64 property' { - It 'Should return True' { - $result | Should Be $true + $mockAppPoolDefaults = @{ + enable32BitAppOnWin64 = $false + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -enable32BitAppOnWin64 $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -enable32BitAppOnWin64 $true | + Should Be $false + } + + } + + Context 'Test the enableConfigurationOverride property' { + + $mockAppPoolDefaults = @{ + enableConfigurationOverride = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -enableConfigurationOverride $true | + Should Be $true } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -enableConfigurationOverride $false | + Should Be $false + } + } - } - Describe "$($script:DSCResourceName)\Set-TargetResource" { + Context 'Test the managedPipelineMode property' { + + $mockAppPoolDefaults = @{ + managedPipelineMode = 'Integrated' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -managedPipelineMode 'Integrated' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -managedPipelineMode 'Classic' | + Should Be $false + } + + } + + Context 'Test the managedRuntimeLoader property' { + + $mockAppPoolDefaults = @{ + managedRuntimeLoader = 'webengine4.dll' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -managedRuntimeLoader 'webengine4.dll' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -managedRuntimeLoader '' | + Should Be $false + } + + } + + Context 'Test the managedRuntimeVersion property' { + + $mockAppPoolDefaults = @{ + managedRuntimeVersion = 'v4.0' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -managedRuntimeVersion 'v4.0' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -managedRuntimeVersion 'v2.0' | + Should Be $false + } + + } + + Context 'Test the passAnonymousToken property' { + + $mockAppPoolDefaults = @{ + passAnonymousToken = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -passAnonymousToken $true | + Should Be $true + } - $mockAppPoolDefaults = @{ - managedRuntimeVersion = 'v4.0' - processModel = @{ - identityType = 'NetworkService' + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -passAnonymousToken $false | + Should Be $false } + } - Mock Get-WebConfigurationProperty -MockWith { - $path = $Filter.Replace('system.applicationHost/applicationPools/applicationPoolDefaults', '') + Context 'Test the startMode property' { + + $mockAppPoolDefaults = @{ + startMode = 'OnDemand' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -startMode 'OnDemand' | + Should Be $true + } - if ([System.String]::IsNullOrEmpty($path)) { - return $mockAppPoolDefaults[$Name] - } else { - $path = $path.Replace('/', '') - return $mockAppPoolDefaults[$path][$Name] + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -startMode 'AlwaysRunning' | + Should Be $false } + } - Mock Set-WebConfigurationProperty -MockWith { } + Context 'Test the queueLength property' { + + $mockAppPoolDefaults = @{ + queueLength = 1000 + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} - Context 'Application pool defaults correct' { - Set-TargetResource -ApplyTo 'Machine' ` - -ManagedRuntimeVersion 'v4.0' ` - -IdentityType 'NetworkService' + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -queueLength 1000 | + Should Be $true + } - It 'Should not call Set-WebConfigurationProperty' { - Assert-MockCalled Set-WebConfigurationProperty -Exactly 0 + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -queueLength 2000 | + Should Be $false } + } - Context 'Application pool different managedRuntimeVersion' { - Set-TargetResource -ApplyTo 'Machine' ` - -ManagedRuntimeVersion 'v2.0' ` - -IdentityType 'NetworkService' + Context 'Test the cpuAction property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + action = 'NoAction' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuAction 'NoAction' | + Should Be $true + } - It 'Should call Set-WebConfigurationProperty once' { - Assert-MockCalled Set-WebConfigurationProperty -Exactly 1 ` - -ParameterFilter { $Name -eq 'managedRuntimeVersion' } + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuAction 'KillW3wp' | + Should Be $false } + } - Context 'Application pool different processModel/@identityType' { - Set-TargetResource -ApplyTo 'Machine' ` - -ManagedRuntimeVersion 'v4.0' ` - -IdentityType 'LocalSystem' + Context 'Test the cpuLimit property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + limit = 0 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuLimit 0 | + Should Be $true + } - It 'Should call Set-WebConfigurationProperty once' { - Assert-MockCalled Set-WebConfigurationProperty -Exactly 1 ` - -ParameterFilter { $Name -eq 'identityType' } + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuLimit 90000 | + Should Be $false } + } - } - } -} -finally -{ - Invoke-TestCleanup + + Context 'Test the cpuResetInterval property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + resetInterval = '00:05:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuResetInterval '00:05:00' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuResetInterval '00:10:00' | + Should Be $false + } + + } + + Context 'Test the cpuSmpAffinitized property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpAffinitized = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpAffinitized $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpAffinitized $true | + Should Be $false + } + + } + + Context 'Test the cpuSmpProcessorAffinityMask property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpProcessorAffinityMask = 4294967295 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpProcessorAffinityMask 4294967295 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpProcessorAffinityMask 1 | + Should Be $false + } + + } + + Context 'Test the cpuSmpProcessorAffinityMask2 property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpProcessorAffinityMask2 = 4294967295 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpProcessorAffinityMask2 4294967295 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpProcessorAffinityMask2 1 | + Should Be $false + } + + } + + Context 'Test the identityType property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + identityType = 'ApplicationPoolIdentity' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -identityType 'ApplicationPoolIdentity' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -identityType 'NetworkService' | + Should Be $false + } + + } + + Context 'Test the Credential property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + identityType = 'SpecificUser' + password = '1q2w3e4r' + userName = 'CONTOSO\JDoe' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when both the userName and the password properties match the desired state' { + + $mockUserName = $mockAppPoolDefaults.processModel.userName + $mockPassword = $mockAppPoolDefaults.processModel.password | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName PSCredential -ArgumentList $mockUserName, $mockPassword + + Test-TargetResource -ApplyTo Machine -identityType 'SpecificUser' -Credential $mockCredential | + Should Be $true + + } + + It 'Should return False when the userName property does not match the desired state' { + + $mockUserName = 'CONTOSO\GFawkes' + $mockPassword = $mockAppPoolDefaults.processModel.password | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName PSCredential -ArgumentList $mockUserName, $mockPassword + + Test-TargetResource -ApplyTo Machine -identityType 'SpecificUser' -Credential $mockCredential | + Should Be $false + + } + + It 'Should return False when the password property does not match the desired state' { + + $mockUserName = $mockAppPoolDefaults.processModel.userName + $mockPassword = '5t6y7u8i' | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName PSCredential -ArgumentList $mockUserName, $mockPassword + + Test-TargetResource -ApplyTo Machine -identityType 'SpecificUser' -Credential $mockCredential | + Should Be $false + + } + + } + + Context 'Test the idleTimeout property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + idleTimeout = '00:20:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -idleTimeout '00:20:00' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -idleTimeout '00:15:00' | + Should Be $false + } + + } + + Context 'Test the idleTimeoutAction property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + idleTimeoutAction = 'Terminate' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -idleTimeoutAction 'Terminate' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -idleTimeoutAction 'Suspend' | + Should Be $false + } + + } + + Context 'Test the loadUserProfile property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + loadUserProfile = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -loadUserProfile $true | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -loadUserProfile $false | + Should Be $false + } + + } + + Context 'Test the logEventOnProcessModel property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + logEventOnProcessModel = 'IdleTimeout' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -logEventOnProcessModel 'IdleTimeout' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -logEventOnProcessModel '' | + Should Be $false + } + + } + + Context 'Test the logonType property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + logonType = 'LogonBatch' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -logonType 'LogonBatch' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -logonType 'LogonService' | + Should Be $false + } + + } + + Context 'Test the manualGroupMembership property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + manualGroupMembership = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -manualGroupMembership $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -manualGroupMembership $true | + Should Be $false + } + + } + + Context 'Test the maxProcesses property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + maxProcesses = 1 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -maxProcesses 1 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -maxProcesses 2 | + Should Be $false + } + + } + + Context 'Test the pingingEnabled property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingingEnabled = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -pingingEnabled $true | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -pingingEnabled $false | + Should Be $false + } + + } + + Context 'Test the pingInterval property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingInterval = '00:00:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -pingInterval '00:00:30' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -pingInterval '00:01:00' | + Should Be $false + } + + } + + Context 'Test the pingResponseTime property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingResponseTime = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -pingResponseTime '00:01:30' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -pingResponseTime '00:02:00' | + Should Be $false + } + + } + + Context 'Test the setProfileEnvironment property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + setProfileEnvironment = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -setProfileEnvironment $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -setProfileEnvironment $true | + Should Be $false + } + + } + + Context 'Test the shutdownTimeLimit property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + shutdownTimeLimit = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -shutdownTimeLimit '00:01:30' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -shutdownTimeLimit '00:02:00' | + Should Be $false + } + + } + + Context 'Test the startupTimeLimit property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + startupTimeLimit = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -startupTimeLimit '00:01:30' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -startupTimeLimit '00:02:00' | + Should Be $false + } + + } + + Context 'Test the orphanActionExe property' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanActionExe = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -orphanActionExe '' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -orphanActionExe 'C:\inetpub\temp\orphanAction.exe' | + Should Be $false + } + + } + + Context 'Test the orphanActionParams property' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanActionParams = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -orphanActionParams '' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -orphanActionParams '/orphanActionParam1' | + Should Be $false + } + + } + + Context 'Test the orphanWorkerProcess property' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanWorkerProcess = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -orphanWorkerProcess $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -orphanWorkerProcess $true | + Should Be $false + } + + } + + Context 'Test the loadBalancerCapabilities property' { + + $mockAppPoolDefaults = @{ + failure = @{ + loadBalancerCapabilities = 'HttpLevel' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -loadBalancerCapabilities 'HttpLevel' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -loadBalancerCapabilities 'TcpLevel' | + Should Be $false + } + + } + + Context 'Test the rapidFailProtection property' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtection = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtection $true | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtection $false | + Should Be $false + } + + } + + Context 'Test the rapidFailProtectionInterval property' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtectionInterval = '00:05:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtectionInterval '00:05:00' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtectionInterval '00:10:00' | + Should Be $false + } + + } + + Context 'Test the rapidFailProtectionMaxCrashes property' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtectionMaxCrashes = 5 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtectionMaxCrashes 5 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtectionMaxCrashes 10 | + Should Be $false + } + + } + + Context 'Test the autoShutdownExe property' { + + $mockAppPoolDefaults = @{ + failure = @{ + autoShutdownExe = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -autoShutdownExe '' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -autoShutdownExe 'C:\inetpub\temp\autoShutdown.exe' | + Should Be $false + } + + } + + Context 'Test the autoShutdownParams property' { + + $mockAppPoolDefaults = @{ + failure = @{ + autoShutdownParams = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -autoShutdownParams '' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -autoShutdownParams '/autoShutdownParam1' | + Should Be $false + } + + } + + Context 'Test the disallowOverlappingRotation property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + disallowOverlappingRotation = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -disallowOverlappingRotation $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -disallowOverlappingRotation $true | + Should Be $false + } + + } + + Context 'Test the disallowRotationOnConfigChange property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + disallowRotationOnConfigChange = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -disallowRotationOnConfigChange $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -disallowRotationOnConfigChange $true | + Should Be $false + } + + } + + Context 'Test the logEventOnRecycle property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -logEventOnRecycle 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -logEventOnRecycle 'Time,Memory,PrivateMemory' | + Should Be $false + } + + } + + Context 'Test the restartMemoryLimit property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + memory = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -restartMemoryLimit 0 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -restartMemoryLimit 1048576 | + Should Be $false + } + + } + + Context 'Test the restartPrivateMemoryLimit property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + privateMemory = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -restartPrivateMemoryLimit 0 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -restartPrivateMemoryLimit 1048576 | + Should Be $false + } + + } + + Context 'Test the restartRequestsLimit property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + requests = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -restartRequestsLimit 0 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -restartRequestsLimit 1000 | + Should Be $false + } + + } + + Context 'Test the restartTimeLimit property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + time = '1.05:00:00' + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -restartTimeLimit '1.05:00:00' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -restartTimeLimit '2.10:00:00' | + Should Be $false + } + + } + + Context 'Test the restartSchedule property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + schedule = @{ + Collection = @( + @{value = '04:00:00'} + @{value = '08:00:00'} + ) + } + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -restartSchedule @('04:00:00', '08:00:00') | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -restartSchedule @('') | + Should Be $false + } + + } + + } + + Describe "how '$($script:DSCResourceName)\Set-TargetResource' responds" { + + Mock -CommandName Assert-Module -MockWith {} + + Context 'All the properties need to be set' { + + $mockAppPoolDefaults = @{ + autoStart = $true + CLRConfigFile = '' + enable32BitAppOnWin64 = $false + enableConfigurationOverride = $true + managedPipelineMode = 'Integrated' + managedRuntimeLoader = 'webengine4.dll' + managedRuntimeVersion = 'v4.0' + passAnonymousToken = $true + startMode = 'OnDemand' + queueLength = 1000 + cpu = @{ + action = 'NoAction' + limit = 0 + resetInterval = '00:05:00' + smpAffinitized = $false + smpProcessorAffinityMask = 4294967295 + smpProcessorAffinityMask2 = 4294967295 + } + processModel = @{ + identityType = 'ApplicationPoolIdentity' + idleTimeout = '00:20:00' + idleTimeoutAction = 'Terminate' + loadUserProfile = $true + logEventOnProcessModel = 'IdleTimeout' + logonType = 'LogonBatch' + manualGroupMembership = $false + maxProcesses = 1 + password = '' + pingingEnabled = $true + pingInterval = '00:00:30' + pingResponseTime = '00:01:30' + setProfileEnvironment = $false + shutdownTimeLimit = '00:01:30' + startupTimeLimit = '00:01:30' + userName = '' + } + failure = @{ + orphanActionExe = '' + orphanActionParams = '' + orphanWorkerProcess = $false + loadBalancerCapabilities = 'HttpLevel' + rapidFailProtection = $true + rapidFailProtectionInterval = '00:05:00' + rapidFailProtectionMaxCrashes = 5 + autoShutdownExe = '' + autoShutdownParams = '' + } + recycling = @{ + disallowOverlappingRotation = $false + disallowRotationOnConfigChange = $false + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + periodicRestart = @{ + memory = 0 + privateMemory = 0 + requests = 0 + time = '1.05:00:00' + schedule = @{ + Collection = @( + @{value = '02:00:00'} + @{value = '04:00:00'} + ) + } + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $mockUserName = 'CONTOSO\GFawkes' + $mockPassword = '5t6y7u8i' | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName PSCredential -ArgumentList $mockUserName, $mockPassword + + $setParamsSplat = @{ + autoStart = $false + CLRConfigFile = 'C:\inetpub\temp\aspnet.config' + enable32BitAppOnWin64 = $true + enableConfigurationOverride = $false + managedPipelineMode = 'Classic' + managedRuntimeLoader = '' + managedRuntimeVersion = 'v2.0' + passAnonymousToken = $false + startMode = 'AlwaysRunning' + queueLength = 2000 + cpuAction = 'KillW3wp' + cpuLimit = 90000 + cpuResetInterval = '00:10:00' + cpuSmpAffinitized = $true + cpuSmpProcessorAffinityMask = 1 + cpuSmpProcessorAffinityMask2 = 1 + identityType = 'SpecificUser' + Credential = $mockCredential + idleTimeout = '00:15:00' + idleTimeoutAction = 'Suspend' + loadUserProfile = $false + logEventOnProcessModel = '' + logonType = 'LogonService' + manualGroupMembership = $true + maxProcesses = 2 + pingingEnabled = $false + pingInterval = '00:01:00' + pingResponseTime = '00:02:00' + setProfileEnvironment = $true + shutdownTimeLimit = '00:02:00' + startupTimeLimit = '00:02:00' + orphanActionExe = 'C:\inetpub\temp\orphanAction.exe' + orphanActionParams = '/orphanActionParam1' + orphanWorkerProcess = $true + loadBalancerCapabilities = 'TcpLevel' + rapidFailProtection = $false + rapidFailProtectionInterval = '00:10:00' + rapidFailProtectionMaxCrashes = 10 + autoShutdownExe = 'C:\inetpub\temp\autoShutdown.exe' + autoShutdownParams = '/autoShutdownParam1' + disallowOverlappingRotation = $true + disallowRotationOnConfigChange = $true + logEventOnRecycle = 'Time,Memory,PrivateMemory' + restartMemoryLimit = 1048576 + restartPrivateMemoryLimit = 1048576 + restartRequestsLimit = 1000 + restartTimeLimit = '2.10:00:00' + restartSchedule = @('06:00:00', '08:00:00') + } + + Mock Invoke-AppCmd + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call all the mocks' { + Assert-MockCalled Invoke-AppCmd -Exactly 52 + } + + } + + Context 'The autoStart property needs to be set' { + + $mockAppPoolDefaults = @{ + autoStart = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + autoStart = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.autoStart:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The CLRConfigFile property needs to be set' { + + $mockAppPoolDefaults = @{ + CLRConfigFile = '' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + CLRConfigFile = 'C:\inetpub\temp\aspnet.config' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.CLRConfigFile:C:\inetpub\temp\aspnet.config'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The enable32BitAppOnWin64 property needs to be set' { + + $mockAppPoolDefaults = @{ + enable32BitAppOnWin64 = $false + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + enable32BitAppOnWin64 = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.enable32BitAppOnWin64:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The enableConfigurationOverride property needs to be set' { + + $mockAppPoolDefaults = @{ + enableConfigurationOverride = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + enableConfigurationOverride = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.enableConfigurationOverride:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The managedPipelineMode property needs to be set' { + + $mockAppPoolDefaults = @{ + managedPipelineMode = 'Integrated' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + managedPipelineMode = 'Classic' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.managedPipelineMode:Classic'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The managedRuntimeLoader property needs to be set' { + + $mockAppPoolDefaults = @{ + managedRuntimeLoader = 'webengine4.dll' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + managedRuntimeLoader = '' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.managedRuntimeLoader:'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The managedRuntimeVersion property needs to be set' { + + $mockAppPoolDefaults = @{ + managedRuntimeVersion = 'v4.0' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + managedRuntimeVersion = 'v2.0' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.managedRuntimeVersion:v2.0'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The passAnonymousToken property needs to be set' { + + $mockAppPoolDefaults = @{ + passAnonymousToken = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + passAnonymousToken = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.passAnonymousToken:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The startMode property needs to be set' { + + $mockAppPoolDefaults = @{ + startMode = 'OnDemand' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + startMode = 'AlwaysRunning' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.startMode:AlwaysRunning'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The queueLength property needs to be set' { + + $mockAppPoolDefaults = @{ + queueLength = 1000 + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + queueLength = 2000 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.queueLength:2000'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuAction property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + action = 'NoAction' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuAction = 'KillW3wp' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.action:KillW3wp'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + limit = 0 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuLimit = 90000 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.limit:90000'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuResetInterval property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + resetInterval = '00:05:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuResetInterval = '00:10:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.resetInterval:00:10:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuSmpAffinitized property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpAffinitized = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuSmpAffinitized = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.smpAffinitized:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuSmpProcessorAffinityMask property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpProcessorAffinityMask = 4294967295 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuSmpProcessorAffinityMask = 1 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.smpProcessorAffinityMask:1'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuSmpProcessorAffinityMask2 property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpProcessorAffinityMask2 = 4294967295 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuSmpProcessorAffinityMask2 = 1 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.smpProcessorAffinityMask2:1'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The identityType property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + identityType = 'ApplicationPoolIdentity' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + identityType = 'SpecificUser' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.identityType:SpecificUser'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The idleTimeout property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + idleTimeout = '00:20:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + idleTimeout = '00:15:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.idleTimeout:00:15:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The idleTimeoutAction property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + idleTimeoutAction = 'Terminate' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + idleTimeoutAction = 'Suspend' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.idleTimeoutAction:Suspend'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The loadUserProfile property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + loadUserProfile = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + loadUserProfile = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.loadUserProfile:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The logEventOnProcessModel property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + logEventOnProcessModel = 'IdleTimeout' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + logEventOnProcessModel = '' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.logEventOnProcessModel:'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The logonType property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + logonType = 'LogonBatch' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + logonType = 'LogonService' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.logonType:LogonService'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The manualGroupMembership property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + manualGroupMembership = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + manualGroupMembership = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.manualGroupMembership:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The maxProcesses property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + maxProcesses = 1 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + maxProcesses = 2 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.maxProcesses:2'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The pingingEnabled property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingingEnabled = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + pingingEnabled = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.pingingEnabled:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The pingInterval property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingInterval = '00:00:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + pingInterval = '00:01:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.pingInterval:00:01:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The pingResponseTime property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingResponseTime = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + pingResponseTime = '00:02:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.pingResponseTime:00:02:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The setProfileEnvironment property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + setProfileEnvironment = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + setProfileEnvironment = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.setProfileEnvironment:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The shutdownTimeLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + shutdownTimeLimit = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + shutdownTimeLimit = '00:02:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.shutdownTimeLimit:00:02:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The startupTimeLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + startupTimeLimit = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + startupTimeLimit = '00:02:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.startupTimeLimit:00:02:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The orphanActionExe property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanActionExe = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + orphanActionExe = 'C:\inetpub\temp\orphanAction.exe' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.orphanActionExe:C:\inetpub\temp\orphanAction.exe'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The orphanActionParams property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanActionParams = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + orphanActionParams = '/orphanActionParam1' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.orphanActionParams:/orphanActionParam1'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The orphanWorkerProcess property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanWorkerProcess = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + orphanWorkerProcess = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.orphanWorkerProcess:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The loadBalancerCapabilities property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + loadBalancerCapabilities = 'HttpLevel' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + loadBalancerCapabilities = 'TcpLevel' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.loadBalancerCapabilities:TcpLevel'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The rapidFailProtection property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtection = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + rapidFailProtection = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.rapidFailProtection:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The rapidFailProtectionInterval property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtectionInterval = '00:05:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + rapidFailProtectionInterval = '00:10:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.rapidFailProtectionInterval:00:10:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The rapidFailProtectionMaxCrashes property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtectionMaxCrashes = 5 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + rapidFailProtectionMaxCrashes = 10 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.rapidFailProtectionMaxCrashes:10'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The autoShutdownExe property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + autoShutdownExe = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + autoShutdownExe = 'C:\inetpub\temp\autoShutdown.exe' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.autoShutdownExe:C:\inetpub\temp\autoShutdown.exe'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The autoShutdownParams property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + autoShutdownParams = '' + } + + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + autoShutdownParams = '/autoShutdownParam1' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.autoShutdownParams:/autoShutdownParam1'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The disallowOverlappingRotation property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + disallowOverlappingRotation = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + disallowOverlappingRotation = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.disallowOverlappingRotation:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The disallowRotationOnConfigChange property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + disallowRotationOnConfigChange = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + disallowRotationOnConfigChange = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.disallowRotationOnConfigChange:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The logEventOnRecycle property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + logEventOnRecycle = 'Time,Memory,PrivateMemory' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.logEventOnRecycle:Time,Memory,PrivateMemory'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The restartMemoryLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + memory = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + restartMemoryLimit = 1048576 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.periodicRestart.memory:1048576'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The restartPrivateMemoryLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + privateMemory = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + restartPrivateMemoryLimit = 1048576 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.periodicRestart.privateMemory:1048576'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The restartRequestsLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + requests = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + restartRequestsLimit = 1000 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.periodicRestart.requests:1000'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The restartTimeLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + time = '1.05:00:00' + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + restartTimeLimit = '2.10:00:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.periodicRestart.time:2.10:00:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The restartSchedule property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + schedule = @{ + Collection = @( + @{value = '04:00:00'} + ) + } + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + restartSchedule = @('08:00:00') + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq "/-applicationPoolDefaults.recycling.periodicRestart.schedule.[value='04:00:00']"} + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq "/+applicationPoolDefaults.recycling.periodicRestart.schedule.[value='08:00:00']"} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq "/-applicationPoolDefaults.recycling.periodicRestart.schedule.[value='04:00:00']"} -Exactly 1 + Assert-MockCalled Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq "/+applicationPoolDefaults.recycling.periodicRestart.schedule.[value='08:00:00']"} -Exactly 1 + } + + } + + } + + } + + #endregion +} +finally +{ + Restore-TestEnvironment -TestEnvironment $TestEnvironment } diff --git a/Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 b/Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 new file mode 100644 index 000000000..5fe29e006 --- /dev/null +++ b/Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 @@ -0,0 +1,2836 @@ +#requires -Version 4.0 + +# Suppressing this rule because IIS requires PlainText for one of the functions used in this test +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param () + +$script:DSCModuleName = 'xWebAdministration' +$script:DSCResourceName = 'MSFT_xWebAppPoolDefaults' + +#region HEADER +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) +} + +Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force + +Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'Tests\MockWebAdministrationWindowsFeature.psm1') + +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:DSCModuleName ` + -DSCResourceName $script:DSCResourceName ` + -TestType Unit +#endregion + +# Begin Testing +try +{ + #region Pester Tests + + InModuleScope $script:DSCResourceName { + + Describe "$($script:DSCResourceName)\Get-TargetResource" { + + Mock Assert-Module + + Context 'Application pool defaults' { + + $mockAppPoolDefaults = @{ + autoStart = $true + CLRConfigFile = '' + enable32BitAppOnWin64 = $false + enableConfigurationOverride = $true + managedPipelineMode = 'Integrated' + managedRuntimeLoader = 'webengine4.dll' + managedRuntimeVersion = 'v4.0' + passAnonymousToken = $true + startMode = 'OnDemand' + queueLength = 1000 + cpu = @{ + action = 'NoAction' + limit = 0 + resetInterval = '00:05:00' + smpAffinitized = $false + smpProcessorAffinityMask = 4294967295 + smpProcessorAffinityMask2 = 4294967295 + } + processModel = @{ + identityType = 'SpecificUser' + idleTimeout = '00:20:00' + idleTimeoutAction = 'Terminate' + loadUserProfile = $true + logEventOnProcessModel = 'IdleTimeout' + logonType = 'LogonBatch' + manualGroupMembership = $false + maxProcesses = 1 + password = 'P@$$w0rd' + pingingEnabled = $true + pingInterval = '00:00:30' + pingResponseTime = '00:01:30' + setProfileEnvironment = $false + shutdownTimeLimit = '00:01:30' + startupTimeLimit = '00:01:30' + userName = 'CONTOSO\JDoe' + } + failure = @{ + orphanActionExe = '' + orphanActionParams = '' + orphanWorkerProcess = $false + loadBalancerCapabilities = 'HttpLevel' + rapidFailProtection = $true + rapidFailProtectionInterval = '00:05:00' + rapidFailProtectionMaxCrashes = 5 + autoShutdownExe = '' + autoShutdownParams = '' + } + recycling = @{ + disallowOverlappingRotation = $false + disallowRotationOnConfigChange = $false + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + periodicRestart = @{ + memory = 0 + privateMemory = 0 + requests = 0 + time = '1.05:00:00' + schedule = @{ + Collection = @( + @{value = '04:00:00'} + @{value = '08:00:00'} + ) + } + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $result = Get-TargetResource -ApplyTo 'Machine' + + It 'Should return the autoStart property' { + $result.autoStart | Should Be $mockAppPoolDefaults.autoStart + } + + It 'Should return the CLRConfigFile property' { + $result.CLRConfigFile | Should Be $mockAppPoolDefaults.CLRConfigFile + } + + It 'Should return the enable32BitAppOnWin64 property' { + $result.enable32BitAppOnWin64 | Should Be $mockAppPoolDefaults.enable32BitAppOnWin64 + } + + It 'Should return the enableConfigurationOverride property' { + $result.enableConfigurationOverride | Should Be $mockAppPoolDefaults.enableConfigurationOverride + } + + It 'Should return the managedPipelineMode property' { + $result.managedPipelineMode | Should Be $mockAppPoolDefaults.managedPipelineMode + } + + It 'Should return the managedRuntimeLoader property' { + $result.managedRuntimeLoader | Should Be $mockAppPoolDefaults.managedRuntimeLoader + } + + It 'Should return the managedRuntimeVersion property' { + $result.managedRuntimeVersion | Should Be $mockAppPoolDefaults.managedRuntimeVersion + } + + It 'Should return the passAnonymousToken property' { + $result.passAnonymousToken | Should Be $mockAppPoolDefaults.passAnonymousToken + } + + It 'Should return the startMode property' { + $result.startMode | Should Be $mockAppPoolDefaults.startMode + } + + It 'Should return the queueLength property' { + $result.queueLength | Should Be $mockAppPoolDefaults.queueLength + } + + It 'Should return the cpuAction property' { + $result.cpuAction | Should Be $mockAppPoolDefaults.cpu.action + } + + It 'Should return the cpuLimit property' { + $result.cpuLimit | Should Be $mockAppPoolDefaults.cpu.limit + } + + It 'Should return the cpuResetInterval property' { + $result.cpuResetInterval | Should Be $mockAppPoolDefaults.cpu.resetInterval + } + + It 'Should return the cpuSmpAffinitized property' { + $result.cpuSmpAffinitized | Should Be $mockAppPoolDefaults.cpu.smpAffinitized + } + + It 'Should return the cpuSmpProcessorAffinityMask property' { + $result.cpuSmpProcessorAffinityMask | Should Be $mockAppPoolDefaults.cpu.smpProcessorAffinityMask + } + + It 'Should return the cpuSmpProcessorAffinityMask2 property' { + $result.cpuSmpProcessorAffinityMask2 | Should Be $mockAppPoolDefaults.cpu.smpProcessorAffinityMask2 + } + + It 'Should return the identityType property' { + $result.identityType | Should Be $mockAppPoolDefaults.processModel.identityType + } + + It 'Should return the Credential (userName) property' { + # Get-DscConfiguration returns MSFT_Credential with empty UserName + $result.Credential.userName | Should Be $mockAppPoolDefaults.processModel.userName + } + + It 'Should return the Credential (password) property' { + # Get-DscConfiguration returns MSFT_Credential with empty Password + $result.Credential.Password | Should Be $mockAppPoolDefaults.processModel.password + } + + It 'Should return the idleTimeout property' { + $result.idleTimeout | Should Be $mockAppPoolDefaults.processModel.idleTimeout + } + + It 'Should return the idleTimeoutAction property' { + $result.idleTimeoutAction | Should Be $mockAppPoolDefaults.processModel.idleTimeoutAction + } + + It 'Should return the loadUserProfile property' { + $result.loadUserProfile | Should Be $mockAppPoolDefaults.processModel.loadUserProfile + } + + It 'Should return the logonType property' { + $result.logonType | Should Be $mockAppPoolDefaults.processModel.logonType + } + + It 'Should return the logEventOnProcessModel property' { + $result.logEventOnProcessModel | Should Be $mockAppPoolDefaults.processModel.logEventOnProcessModel + } + + It 'Should return the manualGroupMembership property' { + $result.manualGroupMembership | Should Be $mockAppPoolDefaults.processModel.manualGroupMembership + } + + It 'Should return the maxProcesses property' { + $result.maxProcesses | Should Be $mockAppPoolDefaults.processModel.maxProcesses + } + + It 'Should return the pingingEnabled property' { + $result.pingingEnabled | Should Be $mockAppPoolDefaults.processModel.pingingEnabled + } + + It 'Should return the pingInterval property' { + $result.pingInterval | Should Be $mockAppPoolDefaults.processModel.pingInterval + } + + It 'Should return the pingResponseTime property' { + $result.pingResponseTime | Should Be $mockAppPoolDefaults.processModel.pingResponseTime + } + + It 'Should return the setProfileEnvironment property' { + $result.setProfileEnvironment | Should Be $mockAppPoolDefaults.processModel.setProfileEnvironment + } + + It 'Should return the shutdownTimeLimit property' { + $result.shutdownTimeLimit | Should Be $mockAppPoolDefaults.processModel.shutdownTimeLimit + } + + It 'Should return the startupTimeLimit property' { + $result.startupTimeLimit | Should Be $mockAppPoolDefaults.processModel.startupTimeLimit + } + + It 'Should return the orphanActionExe property' { + $result.orphanActionExe | Should Be $mockAppPoolDefaults.failure.orphanActionExe + } + + It 'Should return the orphanActionParams property' { + $result.orphanActionParams | Should Be $mockAppPoolDefaults.failure.orphanActionParams + } + + It 'Should return the orphanWorkerProcess property' { + $result.orphanWorkerProcess | Should Be $mockAppPoolDefaults.failure.orphanWorkerProcess + } + + It 'Should return the loadBalancerCapabilities property' { + $result.loadBalancerCapabilities | Should Be $mockAppPoolDefaults.failure.loadBalancerCapabilities + } + + It 'Should return the rapidFailProtection property' { + $result.rapidFailProtection | Should Be $mockAppPoolDefaults.failure.rapidFailProtection + } + + It 'Should return the rapidFailProtectionInterval property' { + $result.rapidFailProtectionInterval | Should Be $mockAppPoolDefaults.failure.rapidFailProtectionInterval + } + + It 'Should return the rapidFailProtectionMaxCrashes property' { + $result.rapidFailProtectionMaxCrashes | Should Be $mockAppPoolDefaults.failure.rapidFailProtectionMaxCrashes + } + + It 'Should return the autoShutdownExe property' { + $result.autoShutdownExe | Should Be $mockAppPoolDefaults.failure.autoShutdownExe + } + + It 'Should return the autoShutdownParams property' { + $result.autoShutdownParams | Should Be $mockAppPoolDefaults.failure.autoShutdownParams + } + + It 'Should return the disallowOverlappingRotation property' { + $result.disallowOverlappingRotation | Should Be $mockAppPoolDefaults.recycling.disallowOverlappingRotation + } + + It 'Should return the disallowRotationOnConfigChange property' { + $result.disallowRotationOnConfigChange | Should Be $mockAppPoolDefaults.recycling.disallowRotationOnConfigChange + } + + It 'Should return the logEventOnRecycle property' { + $result.logEventOnRecycle | Should Be $mockAppPoolDefaults.recycling.logEventOnRecycle + } + + It 'Should return the restartMemoryLimit property' { + $result.restartMemoryLimit | Should Be $mockAppPoolDefaults.recycling.periodicRestart.memory + } + + It 'Should return the restartPrivateMemoryLimit property' { + $result.restartPrivateMemoryLimit | Should Be $mockAppPoolDefaults.recycling.periodicRestart.privateMemory + } + + It 'Should return the restartRequestsLimit property' { + $result.restartRequestsLimit | Should Be $mockAppPoolDefaults.recycling.periodicRestart.requests + } + + It 'Should return the restartTimeLimit property' { + $result.restartTimeLimit | Should Be $mockAppPoolDefaults.recycling.periodicRestart.time + } + + It 'Should return the restartSchedule property' { + + $restartScheduleValues = [String[]]@( + @($mockAppPoolDefaults.recycling.periodicRestart.schedule.Collection).ForEach('value') + ) + + $compareSplat = @{ + ReferenceObject = [String[]]@($result.restartSchedule) + DifferenceObject = $restartScheduleValues + ExcludeDifferent = $true + IncludeEqual = $true + } + + $compareResult = Compare-Object @compareSplat + + $compareResult.Count -eq $restartScheduleValues.Count | Should Be $true + + } + + } + + } + + Describe "how '$($script:DSCResourceName)\Test-TargetResource' responds" { + + Mock Assert-Module + + Context 'Test target resource with no property specified' { + + $mockAppPoolDefaults = @{ + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True' { + Test-TargetResource -ApplyTo 'Machine' | + Should Be $true + } + + } + + Context 'All the properties match the desired state' { + + $mockAppPoolDefaults = @{ + autoStart = $true + CLRConfigFile = '' + enable32BitAppOnWin64 = $false + enableConfigurationOverride = $true + managedPipelineMode = 'Integrated' + managedRuntimeLoader = 'webengine4.dll' + managedRuntimeVersion = 'v4.0' + passAnonymousToken = $true + startMode = 'OnDemand' + queueLength = 1000 + cpu = @{ + action = 'NoAction' + limit = 0 + resetInterval = '00:05:00' + smpAffinitized = $false + smpProcessorAffinityMask = 4294967295 + smpProcessorAffinityMask2 = 4294967295 + } + processModel = @{ + identityType = 'SpecificUser' + idleTimeout = '00:20:00' + idleTimeoutAction = 'Terminate' + loadUserProfile = $true + logEventOnProcessModel = 'IdleTimeout' + logonType = 'LogonBatch' + manualGroupMembership = $false + maxProcesses = 1 + password = 'P@$$w0rD' + pingingEnabled = $true + pingInterval = '00:00:30' + pingResponseTime = '00:01:30' + setProfileEnvironment = $false + shutdownTimeLimit = '00:01:30' + startupTimeLimit = '00:01:30' + userName = 'CONTOSO\JDoe' + } + failure = @{ + orphanActionExe = '' + orphanActionParams = '' + orphanWorkerProcess = $false + loadBalancerCapabilities = 'HttpLevel' + rapidFailProtection = $true + rapidFailProtectionInterval = '00:05:00' + rapidFailProtectionMaxCrashes = 5 + autoShutdownExe = '' + autoShutdownParams = '' + } + recycling = @{ + disallowOverlappingRotation = $false + disallowRotationOnConfigChange = $false + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + periodicRestart = @{ + memory = 0 + privateMemory = 0 + requests = 0 + time = '1.05:00:00' + schedule = @{ + Collection = @( + @{value = '04:00:00'} + @{value = '08:00:00'} + ) + } + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $mockUserName = $mockAppPoolDefaults.processModel.userName + $mockPassword = $mockAppPoolDefaults.processModel.password | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName PSCredential -ArgumentList $mockUserName, $mockPassword + + $mockRestartSchedule = [String[]]@( + @($mockAppPoolDefaults.recycling.periodicRestart.schedule.Collection).ForEach('value') + ) + + $testParamsSplat = @{ + autoStart = $mockAppPoolDefaults.autoStart + CLRConfigFile = $mockAppPoolDefaults.CLRConfigFile + enable32BitAppOnWin64 = $mockAppPoolDefaults.enable32BitAppOnWin64 + enableConfigurationOverride = $mockAppPoolDefaults.enableConfigurationOverride + managedPipelineMode = $mockAppPoolDefaults.managedPipelineMode + managedRuntimeLoader = $mockAppPoolDefaults.managedRuntimeLoader + managedRuntimeVersion = $mockAppPoolDefaults.managedRuntimeVersion + passAnonymousToken = $mockAppPoolDefaults.passAnonymousToken + startMode = $mockAppPoolDefaults.startMode + queueLength = $mockAppPoolDefaults.queueLength + cpuAction = $mockAppPoolDefaults.cpu.action + cpuLimit = $mockAppPoolDefaults.cpu.limit + cpuResetInterval = $mockAppPoolDefaults.cpu.resetInterval + cpuSmpAffinitized = $mockAppPoolDefaults.cpu.smpAffinitized + cpuSmpProcessorAffinityMask = $mockAppPoolDefaults.cpu.smpProcessorAffinityMask + cpuSmpProcessorAffinityMask2 = $mockAppPoolDefaults.cpu.smpProcessorAffinityMask2 + identityType = $mockAppPoolDefaults.processModel.identityType + Credential = $mockCredential + idleTimeout = $mockAppPoolDefaults.processModel.idleTimeout + idleTimeoutAction = $mockAppPoolDefaults.processModel.idleTimeoutAction + loadUserProfile = $mockAppPoolDefaults.processModel.loadUserProfile + logEventOnProcessModel = $mockAppPoolDefaults.processModel.logEventOnProcessModel + logonType = $mockAppPoolDefaults.processModel.logonType + manualGroupMembership = $mockAppPoolDefaults.processModel.manualGroupMembership + maxProcesses = $mockAppPoolDefaults.processModel.maxProcesses + pingingEnabled = $mockAppPoolDefaults.processModel.pingingEnabled + pingInterval = $mockAppPoolDefaults.processModel.pingInterval + pingResponseTime = $mockAppPoolDefaults.processModel.pingResponseTime + setProfileEnvironment = $mockAppPoolDefaults.processModel.setProfileEnvironment + shutdownTimeLimit = $mockAppPoolDefaults.processModel.shutdownTimeLimit + startupTimeLimit = $mockAppPoolDefaults.processModel.startupTimeLimit + orphanActionExe = $mockAppPoolDefaults.failure.orphanActionExe + orphanActionParams = $mockAppPoolDefaults.failure.orphanActionParams + orphanWorkerProcess = $mockAppPoolDefaults.failure.orphanWorkerProcess + loadBalancerCapabilities = $mockAppPoolDefaults.failure.loadBalancerCapabilities + rapidFailProtection = $mockAppPoolDefaults.failure.rapidFailProtection + rapidFailProtectionInterval = $mockAppPoolDefaults.failure.rapidFailProtectionInterval + rapidFailProtectionMaxCrashes = $mockAppPoolDefaults.failure.rapidFailProtectionMaxCrashes + autoShutdownExe = $mockAppPoolDefaults.failure.autoShutdownExe + autoShutdownParams = $mockAppPoolDefaults.failure.autoShutdownParams + disallowOverlappingRotation = $mockAppPoolDefaults.recycling.disallowOverlappingRotation + disallowRotationOnConfigChange = $mockAppPoolDefaults.recycling.disallowRotationOnConfigChange + logEventOnRecycle = $mockAppPoolDefaults.recycling.logEventOnRecycle + restartMemoryLimit = $mockAppPoolDefaults.recycling.periodicRestart.memory + restartPrivateMemoryLimit = $mockAppPoolDefaults.recycling.periodicRestart.privateMemory + restartRequestsLimit = $mockAppPoolDefaults.recycling.periodicRestart.requests + restartTimeLimit = $mockAppPoolDefaults.recycling.periodicRestart.time + restartSchedule = $mockRestartSchedule + } + + It 'Should return True' { + Test-TargetResource -ApplyTo Machine @testParamsSplat | + Should Be $true + } + + } + + Context 'Test the autoStart property' { + + $mockAppPoolDefaults = @{ + autoStart = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -autoStart $true | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -autoStart $false | + Should Be $false + } + + } + + Context 'Test the CLRConfigFile property' { + + $mockAppPoolDefaults = @{ + CLRConfigFile = '' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -CLRConfigFile '' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -CLRConfigFile 'C:\inetpub\temp\aspnet.config' | + Should Be $false + } + + } + + Context 'Test the enable32BitAppOnWin64 property' { + + $mockAppPoolDefaults = @{ + enable32BitAppOnWin64 = $false + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -enable32BitAppOnWin64 $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -enable32BitAppOnWin64 $true | + Should Be $false + } + + } + + Context 'Test the enableConfigurationOverride property' { + + $mockAppPoolDefaults = @{ + enableConfigurationOverride = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -enableConfigurationOverride $true | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -enableConfigurationOverride $false | + Should Be $false + } + + } + + Context 'Test the managedPipelineMode property' { + + $mockAppPoolDefaults = @{ + managedPipelineMode = 'Integrated' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -managedPipelineMode 'Integrated' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -managedPipelineMode 'Classic' | + Should Be $false + } + + } + + Context 'Test the managedRuntimeLoader property' { + + $mockAppPoolDefaults = @{ + managedRuntimeLoader = 'webengine4.dll' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -managedRuntimeLoader 'webengine4.dll' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -managedRuntimeLoader '' | + Should Be $false + } + + } + + Context 'Test the managedRuntimeVersion property' { + + $mockAppPoolDefaults = @{ + managedRuntimeVersion = 'v4.0' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -managedRuntimeVersion 'v4.0' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -managedRuntimeVersion 'v2.0' | + Should Be $false + } + + } + + Context 'Test the passAnonymousToken property' { + + $mockAppPoolDefaults = @{ + passAnonymousToken = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -passAnonymousToken $true | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -passAnonymousToken $false | + Should Be $false + } + + } + + Context 'Test the startMode property' { + + $mockAppPoolDefaults = @{ + startMode = 'OnDemand' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -startMode 'OnDemand' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -startMode 'AlwaysRunning' | + Should Be $false + } + + } + + Context 'Test the queueLength property' { + + $mockAppPoolDefaults = @{ + queueLength = 1000 + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -queueLength 1000 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -queueLength 2000 | + Should Be $false + } + + } + + Context 'Test the cpuAction property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + action = 'NoAction' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuAction 'NoAction' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuAction 'KillW3wp' | + Should Be $false + } + + } + + Context 'Test the cpuLimit property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + limit = 0 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuLimit 0 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuLimit 90000 | + Should Be $false + } + + } + + Context 'Test the cpuResetInterval property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + resetInterval = '00:05:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuResetInterval '00:05:00' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuResetInterval '00:10:00' | + Should Be $false + } + + } + + Context 'Test the cpuSmpAffinitized property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpAffinitized = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpAffinitized $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpAffinitized $true | + Should Be $false + } + + } + + Context 'Test the cpuSmpProcessorAffinityMask property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpProcessorAffinityMask = 4294967295 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpProcessorAffinityMask 4294967295 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpProcessorAffinityMask 1 | + Should Be $false + } + + } + + Context 'Test the cpuSmpProcessorAffinityMask2 property' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpProcessorAffinityMask2 = 4294967295 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpProcessorAffinityMask2 4294967295 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -cpuSmpProcessorAffinityMask2 1 | + Should Be $false + } + + } + + Context 'Test the identityType property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + identityType = 'ApplicationPoolIdentity' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -identityType 'ApplicationPoolIdentity' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -identityType 'NetworkService' | + Should Be $false + } + + } + + Context 'Test the Credential property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + identityType = 'SpecificUser' + password = '1q2w3e4r' + userName = 'CONTOSO\JDoe' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when both the userName and the password properties match the desired state' { + + $mockUserName = $mockAppPoolDefaults.processModel.userName + $mockPassword = $mockAppPoolDefaults.processModel.password | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName PSCredential -ArgumentList $mockUserName, $mockPassword + + Test-TargetResource -ApplyTo Machine -identityType 'SpecificUser' -Credential $mockCredential | + Should Be $true + + } + + It 'Should return False when the userName property does not match the desired state' { + + $mockUserName = 'CONTOSO\GFawkes' + $mockPassword = $mockAppPoolDefaults.processModel.password | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName PSCredential -ArgumentList $mockUserName, $mockPassword + + Test-TargetResource -ApplyTo Machine -identityType 'SpecificUser' -Credential $mockCredential | + Should Be $false + + } + + It 'Should return False when the password property does not match the desired state' { + + $mockUserName = $mockAppPoolDefaults.processModel.userName + $mockPassword = '5t6y7u8i' | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName PSCredential -ArgumentList $mockUserName, $mockPassword + + Test-TargetResource -ApplyTo Machine -identityType 'SpecificUser' -Credential $mockCredential | + Should Be $false + + } + + } + + Context 'Test the idleTimeout property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + idleTimeout = '00:20:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -idleTimeout '00:20:00' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -idleTimeout '00:15:00' | + Should Be $false + } + + } + + Context 'Test the idleTimeoutAction property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + idleTimeoutAction = 'Terminate' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -idleTimeoutAction 'Terminate' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -idleTimeoutAction 'Suspend' | + Should Be $false + } + + } + + Context 'Test the loadUserProfile property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + loadUserProfile = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -loadUserProfile $true | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -loadUserProfile $false | + Should Be $false + } + + } + + Context 'Test the logEventOnProcessModel property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + logEventOnProcessModel = 'IdleTimeout' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -logEventOnProcessModel 'IdleTimeout' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -logEventOnProcessModel '' | + Should Be $false + } + + } + + Context 'Test the logonType property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + logonType = 'LogonBatch' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -logonType 'LogonBatch' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -logonType 'LogonService' | + Should Be $false + } + + } + + Context 'Test the manualGroupMembership property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + manualGroupMembership = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -manualGroupMembership $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -manualGroupMembership $true | + Should Be $false + } + + } + + Context 'Test the maxProcesses property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + maxProcesses = 1 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -maxProcesses 1 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -maxProcesses 2 | + Should Be $false + } + + } + + Context 'Test the pingingEnabled property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingingEnabled = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -pingingEnabled $true | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -pingingEnabled $false | + Should Be $false + } + + } + + Context 'Test the pingInterval property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingInterval = '00:00:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -pingInterval '00:00:30' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -pingInterval '00:01:00' | + Should Be $false + } + + } + + Context 'Test the pingResponseTime property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingResponseTime = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -pingResponseTime '00:01:30' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -pingResponseTime '00:02:00' | + Should Be $false + } + + } + + Context 'Test the setProfileEnvironment property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + setProfileEnvironment = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -setProfileEnvironment $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -setProfileEnvironment $true | + Should Be $false + } + + } + + Context 'Test the shutdownTimeLimit property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + shutdownTimeLimit = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -shutdownTimeLimit '00:01:30' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -shutdownTimeLimit '00:02:00' | + Should Be $false + } + + } + + Context 'Test the startupTimeLimit property' { + + $mockAppPoolDefaults = @{ + processModel = @{ + startupTimeLimit = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -startupTimeLimit '00:01:30' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -startupTimeLimit '00:02:00' | + Should Be $false + } + + } + + Context 'Test the orphanActionExe property' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanActionExe = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -orphanActionExe '' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -orphanActionExe 'C:\inetpub\temp\orphanAction.exe' | + Should Be $false + } + + } + + Context 'Test the orphanActionParams property' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanActionParams = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -orphanActionParams '' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -orphanActionParams '/orphanActionParam1' | + Should Be $false + } + + } + + Context 'Test the orphanWorkerProcess property' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanWorkerProcess = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -orphanWorkerProcess $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -orphanWorkerProcess $true | + Should Be $false + } + + } + + Context 'Test the loadBalancerCapabilities property' { + + $mockAppPoolDefaults = @{ + failure = @{ + loadBalancerCapabilities = 'HttpLevel' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -loadBalancerCapabilities 'HttpLevel' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -loadBalancerCapabilities 'TcpLevel' | + Should Be $false + } + + } + + Context 'Test the rapidFailProtection property' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtection = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtection $true | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtection $false | + Should Be $false + } + + } + + Context 'Test the rapidFailProtectionInterval property' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtectionInterval = '00:05:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtectionInterval '00:05:00' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtectionInterval '00:10:00' | + Should Be $false + } + + } + + Context 'Test the rapidFailProtectionMaxCrashes property' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtectionMaxCrashes = 5 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtectionMaxCrashes 5 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -rapidFailProtectionMaxCrashes 10 | + Should Be $false + } + + } + + Context 'Test the autoShutdownExe property' { + + $mockAppPoolDefaults = @{ + failure = @{ + autoShutdownExe = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -autoShutdownExe '' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -autoShutdownExe 'C:\inetpub\temp\autoShutdown.exe' | + Should Be $false + } + + } + + Context 'Test the autoShutdownParams property' { + + $mockAppPoolDefaults = @{ + failure = @{ + autoShutdownParams = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -autoShutdownParams '' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -autoShutdownParams '/autoShutdownParam1' | + Should Be $false + } + + } + + Context 'Test the disallowOverlappingRotation property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + disallowOverlappingRotation = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -disallowOverlappingRotation $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -disallowOverlappingRotation $true | + Should Be $false + } + + } + + Context 'Test the disallowRotationOnConfigChange property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + disallowRotationOnConfigChange = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -disallowRotationOnConfigChange $false | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -disallowRotationOnConfigChange $true | + Should Be $false + } + + } + + Context 'Test the logEventOnRecycle property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -logEventOnRecycle 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -logEventOnRecycle 'Time,Memory,PrivateMemory' | + Should Be $false + } + + } + + Context 'Test the restartMemoryLimit property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + memory = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -restartMemoryLimit 0 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -restartMemoryLimit 1048576 | + Should Be $false + } + + } + + Context 'Test the restartPrivateMemoryLimit property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + privateMemory = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -restartPrivateMemoryLimit 0 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -restartPrivateMemoryLimit 1048576 | + Should Be $false + } + + } + + Context 'Test the restartRequestsLimit property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + requests = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -restartRequestsLimit 0 | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -restartRequestsLimit 1000 | + Should Be $false + } + + } + + Context 'Test the restartTimeLimit property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + time = '1.05:00:00' + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -restartTimeLimit '1.05:00:00' | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -restartTimeLimit '2.10:00:00' | + Should Be $false + } + + } + + Context 'Test the restartSchedule property' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + schedule = @{ + Collection = @( + @{value = '04:00:00'} + @{value = '08:00:00'} + ) + } + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + It 'Should return True when the property matches the desired state' { + Test-TargetResource -ApplyTo Machine -restartSchedule @('04:00:00', '08:00:00') | + Should Be $true + } + + It 'Should return False when the property does not match the desired state' { + Test-TargetResource -ApplyTo Machine -restartSchedule @('') | + Should Be $false + } + + } + + } + + Describe "how '$($script:DSCResourceName)\Set-TargetResource' responds" { + + Mock -CommandName Assert-Module -MockWith {} + + Context 'All the properties need to be set' { + + $mockAppPoolDefaults = @{ + autoStart = $true + CLRConfigFile = '' + enable32BitAppOnWin64 = $false + enableConfigurationOverride = $true + managedPipelineMode = 'Integrated' + managedRuntimeLoader = 'webengine4.dll' + managedRuntimeVersion = 'v4.0' + passAnonymousToken = $true + startMode = 'OnDemand' + queueLength = 1000 + cpu = @{ + action = 'NoAction' + limit = 0 + resetInterval = '00:05:00' + smpAffinitized = $false + smpProcessorAffinityMask = 4294967295 + smpProcessorAffinityMask2 = 4294967295 + } + processModel = @{ + identityType = 'ApplicationPoolIdentity' + idleTimeout = '00:20:00' + idleTimeoutAction = 'Terminate' + loadUserProfile = $true + logEventOnProcessModel = 'IdleTimeout' + logonType = 'LogonBatch' + manualGroupMembership = $false + maxProcesses = 1 + password = '' + pingingEnabled = $true + pingInterval = '00:00:30' + pingResponseTime = '00:01:30' + setProfileEnvironment = $false + shutdownTimeLimit = '00:01:30' + startupTimeLimit = '00:01:30' + userName = '' + } + failure = @{ + orphanActionExe = '' + orphanActionParams = '' + orphanWorkerProcess = $false + loadBalancerCapabilities = 'HttpLevel' + rapidFailProtection = $true + rapidFailProtectionInterval = '00:05:00' + rapidFailProtectionMaxCrashes = 5 + autoShutdownExe = '' + autoShutdownParams = '' + } + recycling = @{ + disallowOverlappingRotation = $false + disallowRotationOnConfigChange = $false + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + periodicRestart = @{ + memory = 0 + privateMemory = 0 + requests = 0 + time = '1.05:00:00' + schedule = @{ + Collection = @( + @{value = '02:00:00'} + @{value = '04:00:00'} + ) + } + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $mockUserName = 'CONTOSO\GFawkes' + $mockPassword = '5t6y7u8i' | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName PSCredential -ArgumentList $mockUserName, $mockPassword + + $setParamsSplat = @{ + autoStart = $false + CLRConfigFile = 'C:\inetpub\temp\aspnet.config' + enable32BitAppOnWin64 = $true + enableConfigurationOverride = $false + managedPipelineMode = 'Classic' + managedRuntimeLoader = '' + managedRuntimeVersion = 'v2.0' + passAnonymousToken = $false + startMode = 'AlwaysRunning' + queueLength = 2000 + cpuAction = 'KillW3wp' + cpuLimit = 90000 + cpuResetInterval = '00:10:00' + cpuSmpAffinitized = $true + cpuSmpProcessorAffinityMask = 1 + cpuSmpProcessorAffinityMask2 = 1 + identityType = 'SpecificUser' + Credential = $mockCredential + idleTimeout = '00:15:00' + idleTimeoutAction = 'Suspend' + loadUserProfile = $false + logEventOnProcessModel = '' + logonType = 'LogonService' + manualGroupMembership = $true + maxProcesses = 2 + pingingEnabled = $false + pingInterval = '00:01:00' + pingResponseTime = '00:02:00' + setProfileEnvironment = $true + shutdownTimeLimit = '00:02:00' + startupTimeLimit = '00:02:00' + orphanActionExe = 'C:\inetpub\temp\orphanAction.exe' + orphanActionParams = '/orphanActionParam1' + orphanWorkerProcess = $true + loadBalancerCapabilities = 'TcpLevel' + rapidFailProtection = $false + rapidFailProtectionInterval = '00:10:00' + rapidFailProtectionMaxCrashes = 10 + autoShutdownExe = 'C:\inetpub\temp\autoShutdown.exe' + autoShutdownParams = '/autoShutdownParam1' + disallowOverlappingRotation = $true + disallowRotationOnConfigChange = $true + logEventOnRecycle = 'Time,Memory,PrivateMemory' + restartMemoryLimit = 1048576 + restartPrivateMemoryLimit = 1048576 + restartRequestsLimit = 1000 + restartTimeLimit = '2.10:00:00' + restartSchedule = @('06:00:00', '08:00:00') + } + + Mock Invoke-AppCmd + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call all the mocks' { + Assert-MockCalled Invoke-AppCmd -Exactly 52 + } + + } + + Context 'The autoStart property needs to be set' { + + $mockAppPoolDefaults = @{ + autoStart = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + autoStart = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.autoStart:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The CLRConfigFile property needs to be set' { + + $mockAppPoolDefaults = @{ + CLRConfigFile = '' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + CLRConfigFile = 'C:\inetpub\temp\aspnet.config' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.CLRConfigFile:C:\inetpub\temp\aspnet.config'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The enable32BitAppOnWin64 property needs to be set' { + + $mockAppPoolDefaults = @{ + enable32BitAppOnWin64 = $false + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + enable32BitAppOnWin64 = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.enable32BitAppOnWin64:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The enableConfigurationOverride property needs to be set' { + + $mockAppPoolDefaults = @{ + enableConfigurationOverride = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + enableConfigurationOverride = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.enableConfigurationOverride:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The managedPipelineMode property needs to be set' { + + $mockAppPoolDefaults = @{ + managedPipelineMode = 'Integrated' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + managedPipelineMode = 'Classic' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.managedPipelineMode:Classic'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The managedRuntimeLoader property needs to be set' { + + $mockAppPoolDefaults = @{ + managedRuntimeLoader = 'webengine4.dll' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + managedRuntimeLoader = '' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.managedRuntimeLoader:'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The managedRuntimeVersion property needs to be set' { + + $mockAppPoolDefaults = @{ + managedRuntimeVersion = 'v4.0' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + managedRuntimeVersion = 'v2.0' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.managedRuntimeVersion:v2.0'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The passAnonymousToken property needs to be set' { + + $mockAppPoolDefaults = @{ + passAnonymousToken = $true + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + passAnonymousToken = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.passAnonymousToken:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The startMode property needs to be set' { + + $mockAppPoolDefaults = @{ + startMode = 'OnDemand' + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + startMode = 'AlwaysRunning' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.startMode:AlwaysRunning'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The queueLength property needs to be set' { + + $mockAppPoolDefaults = @{ + queueLength = 1000 + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + queueLength = 2000 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.queueLength:2000'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuAction property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + action = 'NoAction' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuAction = 'KillW3wp' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.action:KillW3wp'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + limit = 0 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuLimit = 90000 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.limit:90000'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuResetInterval property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + resetInterval = '00:05:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuResetInterval = '00:10:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.resetInterval:00:10:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuSmpAffinitized property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpAffinitized = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuSmpAffinitized = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.smpAffinitized:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuSmpProcessorAffinityMask property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpProcessorAffinityMask = 4294967295 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuSmpProcessorAffinityMask = 1 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.smpProcessorAffinityMask:1'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The cpuSmpProcessorAffinityMask2 property needs to be set' { + + $mockAppPoolDefaults = @{ + cpu = @{ + smpProcessorAffinityMask2 = 4294967295 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + cpuSmpProcessorAffinityMask2 = 1 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.cpu.smpProcessorAffinityMask2:1'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The identityType property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + identityType = 'ApplicationPoolIdentity' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + identityType = 'SpecificUser' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.identityType:SpecificUser'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The idleTimeout property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + idleTimeout = '00:20:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + idleTimeout = '00:15:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.idleTimeout:00:15:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The idleTimeoutAction property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + idleTimeoutAction = 'Terminate' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + idleTimeoutAction = 'Suspend' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.idleTimeoutAction:Suspend'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The loadUserProfile property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + loadUserProfile = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + loadUserProfile = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.loadUserProfile:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The logEventOnProcessModel property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + logEventOnProcessModel = 'IdleTimeout' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + logEventOnProcessModel = '' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.logEventOnProcessModel:'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The logonType property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + logonType = 'LogonBatch' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + logonType = 'LogonService' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.logonType:LogonService'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The manualGroupMembership property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + manualGroupMembership = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + manualGroupMembership = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.manualGroupMembership:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The maxProcesses property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + maxProcesses = 1 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + maxProcesses = 2 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.maxProcesses:2'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The pingingEnabled property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingingEnabled = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + pingingEnabled = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.pingingEnabled:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The pingInterval property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingInterval = '00:00:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + pingInterval = '00:01:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.pingInterval:00:01:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The pingResponseTime property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + pingResponseTime = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + pingResponseTime = '00:02:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.pingResponseTime:00:02:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The setProfileEnvironment property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + setProfileEnvironment = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + setProfileEnvironment = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.setProfileEnvironment:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The shutdownTimeLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + shutdownTimeLimit = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + shutdownTimeLimit = '00:02:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.shutdownTimeLimit:00:02:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The startupTimeLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + processModel = @{ + startupTimeLimit = '00:01:30' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + startupTimeLimit = '00:02:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.processModel.startupTimeLimit:00:02:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The orphanActionExe property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanActionExe = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + orphanActionExe = 'C:\inetpub\temp\orphanAction.exe' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.orphanActionExe:C:\inetpub\temp\orphanAction.exe'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The orphanActionParams property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanActionParams = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + orphanActionParams = '/orphanActionParam1' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.orphanActionParams:/orphanActionParam1'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The orphanWorkerProcess property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + orphanWorkerProcess = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + orphanWorkerProcess = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.orphanWorkerProcess:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The loadBalancerCapabilities property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + loadBalancerCapabilities = 'HttpLevel' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + loadBalancerCapabilities = 'TcpLevel' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.loadBalancerCapabilities:TcpLevel'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The rapidFailProtection property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtection = $true + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + rapidFailProtection = $false + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.rapidFailProtection:False'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The rapidFailProtectionInterval property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtectionInterval = '00:05:00' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + rapidFailProtectionInterval = '00:10:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.rapidFailProtectionInterval:00:10:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The rapidFailProtectionMaxCrashes property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + rapidFailProtectionMaxCrashes = 5 + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + rapidFailProtectionMaxCrashes = 10 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.rapidFailProtectionMaxCrashes:10'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The autoShutdownExe property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + autoShutdownExe = '' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + autoShutdownExe = 'C:\inetpub\temp\autoShutdown.exe' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.autoShutdownExe:C:\inetpub\temp\autoShutdown.exe'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The autoShutdownParams property needs to be set' { + + $mockAppPoolDefaults = @{ + failure = @{ + autoShutdownParams = '' + } + + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + autoShutdownParams = '/autoShutdownParam1' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.failure.autoShutdownParams:/autoShutdownParam1'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The disallowOverlappingRotation property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + disallowOverlappingRotation = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + disallowOverlappingRotation = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.disallowOverlappingRotation:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The disallowRotationOnConfigChange property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + disallowRotationOnConfigChange = $false + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + disallowRotationOnConfigChange = $true + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.disallowRotationOnConfigChange:True'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The logEventOnRecycle property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory' + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + logEventOnRecycle = 'Time,Memory,PrivateMemory' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.logEventOnRecycle:Time,Memory,PrivateMemory'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The restartMemoryLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + memory = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + restartMemoryLimit = 1048576 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.periodicRestart.memory:1048576'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The restartPrivateMemoryLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + privateMemory = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + restartPrivateMemoryLimit = 1048576 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.periodicRestart.privateMemory:1048576'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The restartRequestsLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + requests = 0 + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + restartRequestsLimit = 1000 + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.periodicRestart.requests:1000'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The restartTimeLimit property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + time = '1.05:00:00' + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + restartTimeLimit = '2.10:00:00' + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq '/applicationPoolDefaults.recycling.periodicRestart.time:2.10:00:00'} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -Exactly 1 + } + + } + + Context 'The restartSchedule property needs to be set' { + + $mockAppPoolDefaults = @{ + recycling = @{ + periodicRestart = @{ + schedule = @{ + Collection = @( + @{value = '04:00:00'} + ) + } + } + } + } + + Mock Get-AppPoolDefault -MockWith {$mockAppPoolDefaults} + + $setParamsSplat = @{ + restartSchedule = @('08:00:00') + } + + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq "/-applicationPoolDefaults.recycling.periodicRestart.schedule.[value='04:00:00']"} + Mock Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq "/+applicationPoolDefaults.recycling.periodicRestart.schedule.[value='08:00:00']"} + + Set-TargetResource -ApplyTo Machine @setParamsSplat + + It 'Should call Invoke-AppCmd' { + Assert-MockCalled Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq "/-applicationPoolDefaults.recycling.periodicRestart.schedule.[value='04:00:00']"} -Exactly 1 + Assert-MockCalled Invoke-AppCmd -ParameterFilter {$ArgumentList[-1] -eq "/+applicationPoolDefaults.recycling.periodicRestart.schedule.[value='08:00:00']"} -Exactly 1 + } + + } + + } + + } + + #endregion +} +finally +{ + Restore-TestEnvironment -TestEnvironment $TestEnvironment +}