Run two PowerShell scripts on a same VM through custom script extension at different stage of Deployment in ARM

Introduction – This blog post illustrates the method through which you can run two different PowerShell scripts on a same VM through custom script extension at different stages /time of deployment in ARM. Currently , it is not possible to run two custom script to perform two different tasks on a same VM through custom script extension.

Assumptions – Here we assume that you are familiar with basics of deploying resources in azure preview portal in ARM mode and use/construction of JSONs.

Problem statement – I had requirement where I had to deploy an IaaS infrastructure in ARM through PowerShell orchestration script and JSON template. This include creation of IaaS VMs like domain controllers and SQL VMs etc. At one point I had to perform the task of creating AD domain users on domain controller through PowerShell script and at the final stage of deployment ( after SQL VMs configuration ) , I had to push group policy on the same domain controller through PowerShell script. After adding a resource block in JSON for second PowerShell script , I ran the complete deployment. As expected , at the final stage while pushing GPO PowerShell script into DC

New-AzureResourceGroup : 11:12:54 AM – Resource Microsoft.Compute/virtualMachines/extensions 'adgptst02/ADGPO' failed with message 'Multiple

VMExtensions per handler not supported for OS type 'Windows'. VMExtension 'ADGPO' with handler 'Microsoft.Compute.CustomScriptExtension' already

added or specified in input.'

This was expected as VM already had custom script extension in it which was injected previously for creating domain users at the earlier stage of deployment.

Resolution/Workaround – Follow the below to resolve this :

1. Remove the resource block for second PowerShell from JSON template as of now.

2. In the PowerShell orchestration script, from where you run the deployment ,  Add the below command to remove custom script extension once the deployment is done . This command will be right after New-azurermresourcegroupdeployment command which perform the deployment:

Remove-AzurermVMCustomScriptExtension -ResourceGroupName $ResourceGroupName -VMName $CustVMname –Name $customscriptname -Force

Note – Replace the variables with actual values. This will remove the custom script extension from the VM and will not have any effect on the configuration done by the custom PowerShell script earlier

3. Create a new JSON template ( from the same template you are using for deployment ) which will have only one resource block for second custom script extension.( delete all other resource blocks as those tasks will already be completed ).You don’t need to make any changes in parameters and variables section of the template as most of the values will not used and will not have any effect. Also you need not to make any change in template parameter file. Below is how the resource block for second custom script extension will look like:

"resources": [

           "type": "Microsoft.Compute/virtualMachines/extensions",
           "name": "[concat(parameters('ADVirtualMachine'),'/ADGPO')]",
           "apiVersion": "2015-06-15",
           "location": "[parameters('location')]",
           "properties": {
               "publisher": "Microsoft.Compute",
               "type": "CustomScriptExtension",
               "typeHandlerVersion": "1.4",
               "settings": {
                   "fileUris": [
                   "commandToExecute": "[variables('ADGPOToExecute')]"
               "protected Settings": {
                   "storageAccountName": "[variables('ADcustomScriptStorageAccountName')]",
                   "storageAccountKey": "[listKeys(variables('ADaccountid'),'2015-05-01-preview').key1]"




4. Save the JSON template with different name along with parent template for the deployment.

5. In the PowerShell orchestration script , Remove-AzurermVMCustomScriptExtension, once again run the New-azurermresourcegroupdeployment , this time with new JSON template. Hence , in short , the process here is to remove the custom script extension first and then add it again with the required script.


Thanks folks , hope it is useful

Happy blogging

Leave a Reply