Extracting Virtual Machine Information in Azure.

Iron Contributor

I just want to shared my modified code in azure vm information extraction.

Data will be the list of VMName, IPAddress, ResourceGroup, VmSize & Tag.

 

# User Authentication
$ua = Get-StoredCredential -Target AzureAccount
$credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $ua.UserName,$ua.Password

# Login to your Azure Account
Connect-AzAccount -Tenant '<Tenant ID>' -Credential $credential

# Get All Virtual Machines & VMSize
$resultVMs = Get-AzVM | Where-Object { $_.Name -and $_.ResourceGroupName -and $_.HardwareProfile.VmSize } `
| Select-Object -Property @{n="Resource"; e={ $_.ResourceGroupName }}, @{n="VMName"; e={ $_.Name }}, @{n="VmSize"; e={ $_.HardwareProfile.VmSize }} `

# Get All Virtual Machines & WorkNo Tag
$resultTags = Get-AzResource -ResourceType "Microsoft.Compute/virtualMachines" `
| Where-Object { $_.Name -and $_.ResourceGroupName -and $_.Tags.WorkNo } `
| Select-Object -Property @{n="Resource"; e={ $_.ResourceGroupName }}, @{n="VMName"; e={ $_.Name }}, @{n="Tags"; e={ $_.Tags.WorkNo }} `

# Get All Virtual Machines & Ip Address
$resultIPAdds = Get-AzNetworkInterface | Where-Object { $_.ResourceGroupName -and $_.VirtualMachine -and $_.IPConfigurations -and $_.IPConfigurations.PrivateIPAddress } `
| Select-Object -Property @{n="Resource"; e={ $_.ResourceGroupName }}, @{n="VMName"; e={ $_.VirtualMachine.Id.Split("/")[-1] }}, @{n="PrivateIPAddress"; e= { $_.IpConfigurations.PrivateIPAddress }} `

# Create Report Array
$report = @()

# Loop All VM's
foreach($resultVM in $resultVMs){

    # Creating Report Header
    $reportdetails = "" | Select VMName, IPAddress, ResourceGroup, VmSize, Tag

    # Save the VM Name
    $reportdetails.VMName = $resultVM.VMName

    # Save the Resource Group Name
    $reportdetails.ResourceGroup = $resultVM.Resource

    # Save the VmSize 
    $reportdetails.VmSize = $resultVM.VmSize

    # Save the IP Address
    $temp = @(($resultIPAdds | Where-Object { $_."VMName" -match $resultVM.VMName }).PrivateIPAddress)
    $reportdetails.IPAddress = $temp[0]

    # Save the Tag
    $temp = @(($resultTags | Where-Object { $_."VMName" -match $resultVM.VMName }).Tags)
    $reportdetails.Tag = $temp[0]

    # Save Report
    $report+=$reportdetails
}

# Generate Report
$report | Export-Excel ".\Desktop\VMList_ $(get-date -f yyyy.MM.dd.HH.mm.ss).xlsx"
Write-Host "Finished..." 

 


Hope it can help others too. :beaming_face_with_smiling_eyes::beaming_face_with_smiling_eyes::beaming_face_with_smiling_eyes:.

4 Replies

@Alan2022 

 

Hey, Alan.

 

It's good to see the final version of what you were working towards with the previous questions - well done!

 

Here's a variation of your finished product that will work for others courtesy of some minor changes.

 

  1. Using Export-Csv rather than Export-Excel, as Export-Csv exists "out of the box";
  2. Remove "Desktop" from the Csv export path as that will fail for anyone that doesn't have a subfolder named "Desktop".

 

I'm not currently working in an Azure-facing role meaning I can't see how well this alternative scales performance-wise - I only have visibility of a small number of Azure virtual machines.

 

That said, it will consume less memory as it is working from the pipeline rather than from data stored in variables - with the unavoidable exception of the private IP address, since they're not exposed via Get-AzResource.

 

This reduces the overhead from three variables storing the full set of Azure VM data down to one, while the rest can be released as soon as they've been processed meaning memory utilisation won't scale as steeply in larger environments.

 

Anyhow, give it a try and let me know how it compares from a run time perspective. I'm hoping it's favourable or better but since using "-ExpandProperties" results in more data being returned, it'll be interesting to see if that outweighs the benefit of removing the separate call to Get-AzVM.

 

# User Authentication
$ua = Get-StoredCredential -Target AzureAccount
$credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $ua.UserName,$ua.Password

# Login to your Azure Account
Connect-AzAccount -Tenant '<Tenant ID>' -Credential $credential

# Build a dictionary of private IP addresses, given they can't be obtained via Get-AzResource -ExpandProperties.
[System.Collections.Generic.Dictionary[int, string]] $PrivateAddressesById = [System.Collections.Generic.Dictionary[int, string]]::new();

Get-AzNetworkInterface |
    Where-Object { $_.ResourceGroupName -and $_.VirtualMachine -and $_.IPConfigurations -and $_.IPConfigurations.PrivateIPAddress } |
        ForEach-Object {
            $PrivateAddressesById.Add($_.Id.GetHashCode(), ($_.IpConfigurations.PrivateIPAddress -join ", "));
        }

# Now, fetch the virtual machine information, making use of the speedy dictionary lookups to flesh out the IPAddress data.
Get-AzResource -ResourceType "Microsoft.Compute/virtualMachines" -ExpandProperties |
    ForEach-Object {
        [PSCustomObject]@{
            VMName = $_.Name;
            IPAddress = $PrivateAddressesById[($_.Properties.networkProfile.networkInterfaces.id.GetHashCode())];
            ResourceGroup = $_.ResourceGroupName;
            VmSize = $_.Properties.hardwareProfile.vmSize;
            Tag = $_.Tags["WorkNo"];
        }
    } | Export-Csv -NoTypeInformation -Path ".\VMList_$([datetime]::Now.ToString("yyyy.MM.dd.HH.mm.ss")).csv";

# Clean up and bail.
$PrivateAddressesById.Clear();
[gc]::Collect();

Write-Host "Finished..."

 

Cheers,

Lain

@LainRobertson

Hi Lain

Yeah now I'm already started how to script using PowerShell its very difficult from the start but once you already familiarized the syntax its not that difficult anymore. So far my code speed from 20mins to 17sec. But your code is beyond my level. Yeah i will try your code hope it can beat my current 17 sec. :D
Thanks again. I will try decoding this.

@Alan2022 

 

All good. You're doing well so far.

 

With the re-write above, here's a brief explainer on some of the thought processes behind it - but the general motivators were seeing if we could improve performance:

 

  1. Try and use server-side filtering wherever it exists rather than client-side, as when used against larger result sets, it's frequently much faster;
  2. Consider eliminating as many web calls as is practical to do, as they also can be expensive;
  3. Consider the size/complexity of the returned object, as that can cause things to slow down both on the server fetching the data as well as the transmission of it back to you - if the result is large enough, of course.

 

In reality, it's always a case-by-case balancing act as to which combination is better, but here's how those thoughts translated:

 

  • Managed to remove the call to Get-AzVM (relates to point 2 above) by getting more data from the existing call to Get-AzResource (relates to point 3 above);
  • On line 18, we brought the "ResourceType" out of the client-side filter (i.e. inside the Where-Object) back over to being a server-side filter by using the "-ResourceType" parameter on Get-AzResource (relates to point 1 above);
  • Line 20 - 26: This is an example of something called a "hash table" (starting with "@{" and ending with the closing "}"). The [PSCustomObject] is like an overriding directive saying, "hey, PowerShell! Convert the following hash table to a PSCustomObject for me, please.";
  • Lines 9, 14 and 22: PowerShell leverages .NET classes, and this is an example of doing just that. We're creating a new .NET Dictionary class (System.Collections.Generic.Dictionary, line 9) and then adding the Azure private IP addresses to it (line 14). Later (line 22), we're looking up those stored addresses so we can populate the hash table with the value we want in the report.

 

That's a pretty basic explanation but it may serve as some useful information in your own learning journey!

 

Cheers,

Lain

 

Edited for spelling and punctuation.

@LainRobertson

Hi Lain

Thanks for the tips.
I will always remember that.
:thumbs_up::thumbs_up::thumbs_up: