This Article

Using .NET Aspire to deploy to Azure, safely

It's Aspire in space, definitely all about the lofty goals.
Note: This article's content was expanded out a little to help with code readability.

If you live under a rock you may not know what .NET Aspire is.

Go learn about it, even try it out, and then come back.


Love it? So do I, it simplifies cloud development on your local machine with excellent deployment tooling (via azd) to boot.

In fact just using by using Aspire as-is you’re set for:

Unfortunately that’s where it stops.

Why?

.NET Aspire’s deployment model is based around a bespoke manifest format, a great idea in principle but extremely limiting in so far as it describes how things should be deployed, but doesn’t describe how things should be deployed.

Confusing right?

It is, but what happens is when that manifest is generated is that a very specific generator in azd comes along and then generates out the rest of the things. In a very very opinionated, and very very much not production ready manner.

There’s quite a lot to say on it, but I’m going to focus on one thing right now.

A single Identity / Principal for all the things.

It reminds me of a few companies I know that deploy each of their apps in Azure on their own App Services and with their own SQL Servers then wonder why their bill is $70,000 per month. Just wrong.

Why is that wrong?

For smaller projects or when you’re deploying a single app it probably doesn’t matter. The resources that you’re creating will be solely used by that application and so they can have all the access in the world.

If you’re not doing that, take a look at Aspire’s Key Vault provisioner.

You should draw your attention to this:

var keyVaultAdministratorRoleAssignment = keyVault.AssignRole(RoleDefinition.KeyVaultAdministrator);
keyVaultAdministratorRoleAssignment.AssignProperty(x => x.PrincipalId, construct.PrincipalIdParameter);
keyVaultAdministratorRoleAssignment.AssignProperty(x => x.PrincipalType, construct.PrincipalTypeParameter);

Yes, that Principal is added as an Administrator against the Key Vault.

It’s important to note that during development this principal is going to be you, and that’s fine as the environment is just for development so that you can connect and build out in the cloud right from your machine.

But in production, it’s the one-identity-rules-them-all, and we definitely don’t want that to have Administrative access to the Key Vault. It’s worth noting that this is just the Key Vault, but the principle (and principal) are engrained in the way that everything is deployed.

If we were deploying everything manually it’s likely that each application would deploy with its own System Managed Identity and we’d give access to those new identities to resources via RBAC. But alas that would be a multi-step deployment process that we want to avoid.

How to fix it?

Without a lot of manual work ignoring the tools provided to you, you can’t. That’s very much ingrained in to the way that the deployment works if you want the lovely developer -> cloud story, which you definitely do.

So this manual work basically involves running azd infra synth and editing everything generated to remove that single identity, wire up new ones and then deploy. azd will use the infra found on disk if it’s available rather than regenerating at provision and deployment time.

I hope you’re comfortable with Bicep.

I’m not comfortable with Bicep

That’s OK because most people aren’t. Frankly you shouldn’t need to be to get what you need.

Whilst it’s not going to be fixed as of Aspire’s GA/1.0, David Fowler has a dream reality where we can optionally control deployment to the extreme. So if you have the luxury of time available to you, definitely wait as it’s better than what I am proposing.

Aspire.Provisioning.RealWorld

Hopefully the above goes in to what’s wrong and you understand that, let’s set the scene a bit more with a sample project:

As you’ll know from above, all of the applications actually have access to everything by default.

Obviously we don’t want that.

That’s where my tiny shim package, Aspire.Provisioning.RealWorld comes in.

As of writing I’ve only added support for KeyVault and the Identities themselves to be created but I’ll be adding more. It also only supports User Assigned Identities right now (not System) as we wouldn’t have the System Assigned Identity at the time of creating the other resources and we can’t do multi-step here.

An example configuration using this package is below:

var id = builder.AddManagedIdentity("myidentity");

var kv = builder.AddZtAzureKeyVault("mykv", b => {
    b.AddManagedIdentity(id, KeyVaultRoles.SecretsUser);
});

builder.AddProject<Projects.MyProject>("myproject")
    .WithManagedIdentity("MYID", id);

This does exactly what you’d expect, adding an identity, assigning it to the app and adding a Key Vault with just the role we need.

Written by Rudi Visser

Fancy reading more?

We'll be writing more in the coming weeks. Check back later!