共计 10619 个字符,预计需要花费 27 分钟才能阅读完成。
本文主要讲如何通过 Powershell 在 IIS 上自动化部署 ASP.NET 网站,而不涉及 Powershell 的基本语法,如果没有 Powershell 基础的同学也可以把本文作为学习 Powershell 的基石,通过学习本文中的脚本再去查阅具体的语法,可能会达到事半功倍的效果。
一般我们建立网站大致需要以下几个步骤:
1、安装.NET Framework
2、安装了 IIS
3、注册、启用 ISAPI 和 CGI 限制
4、建立网站
5、设置默认首页、身份验证、设置 MIME 类型
6、绑定域名或 IP 地址
7、设置权限
8、设置防火墙入站规则
功能介绍
该功能主要是将站点文件夹、Powershell 脚本文件、.NET Framework 安装程序、Powershell 升级程序放在同一个文件夹下,以管理员身份运行脚本文件,脚本自动安装.NET Framework 和升级 Powershell 并将站点文件拷贝到网站目录下,最终建立一个网站。
接下来我们就讲讲如果通过 Powershell 实现上面的步骤:
安装.NET Framework
首先检查是否已经安装了.NET Framework,如果没有再安装。目前我知道的有两种方式可以判断是否已经安装了.NET Framework,一种是检查注册表,一种是检查安装路径(有点不靠谱),在本文中我将通过注册表来检查是否已经安装了.NET Framework。.NET Framework 的注册表路径在“HKLM:\SOFTWARE\Microsoft\NET Framework Setup\”,所以可以通过以下代码来实现:
1 | test-path "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\" |
但是上面的代码只能检查.NET Framework 的安装情况,并不知道是安装了哪个版本,所以还需要配合下面的代码:
1 | $version = gci 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP'| sort pschildname -desc | select -fi 1 -exp pschildname |
gci 是 Get-ChildItem 的缩写,srot 是 Sort-Object 的缩写,可以通过运行 Get-Help Get-ChildItem -Detailed 来查看该函数的详细信息,其他函数只要替换掉 Get-ChilItem 就可以了。具体的代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function CheckFramework{ try { $exists = test-path "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\" if($exists -eq $false) { return $false } else { $version = gci 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP'| sort pschildname -desc | select -fi 1 -exp pschildname if($version -ge "v4.0") { return $true } else { return $false } } } catch { Write-Error $_.Exception.Message }} |
检查后就是安装.NET Framework,调用安装程序是通过 Start-Process 函数来实现的,只需找出文件夹下的 exe 文件,并调用 Start-Process 函数即可:
1 2 | Write-Progress "正在安装.NET Framework" "请稍候……"Get-ChildItem $PSScriptRoot -Filter *.exe | %{start -wait $_ -ArgumentList "/quiet"}<br>Write-Progress "completed" "completed" -Completed |
Write-Progress 是显示进度条信息,$PSScriptRoot 是获取当前脚本所在的路径。-ArgumentList 参数表示该安装过程是以静默安装的方式进行,如果没有该参数就会显示具体的安装过程。接下来是升级 Powershell 到 4.0 版本,因为后面的脚本是基于 4.0 来写的。
升级 Powershell
在升级之前同样是先检查 Powershell 的版本,如果已经是 4.0 版本了就没有必要再重新更新一次了。升级 Powershell 的方式跟安装.NET Framework 的方式是一样的,只是在升级完成时系统会自动重启以完成升级,也可以在安装后不自动重启,只需���-ArgumentList 参数里使用 ”/quiet /norestart” 即可,但是本文中的脚本是会自动重启。如果你的脚本不是基于 4.0 版本的就可以设置为不自动重启了。那么,如何让系统重启后自动执行当前的脚本呢?你可能会想到注册表,没错,本文就是通过写注册表的方式来实现,如果已经是 4.0 版本的话就可以用另外一种方式来实现了,具体的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 | #Register-ScheduledJob 只能在 3.0 以后使用 #$scriptPath = $MyInvocation.ScriptName#$trigger = New-JobTrigger -AtStartup -RandomDelay 00:01:00#Register-ScheduledJob -Trigger $trigger -FilePath $scriptPatp -Name UpgradePowershell$registryValue = "{0}\system32\WindowsPowerShell\v1.0\powershell.exe {1}" -f $env:windir,$MyInvocation.ScriptName$registryPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce"$exists = Test-Path $registryPathif($exists -eq $false){ New-Item $registryPath}New-ItemProperty "$registryPath" Upgrade -PropertyType String -Value "$registryValue" |
注释的代码就是另外一种实现方式,但是只能在 3.0 以后的版本中使用。在 HKCU 下是没有 RunOnce 这个项的,所以需要先判断该注册表路径是否存在,在 HKLM 下的话就有 RunOnce 路径了。RunOnce 表示只会执行一次,执行完后该注册信息就会被删除。
安装 IIS
在安装调用安装 IIS 的方法之前需要先使用下面的代码引入 ServerManager 模块,否则没有办法调用具体的函数:
Import-Module servermanager |
添加功能和角色主要用 Add-WindowsFeature -name,name 参数是功能或角色的名称,如果不知道具体功能和角色的名称可以用 Get-WindowsFeature 来获取相关角色或功能的名称:
1 2 3 4 5 6 7 8 9 | $features = get-windowsfeature web-*foreach($item in $features){ if($item.installed -eq $false) { Write-Host "安装:$item.displayname" $item | add-windowsfeature }} |
首先获取以 web- 开头的所有角色和功能,逐个判断是否已经安装,没有安装的再进行安装。
注册、启用 ISAPI 和 CGI 限制
在运行注册命令之前先判断是否已经注册,如果注册了判断是否已经启用。在 Powershell 注册 ISAPI 和在命令提示符中注册是差不多的,都是要以管理员身份身份运行。如果是直接运行 aspnet_regiis.exe 的全路径的话,Powershell 和 cmd 中的命令是一样的,即:
1 | C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -i |
如果是先切换到 aspnet_regiis.exe 的目录下,在 cmd 下可以直接运行 aspnet_regiis.exe -i,在 Powershell 下则需要运行./aspnet_regiis.exe -i,否则 Powershell 无法识别 aspnet_regiis.exe - i 命令。通过下面的脚本获取是否已经注册和启用,并赋值给 $isapiConfiguration 变量:
1 | $isapiConfiguration = get-webconfiguration "/system.webServer/security/isapiCgiRestriction/add[@path='$isapiPath']/@allowed" |
$isapiPath 是一个变量,存放 isapi 的全路径。如果变量 $isapiConfiguration 等于 null 的话说明尚未注册 isapi,如果变量不等于 null,并且 $isapiConfiguration.Value 等于 false 的话说明未启用 isapi。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | # 检查系统是否是 64bitfunction Is64Bit { [IntPtr]::Size -eq 8 }# 注册或启用 ISAPIfunction RegisterAndEnableIsapi{ $is64Bit = Is64Bit $isapiPath="" if($is64Bit) { $isapiPath ="$env:windir\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" } else { $isapiPath ="$env:windir\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" } $isapiConfiguration = get-webconfiguration "/system.webServer/security/isapiCgiRestriction/add[@path='$isapiPath']/@allowed" if($isapiConfiguration -eq $null) { write-host "IIS 尚未注册 aspnet_isapi.dll" $tmpPath="" if($is64Bit) { $tmpPath = "$env:windir\Microsoft.NET\Framework64\v4.0.30319\" } else { $tmpPath = "$env:windir\Microsoft.NET\Framework\v4.0.30319\" } set-location $tmpPath .\aspnet_regiis.exe -i $isapiConfiguration = get-webconfiguration "/system.webServer/security/isapiCgiRestriction/add[@path='$isapiPath']/@allowed" } if($isapiConfiguration.Value -eq $false) { write-host "IIS 已经注册过 aspnet_isapi.dll,但未启用" set-webconfiguration "/system.webServer/security/isapiCgiRestriction/add[@path='$isapiPath']/@allowed" -value true if(Is64Bit) { set-webconfiguration "/system.webServer/security/isapiCgiRestriction/add[@path='$env:windir\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll']/@allowed" -value true } Write-Host "isapi 已启用" } else { write-host "IIS 已经注册过 aspnet_isapi.dll,且已启用" }} |
创建应用程序池
在新建应用程序池和新建网站之前需要先引入“WebAdministration”模块,否则会出现下面的错误:

该模块在 2.0 版本下是没有的,所以要升级到 4.0 版本。
由于我们手动建立网站的时候会自动创建应用程序池,只需要设置应用程序池的相关属性就可以,但用 Powershell 脚本新建网站的时候是不会自动创建应用程序池的,所以我们需要先创建好应用程序池,在创建网站的时候将其指向到新建的应用程序池。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | set-location iis:\AppPools$existsAppPool = test-path $appPoolNameif($existsAppPool -eq $false){ $appPool = new-item $appPoolName #设置标识:LocalService=1;LocalSystem=2;NewworkService=3;ApplicationPoolIdentity=4 $appPool.ProcessModel.IdentityType=4 #设置.NET Framework 版本 $appPool.managedRuntimeVersion="v4.0" #设置托管管道模式:集成 =0;经典 =1 $appPool.ManagedPipelineMode=0 $appPool.startMode="AlwaysRunning" #设置启用 32 位应用程序 false=0;true=1 $appPool.enable32BitAppOnWin64=0 $appPool | set-item}else{ write-error "应用程序池已经存在"} |
创建网站
因为动态压缩功能只要有安装,在新建网站的时候会自动启用,所以有需要启用动态内容压缩功能的话就需要检查该功能是否已经安装。
1 2 3 4 5 6 7 8 9 | # 安装动态内容压缩功能 function EnableGZip{ $check = get-windowsfeature web-dyn-compression if($check.installed -eq $false) { add-windowsfeature web-dyn-compression }} |
检查网站目录是否存在,如果不存在就新建一个目录并设置权限,如果要关联的目录不存在的话就会出现下面的错误:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # 设置权限 function SetSecurity($name,$path){ $acl= get-acl $path $ar = new-object System.Security.AccessControl.FileSystemAccessRule("$name","ReadAndExecute","ContainerInherit,ObjectInherit","None","Allow") $acl.SetAccessRule($ar) set-acl $acl -path $path}function CheckDirectory($path){ $existsPath=test-path $path if($existsPath -eq $false) { write-host "【$path】目录不存在,新建该目录" new-item -path $path -type directory } #设置 network service 用户的权限 Write-Progress "正在设置目录权限,请稍候……" SetSecurity "network service" $path SetSecurity "everyone" $path Write-Progress "completed" -Completed} |
$name 是“组或用户名”,$path 是站点路径。
将当前文件夹下的站点文件拷贝到站点目录下,由于拷贝文件可能会比较耗时,所以使用了进度条显示拷贝进度,如果不使用进度条的话就只要两条语句就可以完成:
1 2 | $siteFilePath = (get-childitem $psscriptroot | ?{$_.psiscontainer})[0].fullnameCopy-Item "$siteFilePath\*" $sitePath -Force -Recurse |
使用进度条的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # 将脚本文件所在目录下的文件夹下的文件全部拷贝到站点目录下 function CopyFiles{ $siteFilePath = (get-childitem $psscriptroot | ?{$_.psiscontainer})[0].fullname $files=Get-ChildItem "$siteFilePath\*" $count = $files.Length for($i=0;$i -lt $count;$i++) { $copied = $i+1; Copy-Item $files[$i] $sitePath -Force -Recurse $percentage = $copied/$count $msg = "已拷贝:{0:p0}" -f $percentage Write-Progress -Activity "正在拷贝文件到:【$sitePath】目录" -Status $msg -PercentComplete ($percentage*100) } Write-Progress "拷贝结束" -Completed} |
上述准备工作做完之后就是建立网站了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | set-location iis:\sitesif((test-path $siteName) -eq $true){ write-error "站点已经存在";}else{ #新建站点 new-website $siteName -physicalpath $sitepath #绑定域名 new-webbinding -name $siteName -host $hostname -port 80 -protocol http #获取本机 IP $ojbItem = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName . $ipaddress = $ojbItem.IPAddress[0] #绑定 IP 地址和端口 new-webbinding -name $siteName -ip $ipaddress -port $port -protocol http #设置应用程序池 set-itemproperty $siteName -name applicationpool -value $appPoolName #启用 Forms 身份验证 $config = get-webconfiguration system.web/authentication $siteName $config.mode="Forms" $config|set-webconfiguration system.web/authentication #启用匿名身份验证 Set-WebConfigurationProperty -Filter system.webServer/security/authentication/anonymousAuthentication -PSPath MACHINE/WEBROOT/APPHOST -Location $siteName -Name Enabled -Value $true} |
如果有开启防火墙的话还需要添加入站规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | function AddFirewallRule($name,$tcpPorts,$appName = $null,$serviceName = $null){ try { $fw = New-Object -ComObject hnetcfg.fwpolicy2 $rule = New-Object -ComObject HNetCfg.FWRule $rule.Name = $name if ($appName -ne $null) {$rule.ApplicationName = $appName} if ($serviceName -ne $null) {$rule.serviceName = $serviceName} $rule.Protocol = 6 #NET_FW_IP_PROTOCOL_TCP $rule.LocalPorts = $tcpPorts $rule.Enabled = $true $rule.Grouping = "@firewallapi.dll,-23255" $rule.Profiles = 7 # all $rule.Action = 1 # NET_FW_ACTION_ALLOW $rule.EdgeTraversal = $false $fw.Rules.Add($rule) Write-Host "防火墙入站规则添加成功" } catch { Write-Error $_.Exception.Message }} |
创建虚拟目录的比较简单,但是也需要检查虚拟目录的路径是否存在,设置虚拟目录的权限
1 | new-item "$siteName\$name" -type virtualdirectory -physicalpath $path |
如果有需要还可以添加 MIME 类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # 添加扩展名 $mime 为哈希表类型 如 $mimes = @{".a"="application/stream";".b"="application/stream";".c"="application/stream";}function AddMime($mime){ try { if($mimes -eq $null -or $mimes.count -le 0) { return } foreach($item in $mimes.Keys) { Write-Host "添加 MIME 类型:$item" $extension = get-webconfigurationproperty //staticcontent -name collection | ?{$_.fileExtension -eq $item} if($extension -ne $null) { write-host "该扩展名已经存在" } else { add-webconfigurationproperty //staticcontent -name collection -value @{fileExtension=$item;mimeType=$mimes[$item]} } } Write-Host "MIME 类型添加完成" } catch { Write-Error $_.Exception.Message } } |
测试网站
1 2 3 4 5 6 7 8 9 10 | # 请求接口 function GetRequest($url){ $request = [System.Net.HttpWebRequest]::Create($url) $response = [System.Net.HttpWebResponse]$request.GetResponse() $code = [System.Int32]$response.StatusCode $response.Close() $response.Dispose() Write-Host $code} |
测试网站是通过调用.NET Framework 的相关函数来实现的。
以上就是用 Powershell 脚本自动化部署网站的全部过程,可能还有遗落的功能没有实现。如果你对自动化部署网站有兴趣的话可以自己实现一个 Powershell 脚本。也请各位大牛多多指教,指出本文中的不足和错误的地方。
本文永久更新链接地址 :http://www.linuxidc.com/Linux/2016-11/137721.htm






