Overview of my notes for Deploying .NET Applications To Azure, for a guided tutorial I highly recommend Dometrain - From Zero to Hero: Deploying .NET Applications to Azure by Mohamad Lawand
Technologies Used
- Azure Container Registry (Build store and manage container images)
- Azure Container Apps (Built on K8s, is Serverless & Fully managed by Azure)
- Azure SQL Database (Managed PaaS database engine)
- Azure Log Analytics Workspace (Observability and Analytics)
- Github Actions
- Terraform (Consistently build infrastructure using automation)
- .NET SDK
.NET Web API
I built this simple CRUD(ish) app based on the Microsoft templates and adapted it to use EF Core and Postgres - https://github.com/carlpaton/deploying-dotnet-azure
Its .NET 8.0 so still has Swagger which I enabled for all environments so I can use the Swagger UI when deployed to Azure, normally this is only for local development or demo purposes.
Commands
Overview of the commands needed to run for the various CLIs
Docker
1 | docker compose up ~ local database |
Run EF migrations locally
1 | dotnet tool install --global dotnet-ef ~ globally install the EF tooling |
Terraform
1 | terraform version |
Azure
1 | az login --tenant TENANT_ID ~ auth the console to Entra so you can create resources in Azure |
- https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-windows?tabs=azure-cli
- https://learn.microsoft.com/en-us/cli/azure/ad/sp?view=azure-cli-latest#az-ad-sp-create-for-rbac
Terraform
I created vars.tf
, setup.tf
and resource-group.tf
and updated vars with subscription_id
(you get this from az login
), sql_pass
and sql_user
. If you dont set a default, it will prompt for a value. I then ran terraform plan/apply
which created my Resource Group (demo-rg
)
- vars.tf
- variables to be reused in the other .tf files
- set subscription id, you get this when you login with
az login
- setup.tf
- sets the cloud provider, API versions and account to connect to by subscription id
- see azurerm
- resource-group.tf
- groups resources in Azure
- see resource-group
I then added the following .tf
files, one at a time in the order listed here again running terraform plan/apply
- azure-container-registry.tf
- registry to store and manage docker images by version
- see container_registry
- azurerm_log_analytics_workspace.tf
- logging and analytics
- see log_analytics_workspace
- container_app_environment.tf
- container_app.tf
- see container_app
- mssql.tf
- see mssql_server
- see mssql_database
- see mssql_firewall_rule
The end result seen from Azure showed the resources in my Resource Group:
Backend (Long Term Storage)
When I ran the terraform x
commands locally it created some state listed below, this needs to be pushed to blob storage if we want to run these commands in a CI/CD pipeline. These files also contain GUIDs like the subscription ID which I dont think should be pushed to source control.
1 | terraform.tfstate |
Note the file .terraform.lock.hcl
can be commited to source control and I created my backend manually in Azure portal.
- Resource groups -> create
- Name ->
reference-rg
- Region ->
(Asia Pacific) Australia East
- Tags ->
environment=dev, source=azure-portal
- Review and create -> create
- Select
reference-rg
-> create -> search ->storage account
-> selectStorage account
by Microsoft | Azure Service
- Plan ->
Storage account
- Storage account name ->
demoiac
- Region ->
(Asia Pacific) Australia East
- Primary service ->
Azure Blob Storage or Azure Data Lake Storage Gen 2
- Performance ->
Standard
- Redundancy ->
Locally-redundant storage (LRS)
- Review and create -> create
- this will take a minute or so to create
- Go to resource -> Data storage -> Containers -> + Container (New Container)
- Name ->
terraform
-> Create
- Update Setup.tf to include the backend
1 | terraform { |
- To migrate the terraform files from local to the new blob storage run
- run
terraform plan
- Do you want to copy existing state to the new backend -> yes
- Add a testing tag to the existing
demo-rg
resource group
1 | tags = { |
- Run
terraform plan
, this will show the changes, no need to apply it, this will be done next in a pipeline.
… WIP!
Connect To SQL
The username and DNS shown here was just for the demo, best practice is never to commit or share any secrets.
I then added my own IP address to the firewall by navigating to Resource Group (demo-rg
) -> selected the SQL server (demo-sql-dev
) -> Networking (Show networking settings) -> Firewall rules -> Add your client IPv4 address (xxx.xxx.xxx.xxx) -> Azure automagically filled mine in -> I called mine Carl home IP
-> Save. Your IP wont be static so will probs need an update later.
I then needed to get the server address for SQL in Azure by navigating to SQL server
, my server name was demo-sql-dev.database.windows.net, so the suffix comes from mssql.tf where we set the server name
Based on the sql_pass/user
values in vars.tf I build then connected using DBeaver
Github Secrets and Variables
Github Settings -> Secrets and variables -> actions ->
- Secrets tab -> Repository secrets -> New repository secret
- Variables tab -> Repository variables -> New repository variable
For Azure Container Registry
Container Registry -> Settings -> Access keys
1 | ${{ vars.ACR_SERVER }} |
For Azure Container App
Create a service principal and configure its access to Azure resources. See az ad sp create-for-rbac
above, save the whole JSON output into git secrets.
1 | ${{ secrets.AZ_CREDENTIALS }} |
For IAC
Create a service principal and configure its access to Azure resources. See az ad sp create-for-rbac
above, save keys out of the JSON into git secrets.
1 | ${{ secrets.ARM_CLIENT_ID }} ~ appId |
CI/CD
WORKFLOW
- push: When you commit
- pull_request: When you create a pull request
- workflow_dispatch: When you manually trigger a job, useful for pull request job re-runs
JOB:
- build-and-push-image
- STEPS:
- Checkout repository
- Setup WebApi .NET
- Configure Azure Container Registry (ACR)
- Get commit SHA
- Build and push image to Azure Container Registry (ACR)
- STEPS:
- deploy-image-to-container-service
- STEPS
- Login to Azure
- Deploy to Azure Container Apps (ACA)
- STEPS
- build-and-push-image
Finding Logs in Azure
- Resource group ->
demo-rg
- Container App ->
demo-acadev
-> Activity Log -> Create or Update Container App -> Create or Update Container App -> Change history ->properties.provisioningState
- Container App ->
Look for image
, here the version 965d7c3
is my commit Sha so I can see what code I changed
1 | old new |
Then look for latestRevisionName
, here kj2aaos
is the revision running in Azure
1 | old new |
Under Monitoring
-> Log stream
select the Revision matching kj2aaos
, this is going to give you the running container logs, so you can see why you code code sucks :D
Under Monitoring
-> Logs
you can write and execute your own log queries, this is helpful when you have heaps of logs and need to diagnose problems.
References
- https://www.npgsql.org/efcore/?tabs=onconfiguring
- https://datacenters.microsoft.com/globe/explore
- https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/common-deployment-errors
- https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-register-resource-provider