In this post, I am going to share a sample azure-pipelines.yml to deploy an ASP.NET Core Containerized Application and Applying Entity Framework Core Database Migrations.
Here the pipeline is based on the following requirement which I believe is common.
azure-pipelines.yml
Here the pipeline is based on the following requirement which I believe is common.
- When doing the release, we need to apply database migrations in the target database. The migration script will be created on the fly during the execution of the pipeline and will be executed on the target database.
- We might need to execute some additional SQL scripts in the target database after the database is migrated, maybe to CREATE/ALTER stored procedures, etc. The pipeline assumes those scripts are residing in scripts/postdeploy folder in the root of the project.
azure-pipelines.yml
trigger: - master resources: - repo: self variables: # Container registry service connection established during pipeline creation dockerRegistryServiceConnection: '<dockerRegistryServiceConnection>' # Something like xxxxxx.azurecr.io containerRegistry: '<containerRegistry>' # Docker image name imageRepository: '<imageRepository>' # The relative localtion of the Dockerfile dockerfilePath: '$(Build.SourcesDirectory)/Dockerfile' # This docker image will be tagged using this tag: '$(Build.BuildId)' # Agent to be used vmImageName: 'ubuntu-latest' # We have 2 stages, # 1. Build # 2. Release stages: # This stage will and build the docker image and push it to ACR - stage: Build displayName: Build and push stage jobs: - job: Build displayName: Build pool: vmImage: $(vmImageName) steps: - task: Docker@2 displayName: Build and push an image to container registry inputs: command: buildAndPush repository: $(imageRepository) dockerfile: $(dockerfilePath) containerRegistry: $(dockerRegistryServiceConnection) tags: | $(tag) # This stage will, # 1. Create a database migration script named update-database.sql inside scripts folder # 2. Execute the migration script first # 3. Then execute all the other SQL scripts inside scripts/postdeploy folder # 4. Finally spin up the container at the target web app - stage: Release displayName: Release stage jobs: - job: Release displayName: Release pool: vmImage: $(vmImageName) steps: # This task is required to run dotnet-ef which is getting installed in the next task - task: UseDotNet@2 inputs: version: '3.1.200' # Install dotnet-ef - task: DotNetCoreCLI@2 displayName: Install dotnet-ef inputs: command: 'custom' custom: 'tool' arguments: 'install --global dotnet-ef --ignore-failed-sources' # Generate update-database.sql using dotnet-ef - task: PowerShell@2 displayName: Generate Database Migration Script inputs: targetType: 'inline' script: | # Splitted into multiple lines for brevity. Below command needs to be a single line dotnet ef migrations script -i -o "$(Build.SourcesDirectory)/scripts/update-database.sql" --project "$(Build.SourcesDirectory)/<ProjectThatContainsTheDbContext.csproj>" --startup-project "$(Build.SourcesDirectory)/<TheStartupProject.csproj>" # Install SqlServer module to be able to run Invoke-SqlCmd in the next task - task: PowerShell@2 displayName: PowerShell Install-Module SqlServer inputs: targetType: 'inline' script: 'Install-Module -Name SqlServer -AllowPrerelease -Force -Verbose -Scope CurrentUser' # Execute the update-database.sql in the target database - task: PowerShell@2 displayName: PowerShell Invoke-Sqlcmd Database Migration Script inputs: targetType: 'inline' script: | # Splitted into multiple lines for brevity. Below command needs to be a single line Invoke-Sqlcmd -ServerInstance "<ServerInstance>" -Database "<Database>" -Username "<Username>" -Password "<Password>" -Inputfile "$(Build.SourcesDirectory)/scripts/update-database.sql" -Verbose -ConnectionTimeout 120 # Execute other SQL scripts in the target database - task: PowerShell@2 displayName: PowerShell Invoke-Sqlcmd Other SQL Scripts inputs: targetType: 'inline' script: | $files = Get-ChildItem $(Build.SourcesDirectory)/scripts/postdeploy foreach ($f in $files) { # Splitted into multiple lines for brevity. Below command needs to be a single line Invoke-Sqlcmd -ServerInstance "<ServerInstance>" -Database "<Database>" -Username "<Username>" -Password "<Password>" -Inputfile "$f" -Verbose -ConnectionTimeout 120 } # All good, let's spin up a new container at the target web app - task: AzureWebAppContainer@1 displayName: Spin up the container inputs: azureSubscription: '<azureSubscription>' appName: '<appName>' containers: $(containerRegistry)/$(imageRepository):$(tag) # Something like dotnet TheStartupProject.dll containerCommand: '<containerCommand>'
Hope this helps.
Happy Coding.
Regards,
Jaliya