本文主要讲如何通过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\”,所以可以通过以下代码来实现:

test-path "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\"

但是上面的代码只能检查.NET Framework的安装情况,并不知道是安装了哪个版本,所以还需要配合下面的代码:

$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就可以了。具体的代码如下所示:

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函数即可:

Write-Progress "正在安装.NET Framework" "请稍候……"
Get-ChildItem $PSScriptRoot -Filter *.exe | %{start -wait $_ -ArgumentList "/quiet"}
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版本的话就可以用另外一种方式来实现了,具体的代码如下:

#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 $registryPath
if($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来获取相关角色或功能的名称:

$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中的命令是一样的,即: 

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变量:

$isapiConfiguration = get-webconfiguration "/system.webServer/security/isapiCgiRestriction/add[@path='$isapiPath']/@allowed"

$isapiPath是一个变量,存放isapi的全路径。如果变量$isapiConfiguration等于null的话说明尚未注册isapi,如果变量不等于null,并且$isapiConfiguration.Value等于false的话说明未启用isapi。

#检查系统是否是64bit
function Is64Bit
{
[IntPtr]::Size -eq 8
} #注册或启用ISAPI
function 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脚本新建网站的时候是不会自动创建应用程序池的,所以我们需要先创建好应用程序池,在创建网站的时候将其指向到新建的应用程序池。

set-location iis:\AppPools
$existsAppPool = test-path $appPoolName
if($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 "应用程序池已经存在"
}

创建网站

因为动态压缩功能只要有安装,在新建网站的时候会自动启用,所以有需要启用动态内容压缩功能的话就需要检查该功能是否已经安装。

#安装动态内容压缩功能
function EnableGZip
{
$check = get-windowsfeature web-dyn-compression
if($check.installed -eq $false)
{
add-windowsfeature web-dyn-compression
}
}

检查网站目录是否存在,如果不存在就新建一个目录并设置权限,如果要关联的目录不存在的话就会出现下面的错误:

#设置权限
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是站点路径。

将当前文件夹下的站点文件拷贝到站点目录下,由于拷贝文件可能会比较耗时,所以使用了进度条显示拷贝进度,如果不使用进度条的话就只要两条语句就可以完成:

$siteFilePath = (get-childitem $psscriptroot | ?{$_.psiscontainer})[0].fullname
Copy-Item "$siteFilePath\*" $sitePath -Force -Recurse

使用进度条的方式:

#将脚本文件所在目录下的文件夹下的文件全部拷贝到站点目录下
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
}

上述准备工作做完之后就是建立网站了

set-location iis:\sites
if((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
} 

如果有开启防火墙的话还需要添加入站规则

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
}
}

创建虚拟目录的比较简单,但是也需要检查虚拟目录的路径是否存在,设置虚拟目录的权限

new-item "$siteName\$name" -type virtualdirectory -physicalpath $path

如果有需要还可以添加MIME类型

#添加扩展名 $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
}
}

测试网站

#请求接口
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脚本。也请各位大牛多多指教,指出本文中的不足和错误的地方。

利用Powershell在IIS上自动化部署网站的更多相关文章

  1. 使用PowerShell 自动安装IIS 及自动部署网站

    执行环境:Windows Server 2012 R2 安装iis核心代码,可自定义安装项 注意这里不能使用add-windowsfeature  "Web-Filtering", ...

  2. 利用jenkins做项目的自动化部署

    最近领导要求上海本地的项目需要使用进jenkins实现自动化部署,以便可以直接将项目移交给运维的同学,减轻开发的工作量.记录下这次爬坑的过程. 一.前言 Jenkins是基于Java开发的一种持续集成 ...

  3. IIS上架设https网站证书处理备忘

    1. 免费SSL证书申请 https://www.startssl.com 教程:http://hxs.fd.fj.cn/?action=show&id=13 2. 证书转换 申请到的证书有两 ...

  4. 利用 Github 网络钩子实现自动化部署

    GitHub 的网络钩子(webhook)功能,可以很方便的实现自动化部署.本文记录了使用 Node.js 的开发部署过程,当项目的 master 分支被推时,将在服务器进行自动部署 添加网路钩子 在 ...

  5. Centos7.6上利用docker搭建Jenkins来自动化部署Django项目

    一般情况下,将一个项目部署到生产环境的流程如下: 需求分析-原型设计-开发代码-内网部署-提交测试-确认上线-备份数据-外网更新-最终测试,如果发现外网部署的代码有异常,需要及时回滚. 整个过程相当复 ...

  6. 在阿里云Centos7.6上利用docker搭建Jenkins来自动化部署Django项目

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_84 一般情况下,将一个项目部署到生产环境的流程如下: 需求分析-原型设计-开发代码-内网部署-提交测试-确认上线-备份数据-外网更 ...

  7. Asp.net Core IIS上安装部署及502.5错误解决

    总结: 安装Microsoft Visual C++ 2015 Redistributable(https://www.microsoft.com/en-us/download/details.asp ...

  8. jenkins在windows上自动化部署.Net(.Net Core)项目

    前言 什么是持续集成呢?Continuous integration(CI).持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员至少集成一次,也就意味着每天可能会发生多次集成. ...

  9. Windows Server 在IIS上创建安全网站

    第一步.打开服务器管理器,创建用户,并设置密码,自己记录下来,注意勾选密码永不过期. 第二步.设置用户隶属组:IIS_IUSRS. 第三步.设置建立的网站文件夹权限,添加刚才建立的用户,并添加修改.读 ...

随机推荐

  1. css3 - 语言伪类选择器

    <!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...

  2. Android加壳native实现

    本例仅在Android2.3模拟器跑通过,假设要适配其它机型.请自行研究,这里不过抛砖引玉. 0x00 在Android中的Apk的加固(加壳)原理解析和实现,一文中脱壳代码都写在了java层非常ea ...

  3. 大牛blog汇总

    系列专题的文件夹 01. Java String系列 (共3篇) 02. Java异常系列 (共3篇) 03. Java 时间日期系列 (共7篇) 04. java io系列 (共26篇) 05, J ...

  4. C# 比较两个数组中的内容是否相同的算法

    这里要比较的是两个数组中的内容是否相同,以int数组为例 int[] Arraya=new[] {1,2,3,4,5} int[] Arrayb=new[] {5,3,2,1,4} 以上两个数组内的值 ...

  5. C语言malloc

    在子函数里面动态申请的内存不会自动被系统收回的,因为这些空间在堆里面,而不是栈,平常所说的不能返回指向栈的指针,比如在子函数里面定义一个字符指针,指向常量"hello"因为函数调用 ...

  6. 搭建mongoDB 配置副本集 replSet

    mongodb的master_slave和ReplSet是很常见的两种构架: 下面记录下搭建mongodbReplSet 的过程: 首先,进入到一个指定目录下 >cd /opt 下载mongod ...

  7. 多媒体开发之编码gop---什么是GOP

    所谓GOP,意思是画面组,MPEG格中的帧序列,分为I.P.B三种,如排成IBBPBBPBBPBBPBBP...样式,这种连续的帧图片组合即为GOP(画面群,GROUP OF PICTURE),是MP ...

  8. ZeroClipboard—ZeroClipboard的使用

    1.ZeroClipboard的作用: 借助Zero Clipboard能够简单高速地将内容拷贝到剪贴板,相似点击某些网页中"复制"按钮后复制对应区域的内容. 2.ZeroClip ...

  9. [转]***换机房换ip之后不能连外网

    ***换机房换ip之后不能连外网 时间 2015-07-21 15:17:16  Wendal随笔 原文  http://wendal.net/2015/07/21.html 主题 iptables ...

  10. "Installing Software" has encountered a problem---pydev on ubuntu

    "Installing Software" has encountered a problem. An error occurred while collecting items ...