前言
最近在进行 Windows 服务器迁移时,遇到了一个比较经典的问题。
将网站文件夹直接复制到新的服务器后,网站能够正常访问,但是很多功能全部失效,例如:
-
文件上传失败
-
文件下载失败
-
PDF、Excel、Zip 等文件无法生成
-
日志无法写入
-
图片无法保存
-
临时文件创建失败
-
删除文件时报 Access Denied
应用程序没有报编译错误,也没有出现 IIS 启动失败,而是在执行涉及文件操作时不断提示:
Access to the path is denied.
或者
UnauthorizedAccessException
又或者
Access is denied.
排查了很久之后,最终发现原因其实非常简单。
问题原因
Windows 文件夹不仅仅包含文件内容,还包含 NTFS 权限(ACL)。
如果网站目录采用下面几种方式迁移:
-
Windows 复制
-
WinSCP
-
FTP
-
压缩包
-
Robocopy(未复制 ACL)
那么原来的 IIS 权限通常都会丢失。
例如:
D:\Website\ProjectA
原服务器可能拥有:
IIS AppPool\ProjectAPool
的修改权限(Modify)。
但是迁移到新服务器以后,只剩下:
Administrators
SYSTEM
Users
而真正运行网站的 Application Pool 身份已经没有权限了。
因此:
-
可以读取页面
-
但是不能写文件
-
不能删除文件
-
不能创建目录
-
不能上传文件
这就是为什么首页正常,但是上传下载全部失败。
为什么只影响上传下载?
因为 IIS 默认只需要读取网站文件即可。
例如:
wwwroot
index.html
css
js
浏览网页只需要:
Read
权限即可。
但是上传文件时,需要:
Modify
权限。
例如:
wwwroot
upload
image.jpg
如果程序池账号没有 Modify 权限,就会直接失败。
手动修改权限的问题
当然,也可以手动修改。
例如:
右键文件夹
属性
安全
编辑
添加
输入:
IIS AppPool\程序池名称
然后给予:
Modify
权限即可。
但是如果服务器上有:
-
十几个网站
-
几十个网站
-
上百个站点
一个一个添加权限显然效率太低。
于是可以利用 PowerShell 一次性完成。
PowerShell 自动修复所有 IIS 站点权限
下面这个脚本会:
-
自动读取所有 IIS Site
-
获取对应的 Application Pool
-
获取站点物理目录
-
为对应程序池账号授予 Modify 权限
-
自动递归到所有子目录
Import-Module WebAdministration
获取所有 Application Pool 名称
$AppPools = Get-ChildItem IIS:\AppPools | Select-Object -ExpandProperty Name
获取所有 Site
$Sites = Get-Website
foreach (Site in Sites)
{
PhysicalPath = Site.PhysicalPathif (-not (Test-Path $PhysicalPath)) { Write-Host "Skip: $PhysicalPath" continue } Write-Host "" Write-Host "=====================================" Write-Host "Site: $($Site.Name)" Write-Host "Path: $PhysicalPath" # 获取当前站点使用的程序池 $Pool = $Site.applicationPool $Identity = "IIS AppPool\$Pool" Write-Host "Grant -> $Identity" icacls $PhysicalPath ` /grant "$($Identity):(OI)(CI)M" ` /T ` /C ` /Q Write-Host "Done."}
Write-Host ""
Write-Host "All IIS Sites Permission Updated."
脚本原理解析
1、获取所有 IIS 站点
$Sites = Get-Website
等价于打开 IIS 管理器查看:
Sites
中的所有网站。
2、获取站点物理目录
$Site.PhysicalPath
例如:
D:\Website\CRM
D:\Website\API
D:\Website\Admin
3、获取程序池名称
$Pool = $Site.applicationPool
例如:
CRMPool
ApiPool
AdminPool
4、拼接程序池身份
$Identity = "IIS AppPool\$Pool"
最终得到:
IIS AppPool\CRMPool
这是 IIS 应用程序真正运行时使用的身份。
5、授予 Modify 权限
icacls $PhysicalPath `
/grant "$($Identity):(OI)(CI)M"
其中:
| 参数 | 说明 |
|---|---|
| OI | Object Inherit,文件继承 |
| CI | Container Inherit,目录继承 |
| M | Modify(修改权限) |
因此:
(OI)(CI)M
表示:
当前目录及所有子目录、文件都授予 Modify 权限。
6、递归所有子目录
/T
表示:
Recursive
递归整个目录树。
否则只会修改根目录。
7、忽略错误继续执行
/C
即使某个目录权限修改失败,也不会中断整个脚本。
例如:
Site1 成功
Site2 成功
Site3 失败
Site4 继续执行
非常适合批量修复。
8、静默输出
/Q
关闭 icacls 的详细输出。
控制台只保留脚本中输出的日志,更清晰易读。
如何运行脚本
-
使用管理员身份启动 Windows PowerShell。
-
将上述脚本保存为
Grant-IISPermissions.ps1。 -
如系统限制脚本执行,可临时执行:
Set-ExecutionPolicy RemoteSigned -Scope Process
-
运行脚本:
.\Grant-IISPermissions.ps1
执行完成后,将看到类似输出:
=====================================
Site: CRM
Path: D:\Website\CRM
Grant -> IIS AppPool\CRMPool
Done.
=====================================
Site: API
Path: D:\Website\API
Grant -> IIS AppPool\ApiPool
Done.
All IIS Sites Permission Updated.
修复完成后建议验证
建议重点验证以下功能是否恢复正常:
-
文件上传
-
文件下载
-
图片保存
-
PDF 导出
-
Excel 导出
-
ZIP 压缩生成
-
日志写入
-
临时文件创建
-
删除或覆盖文件
如果上述功能均恢复正常,基本可以确认问题由目录权限缺失导致。
总结
Windows 服务器迁移站点后,网站能够访问但上传、下载、导出等涉及文件写入的功能异常 ,大多数情况下都是 IIS 应用程序池账号缺少目标目录的 NTFS Modify 权限所致。
相比逐个站点手动配置权限,本文提供的 PowerShell 脚本能够自动遍历所有 IIS 网站,为对应的应用程序池授予正确的目录权限,特别适用于多站点服务器迁移后的批量修复场景。
建议在每次完成网站迁移后,将此脚本作为标准运维检查项执行一次,可以有效避免因权限问题导致的各类线上故障。