I have been able to export VMData from a single azure subscription using the code below.
$VMs = Get-AzVM
$vmOutput = $VMs | ForEach-Object {
[PSCustomObject]#{
"VM Name" = $_.Name
"VM Profile" = $_.HardwareProfile.VmSize
"VM Type" = $_.StorageProfile.osDisk.osType
"VM Location" = $_.Location
"VM Resource Group Name" = $_.ResourceGroupName -join ','
}
}
$vmOutput | export-csv C:\Azure\VMdata.csv -delimiter ";" -force -notypeinformation
However, when using the code below to try and extract from across multiple subscriptions and get no results.
$azuresubs = Get-AzSubscription
$azureSubs.foreach{
Select-AzSubscription $_
$VMs = Get-AzVM
$vmOutput = $VMs | ForEach-Object {
[PSCustomObject]#{
"VM Name" = $_.Name
"VM Profile" = $_.HardwareProfile.VmSize
"VM Type" = $_.StorageProfile.osDisk.osType
"VM Location" = $_.Location
"VM Resource Group Name" = $_.ResourceGroupName -join ','
}
}
}
$vmOutput | export-csv C:\Azure\VMdata.csv -delimiter ";" -force -notypeinformation
Being new to powershell, I'm sure the syntax above is incorrect so any assistance would be much appreciated.
Thank you
i think what happens here is you overwrite your variable on each pass (and last pass happens to have no vms). so you need to append to it, not overwrite it. define it before loop:
$vmOutput = #()
and then append to it inside the loop
$vmOutput += $Vms | bla-bla-bla
final:
$vmOutput = #()
$azuresubs = Get-AzSubscription
$azureSubs.foreach{
Select-AzSubscription $_
$VMs = Get-AzVM
$vmOutput += $VMs | ForEach-Object {
[PSCustomObject]#{
xxx
}
}
}
Related
I am trying to export VM data from Azure and below script is working perfect if subscription has VMs however it does create a .csv if there is no data (VMs) and I need that even if there is no data powershell should create a blank csv. Below is my script which is working fine if subscription has VMs created in it.
function create($path) {
$exists = Test-Path -path $path
Write-Host "tried the following path: $path, it" $(If ($exists) {"Exists"} Else {"Does not Exist!"})
if (!($exists)) { New-Item $path -itemType Directory }
}
# reading file contents
$subs_file = "C:\Scrpting\Subscriptions\Subscriptions.xlsx"
$azSubs = Import-Excel $subs_file
$azSubs
$output_folder = "C:\audit-automation"
# creating folder for outputing data
create("$output_folder")
# New-Item $output_folder -itemType Directory
# iterating over subscriptions
ForEach ( $sub in $azSubs ) {
# sub
$azsub = $sub.Subscription
# app
$app = $sub.Application
$azsub
$app
# creating folder to save data for apps
# New-Item $output_folder\$app -itemType Directory
# setting config for azure
Set-AzContext -SubscriptionName $azsub
# GET VM INFO
$vms = Get-AzVM
$vmrg = Get-AzVM | Select-Object "ResourceGroupName"
$nics = get-AzNetworkInterface | Where-Object { $_.VirtualMachine -NE $null }
# creating folder to save
# New-Item $output_folder\$app\vm_info -itemType Directory
create("$output_folder\$app")
ForEach ($nic in $nics) {
$info = "" | Select VMName, ResourceGroupName, OS, PrivateIPAddress, PublicIPAddress, SubscriptionID, Status, NICName
$vm = $vms | ? -Property Id -eq $nic.VirtualMachine.id
$info.NICName = $nic.Name
$info.VMName = $vm.Name
$info.SubscriptionID = $azsub
$info.ResourceGroupName = $vm.ResourceGroupName
$info.PrivateIPAddress = $nic.IpConfigurations.PrivateIpAddress
$PublicIPAddress =
(Az vm list-ip-addresses --name $vm.Name --resource-group $vm.ResourceGroupName | ConvertFrom-Json).virtualMachine.network.publicIpAddresses.ipaddress
$info.PublicIPAddress = if ($null -eq $PublicIPAddress ) { "Not Assigned" } else { $PublicIPAddress }
$info.OS = $vm.StorageProfile.osDisk.osType
$info.Status = ((Get-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status).Statuses[1]).code
$info | Export-Csv -Path $output_folder\$app\$app-vm_data$((Get-Date).ToString("yyyy-MM-dd")).csv -Append
}}
You can remove the function create and replace that with just the one line $null = New-Item $output_folder -ItemType Directory -Force.
On file system, the -Force makes the cmdlet return either the DirectoryInfo object of an existing folder or create a new folder and return that.
(don't use this on registry keys!)
Next, if you capture the output you want in the CSV file first, it is easy enough to check if there is output or not and if not, write an empty csv file (only the headers will be in there)
Something like this:
# reading file contents
$subs_file = "C:\Scrpting\Subscriptions\Subscriptions.xlsx"
$azSubs = Import-Excel $subs_file
#$azSubs
$output_folder = "C:\audit-automation"
# creating folder for outputing data
$null = New-Item $output_folder -ItemType Directory -Force
# iterating over subscriptions
foreach($sub in $azSubs) {
# sub
$azsub = $sub.Subscription
# app
$app = $sub.Application
$azsub
$app
# setting config for azure
Set-AzContext -SubscriptionName $azsub
# GET VM INFO
$vms = Get-AzVM
$vmrg = Get-AzVM | Select-Object "ResourceGroupName"
$nics = Get-AzNetworkInterface | Where-Object { $null -ne $_.VirtualMachine }
# creating folder to save
$null = New-Item (Join-Path -Path $output_folder -ChildPath $app) -ItemType Directory -Force
# capture the output of the foreach loop
$result = foreach ($nic in $nics) {
$info = "" | Select VMName, ResourceGroupName, OS, PrivateIPAddress, PublicIPAddress, SubscriptionID, Status, NICName
$vm = $vms | ? -Property Id -eq $nic.VirtualMachine.id
$info.NICName = $nic.Name
$info.VMName = $vm.Name
$info.SubscriptionID = $azsub
$info.ResourceGroupName = $vm.ResourceGroupName
$info.PrivateIPAddress = $nic.IpConfigurations.PrivateIpAddress
$PublicIPAddress = (Az vm list-ip-addresses --name $vm.Name --resource-group $vm.ResourceGroupName |
ConvertFrom-Json).virtualMachine.network.publicIpAddresses.ipaddress
$info.PublicIPAddress = if ($null -eq $PublicIPAddress ) { "Not Assigned" } else { $PublicIPAddress }
$info.OS = $vm.StorageProfile.osDisk.osType
$info.Status = ((Get-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status).Statuses[1]).code
# output the object to be collected in variable $result
$info
}
# test if you actually have data now in the $result variable
if (!#($result.Count)) {
# no data; create an empty csv with just the headers
$result = "" | Select VMName, ResourceGroupName, OS, PrivateIPAddress, PublicIPAddress, SubscriptionID, Status, NICName
}
$outFile = Join-Path -Path $output_folder -ChildPath ('{0}\{0}-vm_data{1:yyyy-MM-dd}.csv' -f $app, (Get-Date))
$result | Export-Csv -Path $outFile -NoTypeInformation
}
add outside the loop, since $vms may be empty
if (!($vms)){
$info = "" | Select VMName, ResourceGroupName, OS, PrivateIPAddress, PublicIPAddress, SubscriptionID, Status, NICName
$info | Export-Csv -Path $output_folder\$app\$app-vm_data$((Get-Date).ToString("yyyy-MM-dd")).csv -Append
}
I am pretty much new to PowerShell and we have customer requirement that they will share Azure VM details in Excel sheet with below columns.
we have to get VM Status details from all the subscriptions & ResourceGroup using the Powershell script.
Outuput:
I am able to perform for single RSG and VM values by using the below code
$SubscriptionName = Get-AzSubscription -SubscriptionId $subscriptionId
$RG = "rgp-use2-prd-bioportalbiopeople1"
$RSGName = Get-AzResourceGroup -Name $RG
$VMs = Get-AzVM -Name "vmbppapiv1prd02"
$VMState = (Get-AzVM -Name $VM -ResourceGroupName $RG -Status).Statuses
$vmOutput = $VMs | ForEach-Object {
[PSCustomObject]#{
"Resource Group Name" = $RSGName.ResourceGroupName
"Subscription Name" = $SubscriptionName.Name
"VM Name" = $_.Name
"VM Type" = $_.StorageProfile.osDisk.osType
"VM Statss" = ($VMState | where code -Like 'PowerState/*')[0].DisplayStatus
}
}
$vmOutput | Format-Table -AutoSize
$vmOutput | export-csv C:\Projects\data.csv
I can't test this myself, but you will have to create nested loops to get the details for all subscriptions and resourcegroups.
Something like this:
$subscriptions = Get-AzSubscription -TenantId "aaaa-aaaa-aaaa-aaaa" # enter the tenant ID here
$VMs = Get-AzVM -Name "vmbppapiv1prd02"
$vmOutput = foreach ($vm in $VMs) {
foreach ($subscription in $subscriptions) {
Set-AzContext -SubscriptionId $subscription.Id
(Get-AzResourceGroup).ResourceGroupName | ForEach-Object {
$vmState = (Get-AzVM -Name $vm.Name -ResourceGroupName $_ -Status).Statuses
[PSCustomObject]#{
"Resource Group Name" = $_
"Subscription Name" = $Subscription.Name
"VM Name" = $vm.Name
"VM Type" = $vm.StorageProfile.osDisk.osType
"VM Status" = ($vmState | where code -Like 'PowerState/*')[0].DisplayStatus
}
}
}
}
$vmOutput | Format-Table -AutoSize
$vmOutput | Export-Csv -Path 'C:\Projects\data.csv' -NoTypeInformation
I'm preparing a table with information about VM name and provision date of OS disk. I can easily retrieve that information from $VM.disks.statuses.time[0] command, if individual VM is assigned to $VM, but I when i try to collect data into table, I got an error:
Cannot index into a null array.
At line:4 char:1
+ [PSCustomObject]#{
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
This is my code:
$VMs = Get-AzVM -status
$vmOutput = $VMs | ForEach-Object {
[PSCustomObject]#{
"VM Name" = $_.Name
"Provision Date" = $_.disks.statuses.time[0].ToString()
}
}
I can reproduce your issue, the issue was caused by the outputs of Get-AzVM -status and Get-AzVM -ResourceGroupName <ResourceGroupName> -Name <Name> -Status are different.
The output of Get-AzVM -status will not has the disks property, but when you get individual VM status via Get-AzVM -ResourceGroupName <ResourceGroupName> -Name <Name> -Status, it will have the property, so you got the error.
Get-AzVM -status:
Get-AzVM -ResourceGroupName <ResourceGroupName> -Name <Name> -Status:
Solution:
To fix the issue, just to use Get-AzVM -ResourceGroupName <ResourceGroupName> -Name <Name> -Status in your script.
$VMs = Get-AzVM -status
$vmOutput = $VMs | ForEach-Object {
$VMstatus = Get-AzVM -ResourceGroupName $_.ResourceGroupName -Name $_.Name -Status
[PSCustomObject]#{
"VM Name" = $VMstatus.Name
"Provision Date" = $VMstatus.disks.statuses.time[0].ToString()
}
}
You can use the Get-AzDisk command to retrieve disk creation information.
$disks = Get-AzDisk | Where-Object { $_.Managedby }
$vmOutput = foreach ($disk in $disks) {
[pscustomobject]#{"VM Name" = ($disk.ManagedBy -split "/")[-1]
"Provisioned Date" = $disk.TimeCreated
}
}
The error says:
Cannot index into a null array.
So my guess is that something here $_.disks.statuses.time[0].ToString() is $null. Therefore you should add some $nullchecks:
$VMs = Get-AzVM -status
$vmOutput = $VMs | ForEach-Object {
if ($_.disks -and $_.disks.statuses -and $_.disks.statuses.time -and ($_.disks.statuses.time.Count -gt 0)){
[PSCustomObject]#{
"VM Name" = $_.Name
"Provision Date" = $_.disks.statuses.time[0].ToString()
}
}
}
Hope that helps.
I want to add StorageAccountName and its SKU to my report, this is the code I'm having now:
VMs = Get-AzureRmVM -Status
$vmOutput = $VMs | ForEach-Object {
[PSCustomObject]#{
"VM Name" = $_.name
"Private IP" =""
"VM Type" = $_.StorageProfile.osDisk.osType
"VM Profile" = $_.HardwareProfile.VmSize
"Environment" = $_.Tags.Environment
"Application" = $_.Tags.Application
"Decommission Date" = $_.Tags.Decomission
"OS Disk Size" = $_.StorageProfile.OsDisk.DiskSizeGB
"Data Disks Total Size" = ($_.StorageProfile.DataDisks.DiskSizeGB | Measure -Sum).Sum
"Data Disks Amount" = ($_.StorageProfile.DataDisks | Measure ).Count
"Managed OS Disk" = ($_.StorageProfile.OSDisk.ManagedDisk | Measure).Count
"Managed Data Disks" = ($_.StorageProfile.DataDisks.ManagedDisk | Measure).Count
"Powerstate" = $_.PowerState
}
}
$nics = get-azurermnetworkinterface | where VirtualMachine -NE $null
$ips =#{}
foreach($nic in $nics){
$vm = $VMs | Where-Object -Property id -EQ $nic.VirtualMachine.id
$prv = $nic.IpConfigurations | select-object -ExpandProperty PrivateIpAddress
$ips.Add($vm.Name,$prv)
}
foreach($vm in $vmOutput)
{
if($ips.ContainsKey($vm."VM Name"))
{
$vm."Private IP"=$ips[$vm."VM Name"]
}
}
$vmOutput | sort "Environment", "VM Type", "VM Profile", "Application" | export-csv VMReport.csv -delimiter ";" -force -notypeinformation
I tried to do it more less the same way IP addresses were implemented into this code, but it's much harder than I expected, as Get-AzureRmStorageAccount cmdlet doesn't store VirtualMachine.Id property like Get-AzureRMNetworkInterface cmdlet does.
Any ideas how can I merge it into one table? Is there any key upon which I can join both information? What I want to achieve is to add StorageAccountName and SKUName columns into the $vmOutput table.
Update:
$sat =#{}
$OutFile = "..."
#part1
foreach ($vmdetails in $VMs)
{
$ResourceGroupName=$vmdetails.ResourceGroupName
$VMName=$vmdetails.name
$storage=$vmdetails.StorageProfile.OsDisk.Vhd.Uri
$DataDiks=$vmdetails.StorageProfile.OsDisk.Name
$ss=$storage.split('/')[2]
$OSStorageAccountName=$ss.split('.')[0]
$DiskType="OSDisk"
$StroageAccountLocation=($StroageAccDetail | where {$_.StorageAccountName -eq $OSStorageAccountName }).location
$StorageAccountType=($StroageAccDetail | where {$_.StorageAccountName -eq $OSStorageAccountName }).AccountType
"$sub,$ResourceGroupName,$VMName,$DataDiks,$DiskType,$OSStorageAccountName" | Out-File -FilePath $OutFile -Append
$vmdatadisks=$vmdetails.StorageProfile.DataDisks
if($vmdatadisks -ne $null){
foreach($vmDatadsik in $vmdatadisks)
{
$vmDatadsikss=$vmDatadsik.vhd.uri
$DiskType="DataDisk"
$ss=$vmDatadsikss.split('/')[2]
$DataDiks=$vmDatadsik.Name
$dataStorageAccountName=$ss.split('.')[0]
"$sub,$ResourceGroupName,$VMName,$DataDiks,$DiskType,$OSStorageAccountName" | Out-File -FilePath $OutFile -Append
}
}
}
#part2
$VMs = Get-AzureRmVM -ResourceGroupName "..." -Name "..."
$storageAccountName = $VMs.StorageProfile.OsDisk.Vhd.Uri.Split("/")[2].Split(".")[0]
Get-AzureRmStorageAccount -StorageAccountName $storageAccountName -ResourceGroupName "..."
Please correct me if I'm misunderstanding you.
Assume the sku you refer to the account storage's sku, you can also add placeholders in the $vmOutput for SA Name and SKUName.
$VMs = Get-AzureRmVM -Status
$vmOutput = $VMs | ForEach-Object {
[PSCustomObject]#{
"VM Name" = $_.name
"ResourceGroupName" =$_.ResourceGroupName #you must include this parameter
"storage" = $_.StorageProfile.OsDisk.Vhd.Uri #you must include this parameter
#your other property
#add placeholder for SA Name and SKUName like below
"StorageAccountName" =""
"StorageAccountSKUName" =""
}
}
Then, iterate all the vms in $vmoutput, and find the related storage account and sku, then add value to the $vmoutput:
foreach($v in $vmOutput){
$resourceGroups = $v.ResourceGroupName
$storage=$v.storage
if($storage -ne $null)
{
$ss=$storage.split('/')[2]
$OSStorageAccountName=$ss.split('.')[0]
$s1 = Get-AzureRmStorageAccount -ResourceGroupName $resourceGroups -StorageAccountName $OSStorageAccountName
#add the value to $vmoutput
$v.StorageAccountName =$OSStorageAccountName
$v.StorageAccountSKUName=$s1.Sku.name
}
else
{
$v.StorageAccountName ="no value"
$v.StorageAccountSKUName="no value"
}
}
I have been able to export out VM Names and IP Addresses from within a single azure subscription using the following script.
$report = #()
$vms = get-azvm
$nics = get-aznetworkinterface | ?{ $_.VirtualMachine -NE $null}
foreach($nic in $nics)
{
$info = "" | Select VmName, ResourceGroupName, HostName, IpAddress
$vm = $vms | ? -Property Id -eq $nic.VirtualMachine.id
$info.VMName = $vm.Name
$info.ResourceGroupName = $vm.ResourceGroupName
$info.IpAddress = $nic.IpConfigurations.PrivateIpAddress
$info.HostName = $vm.OSProfile.ComputerName
$report+=$info
}
$report | Export-Csv -Path "c:\Azure\VMIPs.csv"
However I have tried using the below to export the same data from an azure account that has multiple subscriptions but all I get is a blank CSV file.
$report = #()
$vms = get-azvm
$azureSubs = get-azsubscription
$azureSubs.foreach{
Select-AzSubscription $_ # << change active subscription
$nics = get-aznetworkinterface | ?{ $_.VirtualMachine -NE $null}
foreach($nic in $nics)
{
$info = "" | Select VmName, ResourceGroupName, HostName, IpAddress
$vm = $vms | ? -Property Id -eq $nic.VirtualMachine.id
$info.VMName = $vm.Name
$info.ResourceGroupName = $vm.ResourceGroupName
$info.IpAddress = $nic.IpConfigurations.PrivateIpAddress
$info.HostName = $vm.OSProfile.ComputerName
$report+=$info
}
}
$report | Export-Csv -Path "c:\Azure\VMIPs.csv"
Can anyone assist?
I think you need to move Get-AzVm right after the Select-AzSubscription? because right now you only get vms once (in default subscription)
$azureSubs.foreach{
Select-AzSubscription $_ # << change active subscription
$nics = get-aznetworkinterface | ?{ $_.VirtualMachine -NE $null}
$vms = get-azvm
...
}