如何使用PowerShell命令部署应用至Azure静态网站
本地Azure DevOps Server部署Angular到Azure静态网站的PowerShell方案
Azure静态网站的底层是Azure存储账户$web专用容器托管的静态资源服务,和Azure App Service的底层架构完全不同,Publish-AzWebApp命令无法适配该场景,直接通过Az.Storage模块操作存储Blob即可完成部署,支持直接传入zip格式的编译制品作为输入。
前置准备
- 提前在目标Azure存储账户中启用静态网站功能,设置默认索引文档为
index.html,功能启用后系统会自动创建名为$web的专用Blob容器,所有静态站点内容都存放在这个容器中 - 部署执行的代理机器需要提前安装
AzPowerShell模块(最低兼容版本5.0),可直接在代理机执行Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force完成安装 - 提前在Azure AD创建服务主体,给该主体分配目标存储账户的存储Blob数据参与者权限,记录服务主体的ClientId、ClientSecret、TenantId信息,在DevOps Server流水线中配置为秘密变量,避免明文泄露
- Angular项目编译阶段产出的zip制品包,需要确保解压后根目录直接存在
index.html、打包生成的js/css/资源文件,不要嵌套多层项目文件夹,否则上传后站点会因为找不到入口文件报404
核心部署PowerShell脚本
该脚本可直接在DevOps Server的原生PowerShell任务中调用,入参直接关联流水线产出的zip制品路径即可:
param( [Parameter(Mandatory=$true)] [string]$ArtifactZipPath, # Angular编译产出的zip包本地路径 [Parameter(Mandatory=$true)] [string]$StorageAccountName, # 目标静态网站对应的存储账户名称 [Parameter(Mandatory=$true)] [string]$TenantId, [Parameter(Mandatory=$true)] [string]$ClientId, [Parameter(Mandatory=$true)] [string]$ClientSecret ) # 服务主体登录Azure $secureSecret = ConvertTo-SecureString $ClientSecret -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential($ClientId, $secureSecret) Connect-AzAccount -ServicePrincipal -Tenant $TenantId -Credential $cred | Out-Null # 临时解压制品到本地临时目录,避免污染代理机工作目录 $tempExtractPath = Join-Path $env:TEMP ([System.Guid]::NewGuid().ToString()) New-Item -ItemType Directory -Path $tempExtractPath | Out-Null Expand-Archive -Path $ArtifactZipPath -DestinationPath $tempExtractPath -Force # 获取存储账户上下文,静态网站内容固定存放在$web容器 $storageContext = New-AzStorageContext -StorageAccountName $StorageAccountName -UseConnectedAccount # 清空$web容器内旧版本文件,避免历史残留资源导致版本异常 Get-AzStorageBlob -Container '$web' -Context $storageContext | Remove-AzStorageBlob -Force # 递归上传所有静态资源,手动映射常用文件的Content-Type,避免默认识别错误导致页面无法正常渲染 Get-ChildItem -Path $tempExtractPath -File -Recurse | ForEach-Object { $relativePath = $_.FullName.Substring($tempExtractPath.Length + 1).Replace('\','/') $contentType = switch ($_.Extension.ToLower()) { '.html' { 'text/html; charset=utf-8' } '.js' { 'application/javascript; charset=utf-8' } '.css' { 'text/css; charset=utf-8' } '.json' { 'application/json; charset=utf-8' } '.png' { 'image/png' } '.jpg' { 'image/jpeg' } '.jpeg' { 'image/jpeg' } '.svg' { 'image/svg+xml' } '.ico' { 'image/x-icon' } '.woff' { 'font/woff' } '.woff2'{ 'font/woff2' } '.ttf' { 'font/ttf' } default { 'application/octet-stream' } } # 非入口文件设置1年长缓存,提升访问性能 $cacheControl = if ($relativePath -eq 'index.html') { 'no-cache' } else { 'public, max-age=31536000' } Set-AzStorageBlobContent -File $_.FullName -Container '$web' -Blob $relativePath ` -Context $storageContext -Properties @{ContentType=$contentType, CacheControl=$cacheControl} -Force | Out-Null } # 清理临时解压目录 Remove-Item -Path $tempExtractPath -Recurse -Force
配置注意事项
- 脚本中的敏感参数(ClientId、ClientSecret、TenantId)禁止硬编码,直接引用DevOps Server中配置的秘密变量传参即可,调用示例:
-ClientSecret $(StorageSpnSecret) - 如果Angular路由使用
PathLocationStrategy(HTML5路由模式),需要在存储账户静态网站配置页将错误文档路径也设置为index.html,否则刷新子路由时会返回404错误 - 如果存储账户开启了防火墙限制,需要把DevOps Server代理节点的出口IP加到存储账户的允许IP列表中,否则上传时会报403权限错误
- 如果站点配置了CDN加速,可在上传完成后追加Az.Cdn模块的缓存清除命令,避免用户访问到旧版本资源
- Angular编译时需要确保
base href配置和静态网站的访问路径匹配,否则部署后会出现静态资源加载404的问题
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

