• Bart.Verboven

Moving an Azure VM to another VNet using Bicep


For a consolidation scenario, I needed to move some Azure VMs a to a different VNet.

Although this sounds like a trivial change, this is not possible in the portal, so we will have to play around a bit using code.

We could do this the old fashioned way with PowerShell, Azure CLI or ARM templates, but as I didn't had the chance yet to test Bicep, I used this opportunity to put some basic Bicep to practice.

The Azure VMs in this scenario have all been deployed by the customer by clicking around in the Azure portal, a practice I like to call ClickOps.

If you have already deployed all resources the proper way using IaC, you can skip a couple of steps.

To get you started with all the prerequisites for Bicep, go to

Let's go

The VNet of a network card can't be changed, so we will need to create a new one in the proper VNet, which we will attach to our VM later.

To do this, let's first capture the network card ARM template. (You can skip this step if you've already got the template created)

Go to the NIC and select "Export template" under the Automation section

Select "download" and tick the box "include parameters"

Download and save the zip somewhere, preferably in a repository ;-)

Extract the zip file, you should have 2 files in it:


Now open the template.json in your favorite editor like Visual Studio Code.

We're not going to make the required changes in the json file, that would have been too easy. First we're going to create a Bicep file from it. To do this, run

bicep decompile template.json

If you don't have all the prerequisites yet for bicep, go to to get started.

Changing the network card

The property that you want to change is "subnet".

What you get by default is a concat of the VNet ID and the subnet name.

I don’t like to have the subnet name hardcoded in my resources, so let's swap that one by a variable.

After some cleaning up of those horrible parameter names, you should end up with something like this:

The NIC (click to zoom in)

The changes that you should make are:

  • Change the VNet ID and subnet ID to the destination you want your VM to be in. If you're not sure of the ID, go either to the VNET on the Azure portal in the properties field, of get it with PowerShell or Azure CLI.

  • Rename the network card name to something new. Take this opportunity to get rid of the randomly generated name it most likely has.

  • Change the IP address to something that's in range of the subnet address space, or put it at dynamic.

  • Cleanup all the default naming you get from the export. This is not mandatory, but looks a lot cleaner and less error-prone.

Getting the VM template

Leave your VS code for now and hop back to the Azure portal.

Now we're going to do the exact same export steps for our VM.

Go to the Portal, export template and decompile the template.json to a Bicep file.

bicep decompile template.json

You will notice that you only got the VM resource.

I like to keep my components that make up a VM in one file, so just copy/paste the bicep code of the NIC Bicep file to the template file of the VM.

Now let's clean up this auto-generated mess:

To make sure the network card gets deployed first, put a "dependson" referencing our new network card. You can just reference to the name of the resource object, there's no need to build the entire resource ID string like you needed to do with ARM.

Create a dependency on the NIC

Change the NIC in "networkProfile" to our new NIC as well.

Change the id to our new NIC resource

This concludes the changes to the network part.

The disk and OS profile

Now onwards with the changes to your disk and osprofile.

You might notice that all data disks have the property "attach" to them, which is fine as the disks already exist. However, the OS disk has a "fromimage" or something similar.

We've already got an OS disk that we want to use, so change this to "attach" as well.

Leave the ID as we will keep the original one.

Change createOption to 'Attach'

An existing OS disk also makes the image reference block redundant, so get rid of it.

It looks something like this:

Delete this

The "OsProfile" section also isn't needed, so hit that delete button as well.

Delete this too

That's it, you're done.

Deploying the VM

Now, before you'll be able to deploy your new template, first delete the VM.

This is always required as you need to remove the lease on the disks. Notice that we didn't change the name of the VM itself in our template files. You can reuse it without any issue.

If you want a different name, keep in mind that you get a discrepancy between the name in Azure and the name in the OS, which I'd try to avoid as it's confusing.

The delete button only deletes the VM itself (The portal now warns you for that), so all other components are kept intact.

Keeping the disks is what we want, but to keep everything tidy, you can delete the network card afterwards. We have created that new one anyway.

After that you can deploy the VM from the bicep templates.

We have have reused all disks, so the OS configuration and data is still intact.

To deploy the template, either kick it off from a pipeline, or do it manually.

Deploy using PowerShell

And that concludes our VM migration, learning some basic Bicep as a plus.

As we now have a nice template of the VM that originally got created from the portal, try to refrain making future changes to the VM in the portal and use the template, IaC-style.