diff --git a/DSCResources/MSFT_xWebAppPool/MSFT_xWebAppPool.psm1 b/DSCResources/MSFT_xWebAppPool/MSFT_xWebAppPool.psm1 index 2efb5d92c..422826b4a 100644 --- a/DSCResources/MSFT_xWebAppPool/MSFT_xWebAppPool.psm1 +++ b/DSCResources/MSFT_xWebAppPool/MSFT_xWebAppPool.psm1 @@ -1,137 +1,1167 @@ -function Get-TargetResource +data LocalizedData { - [CmdletBinding()] - [OutputType([System.Collections.Hashtable])] - param - ( - [parameter(Mandatory = $true)] - [System.String] - $Name + # culture="en-US" + ConvertFrom-StringData @' +SetTargetResourceInstallwhatIfMessage=Trying to create AppPool "{0}". +SetTargetResourceUnInstallwhatIfMessage=Trying to remove AppPool "{0}". +AppPoolNotFoundError=The requested AppPool "{0}" is not found on the target machine. +AppPoolDiscoveryFailureError=Failure to get the requested AppPool "{0}" information from the target machine. +AppPoolCreationFailureError=Failure to successfully create the AppPool "{0}". +AppPoolRemovalFailureError=Failure to successfully remove the AppPool "{0}". +AppPoolUpdateFailureError=Failure to successfully update the properties for AppPool "{0}". +AppPoolCompareFailureError=Failure to successfully compare properties for AppPool "{0}". +AppPoolStateFailureError=Failure to successfully set the state of the AppPool {0}. +'@ +} + +# The Get-TargetResource cmdlet is used to fetch the status of role or AppPool on the target machine. +# It gives the AppPool info of the requested role/feature on the target machine. +function Get-TargetResource +{ + [OutputType([hashtable])] + param + ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name ) - $Ensure = 'Absent' - $State = 'Stopped' + $getTargetResourceResult = $null; - # Check if webadministration module is present or not - if(!(Get-Module -ListAvailable -Name WebAdministration)) - { - Throw 'Please ensure that WebAdministration module is installed.' - } + # Check if WebAdministration module is present for IIS cmdlets + if(!(Get-Module -ListAvailable -Name WebAdministration)) + { + Throw "Please ensure that WebAdministration module is installed." + } - # Need to import explicitly to run for IIS:\AppPools - # Setting verbose to false to avoid seeing all the imported command in - # DSC configuration verbose messages when configuration is run with -Verbose specified - Import-Module WebAdministration -Verbose:$false + $AppPools = & $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool $Name - $AppPool = Get-Item -Path IIS:\AppPools\* | ? {$_.name -eq $Name} + if ($AppPools.count -eq 0) # No AppPool exists with this name. + { + $ensureResult = "Absent"; + } + elseif ($AppPools.count -eq 1) # A single AppPool exists with this name. + { + $ensureResult = "Present" + $AppPoolState = & $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool $Name /text:state + [xml] $PoolConfig + $PoolConfig = & $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool $Name /config:* + if($PoolConfig.add.processModel.userName){ + $AppPoolPassword = $PoolConfig.add.processModel.password | ConvertTo-SecureString -AsPlainText -Force + $AppPoolCred = new-object -typename System.Management.Automation.PSCredential -argumentlist $PoolConfig.add.processModel.userName,$AppPoolPassword + } + else{ + $AppPoolCred =$null + } - if($AppPool -ne $null) - { - $Ensure = 'Present' - $State = $AppPool.state - } + } + else # Multiple AppPools with the same name exist. This is not supported and is an error + { + $errorId = "AppPoolDiscoveryFailure"; + $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidResult + $errorMessage = $($LocalizedData.AppPoolUpdateFailureError) -f ${Name} + $exception = New-Object System.InvalidOperationException $errorMessage + $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $null - $returnValue = @{ - Name = $Name - Ensure = $Ensure - State = $State - } + $PSCmdlet.ThrowTerminatingError($errorRecord); + } - return $returnValue + # Add all Website properties to the hash table + $getTargetResourceResult = @{ + Name = $PoolConfig.add.name; + Ensure = $ensureResult; + State = $AppPoolState; + autoStart = $PoolConfig.add.autoStart; + managedRuntimeVersion = $PoolConfig.add.managedRuntimeVersion; + managedPipelineMode = $PoolConfig.add.managedPipelineMode; + startMode = $PoolConfig.add.startMode; + identityType = $PoolConfig.add.processModel.identityType; + userName = $PoolConfig.add.processModel.userName; + password = $AppPoolCred; + loadUserProfile = $PoolConfig.add.processModel.loadUserProfile; + queueLength = $PoolConfig.add.queueLength; + enable32BitAppOnWin64 = $PoolConfig.add.enable32BitAppOnWin64; + managedRuntimeLoader = $PoolConfig.add.managedRuntimeLoader; + enableConfigurationOverride = $PoolConfig.add.enableConfigurationOverride; + CLRConfigFile = $PoolConfig.add.CLRConfigFile; + passAnonymousToken = $PoolConfig.add.passAnonymousToken; + logonType = $PoolConfig.add.processModel.logonType; + manualGroupMembership = $PoolConfig.add.processModel.manualGroupMembership; + idleTimeout = $PoolConfig.add.processModel.idleTimeout; + maxProcesses = $PoolConfig.add.processModel.maxProcesses; + shutdownTimeLimit = $PoolConfig.add.processModel.shutdownTimeLimit; + startupTimeLimit = $PoolConfig.add.processModel.startupTimeLimit; + pingingEnabled = $PoolConfig.add.processModel.pingingEnabled; + pingInterval = $PoolConfig.add.processModel.pingInterval; + pingResponseTime = $PoolConfig.add.processModel.pingResponseTime; + disallowOverlappingRotation = $PoolConfig.add.recycling.disallowOverlappingRotation; + disallowRotationOnConfigChange = $PoolConfig.add.recycling.disallowRotationOnConfigChange; + logEventOnRecycle = $PoolConfig.add.recycling.logEventOnRecycle; + restartMemoryLimit = $PoolConfig.add.recycling.periodicRestart.memory; + restartPrivateMemoryLimit = $PoolConfig.add.recycling.periodicRestart.privateMemory; + restartRequestsLimit = $PoolConfig.add.recycling.periodicRestart.requests; + restartTimeLimit = $PoolConfig.add.recycling.periodicRestart.time; + restartSchedule = $PoolConfig.add.recycling.periodicRestart.schedule; + loadBalancerCapabilities = $PoolConfig.add.failure.loadBalancerCapabilities; + orphanWorkerProcess = $PoolConfig.add.failure.orphanWorkerProcess; + orphanActionExe = $PoolConfig.add.failure.orphanActionExe; + orphanActionParams = $PoolConfig.add.failure.orphanActionParams; + rapidFailProtection = $PoolConfig.add.failure.rapidFailProtection; + rapidFailProtectionInterval = $PoolConfig.add.failure.rapidFailProtectionInterval; + rapidFailProtectionMaxCrashes = $PoolConfig.add.failure.rapidFailProtectionMaxCrashes; + autoShutdownExe = $PoolConfig.add.failure.autoShutdownExe; + autoShutdownParams = $PoolConfig.add.failure.autoShutdownParams; + cpuLimit = $PoolConfig.add.cpu.limit; + cpuAction = $PoolConfig.add.cpu.action; + cpuResetInterval = $PoolConfig.add.cpu.resetInterval; + cpuSmpAffinitized = $PoolConfig.add.cpu.smpAffinitized; + cpuSmpProcessorAffinityMask = $PoolConfig.add.cpu.smpProcessorAffinityMask; + cpuSmpProcessorAffinityMask2 = $PoolConfig.add.cpu.smpProcessorAffinityMask2; + } + + return $getTargetResourceResult; } -function Set-TargetResource +# The Set-TargetResource cmdlet is used to create, delete or configure a website on the target machine. +function Set-TargetResource { - [CmdletBinding()] - param - ( - [parameter(Mandatory = $true)] - [System.String] - $Name, - - [ValidateSet('Present','Absent')] - [System.String] - $Ensure = 'Present', - - [ValidateSet('Started','Stopped')] - [System.String] - $State = 'Started' + [CmdletBinding(SupportsShouldProcess=$true)] + param + ( + [ValidateSet("Present", "Absent")] + [string]$Ensure = "Present", + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name, + + [ValidateSet("Started","Stopped")] + [string]$state = "Started", + + [ValidateSet("true","false")] + [string]$autoStart = "true", + + [ValidateSet("v4.0","v2.0","")] + [string]$managedRuntimeVersion = "v4.0", + + [ValidateSet("Integrated","Classic")] + [string]$managedPipelineMode = "Integrated", + + [ValidateSet("AlwaysRunning","OnDemand")] + [string]$startMode = "OnDemand", + + [ValidateSet("ApplicationPoolIdentity","LocalSystem","LocalService","NetworkService","SpecificUser")] + [string]$identityType = "ApplicationPoolIdentity", + + [string]$userName, + + [System.Management.Automation.PSCredential] + $Password, + + [ValidateSet("true","false")] + [string]$loadUserProfile = "true", + + [string]$queueLength = "1000", + + [ValidateSet("true","false")] + [string]$enable32BitAppOnWin64 = "false", + + [string]$managedRuntimeLoader = "webengine4.dll", + + [ValidateSet("true","false")] + [string]$enableConfigurationOverride = "true", + + [string]$CLRConfigFile = "", + + [ValidateSet("true","false")] + [string]$passAnonymousToken = "true", + + [ValidateSet("LogonBatch","LogonService")] + [string]$logonType = "LogonBatch", + + [ValidateSet("true","false")] + [string]$manualGroupMembership = "false", + + #Format 00:20:00 + [string]$idleTimeout = "00:20:00", + + [string]$maxProcesses = "1", + + #Format 00:20:00 + [string]$shutdownTimeLimit = "00:01:30", + + #Format 00:20:00 + [string]$startupTimeLimit = "00:01:30", + + [ValidateSet("true","false")] + [string]$pingingEnabled = "true", + + #Format 00:20:00 + [string]$pingInterval = "00:00:30", + + #Format 00:20:00 + [string]$pingResponseTime = "00:01:30", + + [ValidateSet("true","false")] + [string]$disallowOverlappingRotation = "false", + + [ValidateSet("true","false")] + [string]$disallowRotationOnConfigChange = "false", + + #format "Time, Memory, PrivateMemory" + [string]$logEventOnRecycle = "Time, Memory, PrivateMemory", + + [string]$restartMemoryLimit = "0", + + [string]$restartPrivateMemoryLimit = "0", + + [string]$restartRequestsLimit = "0", + + [string]$restartTimeLimit = "1.05:00:00", + + #Format 00:00:00 24hr clock and must have 00 for seconds + [string[]]$restartSchedule = @(""), + + [ValidateSet("HttpLevel","TcpLevel")] + [string]$loadBalancerCapabilities = "HttpLevel", + + [ValidateSet("true","false")] + [string]$orphanWorkerProcess = "false", + + [string]$orphanActionExe = "", + + [string]$orphanActionParams = "", + + [ValidateSet("true","false")] + [string]$rapidFailProtection = "true", + + #Format 00:20:00 + [string]$rapidFailProtectionInterval = "00:05:00", + + [string]$rapidFailProtectionMaxCrashes = "5", + + [string]$autoShutdownExe = "", + + [string]$autoShutdownParams = "", + + [string]$cpuLimit = "0", + + [ValidateSet("NoAction","KillW3wp","Throttle","ThrottleUnderLoad")] + [string]$cpuAction = "NoAction", + + #Format 00:20:00 + [string]$cpuResetInterval = "00:05:00", + + [ValidateSet("true","false")] + [string]$cpuSmpAffinitized = "false", + + [string]$cpuSmpProcessorAffinityMask = "4294967295", + + [string]$cpuSmpProcessorAffinityMask2 = "4294967295" ) + + $getTargetResourceResult = $null; - if($Ensure -eq 'Absent') - { - Write-Verbose('Removing the Web App Pool') - Remove-WebAppPool $Name - } - else + if($Ensure -eq "Present") { - $AppPool = Get-TargetResource -Name $Name - if($AppPool.Ensure -ne 'Present') + #Remove Ensure from parameters as it is not needed to create new AppPool + $Result = $psboundparameters.Remove("Ensure"); + + + # Check if WebAdministration module is present for IIS cmdlets + if(!(Get-Module -ListAvailable -Name WebAdministration)) { - Write-Verbose('Creating the Web App Pool') - New-WebAppPool $Name - $AppPool = Get-TargetResource -Name $Name + Throw "Please ensure that WebAdministration module is installed." } - if($AppPool.State -ne $State) + $AppPool = & $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool $Name + + if($AppPool -eq $null) #AppPool doesn't exist so create a new one { - ExecuteRequiredState -Name $Name -State $State - } - } -} + try + { + New-WebAppPool $Name + Wait-Event -Timeout 5 + Stop-WebAppPool $Name + + Write-Verbose("successfully created AppPool $Name") + + #Start site if required + if($autoStart -eq "true") + { + Start-WebAppPool $Name + } + Write-Verbose("successfully started AppPool $Name") -function Test-TargetResource -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [parameter(Mandatory = $true)] - [System.String] - $Name, - - [ValidateSet('Present','Absent')] - [System.String] - $Ensure = 'Present', - - [ValidateSet('Started','Stopped')] - [System.String] - $State = 'Started' - ) - $WebAppPool = Get-TargetResource -Name $Name + $AppPool = & $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool $Name + } + catch + { + $errorId = "AppPoolCreationFailure"; + $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidOperation; + $errorMessage = $($LocalizedData.FeatureCreationFailureError) -f ${Name} ; + $exception = New-Object System.InvalidOperationException $errorMessage ; + $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $null - if($Ensure -eq 'Present') - { - if($WebAppPool.Ensure -eq $Ensure -and $WebAppPool.State -eq $state) + $PSCmdlet.ThrowTerminatingError($errorRecord); + } + } + + if($AppPool -ne $null) { - return $true + #update parameters as required + + $UpdateNotRequired = $true + + #get configuration of AppPool + #[xml] $PoolConfig + [xml]$PoolConfig = & $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool $Name /config:* + + #Update autoStart if required + if($PoolConfig.add.autoStart -ne $autoStart){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /autoStart:$autoStart + } + + #update managedRuntimeVersion if required + if($PoolConfig.add.managedRuntimeVersion -ne $managedRuntimeVersion){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /managedRuntimeVersion:$managedRuntimeVersion + } + #update managedPipelineMode if required + if($PoolConfig.add.managedPipelineMode -ne $managedPipelineMode){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /managedPipelineMode:$managedPipelineMode + } + #update state if required + if($AppPoolState -ne $state){ + $UpdateNotRequired = $false + if($State -eq "Started") + { + start-WebAppPool -Name $Name + } + else + { + Stop-WebAppPool -Name $Name + } + } + #update startMode if required + if($PoolConfig.add.startMode -ne $startMode){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /startMode:$startMode + } + #update identityType if required + if($PoolConfig.add.processModel.identityType -ne $identityType){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.identityType:$identityType + } + #update userName if required + if($identityType -eq "SpecificUser" -and $PoolConfig.add.processModel.userName -ne $userName){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.userName:$userName + } + #update password if required + if($identityType -eq "SpecificUser" -and $Password){ + $clearTextPassword = $Password.GetNetworkCredential().Password + if($clearTextPassword -cne $PoolConfig.add.processModel.password){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.password:$clearTextPassword + } + + } + + #update loadUserProfile if required + if($PoolConfig.add.processModel.loadUserProfile -ne $loadUserProfile){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.loadUserProfile:$loadUserProfile + } + + #update queueLength if required + if($PoolConfig.add.queueLength -ne $queueLength){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /queueLength:$queueLength + } + + #update enable32BitAppOnWin64 if required + if($PoolConfig.add.enable32BitAppOnWin64 -ne $enable32BitAppOnWin64){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /enable32BitAppOnWin64:$enable32BitAppOnWin64 + } + + #update managedRuntimeLoader if required + if($PoolConfig.add.managedRuntimeLoader -ne $managedRuntimeLoader){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /managedRuntimeLoader:$managedRuntimeLoader + } + + #update enableConfigurationOverride if required + if($PoolConfig.add.enableConfigurationOverride -ne $enableConfigurationOverride){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /enableConfigurationOverride:$enableConfigurationOverride + } + + #update CLRConfigFile if required + if($PoolConfig.add.CLRConfigFile -ne $CLRConfigFile){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /CLRConfigFile:$CLRConfigFile + } + + #update passAnonymousToken if required + if($PoolConfig.add.passAnonymousToken -ne $passAnonymousToken){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /passAnonymousToken:$passAnonymousToken + } + + #update logonType if required + if($PoolConfig.add.processModel.logonType -ne $logonType){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.logonType:$logonType + } + + #update manualGroupMembership if required + if($PoolConfig.add.processModel.manualGroupMembership -ne $manualGroupMembership){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.manualGroupMembership:$manualGroupMembership + } + + #update idleTimeout if required + if($PoolConfig.add.processModel.idleTimeout -ne $idleTimeout){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.idleTimeout:$idleTimeout + } + + #update maxProcesses if required + if($PoolConfig.add.processModel.maxProcesses -ne $maxProcesses){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.maxProcesses:$maxProcesses + } + + #update shutdownTimeLimit if required + if($PoolConfig.add.processModel.shutdownTimeLimit -ne $shutdownTimeLimit){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.shutdownTimeLimit:$shutdownTimeLimit + } + + #update startupTimeLimit if required + if($PoolConfig.add.processModel.startupTimeLimit -ne $startupTimeLimit){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.startupTimeLimit:$startupTimeLimit + } + + #update pingingEnabled if required + if($PoolConfig.add.processModel.pingingEnabled -ne $pingingEnabled){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.pingingEnabled:$pingingEnabled + } + + #update pingInterval if required + if($PoolConfig.add.processModel.pingInterval -ne $pingInterval){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.pingInterval:$pingInterval + } + + #update pingResponseTime if required + if($PoolConfig.add.processModel.pingResponseTime -ne $pingResponseTime){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /processModel.pingResponseTime:$pingResponseTime + } + + #update disallowOverlappingRotation if required + if($PoolConfig.add.recycling.disallowOverlappingRotation -ne $disallowOverlappingRotation){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /recycling.disallowOverlappingRotation:$disallowOverlappingRotation + } + + #update disallowRotationOnConfigChange if required + if($PoolConfig.add.recycling.disallowRotationOnConfigChange -ne $disallowRotationOnConfigChange){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /recycling.disallowRotationOnConfigChange:$disallowRotationOnConfigChange + } + + #update logEventOnRecycle if required + if($PoolConfig.add.recycling.logEventOnRecycle -ne $logEventOnRecycle){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /recycling.logEventOnRecycle:$logEventOnRecycle + } + + #update restartMemoryLimit if required + if($PoolConfig.add.recycling.periodicRestart.memory -ne $restartMemoryLimit){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /recycling.periodicRestart.memory:$restartMemoryLimit + } + + #update restartPrivateMemoryLimit if required + if($PoolConfig.add.recycling.periodicRestart.privateMemory -ne $restartPrivateMemoryLimit){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /recycling.periodicRestart.privateMemory:$restartPrivateMemoryLimit + } + + #update restartRequestsLimit if required + if($PoolConfig.add.recycling.periodicRestart.requests -ne $restartRequestsLimit){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /recycling.periodicRestart.requests:$restartRequestsLimit + } + + #update restartTimeLimit if required + if($PoolConfig.add.recycling.periodicRestart.time -ne $restartTimeLimit){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /recycling.periodicRestart.time:$restartTimeLimit + } + + #update restartSchedule if required + #clear current schedule + foreach($schTime in $PoolConfig.add.recycling.periodicRestart.schedule.add.value) + { + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name "/-recycling.periodicRestart.schedule.[value='$schTime']" + } + #add desired schedule + foreach($time in $restartSchedule) + { + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name "/+recycling.periodicRestart.schedule.[value='$time']" + } + + #update loadBalancerCapabilities if required + if($PoolConfig.add.failure.loadBalancerCapabilities -ne $loadBalancerCapabilities){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /failure.loadBalancerCapabilities:$loadBalancerCapabilities + } + + #update orphanWorkerProcess if required + if($PoolConfig.add.failure.orphanWorkerProcess -ne $orphanWorkerProcess){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /failure.orphanWorkerProcess:$orphanWorkerProcess + } + + #update orphanActionExe if required + if($PoolConfig.add.failure.orphanActionExe -ne $orphanActionExe){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /failure.orphanActionExe:$orphanActionExe + } + + #update orphanActionParams if required + if($PoolConfig.add.failure.orphanActionParams -ne $orphanActionParams){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /failure.orphanActionParams:$orphanActionParams + } + + #update rapidFailProtection if required + if($PoolConfig.add.failure.rapidFailProtection -ne $rapidFailProtection){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /failure.rapidFailProtection:$rapidFailProtection + } + + #update rapidFailProtectionInterval if required + if($PoolConfig.add.failure.rapidFailProtectionInterval -ne $rapidFailProtectionInterval){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /failure.rapidFailProtectionInterval:$rapidFailProtectionInterval + } + + #update rapidFailProtectionMaxCrashes if required + if($PoolConfig.add.failure.rapidFailProtectionMaxCrashes -ne $rapidFailProtectionMaxCrashes){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /failure.rapidFailProtectionMaxCrashes:$rapidFailProtectionMaxCrashes + } + + #update autoShutdownExe if required + if($PoolConfig.add.failure.autoShutdownExe -ne $autoShutdownExe){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /failure.autoShutdownExe:$autoShutdownExe + } + + #update autoShutdownParams if required + if($PoolConfig.add.failure.autoShutdownParams -ne $autoShutdownParams){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /failure.autoShutdownParams:$autoShutdownParams + } + + #update cpuLimit if required + if($PoolConfig.add.cpu.limit -ne $cpuLimit){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /cpu.limit:$cpuLimit + } + + #update cpuAction if required + if($PoolConfig.add.cpu.action -ne $cpuAction){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /cpu.action:$cpuAction + } + + #update cpuResetInterval if required + if($PoolConfig.add.cpu.resetInterval -ne $cpuResetInterval){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /cpu.resetInterval:$cpuResetInterval + } + + #update cpuSmpAffinitized if required + if($PoolConfig.add.cpu.smpAffinitized -ne $cpuSmpAffinitized){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /cpu.smpAffinitized:$cpuSmpAffinitized + } + + #update cpuSmpProcessorAffinityMask if required + if($PoolConfig.add.cpu.smpProcessorAffinityMask -ne $cpuSmpProcessorAffinityMask){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /cpu.smpProcessorAffinityMask:$cpuSmpProcessorAffinityMask + } + + #update cpuSmpProcessorAffinityMask2 if required + if($PoolConfig.add.cpu.smpProcessorAffinityMask2 -ne $cpuSmpProcessorAffinityMask2){ + $UpdateNotRequired = $false + & $env:SystemRoot\system32\inetsrv\appcmd.exe set apppool $Name /cpu.smpProcessorAffinityMask2:$cpuSmpProcessorAffinityMask2 + } + + if($UpdateNotRequired) + { + Write-Verbose("AppPool $Name already exists and properties do not need to be updated."); + } + } + } - elseif($WebAppPool.Ensure -eq $Ensure) - { - return $true - } + else #Ensure is set to "Absent" so remove website + { + try + { + $AppPool = & $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool $Name + if($AppPool -ne $null) + { + Stop-WebAppPool $Name + Remove-WebAppPool $Name + + Write-Verbose("Successfully removed AppPool $Name.") + } + else + { + Write-Verbose("AppPool $Name does not exist.") + } + } + catch + { + $errorId = "AppPoolRemovalFailure"; + $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidOperation; + $errorMessage = $($LocalizedData.WebsiteRemovalFailureError) -f ${Name} ; + $exception = New-Object System.InvalidOperationException $errorMessage ; + $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $null - return $false + $PSCmdlet.ThrowTerminatingError($errorRecord); + } + + } } -function ExecuteRequiredState([string] $Name, [string] $State) +# The Test-TargetResource cmdlet is used to validate if the role or feature is in a state as expected in the instance document. +function Test-TargetResource { - if($State -eq 'Started') + [OutputType([bool])] + param + ( + [ValidateSet("Present", "Absent")] + [string]$Ensure = "Present", + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name, + + [ValidateSet("Started","Stopped")] + [string]$state = "Started", + + [ValidateSet("true","false")] + [string]$autoStart = "true", + + [ValidateSet("v4.0","v2.0","")] + [string]$managedRuntimeVersion = "v4.0", + + [ValidateSet("Integrated","Classic")] + [string]$managedPipelineMode = "Integrated", + + [ValidateSet("AlwaysRunning","OnDemand")] + [string]$startMode = "OnDemand", + + [ValidateSet("ApplicationPoolIdentity","LocalSystem","LocalService","NetworkService","SpecificUser")] + [string]$identityType = "ApplicationPoolIdentity", + + [string]$userName, + + [System.Management.Automation.PSCredential] + $Password, + + [ValidateSet("true","false")] + [string]$loadUserProfile = "true", + + [string]$queueLength = "1000", + + [ValidateSet("true","false")] + [string]$enable32BitAppOnWin64 = "false", + + [string]$managedRuntimeLoader = "webengine4.dll", + + [ValidateSet("true","false")] + [string]$enableConfigurationOverride = "true", + + [string]$CLRConfigFile = "", + + [ValidateSet("true","false")] + [string]$passAnonymousToken = "true", + + [ValidateSet("LogonBatch","LogonService")] + [string]$logonType = "LogonBatch", + + [ValidateSet("true","false")] + [string]$manualGroupMembership = "false", + + #Format 00:20:00 + [string]$idleTimeout = "00:20:00", + + [string]$maxProcesses = "1", + + #Format 00:20:00 + [string]$shutdownTimeLimit = "00:01:30", + + #Format 00:20:00 + [string]$startupTimeLimit = "00:01:30", + + [ValidateSet("true","false")] + [string]$pingingEnabled = "true", + + #Format 00:20:00 + [string]$pingInterval = "00:00:30", + + #Format 00:20:00 + [string]$pingResponseTime = "00:01:30", + + [ValidateSet("true","false")] + [string]$disallowOverlappingRotation = "false", + + [ValidateSet("true","false")] + [string]$disallowRotationOnConfigChange = "false", + + #format "Time, Memory, PrivateMemory" + [string]$logEventOnRecycle = "Time, Memory, PrivateMemory", + + [string]$restartMemoryLimit = "0", + + [string]$restartPrivateMemoryLimit = "0", + + [string]$restartRequestsLimit = "0", + + [string]$restartTimeLimit = "1.05:00:00", + + #Format 00:00:00 24hr clock and must have 00 for seconds + [string[]]$restartSchedule = @(""), + + [ValidateSet("HttpLevel","TcpLevel")] + [string]$loadBalancerCapabilities = "HttpLevel", + + [ValidateSet("true","false")] + [string]$orphanWorkerProcess = "false", + + [string]$orphanActionExe = "", + + [string]$orphanActionParams = "", + + [ValidateSet("true","false")] + [string]$rapidFailProtection = "true", + + #Format 00:20:00 + [string]$rapidFailProtectionInterval = "00:05:00", + + [string]$rapidFailProtectionMaxCrashes = "5", + + [string]$autoShutdownExe = "", + + [string]$autoShutdownParams = "", + + [string]$cpuLimit = "0", + + [ValidateSet("NoAction","KillW3wp","Throttle","ThrottleUnderLoad")] + [string]$cpuAction = "NoAction", + + #Format 00:20:00 + [string]$cpuResetInterval = "00:05:00", + + [ValidateSet("true","false")] + [string]$cpuSmpAffinitized = "false", + + [string]$cpuSmpProcessorAffinityMask = "4294967295", + + [string]$cpuSmpProcessorAffinityMask2 = "4294967295" + ) + + $DesiredConfigurationMatch = $true + + # Check if WebAdministration module is present for IIS cmdlets + if(!(Get-Module -ListAvailable -Name WebAdministration)) { - Write-Verbose('Starting the Web App Pool') - start-WebAppPool -Name $Name + Throw "Please ensure that WebAdministration module is installed." } - else - { - Write-Verbose('Stopping the Web App Pool') - Stop-WebAppPool -Name $Name + + $AppPool = & $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool $Name + if($AppPool){ + #get configuration of AppPool + #[xml] $PoolConfig + [xml]$PoolConfig = & $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool $Name /config:* + $AppPoolState = & $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool $Name /text:state } -} + $Stop = $true + + Do + { + #Check Ensure + if(($Ensure -eq "Present" -and $AppPool -eq $null) -or ($Ensure -eq "Absent" -and $AppPool -ne $null)) + { + $DesiredConfigurationMatch = $false + Write-Verbose("The Ensure state for AppPool $Name does not match the desired state."); + break + } + + # Only check properties if $AppPool exists + if ($AppPool -ne $null) + { + #Check autoStart + if($PoolConfig.add.autoStart -ne $autoStart){ + $DesiredConfigurationMatch = $false + Write-Verbose("autoStart of AppPool $Name does not match the desired state."); + break + } + + #Check managedRuntimeVersion + if($PoolConfig.add.managedRuntimeVersion -ne $managedRuntimeVersion){ + $DesiredConfigurationMatch = $false + Write-Verbose("managedRuntimeVersion of AppPool $Name does not match the desired state."); + break + } + + #Check managedPipelineMode + if($PoolConfig.add.managedPipelineMode -ne $managedPipelineMode){ + $DesiredConfigurationMatch = $false + Write-Verbose("managedPipelineMode of AppPool $Name does not match the desired state."); + break + } + + #Check state + if($AppPoolState -ne $state){ + $DesiredConfigurationMatch = $false + Write-Verbose("state of AppPool $Name does not match the desired state."); + break + } + + #Check startMode + if($PoolConfig.add.startMode -ne $startMode){ + $DesiredConfigurationMatch = $false + Write-Verbose("startMode of AppPool $Name does not match the desired state."); + break + } + + #Check identityType + if($PoolConfig.add.processModel.identityType -ne $identityType){ + $DesiredConfigurationMatch = $false + Write-Verbose("identityType of AppPool $Name does not match the desired state."); + break + } + + #Check userName + if($PoolConfig.add.processModel.userName -ne $userName){ + $DesiredConfigurationMatch = $false + Write-Verbose("userName of AppPool $Name does not match the desired state."); + break + } + + #Check password + if($identityType -eq "SpecificUser" -and $Password){ + $clearTextPassword = $Password.GetNetworkCredential().Password + if($clearTextPassword -cne $PoolConfig.add.processModel.password){ + $DesiredConfigurationMatch = $false + Write-Verbose("Password of AppPool $Name does not match the desired state."); + break + } + } + + #Check loadUserProfile + if($PoolConfig.add.processModel.loadUserProfile -ne $loadUserProfile){ + $DesiredConfigurationMatch = $false + Write-Verbose("loadUserProfile of AppPool $Name does not match the desired state."); + break + } + + #Check queueLength + if($PoolConfig.add.queueLength -ne $queueLength){ + $DesiredConfigurationMatch = $false + Write-Verbose("queueLength of AppPool $Name does not match the desired state."); + break + } + + #Check enable32BitAppOnWin64 + if($PoolConfig.add.enable32BitAppOnWin64 -ne $enable32BitAppOnWin64){ + $DesiredConfigurationMatch = $false + Write-Verbose("enable32BitAppOnWin64 of AppPool $Name does not match the desired state."); + break + } + + #Check managedRuntimeLoader + if($PoolConfig.add.managedRuntimeLoader -ne $managedRuntimeLoader){ + $DesiredConfigurationMatch = $false + Write-Verbose("managedRuntimeLoader of AppPool $Name does not match the desired state."); + break + } + + #Check enableConfigurationOverride + if($PoolConfig.add.enableConfigurationOverride -ne $enableConfigurationOverride){ + $DesiredConfigurationMatch = $false + Write-Verbose("enableConfigurationOverride of AppPool $Name does not match the desired state."); + break + } + + #Check CLRConfigFile + if($PoolConfig.add.CLRConfigFile -ne $CLRConfigFile){ + $DesiredConfigurationMatch = $false + Write-Verbose("CLRConfigFile of AppPool $Name does not match the desired state."); + break + } + + #Check passAnonymousToken + if($PoolConfig.add.passAnonymousToken -ne $passAnonymousToken){ + $DesiredConfigurationMatch = $false + Write-Verbose("passAnonymousToken of AppPool $Name does not match the desired state."); + break + } + + #Check logonType + if($PoolConfig.add.processModel.logonType -ne $logonType){ + $DesiredConfigurationMatch = $false + Write-Verbose("logonType of AppPool $Name does not match the desired state."); + break + } + + #Check manualGroupMembership + if($PoolConfig.add.processModel.manualGroupMembership -ne $manualGroupMembership){ + $DesiredConfigurationMatch = $false + Write-Verbose("manualGroupMembership of AppPool $Name does not match the desired state."); + break + } + + #Check idleTimeout + if($PoolConfig.add.processModel.idleTimeout -ne $idleTimeout){ + $DesiredConfigurationMatch = $false + Write-Verbose("idleTimeout of AppPool $Name does not match the desired state."); + break + } + + #Check maxProcesses + if($PoolConfig.add.processModel.maxProcesses -ne $maxProcesses){ + $DesiredConfigurationMatch = $false + Write-Verbose("maxProcesses of AppPool $Name does not match the desired state."); + break + } + + #Check shutdownTimeLimit + if($PoolConfig.add.processModel.shutdownTimeLimit -ne $shutdownTimeLimit){ + $DesiredConfigurationMatch = $false + Write-Verbose("shutdownTimeLimit of AppPool $Name does not match the desired state."); + break + } + + #Check startupTimeLimit + if($PoolConfig.add.processModel.startupTimeLimit -ne $startupTimeLimit){ + $DesiredConfigurationMatch = $false + Write-Verbose("startupTimeLimit of AppPool $Name does not match the desired state."); + break + } + + #Check pingingEnabled + if($PoolConfig.add.processModel.pingingEnabled -ne $pingingEnabled){ + $DesiredConfigurationMatch = $false + Write-Verbose("pingingEnabled of AppPool $Name does not match the desired state."); + break + } + + #Check pingInterval + if($PoolConfig.add.processModel.pingInterval -ne $pingInterval){ + $DesiredConfigurationMatch = $false + Write-Verbose("pingInterval of AppPool $Name does not match the desired state."); + break + } -Export-ModuleMember -Function *-TargetResource + #Check pingResponseTime + if($PoolConfig.add.processModel.pingResponseTime -ne $pingResponseTime){ + $DesiredConfigurationMatch = $false + Write-Verbose("pingResponseTime of AppPool $Name does not match the desired state."); + break + } + #Check disallowOverlappingRotation + if($PoolConfig.add.recycling.disallowOverlappingRotation -ne $disallowOverlappingRotation){ + $DesiredConfigurationMatch = $false + Write-Verbose("disallowOverlappingRotation of AppPool $Name does not match the desired state."); + break + } + #Check disallowRotationOnConfigChange + if($PoolConfig.add.recycling.disallowRotationOnConfigChange -ne $disallowRotationOnConfigChange){ + $DesiredConfigurationMatch = $false + Write-Verbose("disallowRotationOnConfigChange of AppPool $Name does not match the desired state."); + break + } + + #Check logEventOnRecycle + if($PoolConfig.add.recycling.logEventOnRecycle -ne $logEventOnRecycle){ + $DesiredConfigurationMatch = $false + Write-Verbose("logEventOnRecycle of AppPool $Name does not match the desired state."); + break + } + + #Check restartMemoryLimit + if($PoolConfig.add.recycling.periodicRestart.memory -ne $restartMemoryLimit){ + $DesiredConfigurationMatch = $false + Write-Verbose("restartMemoryLimit of AppPool $Name does not match the desired state."); + break + } + + #Check restartPrivateMemoryLimit + if($PoolConfig.add.recycling.periodicRestart.privateMemory -ne $restartPrivateMemoryLimit){ + $DesiredConfigurationMatch = $false + Write-Verbose("restartPrivateMemoryLimit of AppPool $Name does not match the desired state."); + break + } + + #Check restartRequestsLimit + if($PoolConfig.add.recycling.periodicRestart.requests -ne $restartRequestsLimit){ + $DesiredConfigurationMatch = $false + Write-Verbose("restartRequestsLimit of AppPool $Name does not match the desired state."); + break + } + + #Check restartTimeLimit + if($PoolConfig.add.recycling.periodicRestart.time -ne $restartTimeLimit){ + $DesiredConfigurationMatch = $false + Write-Verbose("restartTimeLimit of AppPool $Name does not match the desired state."); + break + } + + #Check restartSchedule + if(($PoolConfig.add.recycling.periodicRestart.schedule.add.value -ne $null) -and ((Compare-Object $restartSchedule $PoolConfig.add.recycling.periodicRestart.schedule.add.value) -ne $null)){ + $DesiredConfigurationMatch = $false + Write-Verbose("restartTimeLimit of AppPool $Name does not match the desired state."); + break + } + if(($PoolConfig.add.recycling.periodicRestart.schedule.add.value -eq $null) -and ($restartSchedule -ne $null)){ + $DesiredConfigurationMatch = $false + Write-Verbose("restartTimeLimit of AppPool $Name does not match the desired state."); + break + } + + #Check loadBalancerCapabilities + if($PoolConfig.add.failure.loadBalancerCapabilities -ne $loadBalancerCapabilities){ + $DesiredConfigurationMatch = $false + Write-Verbose("loadBalancerCapabilities of AppPool $Name does not match the desired state."); + break + } + + #Check orphanWorkerProcess + if($PoolConfig.add.failure.orphanWorkerProcess -ne $orphanWorkerProcess){ + $DesiredConfigurationMatch = $false + Write-Verbose("orphanWorkerProcess of AppPool $Name does not match the desired state."); + break + } + + #Check orphanActionExe + if($PoolConfig.add.failure.orphanActionExe -ne $orphanActionExe){ + $DesiredConfigurationMatch = $false + Write-Verbose("orphanActionExe of AppPool $Name does not match the desired state."); + break + } + + #Check orphanActionParams + if($PoolConfig.add.failure.orphanActionParams -ne $orphanActionParams){ + $DesiredConfigurationMatch = $false + Write-Verbose("orphanActionParams of AppPool $Name does not match the desired state."); + break + } + + #Check rapidFailProtection + if($PoolConfig.add.failure.rapidFailProtection -ne $rapidFailProtection){ + $DesiredConfigurationMatch = $false + Write-Verbose("rapidFailProtection of AppPool $Name does not match the desired state."); + break + } + + #Check rapidFailProtectionInterval + if($PoolConfig.add.failure.rapidFailProtectionInterval -ne $rapidFailProtectionInterval){ + $DesiredConfigurationMatch = $false + Write-Verbose("rapidFailProtectionInterval of AppPool $Name does not match the desired state."); + break + } + + #Check rapidFailProtectionMaxCrashes + if($PoolConfig.add.failure.rapidFailProtectionMaxCrashes -ne $rapidFailProtectionMaxCrashes){ + $DesiredConfigurationMatch = $false + Write-Verbose("rapidFailProtectionMaxCrashes of AppPool $Name does not match the desired state."); + break + } + + #Check autoShutdownExe + if($PoolConfig.add.failure.autoShutdownExe -ne $autoShutdownExe){ + $DesiredConfigurationMatch = $false + Write-Verbose("autoShutdownExe of AppPool $Name does not match the desired state."); + break + } + + #Check autoShutdownParams + if($PoolConfig.add.failure.autoShutdownParams -ne $autoShutdownParams){ + $DesiredConfigurationMatch = $false + Write-Verbose("autoShutdownParams of AppPool $Name does not match the desired state."); + break + } + + #Check cpuLimit + if($PoolConfig.add.cpu.limit -ne $cpuLimit){ + $DesiredConfigurationMatch = $false + Write-Verbose("cpuLimit of AppPool $Name does not match the desired state."); + break + } + + #Check cpuAction + if($PoolConfig.add.cpu.action -ne $cpuAction){ + $DesiredConfigurationMatch = $false + Write-Verbose("cpuAction of AppPool $Name does not match the desired state."); + break + } + + #Check cpuResetInterval + if($PoolConfig.add.cpu.resetInterval -ne $cpuResetInterval){ + $DesiredConfigurationMatch = $false + Write-Verbose("cpuResetInterval of AppPool $Name does not match the desired state."); + break + } + + #Check cpuSmpAffinitized + if($PoolConfig.add.cpu.smpAffinitized -ne $cpuSmpAffinitized){ + $DesiredConfigurationMatch = $false + Write-Verbose("cpuSmpAffinitized of AppPool $Name does not match the desired state."); + break + } + + #Check cpuSmpProcessorAffinityMask + if($PoolConfig.add.cpu.smpProcessorAffinityMask -ne $cpuSmpProcessorAffinityMask){ + $DesiredConfigurationMatch = $false + Write-Verbose("cpuSmpProcessorAffinityMask of AppPool $Name does not match the desired state."); + break + } + + #Check cpuSmpProcessorAffinityMask2 + if($PoolConfig.add.cpu.smpProcessorAffinityMask2 -ne $cpuSmpProcessorAffinityMask2){ + $DesiredConfigurationMatch = $false + Write-Verbose("cpuSmpProcessorAffinityMask2 of AppPool $Name does not match the desired state."); + break + } + + } + + $Stop = $false + } + While($Stop) + + return $DesiredConfigurationMatch +} diff --git a/DSCResources/MSFT_xWebAppPool/MSFT_xWebAppPool.schema.mof b/DSCResources/MSFT_xWebAppPool/MSFT_xWebAppPool.schema.mof index b5bf7e46c..1a6de24be 100644 --- a/DSCResources/MSFT_xWebAppPool/MSFT_xWebAppPool.schema.mof +++ b/DSCResources/MSFT_xWebAppPool/MSFT_xWebAppPool.schema.mof @@ -1,12 +1,58 @@ -[ClassVersion("1.0.0.0"), FriendlyName("xWebAppPool")] +[ClassVersion("1.0.0.1"), FriendlyName("xWebAppPool")] class MSFT_xWebAppPool : OMI_BaseResource { [Key, Description("Name of the Web Application Pool")] String Name; [Write, Description("Web Application Pool Present/Absent"), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; - [Write, Description("State of Web Application Pool"), ValueMap{"Started","Stopped"}, Values{"Started","Stopped"}] String State; + [Write, Description("Should the app pool be started or stopped"), ValueMap{"Started","Stopped"}, Values{"Started","Stopped"}] String State; + [write, Description("When 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."), ValueMap{"true","false"},Values{"true","false"}] string autoStart; + [write, Description("Which version of .Net"), ValueMap{"v4.0","v2.0",""},Values{"v4.0","v2.0",""}] string managedRuntimeVersion; + [write, Description("Specifies the request-processing mode that is used to process requests for managed content."), ValueMap{"Integrated","Classic"},Values{"Integrated","Classic"}] string managedPipelineMode; + [write, Description("App pool always running or on demand"), ValueMap{"AlwaysRunning","OnDemand"},Values{"AlwaysRunning","OnDemand"}] string startMode; + + [write, Description("Who should the app pool run as"), ValueMap{"ApplicationPoolIdentity","LocalSystem","LocalService","NetworkService","SpecificUser"}, + Values{"ApplicationPoolIdentity","LocalSystem","LocalService","NetworkService","SpecificUser"}] + string identityType; + + [write, Description("User name if SpecificUser is choosen for app pool identity")] string userName; + [write, Description("Password of that user"), EmbeddedInstance("MSFT_Credential")] string Password; + [write, Description("Load the users profile"), ValueMap{"true","false"},Values{"true","false"}] string loadUserProfile; + [write, Description("Indicates to HTTP.sys how many requests to queue for an application pool before rejecting future requests.")] string queueLength; + [write, Description("When true, enables a 32-bit application to run on a computer that runs a 64-bit version of Windows."), ValueMap{"true","false"},Values{"true","false"}] string enable32BitAppOnWin64; + [write, Description("Specifies the managed loader to use for pre-loading the application pool.")] string managedRuntimeLoader; + [write, Description("When true, indicates that delegated settings in Web.config files will processed for applications within this application pool. When false, all settings in Web.config files will be ignored for this application pool."), ValueMap{"true","false"},Values{"true","false"}] string enableConfigurationOverride; + [write, Description("Specifies the .NET configuration file for the application pool.")] string CLRConfigFile; + [write, Description("If 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 PassAnonymousToken is false, the token will not be passed."), ValueMap{"true","false"},Values{"true","false"}] string passAnonymousToken; + [write, Description("Specifies the logon type for the process identity."), ValueMap{"LogonBatch","LogonService"},Values{"LogonBatch","LogonService"}] string logonType; + [write, Description("Specifies whether the IIS_IUSRS group Security Identifier (SID) is added to the worker process token. When false, IIS automatically uses an application pool identity as though it were a member of the built-in IIS_IUSRS group, which has access to necessary file and system resources. When true, an application pool identity must be explicitly added to all resources that a worker process requires at runtime."), ValueMap{"true","false"},Values{"true","false"}] string manualGroupMembership; + [write, Description("The period of time a worker process should run if no new requests are received and the worker process is not processing requests.")] string idleTimeout; + [write, Description("The number of worker processes associated with the application pool.")] string maxProcesses; + [write, Description("The period of time that IIS waits for requests to finish running in a worker process before IIS terminates the worker process.")] string shutdownTimeLimit; + [write, Description("The period of time that IIS waits for an application pool to start.")] string startupTimeLimit; + [write, Description("Pinging behavior for the worker process health monitoring."), ValueMap{"true","false"},Values{"true","false"}] string pingingEnabled; + [write, Description("The time interval between health-monitoring pings that the World Wide Web Publishing Service (WWW service) sends to a worker process.")] string pingInterval; + [write, Description("The period of time that a worker process is given to respond to a health-monitoring ping.")] string pingResponseTime; + [write, Description("Specifies whether or not the World Wide Web Publishing Service (WWW Service) should start up another worker process to replace the existing worker process while it is shutting down."), ValueMap{"true","false"},Values{"true","false"}] string disallowOverlappingRotation; + [write, Description("Specifies whether the WWW Service should rotate worker processes in an application pool when the configuration has changed."), ValueMap{"true","false"},Values{"true","false"}] string disallowRotationOnConfigChange; + [write, Description("Specifies that IIS should log an event when an application pool is recycled. The logEventOnRecycle property must have a bit set corresponding to the reason for the recycle if IIS is to log the event.")] string logEventOnRecycle; + [write, Description("Restart app pool when memory exceeds this limit")] string restartMemoryLimit; + [write, Description("Restart app pool when private memory exceeds this limit")] string restartPrivateMemoryLimit; + [write, Description("Specifies that the worker process should be recycled after it processes a specific number of requests.")] string restartRequestsLimit; + [write, Description("Specifies that the worker process should be recycled after a specified amount of time has elapsed.")] string restartTimeLimit; + [write, Description("Restart the app pool at these times")] string restartSchedule[]; + [write, Description("The response behavior of a service when it is unavailable."), ValueMap{"HttpLevel","TcpLevel"},Values{"HttpLevel","TcpLevel"}] string loadBalancerCapabilities; + [write, Description("Value indicating whether to put a worker process in an orphan state when an application pool fails."), ValueMap{"true","false"},Values{"true","false"}] string orphanWorkerProcess; + [write, Description("The path of an executable to run when the service orphans a worker process.")] string orphanActionExe; + [write, Description("Command-line parameters for the executable named by the OrphanActionExe property.")] string orphanActionParams; + [write, Description("Value indicating whether rapid-fail protection is on or off."), ValueMap{"true","false"},Values{"true","false"}] string rapidFailProtection; + [write, Description("The interval of time before the failure count for a process is reset.")] string rapidFailProtectionInterval; + [write, Description("The maximum number of failures allowed within the time specified by the RapidFailProtectionInterval property.")] string rapidFailProtectionMaxCrashes; + [write, Description("The path of an executable to run when the service shuts down an application pool for rapid-fail protection.")] string autoShutdownExe; + [write, Description("Command-line parameters for the executable that is specified by the AutoShutdownExe property.")] string autoShutdownParams; + [write, Description("Configures the maximum percentage of CPU time (in 1/1000ths of one percent) that the worker processes in an application pool are allowed to consume over a period of time as indicated by the resetInterval attribute.")] string cpuLimit; + [write, Description("Configures the action that IIS takes when a worker process exceeds its configured CPU limit. "), ValueMap{"NoAction","KillW3wp","Throttle","ThrottleUnderLoad"},Values{"NoAction","KillW3wp","Throttle","ThrottleUnderLoad"}] string cpuAction; + [write, Description("Specifies the reset period (in minutes) for CPU monitoring and throttling limits on an application pool.")] string cpuResetInterval; + [write, Description("Specifies whether a particular worker process assigned to an application pool should also be assigned to a given CPU."), ValueMap{"true","false"},Values{"true","false"}] string cpuSmpAffinitized; + [write, Description("Specifies the hexadecimal processor mask for multi-processor computers, which indicates to which CPU the worker processes in an application pool should be bound. Before this property takes effect, the smpAffinitized attribute must be set to true for the application pool.")] string cpuSmpProcessorAffinityMask; + [write, Description("Specifies the high-order DWORD hexadecimal processor mask for 64-bit multi-processor computers, which indicates to which CPU the worker processes in an application pool should be bound. Before this property takes effect, the smpAffinitized attribute must be set to true for the application pool.")] string cpuSmpProcessorAffinityMask2; }; - - - - diff --git a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 index ac74a587b..fa507b465 100644 --- a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 +++ b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 @@ -1,160 +1,160 @@ -###################################################################################### -# DSC Resource for IIS Server level Application Ppol Defaults -# ApplicationHost.config: system.applicationHost/applicationPools -# -# only a limited number of settings are supported at this time -# We try to cover the most common use cases -# We have a single parameter for each setting -###################################################################################### -data LocalizedData -{ - # culture="en-US" - ConvertFrom-StringData @' -NoWebAdministrationModule=Please ensure that WebAdministration module is installed. -SettingValue=Changing default value '{0}' to '{1}' -ValueOk=Default value '{0}' is already '{1}' -'@ -} - -function Get-TargetResource -{ - [CmdletBinding()] - [OutputType([System.Collections.Hashtable])] - param - ( - [Parameter(Mandatory)] - [ValidateSet('Machine')] - [string]$ApplyTo - ) - - # Check if WebAdministration module is present for IIS cmdlets - CheckIISPoshModule - - return @{ManagedRuntimeVersion = (GetValue -Path '' -Name 'managedRuntimeVersion') - IdentityType = ( GetValue -Path 'processModel' -Name 'identityType')} -} - - -function Set-TargetResource -{ - [CmdletBinding()] - param - ( - [ValidateSet('Machine')] - [parameter(Mandatory = $true)] - [string]$ApplyTo, - # in the future there will be another CLR version to be allowed - [ValidateSet('','v2.0','v4.0')] - [string]$ManagedRuntimeVersion, - # TODO: we currently don't allow a custom identity - [ValidateSet('ApplicationPoolIdentity','LocalService','LocalSystem','NetworkService')] - [string]$IdentityType - ) - - CheckIISPoshModule - - SetValue -Path '' -Name 'managedRuntimeVersion' -NewValue $ManagedRuntimeVersion - SetValue -Path 'processModel' -Name 'identityType' -NewValue $IdentityType -} - - -function Test-TargetResource -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [ValidateSet('Machine')] - [parameter(Mandatory = $true)] - [string]$ApplyTo, - [ValidateSet('','v2.0','v4.0')] - [string]$ManagedRuntimeVersion, - [ValidateSet('ApplicationPoolIdentity','LocalService','LocalSystem','NetworkService')] - [string]$IdentityType - ) - - CheckIISPoshModule - - if (!(CheckValue -Path '' -Name 'managedRuntimeVersion' -NewValue $ManagedRuntimeVersion)) - { - return $false - } - - if (!(CheckValue -Path 'processModel' -Name 'identityType' -NewValue $IdentityType)) - { - return $false - } - - return $true -} - -###################################################################################### -# Helper Functions -###################################################################################### - -Function CheckValue([string]$path,[string]$name,[string]$newValue) -{ - - if (!$newValue) - { - # if no new value was specified, we assume this value is okay. - return $true - } - - $existingValue = GetValue -Path $path -Name $name - if ($existingValue -ne $newValue) - { - return $false - } - else - { - $relPath = $path + '/' + $name - Write-Verbose($LocalizedData.ValueOk -f $relPath,$newValue); - return $true - } -} - -# some internal helper function to do the actual work: - -Function SetValue([string]$path,[string]$name,[string]$newValue) -{ - # if the variable doesn't exist, the user doesn't want to change this value - if (!$newValue) - { - return - } - - $existingValue = GetValue -Path $path -Name $name - if ($existingValue -ne $newValue) - { - if ($path -ne '') - { - $path = '/' + $path - } - - 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); - } -} - -Function GetValue([string]$path,[string]$name) -{ - if ($path -ne '') - { - $path = '/' + $path - } - - return Get-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/applicationPools/applicationPoolDefaults$path" -name $name -} - -Function CheckIISPoshModule -{ - # Check if WebAdministration module is present for IIS cmdlets - if(!(Get-Module -ListAvailable -Name WebAdministration)) - { - Throw $LocalizedData.NoWebAdministrationModule - } -} - -Export-ModuleMember -Function *-TargetResource +###################################################################################### +# DSC Resource for IIS Server level Application Ppol Defaults +# ApplicationHost.config: system.applicationHost/applicationPools +# +# only a limited number of settings are supported at this time +# We try to cover the most common use cases +# We have a single parameter for each setting +###################################################################################### +data LocalizedData +{ + # culture="en-US" + ConvertFrom-StringData @' +NoWebAdministrationModule=Please ensure that WebAdministration module is installed. +SettingValue=Changing default value '{0}' to '{1}' +ValueOk=Default value '{0}' is already '{1}' +'@ +} + +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory)] + [ValidateSet('Machine')] + [string]$ApplyTo + ) + + # Check if WebAdministration module is present for IIS cmdlets + CheckIISPoshModule + + return @{ManagedRuntimeVersion = (GetValue -Path '' -Name 'managedRuntimeVersion') + IdentityType = ( GetValue -Path 'processModel' -Name 'identityType')} +} + + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [ValidateSet('Machine')] + [parameter(Mandatory = $true)] + [string]$ApplyTo, + # in the future there will be another CLR version to be allowed + [ValidateSet('','v2.0','v4.0')] + [string]$ManagedRuntimeVersion, + # TODO: we currently don't allow a custom identity + [ValidateSet('ApplicationPoolIdentity','LocalService','LocalSystem','NetworkService')] + [string]$IdentityType + ) + + CheckIISPoshModule + + SetValue -Path '' -Name 'managedRuntimeVersion' -NewValue $ManagedRuntimeVersion + SetValue -Path 'processModel' -Name 'identityType' -NewValue $IdentityType +} + + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [ValidateSet('Machine')] + [parameter(Mandatory = $true)] + [string]$ApplyTo, + [ValidateSet('','v2.0','v4.0')] + [string]$ManagedRuntimeVersion, + [ValidateSet('ApplicationPoolIdentity','LocalService','LocalSystem','NetworkService')] + [string]$IdentityType + ) + + CheckIISPoshModule + + if (!(CheckValue -Path '' -Name 'managedRuntimeVersion' -NewValue $ManagedRuntimeVersion)) + { + return $false + } + + if (!(CheckValue -Path 'processModel' -Name 'identityType' -NewValue $IdentityType)) + { + return $false + } + + return $true +} + +###################################################################################### +# Helper Functions +###################################################################################### + +Function CheckValue([string]$path,[string]$name,[string]$newValue) +{ + + if (!$newValue) + { + # if no new value was specified, we assume this value is okay. + return $true + } + + $existingValue = GetValue -Path $path -Name $name + if ($existingValue -ne $newValue) + { + return $false + } + else + { + $relPath = $path + '/' + $name + Write-Verbose($LocalizedData.ValueOk -f $relPath,$newValue); + return $true + } +} + +# some internal helper function to do the actual work: + +Function SetValue([string]$path,[string]$name,[string]$newValue) +{ + # if the variable doesn't exist, the user doesn't want to change this value + if (!$newValue) + { + return + } + + $existingValue = GetValue -Path $path -Name $name + if ($existingValue -ne $newValue) + { + if ($path -ne '') + { + $path = '/' + $path + } + + 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); + } +} + +Function GetValue([string]$path,[string]$name) +{ + if ($path -ne '') + { + $path = '/' + $path + } + + return Get-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/applicationPools/applicationPoolDefaults$path" -name $name +} + +Function CheckIISPoshModule +{ + # Check if WebAdministration module is present for IIS cmdlets + if(!(Get-Module -ListAvailable -Name WebAdministration)) + { + Throw $LocalizedData.NoWebAdministrationModule + } +} + +Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.schema.mof b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.schema.mof index 99b2f22c9..a8df17059 100644 --- a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.schema.mof +++ b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.schema.mof @@ -1,8 +1,8 @@ - -[ClassVersion("1.0.0.0"), FriendlyName("xWebAppPoolDefaults")] -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; -}; + +[ClassVersion("1.0.0.0"), FriendlyName("xWebAppPoolDefaults")] +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; +}; diff --git a/Examples/Sample_xWebsite_NewWebsite.ps1 b/Examples/Sample_xWebsite_NewWebsite.ps1 index 6decebf14..63bc211ff 100644 --- a/Examples/Sample_xWebsite_NewWebsite.ps1 +++ b/Examples/Sample_xWebsite_NewWebsite.ps1 @@ -9,6 +9,11 @@ configuration Sample_xWebsite_NewWebsite [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String]$WebSiteName, + + # Name of the app pool + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [String]$AppPoolName, # Source Path for Website content [Parameter(Mandatory)] @@ -61,6 +66,19 @@ configuration Sample_xWebsite_NewWebsite DependsOn = '[WindowsFeature]AspNet45' } + # Create the new AppPool + xWebAppPool NewAppPool + { + Name = $AppPoolName + Ensure = "Present" + autoStart = "true" + managedRuntimeVersion = "v4.0" + managedPipelineMode = "Integrated" + startMode = "AlwaysRunning" + identityType = "ApplicationPoolIdentity" + restartSchedule = @("18:30:00","05:00:00") + } + # Create the new Website xWebsite NewWebsite { diff --git a/Tests/MSFT_xWebAppPool.Tests.ps1 b/Tests/MSFT_xWebAppPool.Tests.ps1 new file mode 100644 index 000000000..cf0d5d2da --- /dev/null +++ b/Tests/MSFT_xWebAppPool.Tests.ps1 @@ -0,0 +1,848 @@ +$here = Split-Path -Parent $MyInvocation.MyCommand.Path +$ModuleName = "MSFT_xWebAppPool" + +# Add web server if not already installed +if(!(Get-WindowsFeature web-server).Installed) +{ + Add-WindowsFeature Web-Server -Verbose +} + +Import-Module (Join-Path $here -ChildPath "..\DSCResources\$ModuleName\$ModuleName.psm1") + +if (! (Get-Module xDSCResourceDesigner)) +{ + Import-Module -Name xDSCResourceDesigner +} + +if (! (Get-Module WebAdministration)) +{ + Import-Module -Name WebAdministration +} + +Describe "MSFT_xWebAppPool"{ + InModuleScope $ModuleName { + + Function TestTargetResourceSame + { + <# + + .SYNOPSIS + Runs Test-TargetResource on $option so that when $option has the same value as the current setting, $true is returned. + + .DESCRIPTION + Runs Test-TargetResource on $option so that when $option has the same value as the current setting, $true is returned. + + .PARAMETER option + should be the name of the option as defined in xWebAppPool.psm1 + + .PARAMETER appcmdVal + The location that would be used to get this value via appcmd. + + .EXAMPLE + TestTargetResourceSame "autoStart" "add.autoStart" + + #> + [OutputType([bool])] + Param($option,$appcmdVal) + $testParams =@{ + Name = "PesterAppPool" + Ensure = "Present" + } + $curVal = Invoke-Expression "(([xml](& $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool 'PesterAppPool' /config:*)).$($appcmdVal))" + #Write-Host $curVal + $testParams.Add($option,$curVal) + + #need to explicitly set it to the current value. This "resets" all the values so the previous tests don't interfere with this one + Set-TargetResource @testParams | Out-Null #So we only return the value of Test-TargetResource below + + Test-TargetResource @testParams + Return + } + + Function TestTargetResourceDiff + { + <# + + .SYNOPSIS + Runs Test-TargetResource on $option so that when $option has a different value as the current setting, $false is returned. + + .DESCRIPTION + Runs Test-TargetResource on $option so that when $option has a different value as the current setting, $false is returned. + + .PARAMETER option + should be the name of the option as defined in xWebAppPool.psm1 + + .PARAMETER appcmdVal + The location that would be used to get this value via appcmd. + + .PARAMETER value1 + A valid value that $option can be set to. Must be different than value2 + + .PARAMETER value2 + A valid value that $option can be set to. Must be different than value1 + + .EXAMPLE + TestTargetResourceDiff "autoStart" "add.autoStart" "true" "false" + + #> + [OutputType([bool])] + Param($option,$appcmdVal,$value1,$value2) + $testParams =@{ + Name = "PesterAppPool" + Ensure = "Present" + } + + $testParams.Add($option,$value1) + Set-TargetResource @testParams | Out-Null #So we only return the value of Test-TargetResource below + $testParams.$option = $value2 + Test-TargetResource @testParams + Return + } + + Function SetTargetResource + { + <# + + .SYNOPSIS + Runs Set-TargetResource on $option and then verifies that option was changed to the desired value. Returns $true if option was changed. + + .DESCRIPTION + Runs Set-TargetResource on $option and then verifies that option was changed to the desired value. Returns $true if option was changed. + + .PARAMETER option + should be the name of the option as defined in xWebAppPool.psm1 + + .PARAMETER appcmdVal + The location that would be used to get this value via appcmd. + + .PARAMETER value1 + A valid value that $option can be set to. Must be different than value2 + + .PARAMETER value2 + A valid value that $option can be set to. Must be different than value1 + + .EXAMPLE + SetTargetResource "autoStart" "add.autoStart" "true" "false" + + #> + [OutputType([bool])] + Param($option,$appcmdVal,$value1,$value2) + $testParams =@{ + Name = "PesterAppPool" + Ensure = "Present" + } + $curVal = Invoke-Expression "(([xml](& $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool 'PesterAppPool' /config:*)).$($appcmdVal))" + if($curVal -ne $value1) + { + $testParams.Add($option,$value1) + Set-TargetResource @testParams | Out-Null #So we only return the value of Test-TargetResource below + $curVal = Invoke-Expression "(([xml](& $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool 'PesterAppPool' /config:*)).$($appcmdVal))" + $value1 -eq $curVal + } + else + { + $testParams.Add($option,$value2) + Set-TargetResource @testParams | Out-Null #So we only return the value of Test-TargetResource below + $curVal = Invoke-Expression "(([xml](& $env:SystemRoot\system32\inetsrv\appcmd.exe list apppool 'PesterAppPool' /config:*)).$($appcmdVal))" + $value2 -eq $curVal + } + + Return + } + + try + { + $baseParams =@{ + Name = "PesterAppPool" + Ensure = "Present" + } + + It 'Should pass Test-xDscResource Schema Validation' { + $result = Test-xDscResource MSFT_xWebAppPool + $result | Should Be $true + } + + It 'Should create a new App Pool' { + $testParams =@{ + Name = "PesterAppPool" + Ensure = "Present" + } + + # if the app pool exists, remove it + if((Get-ChildItem IIS:\apppools).Name.Contains("PesterAppPool")) + { + Remove-WebAppPool -Name "PesterAppPool" -ErrorAction Stop + } + + Set-TargetResource @testParams + (Get-ChildItem IIS:\apppools).Name.Contains("PesterAppPool") | Should be $true + } + + Context 'Test-TargetResource' { + It 'Passes test when App Pool does exist'{ + $testParams =@{ + Name = "PesterAppPool" + Ensure = "Present" + } + + Test-TargetResource @testParams | Should be $true + } + + It 'Passes autoStart Test when same' { + + TestTargetResourceSame "autoStart" "add.autoStart" | Should be $true + } + + It 'Fails autoStart Test when different' { + + TestTargetResourceDiff "autoStart" "add.autoStart" "true" "false"| Should be $false + } + + It 'Passes Runtime Version Test when same' { + + TestTargetResourceSame "managedRuntimeVersion" "add.managedRuntimeVersion" | Should be $true + } + + It 'Fails Runtime Version Test when different' { + + TestTargetResourceDiff "managedRuntimeVersion" "add.managedRuntimeVersion" "v4.0" "v2.0" | Should be $false + } + + It 'Passes Managed Pipeline Mode Test when same' { + + TestTargetResourceSame "managedPipelineMode" "add.managedPipelineMode" | Should be $true + } + + It 'Fails Managed Pipeline Mode Test when different' { + + TestTargetResourceDiff "managedPipelineMode" "add.managedPipelineMode" "Integrated" "Classic"| Should be $false + } + + It 'Passes Start Mode Test when same' { + + TestTargetResourceSame "startMode" "add.startMode" | Should be $true + } + + It 'Fails Start Mode Test when different' { + + TestTargetResourceDiff "startMode" "add.startMode" "AlwaysRunning" "OnDemand" | Should be $false + } + + It 'Passes Identity Type Test when same' { + + TestTargetResourceSame "identityType" "add.processModel.identityType" | Should be $true + } + + It 'Fails Identity Type Test when different' { + + TestTargetResourceDiff "identityType" "add.processModel.identityType" "ApplicationPoolIdentity" "LocalSystem" | Should be $false + } + + It 'Passes Load User Profile Test when same' { + + TestTargetResourceSame "loadUserProfile" "add.processModel.loadUserProfile" | Should be $true + } + + It 'Fails Load User Profile Test when different' { + + TestTargetResourceDiff "loadUserProfile" "add.processModel.loadUserProfile" "true" "false" | Should be $false + } + + It 'Passes Queue Length Test when same' { + + TestTargetResourceSame "queueLength" "add.queueLength" | Should be $true + } + + It 'Fails Queue Length Test when different' { + + TestTargetResourceDiff "queueLength" "add.queueLength" "2000" "1000" | Should be $false + } + + It 'Passes Enable 32bit Test when same' { + + TestTargetResourceSame "enable32BitAppOnWin64" "add.enable32BitAppOnWin64" | Should be $true + } + + It 'Fails Enable 32bit Test when different' { + + TestTargetResourceDiff "enable32BitAppOnWin64" "add.enable32BitAppOnWin64" "true" "false" | Should be $false + } + + It 'Passes Config Override Test when same' { + + TestTargetResourceSame "enableConfigurationOverride" "add.enableConfigurationOverride" | Should be $true + } + + It 'Fails Config Override Test when different' { + + TestTargetResourceDiff "enableConfigurationOverride" "add.enableConfigurationOverride" "true" "false" | Should be $false + } + + It 'Passes Pass Anon Token Test when same' { + + TestTargetResourceSame "passAnonymousToken" "add.passAnonymousToken" | Should be $true + } + + It 'Fails Pass Anon Token Test when different' { + + TestTargetResourceDiff "passAnonymousToken" "add.passAnonymousToken" "true" "false" | Should be $false + } + + It 'Passes Logon Type Test when same' { + + TestTargetResourceSame "logonType" "add.processModel.logonType" | Should be $true + } + + It 'Fails Logon Type Test when different' { + + TestTargetResourceDiff "logonType" "add.processModel.logonType" "LogonService" "LogonBatch" | Should be $false + } + + It 'Passes Manual Group Membership Test when same' { + + TestTargetResourceSame "manualGroupMembership" "add.processModel.manualGroupMembership" | Should be $true + } + + It 'Fails Manual Group Membership Test when different' { + + TestTargetResourceDiff "manualGroupMembership" "add.processModel.manualGroupMembership" "true" "false" | Should be $false + } + + It 'Passes Idle Timeout Test when same' { + + TestTargetResourceSame "idleTimeout" "add.processModel.idleTimeout" | Should be $true + } + + It 'Fails Idle Timeout Test when different' { + + TestTargetResourceDiff "idleTimeout" "add.processModel.idleTimeout" "00:25:00" "00:20:00" | Should be $false + } + + It 'Passes Max Processes Test when same' { + + TestTargetResourceSame "maxProcesses" "add.processModel.maxProcesses" | Should be $true + } + + It 'Fails Max Processes Test when different' { + + TestTargetResourceDiff "maxProcesses" "add.processModel.maxProcesses" "2" "1" | Should be $false + } + + It 'Passes Shutdown Time Limit Test when same' { + + TestTargetResourceSame "shutdownTimeLimit" "add.processModel.shutdownTimeLimit" | Should be $true + } + + It 'Fails Shutdown Time Limit Test when different' { + + TestTargetResourceDiff "shutdownTimeLimit" "add.processModel.shutdownTimeLimit" "00:02:30" "00:01:30" | Should be $false + } + + It 'Passes Startup Time Limit Test when same' { + + TestTargetResourceSame "startupTimeLimit" "add.processModel.startupTimeLimit" | Should be $true + } + + It 'Fails Startup Time Limit Test when different' { + + TestTargetResourceDiff "startupTimeLimit" "add.processModel.startupTimeLimit" "00:02:30" "00:01:30" | Should be $false + } + + It 'Passes Ping Interval Test when same' { + + TestTargetResourceSame "pingInterval" "add.processModel.pingInterval" | Should be $true + } + + It 'Fails Ping Interval Test when different' { + + TestTargetResourceDiff "pingInterval" "add.processModel.pingInterval" "00:02:30" "00:01:30" | Should be $false + } + + It 'Passes Ping Response Test when same' { + + TestTargetResourceSame "pingResponseTime" "add.processModel.pingResponseTime" | Should be $true + } + + It 'Fails Ping Response Test when different' { + + TestTargetResourceDiff "pingResponseTime" "add.processModel.pingResponseTime" "00:02:30" "00:01:30" | Should be $false + } + + It 'Passes Ping Enabled Test when same' { + + TestTargetResourceSame "pingingEnabled" "add.processModel.pingingEnabled" | Should be $true + } + + It 'Fails Ping Enabled Test when different' { + + TestTargetResourceDiff "pingingEnabled" "add.processModel.pingingEnabled" "true" "false" | Should be $false + } + + It 'Passes Disallow Overlapping Rotation Test when same' { + + TestTargetResourceSame "disallowOverlappingRotation" "add.recycling.disallowOverlappingRotation" | Should be $true + } + + It 'Fails Disallow Overlapping Rotation Test when different' { + + TestTargetResourceDiff "disallowOverlappingRotation" "add.recycling.disallowOverlappingRotation" "true" "false" | Should be $false + } + + It 'Passes Disallow Rotation On Config Change Test when same' { + + TestTargetResourceSame "disallowRotationOnConfigChange" "add.recycling.disallowRotationOnConfigChange" | Should be $true + } + + It 'Fails Disallow Rotation On Config Change Test when different' { + + TestTargetResourceDiff "disallowRotationOnConfigChange" "add.recycling.disallowRotationOnConfigChange" "true" "false" | Should be $false + } + + It 'Passes Log Event On Recycle Test when same' { + + TestTargetResourceSame "logEventOnRecycle" "add.recycling.logEventOnRecycle" | Should be $true + } + + It 'Fails Log Event On Recycle Test when different' { + + TestTargetResourceDiff "logEventOnRecycle" "add.recycling.logEventOnRecycle" "Time, Memory" "Time, Memory, PrivateMemory" | Should be $false + } + + It 'Passes Restart Mem Limit Test when same' { + + TestTargetResourceSame "restartMemoryLimit" "add.recycling.periodicRestart.memory" | Should be $true + } + + It 'Fails Restart Mem Limit Test when different' { + + TestTargetResourceDiff "restartMemoryLimit" "add.recycling.periodicRestart.memory" "0" "1" | Should be $false + } + + It 'Passes Restart Private Mem Limit Test when same' { + + TestTargetResourceSame "restartPrivateMemoryLimit" "add.recycling.periodicRestart.privateMemory" | Should be $true + } + + It 'Fails Restart Private Mem Limit Test when different' { + + TestTargetResourceDiff "restartPrivateMemoryLimit" "add.recycling.periodicRestart.privateMemory" "0" "1" | Should be $false + } + + It 'Passes Restart Requests Limit Test when same' { + + TestTargetResourceSame "restartRequestsLimit" "add.recycling.periodicRestart.requests" | Should be $true + } + + It 'Fails Restart Requests Limit Test when different' { + + TestTargetResourceDiff "restartRequestsLimit" "add.recycling.periodicRestart.requests" "0" "1" | Should be $false + } + + It 'Passes Restart Time Limit Test when same' { + + TestTargetResourceSame "restartTimeLimit" "add.recycling.periodicRestart.time" | Should be $true + } + + It 'Fails Restart Time Limit Test when different' { + + TestTargetResourceDiff "restartTimeLimit" "add.recycling.periodicRestart.time" "2.05:00:00" "1.05:00:00" | Should be $false + } + + It 'Passes Restart Schedule Test when same' { + + TestTargetResourceSame "restartSchedule" "add.recycling.periodicRestart.schedule.add.value" | Should be $true + } + + It 'Fails Restart Schedule Test when different' { + + TestTargetResourceDiff "restartSchedule" "add.recycling.periodicRestart.schedule.add.value" "18:30:00" "10:30:00" | Should be $false + } + + It 'Passes Load Balancer Capabilities Test when same' { + + TestTargetResourceSame "loadBalancerCapabilities" "add.failure.loadBalancerCapabilities" | Should be $true + } + + It 'Fails Load Balancer Capabilities Test when different' { + + TestTargetResourceDiff "loadBalancerCapabilities" "add.failure.loadBalancerCapabilities" "HttpLevel" "TcpLevel" | Should be $false + } + + It 'Passes Orphan Worker Process Test when same' { + + TestTargetResourceSame "orphanWorkerProcess" "add.failure.orphanWorkerProcess" | Should be $true + } + + It 'Fails Orphan Worker Process Test when different' { + + TestTargetResourceDiff "orphanWorkerProcess" "add.failure.orphanWorkerProcess" "true" "false" | Should be $false + } + + It 'Passes Orphan Action Exe Test when same' { + + TestTargetResourceSame "orphanActionExe" "add.failure.orphanActionExe" | Should be $true + } + + It 'Fails Orphan Action Exe Test when different' { + + TestTargetResourceDiff "orphanActionExe" "add.failure.orphanActionExe" "test.exe" "test1.exe" | Should be $false + } + + It 'Passes Orphan Action Params Test when same' { + + TestTargetResourceSame "orphanActionParams" "add.failure.orphanActionParams" | Should be $true + } + + It 'Fails Orphan Action Params Test when different' { + + TestTargetResourceDiff "orphanActionParams" "add.failure.orphanActionParams" "test.exe" "test1.exe" | Should be $false + } + + It 'Passes Rapid Fail Protection Test when same' { + + TestTargetResourceSame "rapidFailProtection" "add.failure.rapidFailProtection" | Should be $true + } + + It 'Fails Rapid Fail Protection Test when different' { + + TestTargetResourceDiff "rapidFailProtection" "add.failure.rapidFailProtection" "true" "false" | Should be $false + } + + It 'Passes Rapid Fail Protection Interval Test when same' { + + TestTargetResourceSame "rapidFailProtectionInterval" "add.failure.rapidFailProtectionInterval" | Should be $true + } + + It 'Fails Rapid Fail Protection Interval Test when different' { + + TestTargetResourceDiff "rapidFailProtectionInterval" "add.failure.rapidFailProtectionInterval" "00:15:00" "00:05:00" | Should be $false + } + + It 'Passes Rapid Fail Protection Interval Max Crashes Test when same' { + + TestTargetResourceSame "rapidFailProtectionMaxCrashes" "add.failure.rapidFailProtectionMaxCrashes" | Should be $true + } + + It 'Fails Rapid Fail Protection Interval Max Crashes Test when different' { + + TestTargetResourceDiff "rapidFailProtectionMaxCrashes" "add.failure.rapidFailProtectionMaxCrashes" "15:00" "05" | Should be $false + } + + It 'Passes Auto Shutdown Exe Test when same' { + + TestTargetResourceSame "autoShutdownExe" "add.failure.autoShutdownExe" | Should be $true + } + + It 'Fails Auto Shutdown Exe Test when different' { + + TestTargetResourceDiff "autoShutdownExe" "add.failure.autoShutdownExe" "test.exe" "test1.exe" | Should be $false + } + + It 'Passes Auto Shutdown Params Test when same' { + + TestTargetResourceSame "autoShutdownParams" "add.failure.autoShutdownParams" | Should be $true + } + + It 'Fails Auto Shutdown Params Test when different' { + + TestTargetResourceDiff "autoShutdownParams" "add.failure.autoShutdownParams" "test.exe" "test1.exe" | Should be $false + } + + It 'Passes CPU Limit Test when same' { + + TestTargetResourceSame "cpuLimit" "add.cpu.limit" | Should be $true + } + + It 'Fails CPU Limit Test Test when different' { + + TestTargetResourceDiff "cpuLimit" "add.cpu.limit" "1" "0" | Should be $false + } + + It 'Passes CPU Action Test when same' { + + TestTargetResourceSame "cpuAction" "add.cpu.action" | Should be $true + } + + It 'Fails CPU Action Test when different' { + + TestTargetResourceDiff "cpuAction" "add.cpu.action" "Throttle" "NoAction" | Should be $false + } + + It 'Passes CPU Reset Interval Test when same' { + + TestTargetResourceSame "cpuResetInterval" "add.cpu.resetInterval" | Should be $true + } + + It 'Fails CPU Reset Interval Test when different' { + + TestTargetResourceDiff "cpuResetInterval" "add.cpu.resetInterval" "00:15:00" "00:05:00" | Should be $false + } + + It 'Passes CPU Smp Affinitized Test when same' { + + TestTargetResourceSame "cpuSmpAffinitized" "add.cpu.smpAffinitized" | Should be $true + } + + It 'Fails CPU Smp Affinitized Test when different' { + + TestTargetResourceDiff "cpuSmpAffinitized" "add.cpu.smpAffinitized" "true" "false" | Should be $false + } + + It 'Passes CPU Smp Processor Affinity Mask Test when same' { + + TestTargetResourceSame "cpuSmpProcessorAffinityMask" "add.cpu.smpProcessorAffinityMask" | Should be $true + } + + It 'Fails CPU Smp Processor Affinity Mask Test when different' { + + TestTargetResourceDiff "cpuSmpProcessorAffinityMask" "add.cpu.smpProcessorAffinityMask" "4294967294" "4294967295" | Should be $false + } + + It 'Passes CPU Smp Processor Affinity Mask 2 Test when same' { + + TestTargetResourceSame "cpuSmpProcessorAffinityMask2" "add.cpu.smpProcessorAffinityMask2" | Should be $true + } + + It 'Fails CPU Smp Processor Affinity Mask 2 Test when different' { + + TestTargetResourceDiff "cpuSmpProcessorAffinityMask2" "add.cpu.smpProcessorAffinityMask2" "4294967294" "4294967295" | Should be $false + } + } + + Context 'Set-TargetResource' { + It 'Should set autoStart Test ' { + + SetTargetResource "autoStart" "add.autoStart" "true" "false"| Should be $true + } + + It 'Should set Runtime Version Test ' { + + SetTargetResource "managedRuntimeVersion" "add.managedRuntimeVersion" "v4.0" "v2.0" | Should be $true + } + + It 'Should set Managed Pipeline Mode Test ' { + + SetTargetResource "managedPipelineMode" "add.managedPipelineMode" "Integrated" "Classic"| Should be $true + } + + It 'Should set Start Mode Test ' { + + SetTargetResource "startMode" "add.startMode" "AlwaysRunning" "OnDemand" | Should be $true + } + + It 'Should set Identity Type Test ' { + + SetTargetResource "identityType" "add.processModel.identityType" "ApplicationPoolIdentity" "LocalSystem" | Should be $true + } + + It 'Should set Load User Profile Test ' { + + SetTargetResource "loadUserProfile" "add.processModel.loadUserProfile" "true" "false" | Should be $true + } + + It 'Should set Queue Length Test ' { + + SetTargetResource "queueLength" "add.queueLength" "2000" "1000" | Should be $true + } + + It 'Should set Enable 32bit Test ' { + + SetTargetResource "enable32BitAppOnWin64" "add.enable32BitAppOnWin64" "true" "false" | Should be $true + } + + It 'Should set Config Override Test ' { + + SetTargetResource "enableConfigurationOverride" "add.enableConfigurationOverride" "true" "false" | Should be $true + } + + It 'Should set Pass Anon Token Test ' { + + SetTargetResource "passAnonymousToken" "add.passAnonymousToken" "true" "false" | Should be $true + } + + It 'Should set Logon Type Test ' { + + SetTargetResource "logonType" "add.processModel.logonType" "LogonService" "LogonBatch" | Should be $true + } + + It 'Should set Manual Group Membership Test ' { + + SetTargetResource "manualGroupMembership" "add.processModel.manualGroupMembership" "true" "false" | Should be $true + } + + It 'Should set Idle Timeout Test ' { + + SetTargetResource "idleTimeout" "add.processModel.idleTimeout" "00:25:00" "00:20:00" | Should be $true + } + + It 'Should set Max Processes Test ' { + + SetTargetResource "maxProcesses" "add.processModel.maxProcesses" "2" "1" | Should be $true + } + + It 'Should set Shutdown Time Limit Test ' { + + SetTargetResource "shutdownTimeLimit" "add.processModel.shutdownTimeLimit" "00:02:30" "00:01:30" | Should be $true + } + + It 'Should set Startup Time Limit Test ' { + + SetTargetResource "startupTimeLimit" "add.processModel.startupTimeLimit" "00:02:30" "00:01:30" | Should be $true + } + + It 'Should set Ping Interval Test ' { + + SetTargetResource "pingInterval" "add.processModel.pingInterval" "00:02:30" "00:01:30" | Should be $true + } + + It 'Should set Ping Response Test ' { + + SetTargetResource "pingResponseTime" "add.processModel.pingResponseTime" "00:02:30" "00:01:30" | Should be $true + } + + It 'Should set Ping Enabled Test ' { + + SetTargetResource "pingingEnabled" "add.processModel.pingingEnabled" "true" "false" | Should be $true + } + + It 'Should set Disallow Overlapping Rotation Test ' { + + SetTargetResource "disallowOverlappingRotation" "add.recycling.disallowOverlappingRotation" "true" "false" | Should be $true + } + + It 'Should set Disallow Rotation On Config Change Test ' { + + SetTargetResource "disallowRotationOnConfigChange" "add.recycling.disallowRotationOnConfigChange" "true" "false" | Should be $true + } + + It 'Should set Log Event On Recycle Test ' { + + SetTargetResource "logEventOnRecycle" "add.recycling.logEventOnRecycle" "Time, Memory" "Time, Memory, PrivateMemory" | Should be $true + } + + It 'Should set Restart Mem Limit Test ' { + + SetTargetResource "restartMemoryLimit" "add.recycling.periodicRestart.memory" "0" "1" | Should be $true + } + + It 'Should set Restart Private Mem Limit Test ' { + + SetTargetResource "restartPrivateMemoryLimit" "add.recycling.periodicRestart.privateMemory" "0" "1" | Should be $true + } + + It 'Should set Restart Requests Limit Test ' { + + SetTargetResource "restartRequestsLimit" "add.recycling.periodicRestart.requests" "0" "1" | Should be $true + } + + It 'Should set Restart Time Limit Test ' { + + SetTargetResource "restartTimeLimit" "add.recycling.periodicRestart.time" "2.05:00:00" "1.05:00:00" | Should be $true + } + + It 'Should set Restart Schedule Test ' { + + SetTargetResource "restartSchedule" "add.recycling.periodicRestart.schedule.add.value" "18:30:00" "10:30:00" | Should be $true + } + + It 'Should set Load Balancer Capabilities Test ' { + + SetTargetResource "loadBalancerCapabilities" "add.failure.loadBalancerCapabilities" "HttpLevel" "TcpLevel" | Should be $true + } + + It 'Should set Orphan Worker Process Test ' { + + SetTargetResource "orphanWorkerProcess" "add.failure.orphanWorkerProcess" "true" "false" | Should be $true + } + + It 'Should set Orphan Action Exe Test ' { + + SetTargetResource "orphanActionExe" "add.failure.orphanActionExe" "test.exe" "test1.exe" | Should be $true + } + + It 'Should set Orphan Action Params Test ' { + + SetTargetResource "orphanActionParams" "add.failure.orphanActionParams" "test.exe" "test1.exe" | Should be $true + } + + It 'Should set Rapid Fail Protection Test ' { + + SetTargetResource "rapidFailProtection" "add.failure.rapidFailProtection" "true" "false" | Should be $true + } + + It 'Should set Rapid Fail Protection Interval Test ' { + + SetTargetResource "rapidFailProtectionInterval" "add.failure.rapidFailProtectionInterval" "00:15:00" "00:05:00" | Should be $true + } + + It 'Should set Rapid Fail Protection Interval Max Crashes Test ' { + + SetTargetResource "rapidFailProtectionMaxCrashes" "add.failure.rapidFailProtectionMaxCrashes" "15" "05" | Should be $true + } + + It 'Should set Auto Shutdown Exe Test ' { + + SetTargetResource "autoShutdownExe" "add.failure.autoShutdownExe" "test.exe" "test1.exe" | Should be $true + } + + It 'Should set Auto Shutdown Params Test ' { + + SetTargetResource "autoShutdownParams" "add.failure.autoShutdownParams" "test.exe" "test1.exe" | Should be $true + } + + It 'Should set CPU Limit Test Test ' { + + SetTargetResource "cpuLimit" "add.cpu.limit" "1" "0" | Should be $true + } + + It 'Should set CPU Action Test ' { + + SetTargetResource "cpuAction" "add.cpu.action" "Throttle" "NoAction" | Should be $true + } + + It 'Should set CPU Reset Interval Test ' { + + SetTargetResource "cpuResetInterval" "add.cpu.resetInterval" "00:15:00" "00:05:00" | Should be $true + } + + It 'Should set CPU Smp Affinitized Test ' { + + SetTargetResource "cpuSmpAffinitized" "add.cpu.smpAffinitized" "true" "false" | Should be $true + } + + It 'Should set CPU Smp Processor Affinity Mask Test ' { + + SetTargetResource "cpuSmpProcessorAffinityMask" "add.cpu.smpProcessorAffinityMask" "4294967294" "4294967295" | Should be $true + } + + It 'Should set CPU Smp Processor Affinity Mask 2 Test ' { + + SetTargetResource "cpuSmpProcessorAffinityMask2" "add.cpu.smpProcessorAffinityMask2" "4294967294" "4294967295" | Should be $true + } + } + + It 'Fails test when App Pool does not exist'{ + $testParams =@{ + Name = 'PesterAppPool' + Ensure = "Present" + } + + # if the app pool exists, remove it + if((Get-ChildItem IIS:\apppools).Name.Contains($apName)) + { + Remove-WebAppPool -Name $apName -ErrorAction Stop + } + + Test-TargetResource @testParams | Should be $false + } + } + finally + { + # if the app pool exists, remove it + if((Get-ChildItem IIS:\apppools).Name.Contains('PesterAppPool')) + { + Remove-WebAppPool -Name 'PesterAppPool' -ErrorAction Stop + } + } + } +}