ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Windows Server 2012 R2 离线安装 IIS 全攻略:从依赖解析到避坑实践

Windows Server 2012 R2 离线安装 IIS 全攻略:从依赖解析到避坑实践

简介:这份离线安装包面向需要在无外网环境中为 Windows Server 2012 R2 部署或更新 IIS 的 IT 管理员与运维人员,解决内网、隔离机房或严格网络安全策略下无法在线拉取系统组件的问题。压缩包共 1568 个文件,约 99.04MB,以 dll 动态库、resx 资源文件、exe 可执行程序为主,辅以 aspx 页面、config 配置、sql 脚本、browser 浏览器定义及 tlb、targets 等组件描述文件,完整覆盖 IIS 角色与 Side-by-Side 组件存储所需内容。已有 572 人学习下载,说明其在离线部署场景中具备一定参考价值。资源保留了 Web 服务器、应用程序池、身份验证、FTP 服务与 Web 部署工具等模块的原始组件结构,便于管理员借助 DISM 从本地源添加角色、补齐 .NET 相关依赖,并在安装后用系统命令核验功能状态,适合作为内网服务器搭建与运维的常备组件库。

1. 内网机房里那台 IIS 装不上的服务器,问题往往不在 IIS 本身

一台跑着 Windows Server 2012 R2 的机器,网卡灯亮着,但就是连不上外网,更别提从服务器管理器里点「添加角色和功能」在线拉 IIS 了。点下去转两圈,弹一个「无法从 Windows 更新下载文件」,或者干脆卡在「正在搜索功能」不动。这种场景在隔离网、生产前置机、测试沙箱里太常见了,运维和开发都遇到过。这份 IIS 离线安装包要解决的就是这件事:把 IIS 及其依赖的角色服务、管理工具、.NET 扩展组件,以离线源的形式一次性备齐,让没有外网、甚至没有域环境的机器也能把 IIS 完整装起来。适合谁?负责内网服务器交付的运维、需要在隔离环境搭 Web 服务的后端、以及做安全测试靶场搭建的工程师。它不是什么黑科技,本质是把 Windows 的按需功能(Features on Demand)源做成本地可挂载的目录,但恰恰是这一步,翻车的人最多。

2. 离线源到底装了什么:IIS 角色拆解与依赖关系

2.1 IIS 不是一个角色,是一组角色服务的集合

很多人以为「装 IIS」就是勾一个 Internet Information Services 复选框,实际上在 Windows Server 2012 R2 里,IIS 是一个父角色,底下挂着几十个角色服务(Role Service),比如 Web 服务器核心、静态内容、默认文档、目录浏览、HTTP 错误、ASP.NET 4.5、.NET 扩展性、ISAPI 扩展、ISAPI 筛选器、请求筛选、Windows 身份验证、管理控制台、管理服务、FTP 服务等等。离线安装包的价值不在于「有没有 IIS 这个角色」,而在于它是否把你要勾的那些子服务对应的源文件都备齐了。缺一个 ASP.NET 4.5 的源,安装就会在进度条 60% 的地方回滚,日志里写「源文件找不到」。

常见做法是:先在一台能联网、系统版本完全一致的机器上,用Install-WindowsFeature把目标角色装一遍,然后从C:\Windows\WinSxS和C:\Windows\System32\ServerManager相关的按需功能目录里把源抽出来,或者直接用Export-WindowsDriver那套思路做 Features on Demand 的 side-by-side 源。这份离线包如果结构规范,根目录下应该能看到sources\sxs这样的目录,里面按组件名分文件夹存放.cab和.mum文件。

2.2 为什么不能直接拷 WinSxS 了事

WinSxS 是组件存储,里面有大量硬链接和版本化目录,直接整目录拷贝到目标机再指过去,十有八九会报「组件存储损坏」或者「源与目标版本不匹配」。正确的离线源必须是「按需功能源」格式,也就是用DISM的/Source参数能识别的那种布局。判断方法很简单:在目标机上跑一条DISM /Online /Get-FeatureInfo /FeatureName:IIS-WebServerRole,看它输出的「源」路径指向哪里,离线包就要能对上那个结构。

我一般会先确认三件事再动手:目标机的系统版本号(winver看 6.3.9600)、语言(zh-CN 还是 en-US,源必须同语言)、以及补丁级别。2012 R2 的源在不同累积更新下会有细微差异,跨补丁级别用源,轻则警告重则失败。

2.3 离线包目录结构对照表

拿到包之后别急着装,先按下面这张表核对目录,缺哪个后面就会在哪一步卡住:

目录/文件作用缺失后的典型现象
sources\sxs\按需功能源根目录DISM 报 0x800f0906 源文件无法下载
sources\sxs\microsoft-windows-iis-*IIS 各角色服务组件安装到对应子服务时回滚
sources\sxs\microsoft-windows-netfx*.NET Framework 3.5/4.5 相关ASP.NET 注册失败,站点 500.19
sources\sxs\microsoft-windows-was-*Windows 进程激活服务WAS 起不来,应用池无法启动
Tools\或根目录脚本安装辅助脚本需要手工敲 DISM,容易漏参数

这张表是我踩过坑之后自己整理的,尤其是netfx那一行,很多人装完 IIS 发现 ASP.NET 页面跑不起来,回头查半天,其实是 .NET 的按需功能源没跟着一起备。

3. 用 DISM 挂源装 IIS:命令、参数与验证

3.1 先把离线包放到本地固定路径

不要从 U 盘或网络共享直接指源,安装过程中源路径断开是经典翻车点。先把整个包拷到目标机的本地盘,比如D:\IIS_Offline,并确认当前账户是本地管理员。然后以管理员身份开 PowerShell,先做一次源可用性探测:

# 确认离线源目录存在且包含 sxs 子目录 $src = "D:\IIS_Offline\sources\sxs" if (Test-Path $src) { Write-Host "源目录存在,文件数:" (Get-ChildItem $src -Recurse -File).Count } else { Write-Error "源目录不存在,检查拷贝是否完整" } # 查看目标机上 IIS 父角色的当前状态与可用源 DISM /Online /Get-FeatureInfo /FeatureName:IIS-WebServerRole

第一段是防呆,确认拷贝没断在半路;第二段Get-FeatureInfo会输出该功能的当前状态(Enabled/Disabled)以及系统认为可用的源路径。如果这里显示的源路径和你放的路径不一致,说明系统还没认这个源,后面/Source参数必须显式指定,不能指望它自动找。

3.2 用 Install-WindowsFeature 指定源安装

比 DISM 更顺手的是 PowerShell 的Install-WindowsFeature,它支持-Source和-Restart,还能一次勾多个子服务。下面这条是我在隔离环境里最常用的组合,覆盖 Web 核心、静态内容、ASP.NET 4.5、管理控制台和 IIS 管理服务:

# 指定离线源安装 IIS 及常用子服务 Install-WindowsFeature -Name Web-Server, ` Web-Static-Content, ` Web-Default-Doc, ` Web-Http-Errors, ` Web-Asp-Net45, ` Web-Net-Ext45, ` Web-ISAPI-Ext, ` Web-ISAPI-Filter, ` Web-Mgmt-Console, ` Web-Mgmt-Service ` -Source "D:\IIS_Offline\sources\sxs" ` -Restart:$false -Verbose

参数逐个说清楚:-Name后面跟的是角色服务的技术名,不是显示名,写错一个就整条失败;-Source必须指到sxs这一层,指到上一级sources会报源无效;-Restart:$false表示先不自动重启,方便你看日志,如果装了 .NET 相关组件系统可能仍要求重启,这时手动重启更可控;-Verbose会打印每个子组件的安装进度,卡住时能立刻定位是哪个组件在等源。

装完立刻验证,别等配站点时才发现问题:

# 验证 IIS 角色与关键子服务状态 Get-WindowsFeature -Name Web-Server, Web-Asp-Net45, Web-Mgmt-Console | Select-Object Name, InstallState # 确认 W3SVC 服务已注册并启动 Get-Service W3SVC | Select-Object Name, Status, StartType

InstallState应该是Installed,W3SVC应该是Running。如果W3SVC是Stopped且启动报错,多半是 WAS(Windows 进程激活服务)没装全,回到 2.3 的表里查was-*组件。

3.3 用 DISM 兜底:当 PowerShell 命令不认源时

有些精简版或经过第三方封装的 2012 R2 镜像,Install-WindowsFeature会报「源不可用」,但 DISM 反而能过。这时用 DISM 的/Enable-Feature逐条来:

# 用 DISM 启用 IIS 核心与 ASP.NET 4.5,指定本地源 DISM /Online /Enable-Feature /FeatureName:IIS-WebServerRole /Source:D:\IIS_Offline\sources\sxs /All DISM /Online /Enable-Feature /FeatureName:IIS-ASPNET45 /Source:D:\IIS_Offline\sources\sxs /All DISM /Online /Enable-Feature /FeatureName:IIS-ManagementConsole /Source:D:\IIS_Offline\sources\sxs /All

/All的作用是自动启用父功能,比如你只点名IIS-ASPNET45,它会顺带把依赖的IIS-WebServer拉起来,省得手工排依赖顺序。代价是它可能多装一些你没打算要的组件,隔离环境里问题不大,生产机上建议还是显式列全。DISM 的日志在C:\Windows\Logs\DISM\dism.log,失败时直接搜0x800f开头的错误码,比看 PowerShell 的红字有用。

4. 装完 IIS 之后的配置收尾:应用池、绑定与权限

4.1 应用池的 .NET 版本别配错

IIS 装好只是有了壳,站点能不能跑取决于应用池。2012 R2 默认应用池是DefaultAppPool,托管管道模式是集成(Integrated),.NET CLR 版本默认 v4.0。如果你要跑的是老 ASP.NET 2.0 应用,得新建一个应用池并把 CLR 版本改成 v2.0,同时确认 .NET 3.5 的按需功能源也装了,否则应用池一启动就报「无法加载 ISAPI 筛选器」。

# 导入 WebAdministration 模块并新建应用池 Import-Module WebAdministration New-WebAppPool -Name "AppPool_Demo" Set-ItemProperty IIS:\AppPools\AppPool_Demo -Name managedRuntimeVersion -Value "v4.0" Set-ItemProperty IIS:\AppPools\AppPool_Demo -Name managedPipelineMode -Value "Integrated" # 建站点并绑定到 8080 端口,物理路径指向本地目录 New-Item -ItemType Directory -Path "D:\www\demo" -Force New-WebSite -Name "DemoSite" -Port 8080 -PhysicalPath "D:\www\demo" -ApplicationPool "AppPool_Demo"

managedRuntimeVersion写v4.0对应 .NET 4.x,写v2.0对应 2.0/3.5;managedPipelineMode选Integrated还是Classic取决于应用是否依赖老式 ISAPI 行为,拿不准就先 Integrated,跑不通再切 Classic 对比。New-WebSite的-Port别和已有站点冲突,2012 R2 上默认站点占 80,测试环境用 8080 更省事。

4.2 权限与防火墙:两个最容易被忽略的收尾

站点建好、浏览器访问 403 或超时,九成是这两件事。第一,物理路径的 NTFS 权限要给IIS_IUSRS组读权限,应用池标识如果是ApplicationPoolIdentity,实际生效的是虚拟账户,给组权限最稳。第二,Windows 防火墙默认拦入站 8080,得手动放行:

# 给站点目录授予 IIS_IUSRS 读权限 icacls "D:\www\demo" /grant "IIS_IUSRS:(OI)(CI)(RX)" /T # 放行 8080 入站(仅测试环境,生产按最小化原则收紧) New-NetFirewallRule -DisplayName "IIS-Demo-8080" -Direction Inbound ` -Protocol TCP -LocalPort 8080 -Action Allow

icacls里(OI)(CI)表示对象继承和容器继承,(RX)是读加执行,/T递归到子目录。防火墙规则只放行指定端口,别图省事关整个防火墙,隔离环境里也有横向风险。

5. 避坑清单:离线装 IIS 最常见的五个翻车点

5.1 现象:安装进度到一半回滚,日志报 0x800f0906

原因:离线源里缺某个子组件的.cab,或者源的语言与系统语言不一致。2012 R2 中文版必须用 zh-CN 的源,拿 en-US 的源去装,DISM 会认为「源中找不到适用组件」。

解决:先看C:\Windows\Logs\DISM\dism.log里最后失败的功能名,回到离线包核对对应目录是否存在;语言不一致就换同语言源,别试图混用。

5.2 现象:Install-WindowsFeature报「源不可用」,但目录明明在

原因:-Source指错了层级,指到了sources而不是sources\sxs;或者路径里有空格没加引号;再或者当前 PowerShell 不是管理员会话。

解决:路径统一用引号包住,确认指到sxs;用whoami /groups确认在 Administrators 组且已提权。还不行就换 DISM 逐条启用,DISM 对路径的容错稍好。

5.3 现象:IIS 装好了,ASP.NET 页面报 500.19

原因:web.config里引用了未安装的模块,典型是 URL 重写模块或 ASP.NET 4.5 的某个 handler 没装全。离线包如果只装了 Web 核心没装Web-Asp-Net45,就会这样。

解决:Get-WindowsFeature Web-Asp-Net45看状态,没装就补装;补装时源路径照旧。500.19 的错误页会直接写出缺哪个模块名,照着补即可。

5.4 现象:站点能访问静态页,但应用池一启动就停

原因:应用池的标识账户没有权限访问站点目录,或者 .NET CLR 版本与应用不匹配。前者报 503,后者事件日志里会有「应用程序池被自动禁用」。

解决:先给IIS_IUSRS授目录读权限;再看应用池的managedRuntimeVersion是否和应用目标框架一致;事件查看器里「应用程序」日志会有具体原因。

5.5 现象:装完重启后 IIS 管理控制台打不开

原因:Web-Mgmt-Console装了但Web-Mgmt-Service没装或没启动,管理控制台依赖管理服务做远程和本地连接。

解决:Get-Service WMSVC看状态,没启动就Start-Service WMSVC并设为自动;如果服务不存在,说明管理服务组件没装,回源补装Web-Mgmt-Service。

6. 把离线源做成可复用基线:一次准备,多机交付

单台机器装完就完事,那是救火;把离线源整理成可复用基线,才是省事。我的习惯是:在一台「基准机」上把常用 IIS 组合装全,然后用Export-WindowsFeature的思路把源固化下来——2012 R2 没有直接的导出命令,实际做法是把sources\sxs连同你验证过的组件清单一起打包,清单里记下系统版本号、语言、补丁级别和每个组件的技术名。下次交付新机器,先比对版本和语言,一致就直接挂源装,不一致就先补差异组件。

验证基线是否可靠,有个笨但有效的办法:找一台干净的同版本虚拟机,断网,挂源,跑一遍第 3 章的安装命令,再跑第 4 章的建站命令,浏览器能出默认页就算过。这一步别省,我见过太多次「在我机器上是好的」,结果换台机器就因为少一个netfx组件卡住。

再补一个参数层面的技巧:Install-WindowsFeature支持-WhatIf,正式装之前先跑一遍看它会动哪些组件,输出里如果出现你没预期的父功能,说明依赖链比你想的长,提前把源备齐。命令如下:

# 预演安装,不实际改动系统 Install-WindowsFeature -Name Web-Server, Web-Asp-Net45 ` -Source "D:\IIS_Offline\sources\sxs" -WhatIf

-WhatIf会列出将被启用的功能清单,拿这份清单去核对离线包目录,比装到一半回滚再查日志快得多。从那以后我每次交付隔离环境的 IIS,都强制先跑一遍-WhatIf加目录核对,再正式装,省下的回滚时间够喝好几杯茶。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进