用于配置 IIS 应用程序池的 DSC

DSC to Configure an IIS App Pool

我们一直在研究所需的状态配置,我被要求使用 powershell DSC 设置原型来配置 IIS 应用程序池。我知道创建配置的所有步骤,只是不确定我的配置中可能有什么。我计划使用 xWebAdministration 资源,因为它有 xWebAppPool 和 xWedbAdministration 之类的东西。关于我还可以用什么来设置它有什么建议吗?

您可能会使用 WindowsFeature 资源来安装所需的角色和功能(Web-Server 等),您可能会使用 File 资源来创建目录并且可能复制站点的文件,您可以使用 Registry 资源启用 Web 管理服务以便您可以远程管理 IIS,然后使用 Service 资源启动该服务。

前段时间我接到了完全相同的挑战任务,我创建了一个 DSC 资源原型来完成这个任务。经过初步测试后,现在可以在生产中运行。

源代码在 gihub (https://github.com/RafPe/cWebAdmin) 上,欢迎所有反馈:)

也许这会让你知道如何在你这边挑战这个

如果您有许多站点要置于配置控制之下,您可以使用我的 DSC 生成器为 IIS 功能、网站、应用程序池和虚拟目录生成 DSC。

然后我使用 Octopus Deploy 将 DSC 交付到服务器并应用 DSC。

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

*意识到这已经很老了,但只是偶然发现它,想为您的 DSC 获得的场景提供建议 big/unwieldly。

我首先将您的节点定义文件与您的配置文件分开,然后映射一个引用。我把我的文件并排放置,所以我会像下面这样参考。我不会进入加密,很好的 MS 指导。

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

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

现在,为了使事情变得更加动态,您可以在节点定义文件中创建数组或散列 table 集合,如下所示,用于文件夹、站点等。*我确实必须更改 xcertificatedsc 才能拥有它传递一系列帐户,如果您需要帮助,这并不难。

        @{
            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= @{
                            "somecert@domain.com" = @("Domain\User1$","Domain\User2$")
                        }
        },

然后我在我的配置中引入非节点数据,这样站点就更适合table,我们稍后将在配置ps1 文件中加入。我还将展示如何迭代文件夹创建。

        @{
                    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"
                },

要遍历文件夹,您只需在节点定义文件中引用数组的元素

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

站点加入有点复杂,我对我目前引用元素而不是更容易阅读的名称的情况不太满意,但它有效。加入 node/nonnode 数据后,脚本的下一部分用于较少使用的参数。例如,我们不会在始终 运行 模式下引入很多池(除非在相关应用程序的 web.config 中验证了正确的页面初始化)。如果我要介绍一个站点并且关联服务帐户在域中未激活,我将确保关联池已停止,这样它就不会淹没 iis 工作进程。否则,您应该能够映射出节点部分(数组引用)与非节点站点数据中设置的内容。


        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)
            }
        }

需要注意的一件事是我使用 GMSA,因此没有密码,并且使用了 kerberos 令牌。由于 PSCredential 要求,您仍然需要伪造的密码参考,因此您可以像下面这样向您的 allnodes 数据添加一些内容以供参考:

GmsaPwd    = "none"

还有很多关于使用角色的指导,但下面是一个简单的参考。

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