Azure DevOps の Pipeline で Core じゃない ASP.NET のプロジェクトをビルドして runFromZip でデプロイする

アプリを動かすのに必要な zip ファイルさえ作れたら勝利なのでコマンドで FileSystem へのデプロイ方法を調べたんですが、なかなか情報がありませんでした。ということで、見つけたのはこちらの Stackoverflow の質問

[How to use MsBuild MsDeployPublish to target local file system?](https://stackoverflow.com/questions/9169341/how-to-use-msbuild-msdeploypublish-to-target-local-file-system)

上記質問の回答から引用：

```
msbuild ProjectFile.csproj /p:Configuration=Release ^
                           /p:Platform=AnyCPU ^
                           /t:WebPublish ^
                           /p:WebPublishMethod=FileSystem ^
                           /p:DeleteExistingFiles=True ^
                           /p:publishUrl=c:\output

Or if you are building the solution file:

msbuild Solution.sln /p:Configuration=Release ^ 
                     /p:DeployOnBuild=True ^
                     /p:DeployDefaultTarget=WebPublish ^
                     /p:WebPublishMethod=FileSystem ^
                     /p:DeleteExistingFiles=True ^
                     /p:publishUrl=c:\output
```

なるほどね。DeployOnBuild, DeployDefaultTarget, WebPublishMethod, publishUrl あたりが肝かな？ということでコマンドでのやり方がわかったら、あとはビルドパイプラインの YAML に落とし込むだけです。

ASP.NET のビルドを行うパイプラインのひな型をベースに少し弄って対応しました。とりあえず trigger はお試しなので CI Tirgger で master ブランチに変更があったらという感じにしてデプロイ先も 1 つの環境だけにしてます。

ということで以下のような YAML ファイルが完成しました。

```yaml
trigger:
- master

pool:
  vmImage: 'windows-latest'

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

stages:
# ビルドを行うステージ
- stage: Build
  jobs:
  - job: build
    steps:
    - task: NuGetToolInstaller@1

    - task: NuGetCommand@2
      inputs:
        restoreSolution: '$(solution)'
    
    # ここで $(Build.BinariesDirectory) に対してデプロイするファイルを出力している
    - task: VSBuild@1
      inputs:
        solution: '$(solution)'
        msbuildArgs: '/p:DeployBuild=true /p:DeployOnBuild=true /p:WebPublishMethod=FileSystem /p:DeleteExistingFiles=True /p:publishUrl=$(Build.BinariesDirectory) /p:DeployDefaultTarget=WebPublish'
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'

    - task: VSTest@2
      inputs:
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'
    
    # ビルド成果物を zip に固める
    - task: ArchiveFiles@2
      inputs:
        rootFolderOrFile: '$(Build.BinariesDirectory)'
        includeRootFolder: false
        archiveType: 'zip'
        archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
        replaceExistingArchive: true
    
    # 固めた zip をビルド成果物としてアップロード
    - publish: '$(build.artifactStagingDirectory)/$(Build.BuildId).zip'
      artifact: webapp

# デプロイを行うステージ
- stage: Deploy
  dependsOn:
  - Build
  jobs:
  - deployment: Deploy
    environment: Production
    strategy:
      runOnce:
        deploy:
          steps:
          # ビルドで作った成果物をダウンロードして
          - download: current
            artifact: webapp

          # Azure App Service の staging スロットめがけてデプロイ
          - task: AzureRmWebAppDeployment@4
            inputs:
              ConnectionType: 'AzureRM'
              azureSubscription: 'DevOpsLab'
              appType: 'webApp'
              WebAppName: 'webformsappdemo'
              deployToSlotOrASE: true
              ResourceGroupName: 'AzureDevOpsLab-rg'
              SlotName: 'staging'
              packageForLinux: '$(Pipeline.Workspace)/**/*.zip'
              enableCustomDeployment: true
              DeploymentType: 'runFromZip'

          # staging と production をスワップ
          - task: AzureAppServiceManage@0
            inputs:
              azureSubscription: 'DevOpsLab'
              Action: 'Swap Slots'
              WebAppName: 'webformsappdemo'
              ResourceGroupName: 'AzureDevOpsLab-rg'
              SourceSlot: 'staging'
```

ASP.NET Web Forms のプロジェクトを単体テストつきで新規作成したプロジェクトを突っ込んだリポジトリーに上記パイプラインを設定すると、ちゃんと Azure のほうにデプロイされました。

めでたしめでたし。