Delete all the resources but not the resource group using Azure CLI - azure

There is an solution in the Stack overflow that contains the commands for deleting the resources in a resource group but it's not working for us.
In the Azure Cloud Shell,
When using the command given from above link, getting the error as:
unrecognized arguments: ConvertFron-Json Foreach-Object {az resource delete --ids /usr/bin/cloudshellhelp.id --verbose
for the command: az resource list --resource-group senthil-b-rg ConvertFrom-Json Foreach-Object {az resource delete --resource-group senthil-b-rg --ids $_.id --verbose}
If I keep pipe (|) symbol in the command:
az resource list --resource-group senthil-b-rg \
| ConvertFrom-Json | Foreach-Object {az resource delete --resource-group senthil-b-rg --ids $_.id --verbose}
Errors are
bash:ConvertFrom-Json: command not found
bash: Foreach-Object: command not found
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
BrokenPipleError: [Errno 32] Broken pipe

I tried to reproduce the same in my environment and got the same error as below:
az resource list --resource-group myresourcegroup \
| ConvertFrom-Json | Foreach-Object {az resource delete --resource-group myresourcegroup --ids $_.id --verbose}
Response:
In my resource group, I have resources like below:
To resolve the error, I tried to run the same command in PowerShell and got the same warning as below:
But the resources got deleted in the Portal:
Please note that, while deleting the resources you need to follow the order:
Make sure to delete the child (nested) resources initially.
Delete the resources that manage the other resources.
Reference:
Delete resource group and resources - Azure Resource Manager | Microsoft Docs
UPDATE:
To get rid of the warning, you need to add --only-show-errors in the command like below:
$resources = az resource list --resource-group myresourcegroup | ConvertFrom-Json
foreach ($resource in $resources) {
az resource delete --resource-group myresourcegroup --ids $resource.id --only-show-errors
}
Response:
Make sure to remove --verbose as --only-show-errors doesn't work with it.

Related

How to get resource name from resource Id in azure cli

I need to get the network interface card name from the resource Id.
vmsList=$(az vm list --show-details --query '[?name!=`null`].[name]' -o tsv)
for vm in ${vmsList[#]}
do
nics="$(az vm nic list --vm-name $vm --query "[].{id:id}" --output tsv)"
for nic in ${nics[#]}
do
az vm nic show --vm-name $vm --nic $nic --query '{Name:name,Location:location}' -o json
done
done
But it is giving us this error.
'/subscriptions/subscriptionName/resourceGroups/resourceGroupName/providers/Microsoft.Network/networkInterfaces/networkInterfaceCardName' not found on VM 'VMName'
ERROR: Operation returned an invalid status 'Bad Request'
I know this error is because we just need to pass only network interface name to az vm nic show command. But I am stuck to get the resource name only from resource Id.
We got the below error when we tried to get NetworkInterface Name with Resource ID
To get the NIC details of particular VM, use the below command
$virtualmachine = Get-AzVM -VMName "VMName"
$virtualmachine.NetworkProfile
$nic = $virtualmachine.NetworkProfile.NetworkInterfaces
foreach($nics in $nic) {
($nics.Id -split '/')[-1]
}
To get the NIC details of all the VM's in a ResourceGroup ,use
$virtualmachine= Get-AzVM -ResourceGroupName "YourRGName"
$virtualmachine.NetworkProfile
$nic = $virtualmachine.NetworkProfile.NetworkInterfaces
foreach($nics in $nic) {
($nics.Id -split '/')[-1]
}
References taken from :
Azure VM NIC name using PowerShell
Network interface object for an Azure VM

Azure CLI - Delete resource without deleting resource group

The goal is to delete all resources in a resourcegroup without deleting it using azure CLI.
From reading the doc, I can do this:
az resource delete -g MyResourceGroup -n MyVm
Therefore I assumed I can do the following
az resource list --resource-group MyResourceGroup | az resource delete
A similar command in Azure Powershell would work like the above. I am quite new to CLI, is this method possible? What is the efficient way of removing all resources in a resource group (if we have multiple types of resources)
If can you run Azure CLI in powershell, you could use ConvertFrom-Json to convert the JSON result to a list of objects from az resource list, then run az resource delete on each object id using a foreach loop.
$resources = az resource list --resource-group myResourceGroup | ConvertFrom-Json
foreach ($resource in $resources) {
az resource delete --resource-group myResourceGroup --ids $resource.id --verbose
}
We could also run this entirely in the pipeline using Foreach-Object, which is close to what you are trying to do.
az resource list --resource-group myResourceGroup
| ConvertFrom-Json
| Foreach-Object {az resource delete --resource-group myResourceGroup --ids $_.id --verbose}
If you don't want to use powershell at all, we can use bash to parse the JSON output ourselves using grep and awk.
#!/bin/bash
resources="$(az resource list --resource-group myResourceGroup | grep id | awk -F \" '{print $4}')"
for id in $resources; do
az resource delete --resource-group myResourceGroup --ids "$id" --verbose
done
As #Hong Ooi helpfully pointed out in the comments, the main issue with the above is that some resources depend on other resources, so order of deletion matters. One example is that you cannot delete virtual machine disks before the virtual machine is deleted.
To get around this, we could define an ordering of resource types in which to delete resources, as shown in the example hash table below:
$resourceOrderRemovalOrder = [ordered]#{
"Microsoft.Compute/virtualMachines" = 0
"Microsoft.Compute/disks" = 1
"Microsoft.Network/networkInterfaces" = 2
"Microsoft.Network/publicIpAddresses" = 3
"Microsoft.Network/networkSecurityGroups" = 4
"Microsoft.Network/virtualNetworks" = 5
}
Then sort the resources by their resource types and delete them:
$resources = az resource list --resource-group myResourceGroup | ConvertFrom-Json
$orderedResources = $resources
| Sort-Object #{
Expression = {$resourceOrderRemovalOrder[$_.type]}
Descending = $False
}
$orderedResources | ForEach-Object {
az resource delete --resource-group myResourceGroup --ids $_.id --verbose
}
Or in one pipeline if you prefer:
az resource list --resource-group myResourceGroup
| ConvertFrom-Json
| Sort-Object #{Expression = {$resourceOrderRemovalOrder[$_.type]}; Descending = $False}
| ForEach-Object {az resource delete --resource-group myResourceGroup --ids $_.id --verbose}
There is a faster and simpler approach:
az deployment group create --mode complete --template-uri data:application/json,%7B%22%24schema%22%3A%22https%3A%2F%2Fschema.management.azure.com%2Fschemas%2F2019-04-01%2FdeploymentTemplate.json%23%22%2C%22contentVersion%22%3A%221.0.0.0%22%2C%22resources%22%3A%5B%5D%7D --name clear-resources --resource-group <RG_NAME>
The above data URI (which may also be Base64-encoded) represents this file:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": []
}
In an Azure DevOps pipeline you have to pass an actual file with the JSON contents above as DevOps only allows HTTP(s) schemes with --template-uri:
az deployment group create --mode complete --template-file ./clear-resources.json --resource-group <RG_NAME>

How can i pass a variable between tasks in Azure Devops/VSTS

For Task 1 I have a CLI task which simply gets the subnet name and subnet ref as below
$subnetname1 = az network vnet subnet list --resource-group vnetrg01 --vnet-name vnet01 --query "[].name" -o tsv
$subnetref1 = az network vnet subnet list --resource-group vnetrg01 --vnet-name vnet01 --query "[].id" -o tsv
For task 2 I want to deploy an arm template which will use parameters from the pipleine variables in Azure Devops
So for example the result of $subnetref1 in Task 1 above needs to populate the pipleline variable for subnetref (which is setup as a variable in the pipleine) which will then be passed to the arm template override parameters
cant seem to get this working
You can do it with Powershell command,
In the first PowerShell task set the variable as environment variable:
$subnetname1 = az network vnet subnet list --resource-group vnetrg01 --vnet-name vnet01 --query "[].name" -o tsv
Write-Host $subnetname1
Write-Host ("##vso[task.setvariable variable=subnetname1;]$subnetname1")
In the second task read the variable in this way:
$subnetname1 = $env:subnetname1
Write-Host $subnetname1

New-AzureRmKeyVault : 'vaultName' does not match expected pattern

I am trying to set up Azure Key Vault for my application by following this tutorial: https://blogs.technet.microsoft.com/kv/2015/06/02/azure-key-vault-step-by-step/
In the Create and configure key vault section in the tutorial, Right after doing these two steps:
I am not able to do this step:
It shows error message in my PowerShell:
New-AzureRmKeyVault : 'vaultName' does not match expected pattern '^[a-zA-Z0-9-]{3,24}$'.
At line:1 char:1
+ New-AzureRmKeyVault -VaultName ProfileKeyVault -ResourceGro ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [New-AzureRmKeyVault], ValidationException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.KeyVault.NewAzureKeyVault
Try putting the vault name in quotes.
New-AzureRmKeyVault -VaultName 'Contoso03Vault' -ResourceGroupName 'Group14' -Location 'East US'
When I got the error
'vaultName' does not match expected pattern
I found using Powershell with raw Az CLI Commands you have to use Double Quotes to create the KeyVault.
az keyvault create --name "vault-Name-01" --resource-group $resourceGroupName --location $location --sku Standard --enabled-for-disk-encryption true
In a Powershell script you pass in the variable wrapped with double quotes
[string]$vaultName = "vault-Name-01"
az keyvault create --name $vaultName --resource-group $resourceGroupName --location $location -sku Standard --enabled-for-disk-encryption true
Then when you want to set a value you remove the double quotes:
az keyvault secret set --vault-name $vaultName.Replace("`"","") --name $_.name --value $_.value
Further investigation reveals the problem applies to listing and showing the vault secrets and I bumped into the infamous error:
Max retries exceeded attempting to connect to vault. The vault may not exist or you may need to flush your DNS cache and try again later.
The KeyVault exists, because I can list the vaults secrets using the VaultName without quotes:
$secretNames = az keyvault secret list --vault-name $sourceVaultName.Replace("`"","") -o json --query "[].name" | ConvertFrom-Json
However you'll get the Max Retires error if you don't include a quoted $sourceVaultName when trying to show the secrets:
$secrets = $secretNames | % {
$secret = az keyvault secret show --name $_ --vault-name $sourceVaultName -o json | ConvertFrom-Json
[PSCustomObject]#{
name = $_;
value = $secret.value;
}
}
If any of these calls timeout and the result is blank refer to this solution: https://stackoverflow.com/a/67472219/495455
It's almost like two developers worked on the code and had different opinions... https://github.com/Azure/azure-cli/issues/13952

Azure CLI how to check if a resource exists

I'm starting to write a bash script to provision a VM in a new or existing resource group so that we can enforce naming convention and configuration.
In a bash script how can I check that a resource already exists so I don't try to create it again?
# 1. If a new resource group is desired, create it now. Microsoft Docs
az group create --name $RESOURCEGROUPNAME --location $LOCATION
# 2. Create a virtual network and subnet if one has not already been created. Microsoft Docs
# Consider a separate VNet for each resource group.
# az network vnet list -output table
az network vnet create \
--resource-group $RESOURCEGROUPNAME \
--name $RESOURCEGROUPNAME-vnet \
--address-prefix 10.0.x.0/24 \
--subnet-name default \
--subnet-prefix 10.0.x.0/24
# x is the next available 3rd octet value
# 3. Create a public IP Address. Microsoft Docs
az network public-ip create \
--resource-group $RESOURCEGROUPNAME \
--name $VMNAME-ip \
--dns-name $DNSNAME
# 4. Create a network security group. Microsoft Docs
az network nsg create \
--resource-group $RESOURCEGROUPNAME \
--name $VMNAME-nsg
# 5. Create a rule to allow SSH to the machine. Microsoft Docs
az network nsg rule create \
--resource-group $RESOURCEGROUPNAME \
--nsg-name $VMNAME-nsg \
--name allow-ssh \
--protocol tcp \
--priority 1000 \
--destination-port-range 22 \
--access allow
# 6. Create a virtual NIC. Microsoft Docs
az network nic create \
--resource-group $RESOURCEGROUPNAME \
--name $VMNAME-nic \
--vnet-name $RESOURCEGROUPNAME-vnet \
--subnet default \
--public-ip-address $VMNAME-ip \
--network-security-group $VMNAME-nsg
# 7. Create an availability set, if redundancy is required. Microsoft Docs
az vm availability-set create \
--resource-group $RESOURCEGROUPNAME \
--name $AVSETNAME-as
# 8. Create the VM. Microsoft Docs
az vm create \
--resource-group $RESOURCEGROUPNAME \
--location $LOCATION \
--name $VMNAME \
--image UbuntuLTS \
--size $VMSIZE \
--availability-set $AVSETNAME-as \
--nics $VMNAME-nic \
--admin-username $ADMINUSERNAME \
--authentication-type ssh
--ssh-key-value #$SSHPUBLICKEYFILE \
--os-disk-name $VMNAME-osdisk
This should work in bash script:
if [ $(az group exists --name $RESOURCEGROUPNAME) = false ]; then
az group create --name $RESOURCEGROUPNAME --location $LOCATION
fi
In a bash script how can I check that a resource already exists so I
don't try to create it again?
We can use CLI 2.0 command az group exists to test the resource group exist or not, like this:
C:\Users\user>az group exists -n jasontest
false
In this way, before we create it, we can test the name available or not. In new resource group, we can create new Vnet and other resources.
For now, there is no CLI 2.0 command to test other resource exist or not. If you want to create resource in an existing resource group, maybe we should use CLI 2.0 command to list the resources, and use bash to make sure the resource exist or not.
You can use JMESPath queries to do this. All resource types support this, AFAIK.
For example, for VMs:
az vm list --resource-group $RESOURCEGROUPNAME --query "[?name=='$VMNAME'] | length(#)"
This will output the number of matching VMs - either 1 or 0.
You can use this to create if/else logic in bash as follows.
if [[ $(az vm list --resource-group $RESOURCEGROUPNAME --query "[?name=='$VMNAME'] | length(#)") > 0 ]]
then
echo "VM exists"
else
echo "VM doesn't exist"
fi
If a resource show command returns an empty string and a success status code (0), then the resource does not exist.
Edit: ChrisWue pointed out that this is no longer true. It must have changed since I left the Azure CLI team (it used to be a requirement that all commands worked like this). Or it may be that there is a bug for the key vault commands he mentioned below.
this work for my batch commands
call az webapp show --subscription <yoursubs> --resource-group <yourrg> --name <yourappname> -query name
if %errorlevel% == 1 (
az webapp create ...
)
As mentioned in another answer - there is no generic "exists" command. One line of reasoning I've found was that "create" is meant to be idem potent - therefor if you have a script that creates resources (for example as part of a build pipeline) it doesn't matter how often you execute it since "it will do the right thing".
If you still need to do this you can do it in shell like this (the example is for keyvault but it should work for all resource types that have a show command)
if az keyvault show -n my-keyvault -o none; then
echo "keyvault exists"
else
echo "keyvault doesn't exist"
fi
It should be noted that az will output an error message to stderr if the resource doesn't exists - this doesn't affect the check but if it bothers you then you can redirect stderr to /dev/null
In our case we needed this because we don't run the infra scripts if the setup hasn't changed (cuts our build time in half). We dectect this by creating a hash of the infra-scripts and store it in a keyvault. When the script runs it creates the keyvault (to make sure it exists) and then tries to check the secret that contains the hash. If the hash is still the same then don't run the rest of the script.
Catch is that keyvault create nukes the access policies which also includes the web-app managed identity access policy which won't get added if the rest of the script doesn't run ... so the fix is to check if the keyvault exists first and to not create it if it does.

Resources