user2938241
user2938241

Reputation: 35

DSC to Configure an IIS App Pool

We've been researching desired state configuration, and I was asked to set up a prototype using powershell DSC to configure an IIS app pool. I know all the steps to creating a configuration, I just am unsure of what I might have in my configuration. I plan to use the xWebAdministration resource because it has things like xWebAppPool and xWedbAdministration. Are there any suggestions on what else I might use to set this up?

Upvotes: 2

Views: 4934

Answers (4)

QuilleyPowers
QuilleyPowers

Reputation: 110

*Realize this is quite old, but just stumbled across it and wanted to offer recommendations for the scenario your DSCs get big/unwieldly.

I'd start off by separating your your node definition file from your configuration file then map a reference. I keep my files side-by-side, so I'd reference like below. I won't go into encryption, good MS guidance on that.

    $File = 'WebApp\NodeDefinitions.psd1'
    $Parent = Split-Path -Parent $PSScriptRoot
    $Path = Join-Path $Parent $File

    WebApp -RunAs $RunAs -ConfigurationData "$Path" -OutputPath $localpath -verbose

Now to make things a little more dynamic you'd create arrays or hash table collections in your node definition file like below for folders, sites, etc. *I did have to alter xcertificatedsc to have it pass an array of accounts, which isn't difficult if you need an assist.

        @{
            NodeName         = "Vm-Web-1"
            Role             = "DevWeb","Web","WS","SMP"
            IUSRS            = "Domain\User1$","Domain\User2$"
            Folders  = @(
                            ("Dir1","F:\inetpub\wwwroot\SomeApp","Present"),
                            ("Dir2","F:\inetpub\wwwroot\SomeApp2","Present"),
                            ("Dir3","F:\inetpub\wwwroot\SomeApp2","Absent") #If something's moved or a mistake
                        )

            Sites    = @(
                            @("MyApp1","Domain\User1$","Present"),
                            @("MyApp2","Domain\User2$","Present")
                        )
            CertPerms= @{
                            "[email protected]" = @("Domain\User1$","Domain\User2$")
                        }
        },

Then I introduce non-node data in my configuration so that sites are more portable that we'll later join in the configuration ps1 file. I'll also show how I'd iterate folder creation.

        @{
                    Name = "MyApp1"
                    PoolConfigName = "ApMyApp1"
                    PoolName       = "ApMyApp1"
                    PoolRtVer      = "v4.0"
                    SiteConfigName = "WaMyApp1"
                    SitePath       = "ApMyApp1"
                    SiteName       = "ApMyApp1"
                    SiteDepends    = "[File]Dir1"
                },
                @{
                    Name = "MyApp2"
                    PoolConfigName = "ApMyApp2"
                    PoolName       = "ApMyApp2"
                    PoolRtVer      = "v4.0"
                    SiteConfigName = "WaMyApp2"
                    SitePath       = "ApMyApp2"
                    SiteName       = "ApMyApp2"
                    SiteDepends    = "[File]Dir2"
                },

To iterate over folders, you just reference elements of your array in the nodedefintion file

        Foreach($Folder in $Node.Folders){
            File $Folder[0]
            {
                Ensure = $Folder[2]
                Type = "Directory"
                DestinationPath = $Folder[1]
            }
        }

The site joining is a little more complex and I'm not overly happy with my current situation referencing the element instead of an easier to read name, but it works. After joining node/nonnode data, the next part of the script is for lesser used parameters. We don't introduce a lot of pools in always running mode for example (unless proper page initialization is validated in the web.config of the correlating app). If I'm introducing a site and the correlating service account isn't active on the domain, I'll make sure the correlating pool is stopped so it doesn't flood the iis worker process. Otherwise you should be able to map out what's set in the node section (array references) vs the non-node site data.


        Foreach($SiteName in $Node.Sites){
            $Site = $ConfigurationData.Sites.Where{$_.Name -eq $SiteName[0]}
            if ([string]::IsNullOrWhiteSpace($Site.PoolIdleTO))
            {
                $PoolIdleTO = 20
            }
            else
            {
                $PoolIdleTO = $Site.PoolIdleTO
            }
            if ([string]::IsNullOrWhiteSpace($Site.PoolStartMode))
            {
                $PoolStartMode = "OnDemand"
            }
            else
            {
                $PoolStartMode = $Site.PoolStartMode
            }
            if ([string]::IsNullOrWhiteSpace($SiteName[3]))
            {
                $State = "Started"
            }
            else
            {
                $State = $SiteName[3]
            }
            xWebAppPool $Site.PoolConfigName
            {
                Name                            = $Site.PoolName
                Ensure                          = $SiteName[2]
                State                           = $State
                autoStart                       = $true
                enable32BitAppOnWin64           = $false
                enableConfigurationOverride     = $true
                managedPipelineMode             = "Integrated"
                managedRuntimeVersion           = $Site.PoolRtVer
                startMode                       = $PoolStartMode
                queueLength                     = 1000
                cpuAction                       = "KillW3wp"
                cpuLimit                        = 95000
                cpuResetInterval                = (New-TimeSpan -Minutes 1).ToString()
                cpuSmpAffinitized               = $false
                cpuSmpProcessorAffinityMask     = 4294967295
                cpuSmpProcessorAffinityMask2    = 4294967295
                identityType                    = 'SpecificUser'
                Credential                      = New-Object System.Management.Automation.PSCredential($SiteName[1], (ConvertTo-SecureString $Node.GmsaPwd.ToString() -AsPlainText -Force))
                idleTimeout                     = (New-TimeSpan -Minutes $PoolIdleTO).ToString()
                idleTimeoutAction               = 'Suspend'
                loadUserProfile                 = $false
                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 1).ToString()
                rapidFailProtectionMaxCrashes   = 5
                autoShutdownExe                 = 'C:\Windows\System32\iisreset.exe'
                autoShutdownParams              = ''
                disallowOverlappingRotation     = $false
                disallowRotationOnConfigChange  = $false
                logEventOnRecycle               = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory'
                restartMemoryLimit              = 3221225472
                restartPrivateMemoryLimit       = 8000000
                restartRequestsLimit            = 20000000
                restartTimeLimit                = (New-TimeSpan -Minutes 0).ToString()
                restartSchedule                 = "00:00:00"
                DependsOn                       = '[WindowsFeature]IIS'
            }
            <#!!!Imperative method (runs immediately) to ensure service accounts get IIS metabase access!!#
            #Need to move this into the function with a flag, obviously there'll be looping challenges...
            Invoke-Command -Session (New-PSSession -ComputerName $Node.NodeName -Credential $RunAs -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck)) -ScriptBlock {
                param ([string] $User)
                C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -ga $User
            } -ArgumentList $SiteName[1]
            #>
            cIisAccess "IisMetabaseAccess$($Site.SiteConfigName + $SiteName[1])"
            {
                Account = $SiteName[1]
                Ensure="Present"
                
            }
            xWebApplication $Site.SiteConfigName
            {
                Website             = "Default Web Site"
                Name                = $Site.SiteName
                WebAppPool          = $Site.PoolName
                PhysicalPath        = $Node.DfSitePath + $Site.SitePath
                Ensure              = $SiteName[2]
                PreloadEnabled      = $true
                DependsOn           = "[xWebAppPool]$($Site.PoolConfigName)",$($Site.SiteDepends)
            }
        }

Where one thing to note is I use GMSAs so there's no password and a kerberos token is used. You'd still need a phony password reference due to PSCredential requirements, so you can just add something to your allnodes data like below for reference:

GmsaPwd    = "none"

There's also lots of guidance on using roles out there, but a simple ref is below.

Node $AllNodes.Where{$_.Role -contains "Web"}.NodeName
   {
     #Embed site/folder iterators here if preferred
   }

Upvotes: 1

KevinC
KevinC

Reputation: 171

If you have many sites that you are trying to bring under configuration control, you could use my DSC generator to produce the DSC for IIS Features, Web sites, app pools, and virtual directories.

I then use Octopus Deploy to deliver the DSC to the server and apply the DSC.

https://github.com/kevinsea/dsc-generator

Upvotes: 5

erPe
erPe

Reputation: 578

Some time ago I have been tasked with exactly the same challenge and I have created a prototype of DSC resource that would acomplish this. After initial tests this runs now in production.

Source code is on gihub (https://github.com/RafPe/cWebAdmin ) and all feedback would be more than welcome :)

Maybe this would give you an idea how to challenge this on your end

Upvotes: 1

briantist
briantist

Reputation: 47842

You would probably use the WindowsFeature resource to install the Roles and Features needed (Web-Server, etc.), you'd probably use the File resource to create the directory and maybe copy the site's files, you might use the Registry resource to enable the Web Management Service so that you can manage IIS remotely, then use the Service resource to start that service.

Upvotes: 2

Related Questions