Error: New-AzResourceGroupDeployment: Cannot retrieve the dynamic parameters for the cmdlet. Cannot find path ‘.json’ because it does not exist.

I received the above error today whilst working on a new Bicep template which caused me a bit of confusion as it seemed to be indicating that a file was missing.

What I figured out in the end is that it was really saying that I had messed up something in my Bicep template file (in this instance I had mis-typed a parameter name). I suspect that during ‘compilation’ Bicep creates a .json file which corresponds to the equivalent ARM template and because it could not create this file due to the error it was showing me this slightly misleading message instead.

So if anyone else receives this error it is probably worth checking your bicep file before looking for anything more complicated.

How To: Deploy Multiple Virtual Machines Using Bicep, PowerShell and a little XML.

Continuing my journey with Bicep, this time by deploying two virtual machines.

Following on from my previous post I thought it would be interesting to look at how I can use Bicep to deploy multiple Virtual Machines.

Now obviously there are several ways that this can be done, for example you could extend the parameters if the bicep file and call this file multiple times (which is sort of what I have done), you could also list separate VM blocks within the same file or alternatively you could use the looping feature provided by Bicep, however this would require the management of arrays or other constructs within Bicep, which to me looked a little complicated for what I wanted to do as a first step.

I suspect that adding multiple VM blocks (or using loops) within one Bicep file would be the most effective method in terms of processing & deployments. However, It did not seem to give me the control I wanted over the deployment process and repeating the bicep template for a VM x number of times seemed contrary to how I would normally develop code (re-use etc.).

What I wanted was to have one Bicep template which could be called multiple times, this then added the question of how I wanted to deal with components which may only exist once in the deployment such as a VNet and related Subnet. There does not seem to be a way of checking if a resource exists prior to the deployment within Bicep, so this led me to two decisions one to split out the VNet creation and secondly to add some extra logic to the PowerShell component of the solution.

As a result I ended up with some differences to the code in the first example. Firstly I created a Bicep module which was there to manage the deployment of the VNet. To do this I created a new folder (modules) and added the following template/code.

param VNetName string
param Location string
param SubNetName string

resource VNet_Default 'Microsoft.Network/virtualNetworks@2020-08-01' = {
  name: VNetName
  location: Location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/24'
      ]
    }
    subnets: [
      {
        name: SubNetName
        properties: {
          addressPrefix: '10.0.0.0/24'
          delegations: []
          privateEndpointNetworkPolicies: 'Enabled'
          privateLinkServiceNetworkPolicies: 'Enabled'
        }
      }
    ]
    virtualNetworkPeerings: []
    enableDdosProtection: false
  }
}

output VNetId string = VNet_Default.id

This will create a VNet and a Subnet with the simple parameters of Name, SubnetName and Location. To call this from the main Bicep file just add following to a separate Bicep file which in this instance I have called VNet-Deployment.Bicep (Please note I am assuming at this point that a resource group already exists).

param VNetName string
param Location string
param SubNetName string

module VNet 'modules/VNet.bicep' = {
  name: 'appService'
  params: {
    Location: Location
    VNetName: VNetName
    SubNetName: SubNetName
  }
}

This provides me with a discrete VNet template and module file for managing this one off part of my deployment. To run this I just need to call this from a PowerShell command, similar to the below.

New-AzResourceGroupDeployment -ResourceGroupName  VirtualMachines -TemplateFile $VNetBicepFile -VNetName $VNetName -SubNetName $SubNetName -Location $Location

Now that I have the core elements deployed I can start to work on the deployment of the VMs. To do this I firstly wanted a way that I can manage the parameters that I would need for this. For example if I am deploying two VMs I would need a collection of some kind containing the VM Names, NIC Names etc I would also need to modify the main bicep file to allow for the new parameters. This Bicep file now looks like the below.

param VMName string
param VNetId string
param Location string
param SubNetName string
param OSDiskName string
param NICName string
param PublicIPAddressName string

resource VM_PublicIP 'Microsoft.Network/publicIPAddresses@2021-02-01' ={
  name:PublicIPAddressName
  location: Location
  sku: {
    name: 'Basic'
    tier: 'Regional'
  }
  properties: {
    publicIPAddressVersion: 'IPv4'
    publicIPAllocationMethod: 'Dynamic'
  }
}

resource NIC_VM 'Microsoft.Network/networkInterfaces@2020-08-01' = {
  name: NICName
  location: Location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          publicIPAddress: {
            id: VM_PublicIP.id
          }
          privateIPAllocationMethod: 'Dynamic'
          subnet: {
            id: '${VNetId}/subnets/${SubNetName}'
          }
          primary: true
          privateIPAddressVersion: 'IPv4'
        }
      }
    ]
    dnsSettings: {
      dnsServers: []
    }
    enableAcceleratedNetworking: false
    enableIPForwarding: false
  }
}

resource VirtualMachine 'Microsoft.Compute/virtualMachines@2021-03-01' = {
  name: VMName
  location: Location
  properties:{
    hardwareProfile: {
      vmSize:'Standard_D2s_v3'
      }
      storageProfile: {
        osDisk: {
          name: OSDiskName
          createOption: 'FromImage'
          osType: 'Windows'
          managedDisk: {
            storageAccountType: 'StandardSSD_LRS'
          }
        }
        imageReference: {
          publisher: 'MicrosoftWindowsDesktop'
          offer: 'Windows-10'
          sku: '19H1-ent'
          version: '18362.1198.2011031735'
        }
      }
      osProfile: {
        computerName: VMName
        adminUsername: 'vmAdministrator'
        adminPassword: 'Adm1nP@55w0rd'
      }
      networkProfile: {
        networkInterfaces: [
          {
            id: NIC_VM.id
          }
        ]
      }
  }
}

In order to manage the collection of parameters I would need for deploying my VMs, I used an old technique that has served me well over the years, which is to use an XML based configuration file, which would look something like this.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Deployment Name="VM Deployment" ResourceGroupName="VirtualMachines" VNetName="VMVNet01" Region="uksouth" SubNetName="VMSubNet01" />
    <VirtualMachines>
        <VM VMName="VM01" NetworkInterface="Vm01Nic01" PublicIPName="Vm01Pip01" OSDiskName="Vm01OSDisk01" />
        <VM VMName="VM02" NetworkInterface="Vm01Nic02" PublicIPName="Vm01Pip02" OSDiskName="Vm01OSDisk02" />
    </VirtualMachines>    
</Configuration>

This provides me with all the details I currently need for deploying the environment and can be easily extended to include additional components. You will notice that I have included configuration values for the VNet here as well.

To reference these values in PowerShell I just need to reference the config file and then the various elements as and when required. To do this you need to first add the reference, which I have done like this.

$Path = "Z:\Development\themicrosoftcloudblog\Samples\BICEP\Deploy Virtual Machines\Deploy Multiple VMs\"
$ConfigFile = $Path + "Config.xml"

Then you can start to reference the configuration values within your PowerShell script. I have done this two ways in this instance, firstly for the one of values such as the VNet parameters which can simply be done by referencing the correct element in the XML file, like this.

$VNetName = $Config.Configuration.Deployment.VNetName

and for other components such as the multiple VMs you can loop through the desired element and leverage the individual parameters like so.

#Deploy Virtual Machines
foreach ($VM in $Config.Configuration.VirtualMachines.VM)
{
    New-AzResourceGroupDeployment -ResourceGroupName $ResourceGroupName 
        -TemplateFile $VMBicepFile -VMName $VM.VMName -SubNetName $SubNetName 
        -Location $Location -OSDiskName $VM.OSDiskName -VNetID $VNetID.id 
        -NICName $VM.NetworkInterface -PublicIPAddressName $VM.PublicIPName | Out-Null
    }

The above script would loop x number of times, creating a deployment for each loop. This may not be the most efficient way of doing this, but it seems to me to provide a level of control which may not be available using just Bicep.

I have uploaded the sample files to Git for reference and they can be found here https://bit.ly/3sFJPuG.

How to: Deploy a Virtual Machine to Azure using Bicep.

As some of the readers will know I became aware of and blogged about Bicep a month or so ago. Since then work has interfered with me giving this more than a cursory glance. However, I started to look at this in a little more detail last week.

Anyway, I was interested in seeing how I could use bicep to deploy a virtual machine to Azure. As a result of this I came up with the following simple example. Please note I only included the components that I would need to deploy and access a Windows VM, this included a VNet, Public IP (to allow for RDP connections) and a network interface. I have not included elements such as additional storage etc. and have made an assumption that a target resource group already exists.

Just for ease of use I have included everything in one bicep file and have not parametrising some of the variables, I will do this as part of my next post in this series which will take this starting file and extend it to allow deployment of multiple VMs.

First off, I needed to create a VNet and a subnet, to do this I used the following code.

resource VNet_Default 'Microsoft.Network/virtualNetworks@2020-08-01' = {
  name: 'VMVNet01'
  location: 'uksouth'
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/24'
      ]
    }
    subnets: [
      {
        name: 'VMSubnet'
        properties: {
          addressPrefix: '10.0.0.0/24'
          delegations: []
          privateEndpointNetworkPolicies: 'Enabled'
          privateLinkServiceNetworkPolicies: 'Enabled'
        }
      }
    ]
    virtualNetworkPeerings: []
    enableDdosProtection: false
  }
}

I also want a public IP address as this will allow me to access the VM via Remote Desktop. However, if you are using a bastion service or similar or want to use SSH then this may not be required or will need to be modified.

resource VM_PublicIP 'Microsoft.Network/publicIPAddresses@2021-02-01' ={
  name:'VMPublicIPAddress'
  location: 'uksouth'
  sku: {
    name: 'Basic'
    tier: 'Regional'
  }
  properties: {
    publicIPAddressVersion: 'IPv4'
    publicIPAllocationMethod: 'Dynamic'
  }
}

Finally, before getting to the VM creation template I need to add a network interface to the VM incorporating the VNet and Public IP address created using the above sections, to do this use the below structure.

resource NIC_VM 'Microsoft.Network/networkInterfaces@2020-08-01' = {
  name: 'nic01'
  location: 'uksouth'
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          publicIPAddress: {
            id: VM_PublicIP.id
          }
          privateIPAllocationMethod: 'Dynamic'
          subnet: {
            id: '${VNet_Default.id}/subnets/${'vmSubnet'}'
          }
          primary: true
          privateIPAddressVersion: 'IPv4'
        }
      }
    ]
  }
}

At this point I have the code for all the prerequisite components that the virtual machine requires. However, there is one area which required a bit of thought/work and that is the information needed for the underlying OS image, which includes the parameters publisher, offer, SKU & version. To get these details there are a suite of PowerShell commands which can be used for this (Get-AzVMImagePublisher, Get-AzVMImageOffer, Get-AzVMImageSku & Get-AzVMImage) see https://bit.ly/3lQanbd for more information and the links to the other commands.

Once you have the details for the above then use the following to create the VM.

resource VirtualMachine 'Microsoft.Compute/virtualMachines@2021-03-01' = {
  name: 'VM01'
  location: 'uksouth'
  properties:{
    hardwareProfile: {
      vmSize:'Standard_D2s_v3'
      }
      storageProfile: {
        osDisk: {
          createOption: 'FromImage'
          osType: 'Windows'
          managedDisk: {
            storageAccountType: 'StandardSSD_LRS'
          }
        }
        imageReference: {
          publisher: 'MicrosoftWindowsDesktop'
          offer: 'Windows-10'
          sku: '19H1-ent'
          version: '18362.1198.2011031735'
        }
      }
      osProfile: {
        computerName: 'testcomputer'
        adminUsername: 'vmAdministrator'
        adminPassword: 'Adm1nP@55w0rd'
      }
      networkProfile: {
        networkInterfaces: [
          {
            id: NIC_VM.id
          }
        ]
      }
  }
}

At this point you will just need to run the following line of PowerShell to deploy the templates. Please note this is assuming that the resource group already exists.

New-AzResourceGroupDeployment -ResourceGroupName '<ResourceGroupName>' -TemplateFile ./<filename>.bicep 

I will upload the full sample code to GitHub and will publish the link here a little later.

UPDATED:
I have now uploaded the code samples to my public GitHub repository which can be found here https://bit.ly/3lQ2FOw.

Bicep, a declarative language which provides an alternative to JSON for deploying ARM templates.

For anyone like me who has never been particularly comfortable working with JSON when deploying resources to Azure. There is a new language called Bicep which provides a more declarative approach to working with ARM templates.

According to Microsoft Bicep offers the following benefits:

  • Support for all resource types and API versions: Bicep immediately supports all preview and GA versions for Azure services. As soon as a resource provider introduces new resources types and API versions, you can use them in your Bicep file. You don’t have to wait for tools to be updated before using the new services.
  • Simple syntax: When compared to the equivalent JSON template, Bicep files are more concise and easier to read. Bicep requires no previous knowledge of programming languages. Bicep syntax is declarative and specifies which resources and resource properties you want to deploy.
  • Authoring experience: When you use VS Code to create your Bicep files, you get a first-class authoring experience. The editor provides rich type-safety, intellisense, and syntax validation.
  • Modularity: You can break your Bicep code into manageable parts by using modules. The module deploys a set of related resources. Modules enable you to reuse code and simplify development. Add the module to a Bicep file anytime you need to deploy those resources.
  • Integration with Azure services: Bicep is integrated with Azure services such as Azure Policy, template specs, and Blueprints.
  • No state or state files to manage: All state is stored in Azure. Users can collaborate and have confidence their updates are handled as expected. Use the what-if operation to preview changes before deploying your template.
  • No cost and open source: Bicep is completely free. You don’t have to pay for premium capabilities. It’s also supported by Microsoft support.

I only came across this on Friday, so not had a huge amount of time to work with it, but it looks very promising and seems to fit more into my way of working than when using JSON directly.

For more information check out the MS documentation https://bityl.co/7YWC.