上一篇 下一篇 分享链接 返回 返回顶部

香港服务器运行 Windows Server 时,我如何用 PowerShell DSC 把跨区域的大规模环境“捏”成一张脸

发布人:Minchunlin 发布时间:2025-08-26 10:07 阅读量:672


那是一个凌晨 2 点的周末变更窗口。香港机房的空调压缩机刚刚切换,走廊里有一点潮湿的味道。我盯着 NOC 屏幕上几十台新上架的 Windows Server 2022 Datacenter Core(Dell R6525,EPYC 7543,256GB RAM,2×1.92TB NVMe,双口 X710 10GbE)的心跳,另一端是新加坡、东京和美国西海岸的分站。

一周前,我们还在靠“金手指 + 文档”的方式做系统基线:有人漏了一个本地策略,有人忘了启用 TLS1.2,还有人装了不同版本的 .NET。线上事故不是来自“未知”,而是来自“不一致”。我决定用 PowerShell DSC(Desired State Configuration)把这些跨区域的 Windows 主机,拉回到统一的、可审计、可回滚的状态。

场景与基线(硬件/网络/OS)

机房/区域 机型/规格 OS 主要角色 互联延迟(往返) 出口带宽
香港(主) Dell R6525 / EPYC 7543 / 256GB / 2×1.92TB NVMe / X710 10GbE Windows Server 2022 DC Core Pull/Report 服务、Web、API、监控 HK↔SG ≈ 35ms;HK↔TYO ≈ 50ms;HK↔SJC ≈ 130ms 2×10Gbps
新加坡 同上(小一档 CPU) 2022/2019 混部 Web、API ↔HK ≈ 35ms 10Gbps
东京 同上 2019/2022 混部 Web、任务队列 ↔HK ≈ 50ms 10Gbps
美国西海岸 2022 DC 缓存/边缘任务 ↔HK ≈ 130ms 10Gbps  

目标:用 DSC 实现跨区域“同一面孔”的系统基线(安全策略、特性/角色、服务、软件版本、注册表、文件/权限、网络、证书),并做到可持续纠偏(drift correction)和可审计(合规报告)。

1. 技术路线与架构决策

模式选择:跨区域规模化 → 采用 Pull 模式(节点定时从 Pull 服务器拉配置+模块),同时启用 Report 服务器收集合规/偏离状态。

Push 只用于小规模临时救火。

配置组织:角色化 + 组合资源(Composite Resource)。将“基地线(Baseline)”“WebRole”“AppRole”“Agent/Metrics”等解耦,便于分区复用与版本化。

安全:Pull/Report 端点用 IIS + HTTPS(TLS1.2);证书统一由内部 PKI 颁发,CRL/OCSP 跨区可达;节点端 LCM(Local Configuration Manager)禁用 HTTP。

模块管理:不直接连外网 PSGallery;在香港建立 内网 NuGet 源 + 镜像模块,跨区用 DFS-R 只读分发。

拓扑(文字示意):

节点(TYO/SG/SJC) ←HTTPS 443→ HK Pull(IIS/DSC-Service) ←DFS-R→ SG 只读镜像;

节点上报合规 → Report Server(同 HK IIS 站点)。

2. 准备工作(必做检查清单)

Windows Server 2019/2022 已内置 WMF 5.1(含 DSC v2),确保补丁最新。

端口:节点出站 HTTPS/443 到 https://dsc.hk.example.com;管理段 WinRM 5986(只给维护用)。

TLS:强制 TLS1.2;确保证书链/CRL 可达。

PowerShell 模块(在内网源提前准备好版本):

PSDscResources = 2.12.x(内置资源增强包)

NetworkingDsc、ComputerManagementDsc、xWebAdministration 或 WebAdministration、xCertificate

(可选)SqlServerDsc、cChoco(若用 Chocolatey 需遵循软件仓库合规)

3. 在香港搭建 DSC Pull/Report 服务(IIS)

# 以提升权限的 PowerShell 在香港 Pull 节点执行
Install-WindowsFeature -Name Web-Server,Web-Http-Redirect,Web-Static-Content,Web-Default-Doc,Web-Http-Errors,Web-Http-Logging,Web-Filtering,Web-Mgmt-Tools -IncludeManagementTools
Install-WindowsFeature -Name DSC-Service

# 生成注册密钥
$RegPath = 'C:\Program Files\WindowsPowerShell\DscService'
New-Item -ItemType Directory -Path $RegPath -Force | Out-Null
$Guid = [guid]::NewGuid()
$Guid | Out-File "$RegPath\RegistrationKeys.txt" -Encoding ascii

# 绑定 HTTPS 证书(假设证书私钥已导入本机,主题 dsc.hk.example.com)
$cert = (Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -like '*dsc.hk.example.com*' } | Sort-Object NotAfter -Descending)[0]
New-WebBinding -Name 'PSDSCPullServer' -Protocol https -Port 443
Push-Location IIS:\SslBindings
New-Item "0.0.0.0!443" -Thumbprint $cert.Thumbprint -SSLFlags 0
Pop-Location

# 设置目录权限(IIS AppPool\PSDSCPullServer)
$svcAcct = 'IIS AppPool\PSDSCPullServer'
icacls "$RegPath" /grant "$svcAcct:(OI)(CI)(M)" /T

注意:Install-WindowsFeature DSC-Service 会创建 PSDSCPullServer 站点与典型的 OData 端点 /PSDSCPullServer.svc 与 /PSDSCComplianceServer.svc。我们用同一站点承载 Pull 与 Report。

4. 角色化建模:把“基线”和“业务”拆开

我把“基地线”和“业务角色”拆成组合资源,代码在内部 Git(示例浓缩如下)。

4.1 基线(Baseline):统一时区/日志、启用 TLS1.2、必要特性、常用服务与本地策略。

configuration Baseline
{
    Import-DscResource -ModuleName PSDscResources,ComputerManagementDsc,NetworkingDsc

    Node $AllNodes.NodeName {
        # 主机名 & 时区
        Computer 'Hostname' {
            Name = $Node.ComputerName
        }
        TimeZone 'HKTime' {
            IsSingleInstance = 'Yes'
            TimeZone = $Node.TimeZone # 'China Standard Time' 或地区自定
        }

        # 启用 TLS1.2、禁用 SSL3/TLS1.0(注册表基线)
        Registry 'TLS12_Client' {
            Key = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client'
            ValueName = 'Enabled'
            ValueData = 1
            ValueType = 'Dword'
            Ensure = 'Present'
        }

        # 必要 WindowsFeature
        WindowsFeature 'NET-Framework-Features' { Name = 'NET-Framework-Features'; Ensure = 'Present' }
        WindowsFeature 'Web-WebServer'         { Name = 'Web-WebServer'; Ensure = 'Present' } # 仅示例,可按角色条件化

        # 本地管理员组(最小权限)
        Group 'LocalAdmins' {
            GroupName = 'Administrators'
            MembersToInclude = $Node.LocalAdmins
            Ensure = 'Present'
        }

        # 防火墙基线(示意)
        Firewall 'Allow_HTTPS_Out' {
            Name                  = 'Allow_HTTPS_Out'
            Ensure                = 'Present'
            Enabled               = 'True'
            Direction             = 'Outbound'
            LocalPort             = '443'
            Protocol              = 'TCP'
            Action                = 'Allow'
        }
    }
}

4.2 WebRole:IIS 站点、AppPool、日志策略与反向代理依赖。

configuration WebRole
{
    Import-DscResource -ModuleName PSDscResources, xWebAdministration

    Node $AllNodes.Where({ $_.Role -contains 'Web' }).NodeName {
        WindowsFeature 'Web-Server' { Name='Web-Server'; Ensure='Present' }

        xWebAppPool 'AppPool_Main' {
            Name = 'site-main'
            startMode = 'AlwaysRunning'
            managedRuntimeVersion = 'v4.0'
            identityType = 'ApplicationPoolIdentity'
        }

        xWebSite 'MainSite' {
            Name = 'MainSite'
            State = 'Started'
            PhysicalPath = 'C:\Sites\Main'
            BindingInfo = @(
                MSFT_xWebBindingInformation{
                    Protocol='https'; Port=443; HostName=$Node.SiteFQDN; CertificateThumbprint=$Node.SiteCertThumb; CertificateStoreName='My'
                }
            )
            ApplicationPool = 'site-main'
            DependsOn = '[WindowsFeature]Web-Server','[xWebAppPool]AppPool_Main'
        }
    }
}

4.3 监控/Agent 模块:比如安装自研监控或 Telegraf/OMS(按你们合规情况替换)。

5. ConfigurationData:按区域/角色装配

ConfigData.psd1 用于为不同区域赋予不同变量(时区、站点域名、本地管理员等),MOF 时再渲染。

@{
    AllNodes = @(
        @{ NodeName='HK-WEB-01';  Region='HK'; Role=@('Web');  ComputerName='HK-WEB-01'; TimeZone='China Standard Time'; LocalAdmins=@('DOMAIN\ops-hk'); SiteFQDN='www.hk.example.com';  SiteCertThumb='‎ABCD1234...' },
        @{ NodeName='SG-WEB-01';  Region='SG'; Role=@('Web');  ComputerName='SG-WEB-01'; TimeZone='Singapore Standard Time'; LocalAdmins=@('DOMAIN\ops-sg'); SiteFQDN='www.sg.example.com';  SiteCertThumb='‎EFGH5678...' },
        @{ NodeName='TYO-API-01'; Region='TYO';Role=@('API');  ComputerName='TYO-API-01'; TimeZone='Tokyo Standard Time'; LocalAdmins=@('DOMAIN\ops-tyo') },
        @{ NodeName='SJC-EDGE-01';Region='SJC';Role=@('Edge'); ComputerName='SJC-EDGE-01'; TimeZone='Pacific Standard Time'; LocalAdmins=@('DOMAIN\ops-us') }
    );
    NonNodeData = @{
        PullServerUrl = 'https://dsc.hk.example.com/PSDSCPullServer.svc'
        ReportServerUrl = 'https://dsc.hk.example.com/PSDSCComplianceServer.svc'
        RegistrationKey = '放空,节点 LCM 自带读取本机 GUID/Key 文件'
    }
}

6. 编译 MOF:把“意图”变成“状态文件”

我将 Baseline 与 WebRole 分开编译,再用 Partial Configuration(可选)组合;这里演示简单的单配置合成:

. .\Baseline.ps1
. .\WebRole.ps1
$ConfigData = Import-PowerShellDataFile .\ConfigData.psd1

configuration HK_Fleet
{
    Import-DscResource -ModuleName PSDscResources, xWebAdministration, ComputerManagementDsc, NetworkingDsc
    Baseline
    WebRole
}

HK_Fleet -ConfigurationData $ConfigData -OutputPath .\out\mof


生成的 .\out\mof\HK-WEB-01.mof 等文件会被 Pull 服务器按 ConfigurationID(GUID) 识别。生产里我会将节点名映射成固定 GUID(注册时回传),避免重装后重复绑定。

7. 配置 LCM(跨区域频率、代理、报告)

每台目标节点都需要一份 LCM Meta-Configuration(meta.mof),指明 Pull/Report 地址、刷新频率、纠偏策略等。

[DSCLocalConfigurationManager()]
configuration SetLCM
{
    param(
        [Parameter(Mandatory)][string]$PullUrl,
        [Parameter(Mandatory)][string]$ReportUrl
    )
    Node localhost {
        Settings {
            RefreshMode = 'Pull'
            ConfigurationMode = 'ApplyAndAutoCorrect' # 偏离自动纠正
            ConfigurationModeFrequencyMins = 30        # 纠偏频率
            RefreshFrequencyMins = 15                  # 拉取频率(跨区别设太小)
            RebootNodeIfNeeded = $true
            AllowModuleOverwrite = $true
        }
        ConfigurationRepositoryWeb PullSrv {
            ServerURL = $PullUrl
            RegistrationKey = (Get-Content 'C:\Program Files\WindowsPowerShell\DscService\RegistrationKeys.txt')
            ConfigurationNames = @('HK_Fleet') # 或使用 ConfigurationID(GUID)
        }
        ReportServerWeb ReportSrv {
            ServerURL = $ReportUrl
        }
    }
}

# 若美国区必须走企业代理,先设置 WinHTTP 代理(LCM 遵循 WinHTTP)
# netsh winhttp set proxy proxy.corp:8080 bypass-list="*.example.com"

SetLCM -PullUrl 'https://dsc.hk.example.com/PSDSCPullServer.svc' -ReportUrl 'https://dsc.hk.example.com/PSDSCComplianceServer.svc' -OutputPath .\out\meta
Set-DscLocalConfigurationManager -Path .\out\meta -Verbose

参数经验值:跨区建议 RefreshFrequencyMins=30 起步,ConfigurationModeFrequencyMins=60,并针对高延迟区(SJC)放宽;初次批量下发可临时缩短,稳定后回调。

8. 将 MOF 与模块发布到 Pull 服务器

Pull 服务器上有两个核心目录(默认):

配置:C:\Program Files\WindowsPowerShell\DscService\Configuration

模块:C:\Program Files\WindowsPowerShell\DscService\Modules

每个文件都需要对应的 校验文件(.checksum)。我用以下脚本自动发布:

$Out = "C:\repo\out\mof"
$CfgDir = "C:\Program Files\WindowsPowerShell\DscService\Configuration"

Copy-Item "$Out\*.mof" $CfgDir -Force
Get-ChildItem $CfgDir -Filter *.mof | ForEach-Object { New-DscChecksum -Path $_.FullName -OutPath $CfgDir -Force }

# 模块打包:将模块目录压成 {Name}_{Version}.zip
$ModSrc = "C:\repo\modules"
$ModDst = "C:\Program Files\WindowsPowerShell\DscService\Modules"
Get-ChildItem $ModSrc -Directory | ForEach-Object {
    $name = $_.Name
    $ver  = (Get-Module -ListAvailable $name | Sort-Object Version -Descending | Select-Object -First 1).Version.ToString()
    $zip  = Join-Path $ModDst "$name_$ver.zip"
    if (Test-Path $zip) { Remove-Item $zip -Force }
    Add-Type -AssemblyName 'System.IO.Compression.FileSystem'
    [System.IO.Compression.ZipFile]::CreateFromDirectory($_.FullName, $zip)
    New-DscChecksum -Path $zip -OutPath $ModDst -Force
}

9. 首次应用与合规验证

在节点上:

# 立即触发拉取
Update-DscConfiguration -Wait -Verbose

# 查看最近 10 次状态
Get-DscConfigurationStatus -All

# 漂移检测(不应用)
Test-DscConfiguration -Detailed

在 Pull/Report 服务器:(IIS 日志 + 合规 API)

# 拉取某节点的最新报告(示意)
# 报告是 OData,可用 Invoke-RestMethod 调 Report URL
$reportUrl = 'https://dsc.hk.example.com/PSDSCComplianceServer.svc/Nodes(ComputerName=''SG-WEB-01'')/Reports'
Invoke-RestMethod -Method Get -Uri $reportUrl -UseDefaultCredentials

10. 版本管理与 CI/CD

我把 DSC 代码与配置数据放在 Git:

/dsc
  /composites     # 组合资源(Baseline、WebRole、Agent)
  /modules        # 内部镜像模块
  /configs        # *.ps1(configuration 入口)
  /data           # *.psd1(ConfigData)
  /out            # 编译输出(gitignore)
  build.ps1       # 编译 & 单元测试 & 发布

build.ps1(精简版):

# 1) Pester 测试组合资源的参数与默认值(略)
# 2) 编译
.\configs\Build-All.ps1
# 3) 生成校验 & 发布到 Pull(仅在 main 分支 + tag 触发)
if ($env:CI -and $env:GIT_TAG) { .\scripts\Publish-ToPull.ps1 }

关键模块版本(供审计)

模块 版本 用途
PSDscResources 2.12.x File/Archive/Group/Service 等基础资源
xWebAdministration 3.3.x IIS 站点、AppPool、Binding
ComputerManagementDsc 9.x 本地策略、时区、用户/组
NetworkingDsc 9.x NIC、IP、DNS、Firewall
xCertificate 3.x 证书部署/信任链

11. 现场遇到的“坑”与修复手记

节点无法从 Pull 拉取,报 TLS/证书错误

现象:Update-DscConfiguration 报 The underlying connection was closed: Could not establish trust relationship...

排查:curl https://dsc.hk.example.com 正常,但在美西区失败。

根因:美西到香港的防火墙未放行内部 CRL/OCSP 地址,证书链校验超时。

修复:

在边缘区临时导入完整链并设置 certutil -setreg chain\ChainCacheResyncFiletime @now;

网络放行 CRL/OCSP;

最终将 Pull 站点证书切换为 公共可信 CA,跨区不再依赖内网 CRL。

LCM 不走用户代理,拉配置超时

现象:SJC 节点出站必须走企业代理,但 LCM 不遵循 IE 代理。

修复:设置 WinHTTP 代理:

netsh winhttp set proxy proxy.corp:8080 bypass-list="*.example.com";并验证 netsh winhttp show proxy。之后 LCM 拉取正常。

模块版本不一致导致“隐形漂移”

现象:同一个 Composite 在不同区行为不同。
根因:Pull 服务器的 Modules 中同时存在 xWebAdministration_3.2 与 3.3,部分节点拿到了旧版。
修复:强制在配置中 pin 版本,并清理旧 zip;加上 模块校验 Pester 测试。

ApplyAndAutoCorrect 触发频繁重启

现象:IIS 角色频繁被“纠偏”重启 AppPool。

修复:对“易变”对象(日志轮转、缓存文件)改用 Script/File 的 MatchSource 策略,只做存在性/权限校验,不做内容强更;把 ConfigurationModeFrequencyMins 调整为 60。

PSGallery 被拦截,依赖无法下载

修复:完全改为 内网 NuGet 源;Register-PSRepository -Name Internal -SourceLocation http://nuget.hk.example.com/nuget -InstallationPolicy Trusted;CI 将模块打包入 Modules。

12. 性能与带宽侧写(上线观测)

区域 首次应用耗时(平均) 后续纠偏(平均) 峰值带宽(拉包) 备注
香港 3m20s 20–40s 120 Mbps 本地拉取
新加坡 4m10s 30–50s 80 Mbps 35ms 延迟
东京 4m40s 35–55s 75 Mbps 50ms 延迟
美西 6m30s 45–70s 60 Mbps 130ms 延迟,代理

调优要点:首更阶段分批(10 台一组),模块压缩包按需拆分;稳定运行后延长 RefreshFrequencyMins,合规报告保留 30 天滚动。

13. 运营与回滚

变更窗口:灰度到 SG→TYO→SJC→HK;每批 10–20 台。

回滚:Git tag 回滚到上一个 MOF + Modules 版本,重新生成 checksum;节点自动回退。

取证:Report Server 报表 + 节点 Get-DscConfigurationStatus -All 导出到 SIEM。

14. FAQ(我踩过的几个问号)

域加/脱域能否走 DSC? 能,xComputer/Computer 支持,但请把域凭据用证书加密(PsDscRunAsCredential)并限制只在安全 VLAN 执行。

混合云(部分在公有云)怎么做? Pull 地址走公网域名 + 公共 CA 证书,安全靠 mTLS(需自定义)或在云侧开专线/VPN;报告同理。

Azure Automation DSC 要不要用? 规模更大/全球多点、且你们有 Azure 经验时非常香;但我这套在纯自建环境里可控性更强。

15. 从凌乱到一致:DSC 带来的改变

变更窗口结束时,合规面板全绿。我把椅子往后一靠,听到机房里风道的低鸣——那是所有服务器在同一节拍呼吸的声音。

这一次,我们没有靠记忆和个人习惯去拼“差不多”,而是把“系统意图”写进了代码:无论是香港的主集群,还是东京的夜班节点,抑或远在太平洋彼岸的边缘机,都在以同一种方式被构建、被检查、被纠偏。

后来新同事问我,“跨区域一致性最难的是什么?” 我想了想:不是技术,而是克制——克制在每台机器上“手动一把”的冲动,把一切回到 DSC 的声明式世界里。因为只有那样,凌晨两点的你,才配得上一个稳定的夜晚。

附:完整最小可用样例(可直接复刻到实验环境)

实验假设:单 Pull/Report 在香港;目标 2 台(HK-WEB-01、SG-WEB-01);只下发 Baseline + 简易 IIS 站点。

香港 Pull:按第 3 节安装并绑定证书。

在你的管理机(可就是 Pull 机)准备文件:

Baseline.ps1 / WebRole.ps1(上文示例)

ConfigData.psd1(按你的主机名/证书修改)

Build.ps1(编译+发布)

# Build.ps1
. .\Baseline.ps1
. .\WebRole.ps1
$cd = Import-PowerShellDataFile .\ConfigData.psd1
configuration HK_Fleet { Import-DscResource -ModuleName PSDscResources, xWebAdministration, ComputerManagementDsc, NetworkingDsc; Baseline; WebRole }
HK_Fleet -ConfigurationData $cd -OutputPath .\out\mof
.\Publish-ToPull.ps1

在目标节点执行 LCM 配置(第 7 节脚本,换成你的 Pull/Report URL)。

Update-DscConfiguration -Wait -Verbose,观察站点创建、证书绑定、组策略应用。

用 Test-DscConfiguration 验证漂移,用 Get-DscConfigurationStatus 看历次结果

目录结构
全文