前言
最近在进行 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.PhysicalPath
if (-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)(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 网站,为对应的应用程序池授予正确的目录权限,特别适用于多站点服务器迁移后的批量修复场景。
建议在每次完成网站迁移后,将此脚本作为标准运维检查项执行一次,可以有效避免因权限问题导致的各类线上故障。
评论交流
欢迎留下你的想法