4 min read

Deploying .NET Apps to Azure Container Apps with One Command, azd up

Justin Yoo

In my previous blog posts of containerising .NET apps and Function apps, I discussed how to containerise .NET apps and Azure Functions apps with and without Dockerfile. However, deploying these containerised apps to Azure Container Apps (ACA) is a different story. Since its release in May 2023, Azure Developer CLI (azd) has evolved significantly. azd nowadays even automatically generates Bicep files for us to immediately provision and deploy applications to Azure. With this feature, you only need the azd up command for provisioning and deployment. Throughout this post, I'm going to discuss how to provision and deploy .NET apps including Azure Functions to ACA through just one command, azd up.

You can find a sample code from this GitHub repository.

Prerequisites

There are a few prerequisites to containerise .NET apps effectively.

Running the app locally

The sample app repository already includes the following apps:

Let's make sure those apps running properly on your local machine. In order to run those apps locally, open three terminal windows and run the following commands on each terminal:

# Terminal 1 - ASP.NET Core Web API
dotnet run --project ./ApiApp

# Terminal 2 - Azure Functions
cd ./FuncApp
dotnet clean && func start

# Terminal 3 - Blazor app
dotnet run --project ./WebApp

Open your web browser and navigate to https://localhost:5001 to see the Blazor app running. Then navigate to https://localhost:5001/weather to see the weather data fetched from the ApiApp and the greetings populated from the FuncApp.

All apps up & running

Now, let's start using azd to provision and deploy these apps to ACA. Make sure that you've already logged in to Azure with the azd auth login command.

azd init – Initialisation

In order to provision and deploy the apps to ACA, you need to initialise the azd configuration. Run the following command:

azd init

You'll be prompted to initialise the app. Choose the Use code in the current directory option.

Use code in the current directory

azd automatically detects your three apps as shown below. In addition to that, it says it will use Azure Container Apps. Choose the Confirm and continue initializing my app option.

Confirm and continue

The function app asks the target port number. Enter 80.

Enter the target port number

And finally, it asks the environment name. Enter any name you want. I just entered aca0906 for now.

Enter the environment name

Now, you've got two directories and two files generated:

  • .azure directory
  • infra directory
  • next-steps.md file
  • azure.yaml file

Directories and files generated

Under the infra directory, there are bunch of Bicep files automatically generated through azd init.

Bicep files generated

As a result of running the command, azd init, you don't have to write all necessary Bicep files. Instead, it generates them for you, which significantly reduces the time for infrastructure provisioning. Now, you're ready to provision and deploy your apps to ACA. Let's move on.

azd up – Provision and deployment

All you need to run at this stage is:

azd up

Then, it asks you to confirm the subscription and location to provision the resources. Choose the appropriate options and continue.

Choose subscription and location for resource provisioning

All apps are containerised and deployed to ACA. Once the deployment is done, you can see the output as shown below:

Deployment done

Click the web app URL and navigate to the /weather page. But you will see the error as shown below:

Error on the web app

This is because each app doesn't know where each other is. Therefore, you should update the Bicep files to let the web app know where the other apps are.

Update Bicep files – Service discovery

Open the infra/main.bicep file and update the webApp resource:

module webApp './app/WebApp.bicep' = {
  name: 'WebApp'
  params: {
    ...
    // Add these two lines
    apiAppEndpoint: apiApp.outputs.uri
    funcAppEndpoint: funcApp.outputs.uri
  }
  scope: rg
}

Then, open the infra/app/WebApp.bicep file and add both apiAppEndpoint and funcAppEndpoint parameters:

...
@secure()
param appDefinition object

// Add these two lines
param apiAppEndpoint string
param funcAppEndpoint string
...

In the same file, change the env variable:

// Before
var env = map(filter(appSettingsArray, i => i.?secret == null), i => {
  name: i.name
  value: i.value
})

// After
var env = union(map(filter(appSettingsArray, i => i.?secret == null), i => {
  name: i.name
  value: i.value
}), [
  {
    name: 'API_ENDPOINT_URL'
    value: apiAppEndpoint
  }
  {
    name: 'FUNC_ENDPOINT_URL'
    value: funcAppEndpoint
  }
])

This change passes the API and Function app endpoints to the web app as environment variables, so that the web app knows where the other apps are.

Once you've made the changes, run the azd up command again. It will update the resources in ACA. After that, go to the web app URL and navigate to the /weather page. You will see the weather data and greetings fetched from the API and Function apps.

All apps up & running


So far, I've discussed how to provision and deploy .NET apps including Azure Functions to ACA with just one command, azd up. This is a very convenient way to deploy apps to Azure. However, to let the apps know each other, you should slightly tweak the auto-generated Bicep files. With this little tweak, all your .NET apps will be seamlessly provisioned and deployed to ACA.

One more thing I'd like to mention here, though, is that, if you use .NET Aspire, this sort of service discovery is automatically handled.

More about deploying .NET apps to ACA?

If you want to learn more options about deploying .NET apps to ACA, the following links might be helpful.