By following a few conventions, the Azure Developer CLI (azd) makes it quick and easy to get up and running on Azure. Applying those conventions to a project and create an azd template is what our team affectionately calls “azdev-ify”.
azd commands are simple but the work azd does under the hood to connect the Azure services and supporting engineer systems, is complex. We want you to focus on business logic for your app. You can say goodbye to going to multiple places and running one-off scripts to stand up Azure resources. Everything is automatable and repeatable. You get a sample CI/CD pipeline set up. If Azure Monitor is included in the template infrastructure, you can even observe and analyze your application health!
To “azdev-ify" your project, it’s important to first understand how azd works. As PM for the tool, I am often asked what exactly the minimum is to get started. This is the first of a series of blog posts that focus on stepping through the process of making a simple Python app azd compatible.
So, let’s get started!
What makes a project compatible with azd?
An azd compatible project follows azd conventions. At the minimum, an azd-compatible repository must have the following:
- /infra – a folder for infrastructure code.
- Application source code.
- “azure.yaml” - a metadata file located at the root of the project that describes the app and tells azd how to connect the application code to the Azure resources.
The “infra” folder
The infra folder contains the infrastructure as code (IaC) files you need to provision the Azure resources.
Update(11/2/2022): this blog post was originally published on Oct 12, 2022 (based on azd version 0.3.0-beta.2.) It has since been updated to sync with azd version 0.4.0-beta.1.
Let us take a moment to examine the folder content of our Todo azd templates:
- main.parameters.json is a required file. It includes the parameters needed by the main.bicep.
- main.bicep is a required file and serves as an entry point.
It references resources.bicep. resources.bicep defines the resources the app needs. To azd, this file is optional, and you can change the file name if you’d like.(We removed resource.bicep as of version 0.4.0-beta.1.)- In /app, files are further organized by functionality and are referred by main.bicep:
- /core folder is simply a reference library that contains all the building blocks (Bicep modules) used by all the azd templates our team authored. For now, we include the same /core folder in each of the ToDo azd template. And yes, we plan to eventually move /core to a Bicep registry.
The App
Let's start with something simple - TheCatSaidNo. To deploy this app to Azure, you need an Azure App Service with built-in language for Python.
To run it locally:
- Make sure you have Python (3.8+) installed
- Get the code by running
git clone https://github.com/luabud/TheCatSaidNo
- Build and Run
cd thecatsaidno py -m venv .venv .venv\scripts\activate pip3 install -r ./requirements.txt flask run
- Browse to http://localhost:5000. An extremely useful website that is approved by your cat!
To azdev-ify
Before you get started, please make sure you have azd installed on your development environment. The code used in the blog is based on azd version 0.4.0-beta.1.
Step 1 - add the /infra folder and Bicep files to provision Azure resources
To deploy the Azure infrastructure so that your cat can say no:
- Create a new /infra folder in your TheCatSaidNo project
- Refer to a sample, e.g., ToDo Python Mongo. Copy everything in /infra except the /app folder to your /infra folder.
- main.bicep – remove outputs you don’t need. azd saves these outputs automatically to the active environment .env file so they can be used from within the application. The following is all you need for outputs. Make sure you modify the output for REACT_APP_WEB_BASE_URL to web.outputs.URI.
output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId output REACT_APP_WEB_BASE_URL string = web.outputs.uri
- We need an Azure App Service Plan for defining a set of compute resources and an Azure App Service deployed in Python for hosting the app, I will keep it simple and reference the modules in /core directly. The resulting main.bicep should look like this:
targetScope = 'subscription' @minLength(1) @maxLength(64) @description('Name of the the environment which is used to generate a short unique hash used in all resources.') param environmentName string @minLength(1) @description('Primary location for all resources') param location string // Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,: // "resourceGroupName": { // "value": "myGroupName" // } param appServicePlanName string = '' param resourceGroupName string = '' param webServiceName string = '' // serviceName is used as value for the tag (azd-service-name) azd uses to identify param serviceName string = 'web' @description('Id of the user or app to assign application roles') param principalId string = '' var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = { 'azd-env-name': environmentName } // Organize resources in a resource group resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: !empty(resourceGroupName) ? resourceGroupName : '${abbrs.resourcesResourceGroups}${environmentName}' location: location tags: tags } // The application frontend module web './core/host/appservice.bicep' = { name: serviceName scope: rg params: { name: !empty(webServiceName) ? webServiceName : '${abbrs.webSitesAppService}web-${resourceToken}' location: location tags: union(tags, { 'azd-service-name': serviceName }) appServicePlanId: appServicePlan.outputs.id runtimeName: 'python' runtimeVersion: '3.8' scmDoBuildDuringDeployment: true } } // Create an App Service Plan to group applications under the same payment plan and SKU module appServicePlan './core/host/appserviceplan.bicep' = { name: 'appserviceplan' scope: rg params: { name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}' location: location tags: tags sku: { name: 'B1' } } } // App outputs output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId output REACT_APP_WEB_BASE_URL string = web.outputs.uri
Important to note serviceName can be anything but must match the service name you specify later in azure.yaml. I am calling my frontend "web".
param serviceName string = 'web'
- run azd init to initialize your project.
- select empty template
- supply an environment name. Can be anything. I use "tsn106"
- pick a region
- supply an Azure subscription
- run azd provision to provision the Azure resources.
Step 2 - connect the pieces and deploy the app
After running azd init, the azure.yaml file is also added to the root of your project. Think of azure.yaml as the app’s user manual for azd. azd refers to this file to learn more about the app so that it knows, for instance, what Azure service will be hosting your app, how to build and deploy your app.
Open and edit the file. `name` and `services` are the minimum requirements here:
- name: the name of my project is TheCatSaidNo
- services: TheCatSaidNo needs a web frontend hosted on Azure App Service. It has one service which is web. Make sure you use the same name in main.bicep, i.e.:
param serviceName string = 'web'
In Azure Portal, if you look at the Azure App Service provisioned in the previous step, you will notice these tags. This is how azd locates the host in the resource group rg-tsn106.
- project: It’s good to organize all code under /src but if you have a company convention you need to follow, you can overwrite the location using "project". In my case, source code for “web” is at the root of my project, so:
project: .
- The resulting azure.yaml looks like this:
name: TheCatSaidNo services: web: project: . language: py host: appservice
Finally, deploy the app by running: azd deploy.
Time to call the cat over and show her the app running on Azure!
You can get the code for this blog here. Next, setting up Azure Monitor: Part 2 - Observability for your azd-compatible app.
We love your feedback! If you have any comments or ideas, feel free to add a comment or submit an issue to the Azure Developer CLI Repo.