ForEach-Object return all objects three times in powershell (Azure VM) - azure

I have this Powershell line which should list all virtual machines from selected subscription and I have total of 3 subscriptions.
$azureSubscriptionID = "xxxxx-xxxxx-xxxxx-xxxx"
foreach ($subs in $azureSubscriptionID)
{
Write-Output "Collecting VM facts from subscription $subs"
$vms += Get-AzureRMSubscription | ForEach-Object {Select-AzureRMSubscription $_ | Out-Null; Get-AzureRmVM -WarningAction SilentlyContinue}
}
Issue is that when running the script and using $vms is that it will list all available vms subscriptions three times in a row like this:
VM A VM B VM C
VM A VM B VM C
VM A VM B VM C
What am I doing wrong and how to fix that? or are there alternative ways to get all vms from X subscription in few lines? Using this in Azure runbook.

If you just want to cycle through all of your subscriptions and list all of the VMs, you can do the following:
Get-AzureRMSubscription | ForEach-Object {
$sub = Select-AzureRMSubscription $_
Write-Output "Collecting VM facts from subscription $($sub.Subscription.Id)"
Get-AzureRmVM
}
The issue with your attempt is you are getting all subscriptions (Get-AzureRmSubscription) during each loop iteration regardless of the value(s) contained in $azureSubscriptionID. To fix your code, you would need to run Get-AzureRMSubscription -SubscriptionId $subs or Select-AzureRMSubscription -SubscriptionId $subs.
If you want to do further processing with the data you have gathered, I would consider some alternative approach for when you explicitly target known subscriptions.
$azureSubscriptionIDs = "xxxxx-xxxxx-xxxxx-xxxx","yyyyy-yyyyy-yyyyy-yyyy","zzzzz-zzzzz-zzzzz-zzzz"
# $vms is an array of custom objects
# each custom object contains a subscription ID and the associated VMs Names
$vms = foreach ($sub in $azureSubscriptionIDs) {
$null = Select-AzureRMSubscription -SubscriptionId $sub
$subvms = Get-AzureRmVM | Select -Expand Name
$sub | Select #{n='Subscription';e={$_}},#{n='VMs';e={$subvms}}
}
# You can access the subscription ID now with the Subscription property
# You can access the VMs Names with the VMs property
# List all vms under subscription 'xxxxx-xxxxx-xxxxx-xxxx'
$vms | Where Subscription -eq 'xxxxx-xxxxx-xxxxx-xxxx' | Select -Expand VMs
# List all vms for each subscription with a custom console message
foreach ($sub in $vms) {
Write-Output "Here are all the VMs for subscription $($sub.Subscription)"
$sub.VMs
}

Related

Azure Powershell Question for Virtual Machine

I am reviewing a script that is supposed to delete a vm along with all of the resources attributed to the vm
Write-Host -NoNewline -ForegroundColor Green "Please enter the VM name you would like to remove:"
$VMName = Read-Host
$vm = Get-AzVm -Name $VMName
if ($vm) {
$RGName=$vm.ResourceGroupName
Write-Host -ForegroundColor Cyan 'Resource Group Name is identified as-' $RGName
#boot diagnostics container auto generated in storage account. Auto delete this storageURI property
$diagSa = [regex]::match($vm.DiagnosticsProfile.bootDiagnostics.storageUri, '^http[s]?://(.+?)\.').groups[1].value
Write-Host -ForegroundColor Cyan 'Marking Disks for deletion...'
$tags = #{"VMName"=$VMName; "Delete Ready"="Yes"}
$osDiskName = $vm.StorageProfile.OSDisk.Name
$datadisks = $vm.StorageProfile.DataDisks
$ResourceID = (Get-Azdisk -Name $osDiskName).id
New-AzTag -ResourceId $ResourceID -Tag $tags | Out-Null
if ($vm.StorageProfile.DataDisks.Count -gt 0) {
foreach ($datadisks in $vm.StorageProfile.DataDisks){
$datadiskname=$datadisks.name
$ResourceID = (Get-Azdisk -Name $datadiskname).id
New-AzTag -ResourceId $ResourceID -Tag $tags | Out-Null
}
}
if ($vm.Name.Length -gt 9){
$i = 9
}
else
{
$i = $vm.Name.Length - 1
}
$azResourceParams = #{
'ResourceName' = $VMName
'ResourceType' = 'Microsoft.Compute/virtualMachines'
'ResourceGroupName' = $RGName
}
$vmResource = Get-AzResource #azResourceParams
$vmId = $vmResource.Properties.VmId
$diagContainerName = ('bootdiagnostics-{0}-{1}' -f $vm.Name.ToLower().Substring(0, $i), $vmId)
$diagSaRg = (Get-AzStorageAccount | where { $_.StorageAccountName -eq $diagSa }).ResourceGroupName
$saParams = #{
'ResourceGroupName' = $diagSaRg
'Name' = $diagSa
}
Write-Host -ForegroundColor Cyan 'Removing Boot Diagnostic disk..'
if ($diagSa){
Get-AzStorageAccount #saParams | Get-AzStorageContainer | where {$_.Name-eq $diagContainerName} | Remove-AzStorageContainer -Force
}
else {
Write-Host -ForegroundColor Green "No Boot Diagnostics Disk found attached to the VM!"
}
Write-Host -ForegroundColor Cyan 'Removing Virtual Machine-' $VMName 'in Resource Group-'$RGName '...'
$null = $vm | Remove-AzVM -Force
Write-Host -ForegroundColor Cyan 'Removing Network Interface Cards, Public IP Address(s) used by the VM...'
foreach($nicUri in $vm.NetworkProfile.NetworkInterfaces.Id) {
$nic = Get-AzNetworkInterface -ResourceGroupName $vm.ResourceGroupName -Name $nicUri.Split('/')[-1]
Remove-AzNetworkInterface -Name $nic.Name -ResourceGroupName $vm.ResourceGroupName -Force
foreach($ipConfig in $nic.IpConfigurations) {
if($ipConfig.PublicIpAddress -ne $null){
Remove-AzPublicIpAddress -ResourceGroupName $vm.ResourceGroupName -Name $ipConfig.PublicIpAddress.Id.Split('/')[-1] -Force
}
}
}
Write-Host -ForegroundColor Cyan 'Removing OS disk and Data Disk(s) used by the VM..'
Get-AzResource -tag $tags | where{$_.resourcegroupname -eq $RGName}| Remove-AzResource -force | Out-Null
Write-Host -ForegroundColor Green 'Azure Virtual Machine-' $VMName 'and all the resources associated with the VM were removed sucessfully...'
}
else{
Write-Host -ForegroundColor Red "The VM name entered doesn't exist in your connected Azure Tenant! Kindly check the name entered and restart the script with correct VM name..."
}
I had a question: what does this block of code exactly do:
$diagSa = [regex]::match($vm.DiagnosticsProfile.bootDiagnostics.storageUri, '^http[s]?://(.+?)\.').groups[1].value
I know it matches the storage uri, but how? And why is this needed? I am not sure what the .groups[1].value is referring to either
$diagSa =
[regex]::match($vm.DiagnosticsProfile.bootDiagnostics.storageUri,
'^http[s]?://(.+?).').groups[1].value
I know it matches the storage uri, but how?
You are using the [regex] type accelerator & match method () in the above expression.
The Match() method is a way to instruct PowerShell to attempt to match a string inside of another string. The Match() method has two parameters; the string you'd like to match on and the regular expression you'd like to test against.
Whenever a match is found and a regex group is used; (), the [regex] type accelerator has a Captures property. This Captures property then has a property called Groups. This is a collection that contains lots of attributes of what was matched. The second element in that collection contains the actual value that was matched.
what the .groups[1].value is referring to either
groups[1].values returns the storage account name where the boot diagnostics container resides.
And why is this needed?
When creating an Azure VM, you always have the option of creating a boot diagnostics container. This is useful to troubleshooting VM boot issues but doesn’t get removed when a VM is deleted. Let’s remedy that.
To remove the boot diagnostics container, you first need to figure out the name of the storage account the container resides on. To find that storage account, you’ll have to do some parsing of the storageUri property that’s exists in the DiagnosticsProfile object on the VM.
for more information about [regex]::match().group[1].value expression refer the below blog :
https://mcpmag.com/articles/2015/09/30/regex-groups-with-powershell.aspx

How to output Subscription name with Get-AzVM

I am currently trying to output a list of VMs that are not compliant with a policy, all is working except I cant figure out how to output the subscription the VM lives in, since its not a property of Get-AzVm. If someone can please help me out, I am embarrassed I cant figure it out since it seems pretty simple. The current output will use the last subscription context for all the VMs, even though I have multiple subscriptions. Thanks a lot!
$vmsNotBackedUp = #()
$vms_results = #()
$subscriptions = Get-AzSubscription
#set policy definition
$poldef = '013e242c-8828-4970-87b3-ab247555486d'
#Get VMs resource ID that are not backed up from Azure Policy, store in $resourceIDs variable
foreach ($sub in $subscriptions) {
Set-AzContext -Subscription $sub.Id
$resourceIDs =(Get-AzPolicyState -Filter "PolicyDefinitionName eq '$poldef' and ComplianceState eq 'NonCompliant'").ResourceId
$vmsNotBackedUp += Get-AzVM | Where-Object{$_.Id -in $resourceIDs}
$currentContext = $sub.Name
$currentContext
}
Write-Output("The Following VMs were not able to be backed up, may need investigation")
#$vmsNotBackedUp|Select-Object -Property Name,ResourceGroupName,Location
foreach ($vm in $vmsNotBackedUp) {
$output_data = [PSCustomObject]#{
vmName = $vm.Name
ResourceGroup = $vm.ResourceGroupName
vmLocation = $vm.Location
vmOS = $vm.StorageProfile.OsDisk.OsType
vmSub = $currentContext
}
$vms_results += $output_data
}
Since you already have the subscription ID in $sub.Id, you could add this as a property to the VMs you enumerate in your script. Something like this:
$vmsNotBackedUp += Get-AzVM |
Where-Object{$_.Id -in $resourceIDs} |
Add-Member -MemberType NoteProperty -Name 'Subscription' -Value $sub.id -PassThru

Adding tags to azure VMs via CSV

trying to add tags to VMs via CSV data, right now I have the below code:
if ($context.Account -eq $null) {
# Login-AzureAccount
Connect-AzAccount
}
# Select Azure Subscription
$subscriptionId = (Get-AzSubscription | Out-GridView -Title "Select an Azure Subscription ..." -PassThru).SubscriptionId
#Select specified subscription ID
Select-AzSubscription -SubscriptionId $subscriptionId
$InputCSVFilePath = "test.csv"
$csvItems = Import-Csv $InputCSVFilePath
################
foreach ($item in $csvItems){
Clear-Variable r
#$r = Get-AzResource -ResourceGroupName $item.ResourceGroup -Name $item.VM -ErrorAction Continue
$r = Get-AzResource -Name $item.VM
################
if ($r -ne $null){
if ($r.Tags){
# Tag - Client DL
if ($r.Tags.ContainsKey("Client_DL")){
$r.Tags["Client_DL"] = $item.ClientDL
}else{
$r.Tags.Add("Client_DL", $item.ClientDL)
}
# Tag - Priority
if ($r.Tags.ContainsKey("Priority")){
$r.Tags["Priority"] = $item.Priority
}else{
$r.Tags.Add("Priority", $item.Priority)
}
}
}else{
Write-Host "No VM found named $($item.VMName)!"
}
}
I verified that my code does indeed go through the functions but for some reason the tags are not being set on my VM's. I ran the commands manually in powershell and I was able to set a tag by doing:
$r = Get-AzResource -Name TestVM
$r.Tags.Add("Client_DL", "TEST-DL")
Am I missing something? i'm running a Set-PSDebug -Trace 2 when running my code and it seems to check out just fine, but the tags aren't getting set/written.
So you're adding the tags in memory but you're not calling any of the Az cmdlets to set the tags back on the resource.
You can see in the example in the docs here they return the VM with Get-AzResource, append their new tag to the existing tags and then use Set-AzResource to write the newly added tag back.
Just be careful of that
When updating tags through PowerShell, tags are updated as a whole. If you are adding one tag to a resource that already has tags, you will need to include all the tags that you want to be placed on the resource
Alternatively you could use Update-AzTag which has an -Operation parameter and lets you choose whether you want to merge, replace or delete the existing tags.
https://learn.microsoft.com/en-us/powershell/module/az.resources/update-aztag?view=azps-6.0.0
Ultimately you'd need to set back your $r.Tags value to your resource as the last operation within your if statement.

Recursively list all resource tags within Azure Resource Groups

We have a large number of Azure Subscriptions which currently run into the hundreds.
I'm looking to generate a report (ideally using Azure Powershell or Azure CLI) to recursively extract a list of all tags assigned to every single resource within every resource group, for between 40-50 of the subscriptions.
Currently, I can list all tags assigned at Resource Group level, but I simply can't find a way to list the tags assigned to the individual resources within each Resource Group. The list of subscriptions and resource groups on which I'd like to extract this report, are saved in a CSV file which includes two columns displaying the Subscription name and Resource Group respectively.
Any tips on how to achieve the above would be fantastic and most appreciated.
Not detailed code but the idea here.
1.You should write a loop, in the loop, change the subscription each time by using this cmdlet:
Set-AzContext -Subscription $subscription_name.
2.Then get all the resource group in the specified subscription by using this cmdlet:
$resource_groups = Get-AzResourceGroup
3.Then write a nested loop(loop for each resource group), in this nested loop, use this cmdlet to get all azure resources within a resource group:
foreach($rg in $resource_groups){
$azure_resources = Get-AzResource -ResourceGroupName $rg.ResourceGroupName
}
4.Write another nested loop in step 3, this loop is used to go though all the azure resources within the specified resource group. Then use the code below to fetch tags for each azure resource within the resource group:
foreach($r in $azure_resources){
#the following code can get all the tags for one resource
$r.tags
}
Based on Ivan Yang's logic. I have built the PowerShell Script;
#---------DECLARE VARIABLES------------------------------------#
$bnsSubscription = Get-AzSubscription
$day = Get-Date -Format " ddMMMyyyy"
$tagPath = "C:\mytempfolder\"+"$day-Tag-Details.csv"
$tagFolderPath = "C:\mytempfolder\"
#---------DECLARE VARIABLES------------------------------------#
function Get-ResourceTag {
foreach ($subs in $bnsSubscription) {
Select-AzSubscription -SubscriptionName $subs.Name | Out-Null
Write-Host 'The selected Subscription is' ($subs).Name
New-Item -ItemType file -Path "$tagFolderPath\$($subs.Name).csv" -Force
$resource_groups = Get-AzResourceGroup
$resource_groups_details = Get-AzResourceGroup | Sort-Location ResourceGroupName | Format-Table -GroupBy Location ResourceGroupName,ProvisioningState,Tags
Write-Host 'The selected Resource Group is' ($resource_groups).Name 'and the tag information as follows'
#$resource_groups_details
$resource_groups | Select-Object ResourceGroupName,Tags | Export-CSV -Path "$tagFolderPath\$($subs.Name).csv" -Append
$OutputFile = #()
foreach($rg in $resource_groups){
$azure_resources = Get-AzResource -ResourceGroupName $rg.ResourceGroupName
$TestTags = $Resource.Tags.GetEnumerator()
foreach($r in $azure_resources){
Write-Host 'The selected resource is' ($r).Name 'and the information as follows'
$RGHT = New-Object "System.Collections.Generic.List[System.Object]"
$RGHT.Add("RGName",$r.ResourceGroupName)
$RGHT.Add("ResourceName",$r.name)
$RGHT.Add("Location",$r.Location)
$RGHT.Add("Id",$r.ResourceId)
$RGHT.Add("ResourceType",$r.ResourceType)
$RGHT.Add("ResourceTags",$r.Tags)
$OutputFile += New-Object psobject -Property $RGHT
$OutputFile | Export-Csv -Path "C:\mytempfolder\test22.csv" -append -NoClobber -NoTypeInformation -Encoding UTF8 -Force
}
}
}
}
#---------CALL FUNCTION------------------------------------#
Get-ResourceTag

How to Get Data size of Cosmos DB from a Subscription using Powershell

I want to get Data sizes of Cosmos DB Storage Accounts from multiple Subscriptions.
For instance, we have a Subscription which has 4 Cosmos DB Accounts in 4 regions.
PS V:\> Get-AzResource -ResourceType Microsoft.DocumentDb/databaseAccounts | ft
Name ResourceGroupName ResourceType Location
---- ----------------- ------------ --------
Account1 dbcosmosdb Microsoft.DocumentDb/databaseAccounts eastasia
Account2 dbcosmosdb Microsoft.DocumentDb/databaseAccounts eastus2
Account3 dbcosmosdb Microsoft.DocumentDb/databaseAccounts northeurope
Account4 dbcosmosdb Microsoft.DocumentDb/databaseAccounts westus
Now I would like to query all 4 Cosmos DB Accounts to get the Data size used of each Account.
Example, Account1 has 137 GB Used so far. I would like to see that Number using Powershell so that i can query through multiple Subscriptions and add this my telemetry reporting.
You could use the Get-AzMetric command, try the script as below, it works fine on my side.
$ids = (Get-AzResource -ResourceType Microsoft.DocumentDb/databaseAccounts).ResourceId
foreach($item in $ids){
$name = (Get-AzResource -ResourceId $item).Name
$metric = Get-AzMetric -ResourceId $item -MetricName "DataUsage" -WarningAction Ignore
$data = ($metric.Data | Select-Object -Last 1).Total/1024/1024/1024
Write-Output "$name : $data GB"
}

Resources