User input VM name should start via powerhsell - powershell

Purpose - I am trying to start vm if it is in stop state
Note - user will input Vm name , if its stop then it will start otherwise it will pop up that server already in started state.
$user = 'tooltest' #Vmname
$rg = Get-AzureRmResourceGroup
$data= $rg.ResourceGroupName
foreach ( $d in $data){
$res = Get-AzureRmResource | Where-Object {$_.ResourceGroupName -eq $d}
if ( $res.Name -eq $user){
Write-Output $res.Name
Write-Output $res.ResourceGroupName
$gg = Get-AzureRmVM -ResourceGroupName [string]$res.ResourceGroupName -Name $user -Status
If i m trying to print $res.ResourceGroupName - i am getting output of resourcegroup name 26 times (26 resources i have in That RG)
I wanted to print RG only one time , can anybody help me on that

One way to filter your output is just to select how many objects do you want with the Select-Object cmdlet.
Write-Output $res.ResourceGroupName | Select -First 1

Related

Powershell to count and group Event ID 4625?

How can I get the total number of Event ID error 4625 and for each and every Windows Server in my AD domain using the below Powershell script?
$DCServers = Get-ADDomainController -filter * | select -ExpandProperty hostname
$events = #()
$totalCt = 0
$servers = #()
Foreach ($Server in $DCServers)
{
Write-Host "Calling Get-WinEvent for $Server"
$serverEvents = Get-WinEvent -ComputerName $Server -FilterHashtable #{ logname = 'Security'; id = 4625 } -EA 0
if (!$?)
{
Write-Host "Get-WinEVent failure for $Server"
continue
}
if ($null -ne $serverEvents)
{
$totalCt += $serverEvents.Count
$servers += [PsCustomObject] #{ $server = $serverEvents.Count }
Write-Host $server $serverEvents.Count
}
$serverEvents | ForEach-Object {
$events += [PsCustomObject] #{
Date = $_.TimeCreated
"Event Id" = $_.Id
"User Name" = $_.Properties[6].Value + "\" + $_.Properties[5].Value ## fixed
"IPAddress" = $_.Properties[21].Value
"FailureReason" = (($_.message -split "\n") | Select-String -Pattern "Failure Reason:\s+(.+)").matches[0].groups[1].value
"Status Code" = $_.message -split '\s{4}' | Select-String -Pattern "Status"
"Logon Type" = $_.Properties[10].Value
"DC Logged On" = $_.Properties[13].value ## this is "workstation that processed the request", not the DC Logged On
}
}
}
$HTML = '<h1>Head</h1>'
$GetDate = Get-Date
$Report = 'C:\clu\temp-4625.html'
#convert the array of events to HTML
$Events |
Select-Object Date, "Event Id", "User Name", "FailureReason", "Status Code", "DC Logged On", "Logon Type" |
Convertto-html -head $HTML -PreContent "<H2>Accounts that Failed to Log On</H2>", "<H2>$GetDate </H2>" -PostContent "<p></p>Total 4625 records: $totalCt <p></p>" |
Out-File $Report -append
Write-Host "Total 4625 records: $totalCt"
Write-Host "4625 records per server:"
$servers | ft -auto
Write-Host "4625 records grouped by user"
$events | group "User Name" | sort Count
The goal is to see which servers have Event 4625 and group it by the content to see which IP or AD account failed logins where possible?
Before digging into how to extract the workstation IP address and how to group the events by specific properties, let me suggest rewriting your existing code slightly, given your goal.
Doing $event = New-Object psobject |Select listOfPropertyNames and then assigning the values to each property separately is going to be slow - something I imagine you might want to avoid if you have many servers to query.
Since PowerShell 3.0, we can instantiate a new custom object in one go with the following syntax:
$newObject = [pscustomobject]#{
PropertyName = "Value"
}
So, refactoring your existing code, we end up with something like:
$events += [pscustomobject]#{
Date = $_.TimeCreated
"Event Id" = $_.Id
"User Name" = $_.Properties[5].Value + "\" + $_.Properties[6].Value
"FailureReason" = (($_.Message -split "\n") | Select-String -Pattern "Failure Reason:\s+(.+)").Matches[0].Groups[1].Value
"Status Code" = $_.Message -split '\s{4}' | Select-String -Pattern "Status"
"Logon Type" = $_logontype[ [int] $_.Properties[10].Value ]
"DC Logged On" = $_.Properties[13].Value
}
Another change we might wanna make is change the data type of $events - when you instantiate an empty array with #(), PowerShell will allow you to add new items to the array with +=, but there's a catch - arrays are of a fixed size, so if you keep adding new items via +=, PowerShell will have to stop and resize the array by creating a larger underlying array and then copying the existing array items into the new, larger array. This obviously takes some time and might incur unnecessary memory acquisition as well.
To work around this, use a list instead - lists are designed with dynamic sizing in mind, so will perform better even when you add a 1000s of items to it:
$events = New-Object 'System.Collections.Generic.List[psobject]'
Now, back to the question - how to group all the events by IP address - first of all, we need to extract the workstation IP address in order to me able to group on it later, so let's add an extra property to the custom object we created:
$events += [pscustomobject]#{
# ...
IPAddress = $_.Properties[21].Value
}
Now that the IPAddress is present, grouping the objects based on it is as simple as:
$events | Group-Object IPAddress
Note that you can also get these fields from the xml of the event:
$a = Get-WinEvent -Max 1 #{logname='Security'; id=4625}
$xml = [xml]$a.ToXml()
$xml.event.EventData.data
Name #text
---- -----
SubjectUserSid S-1-5-18
SubjectUserName COMP$
SubjectDomainName DOM
SubjectLogonId 0x3e7
TargetUserSid S-1-0-0
TargetUserName admin
TargetDomainName COMP
Status 0xc000006d
FailureReason %%2313
SubStatus 0xc000006a
LogonType 7
LogonProcessName User32
AuthenticationPackageName Negotiate
WorkstationName COMP
TransmittedServices -
LmPackageName -
KeyLength 0
ProcessId 0xa60
ProcessName C:\Windows\System32\svchost.exe
IpAddress 127.0.0.1
IpPort 0
Compare with .Properties
$a.properties
Value
-----
S-1-5-18
COMP$
DOM
999
S-1-0-0
admin
COMP
-1073741715
%%2313
-1073741718
7
User32
Negotiate
COMP
-
-
0
2656
C:\Windows\System32\svchost.exe
127.0.0.1
0

Input and Output of foreach into columns in table

I currently want to check if a list of processes are running, then display the result within a table such as:
Process Status
======= ======
Process 1 Running
Process 2 Not Running
Process 3 Running
I have the below code which produces an output showing each input and output as a string, but it looks messy depending on the length of the Process name.
$Node = Read-Host -Prompt 'Input Node name'
$Process = #("Process1", "Process2", "Process3")
$Process | foreach-object {if(!(Get-Process -Name $_ -ComputerName $Node - ErrorAction SilentlyContinue)) {"$_ - Not Running"} else {"$_ - Running"}}
I am at a loss. All help appreciated.
Better (faster) to make a single remoting call to get all the processes, than one per process, so do that and store all the results - at least the names of the processes.
The next part is non-trivial. The way PowerShell and the neatly formatted tables work, is by making one object (bundle of things all together) for each table row, with each object having properties for each column name.
# Processes to look for
$Process = #("Process1", "Process2", "Process3")
$Node = Read-Host -Prompt 'Input Node name'
# Get running processes, and only keep their names
$runningProcesses = Get-Process -ComputerName $Node -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty Name
$Process | ForEach-Object {
# For each process name to look for, generate a hashtable of
# columns and their values,
# then cast it into a PS Object
[PSCustomObject]#{
'ProcessName' = $_
'Status' = if ($runningProcesses -contains $_) { "Running" } else { "Not Running" }
}
}
This gives a neat formatted table output, and is also structured data so you can feed the output of this into | ForEach-Object { $_.Status } and pick out the individual parts by name, something you can't do as neatly with your string formatted approach.
Try this:
$node = Read-Host -Prompt 'Input Node name'
$processList = "Process1", "Process2", "Process3"
$processList |
ForEach-Object {
[PsCustomObject]#{
NodeName = $node
ProcessName = $_
IsRunning = (Get-Process -Name $_ -ComputerName $node -ErrorAction SilentlyContinue | Select-Object -First 1) -ne $null
}
}
Output will be like this:
NodeName ProcessName IsRunning
-------- ----------- ---------
Node1 Process1 True
Node1 Process2 True
Node1 Process3 False

PowerShell :: How to filter worker processes list by User Name

Basically as per screen-shot there are multiple worker processes are running on machine in IIS but we need w3wp which is running under Sitecore User Username.
We tried below PS script but getting blank value in User Name column
$processlist = get-process | where {$_.cpu -gt 5 -and $_.Name -eq 'w3wp'} |select Name, #{l="User name";e={$_.getowner().user}} | ft -AutoSize
foreach($proc in $processlist){
if($proc -eq "Sitecore User" ){
C:\Users\Administrator\Documents\someexe.exe $proc.Id "C:\Users\Administrator\Documents\output.dmp"
}
}
and finally we need to perform some action on process Id.
I suggest the following PoSh-Script that should give you all the necessary info and more:
# Ensure to import the WebAdministration module
Import-Module WebAdministration
# Declare the variables
$server = "localhost"
$search = "*"
$wmiQuery=[wmisearcher]"SELECT * FROM __Namespace where NAME like 'WebAdministration' or NAME like 'MicrosoftIISv2'"
$wmiQuery.Scope.Path = "\\" + $server + "\root"
$WebNamespace = $wmiQuery.Get()
# Checking if the the server has IIS installed
if($WebNamespace -like '*WebAdministration*')
{
"IIS found on $server"
$WPlist=Get-WmiObject -NameSpace 'root\WebAdministration' -class 'WorkerProcess' -ComputerName 'LocalHost'
# Loop through the list of active IIS Worker Processes w3wp.exe and fetch the PID, AppPool Name and the startTime
forEach ($WP in $WPlist)
{
if ($WP.apppoolname -like$search)
{
write-host "Found:""PID:"$WP.processid "AppPool_Name:"$WP.apppoolname
(get-process -ID $WP.processid|select starttime)
}
}
}
Else
{
write-host"WARNING: IIS not detected."
}
Ref: https://blogs.msdn.microsoft.com/webtopics/2015/11/28/query-the-active-worker-process-information-in-iis-7-x-using-powershell/

Retrieve data from last line in vmware.log file?

I currently have a script that retrieves the last modified date of the .vmx in a VM's datastore in vCenter. I need to make changes to instead use and display the last date in the vmware.log file (located in the same datastore as the .vmx)
I'm not sure how to grab that line and convert it to a XX/XX/XXXX format. In the log file, it shows it as Dec 23 10 for example. If this is not possible, no worries. I just need to pull the last line in the log file and export it to a .csv file. Below is my current code:
add-pssnapin VMware.VimAutomation.Core
# ---------- Only modify the fields in this area -------------
$vCenter = 'qlab-copsmgr' #name of the vCenter
$dataCenter = 'Fly-away Kit' #name of the DataCenter
$outputFile = $vCenter + '-LastDateUsed.csv' #desired output file name
# ---------- No modification is needed in the below code. Do not edit -------------
$columnName = "Name,DataStore,Date Last Used" | Out-File .\$OutputFile -Encoding ascii
Connect-VIServer $vCenter -WarningAction SilentlyContinue
$vmList = Get-VM | where { $_.PowerState -eq “PoweredOff”} | select Name
$vmList = $vmList -replace 'Name : ', '' -replace '#{Name=', '' -replace '}', ''
ForEach ($VM in $vmList)
{
# Get configuration and path to vmx file
$VMconfig = Get-VM $VM | Get-View | select config
$VMXpath = $VMconfig.config.files.VMpathName
# Remove and/or replace unwanted strings
$VMXpath = $VMXpath -replace '\[','' -replace '\] ','\' -replace '#{Filename=','/' -replace '}','' -replace '/','\'
# List the vmx file in the datastore
$VMXinfo = ls vmstores:\$VCenter#443\$DataCenter\$VMXpath | Where {$_.LastWriteTime} | select -first 1 | select FolderPath, LastWriteTime
# Remove and/or replace unwanted strings
$VMXinfo = $VMXinfo -replace 'DatastoreFullPath=', '' -replace '#{', '' -replace '}', '' -replace ';', ',' -replace 'LastWriteTime=', ''
# Output vmx information to .csv file
$output = $VM + ', ' + $VMXinfo
$output
echo $output >> $OutputFile
}
I also needed to pull the last event from the vmware.log file in order to backtrack the power off time for VMs where there is no vCenter event history. I looked at file timestamps but found that some VM processes and possibly backup solutions can make them useless.
I tried reading the file in place but ran into issues with the PSDrive type not supporting Get-Content in place. So for better or worse for my solution I started with one of LucD's scripts - the 'Retrieve the logs' script from http://www.lucd.info/2011/02/27/virtual-machine-logging/ which pulls a VMs vmware.log file and copies it to local storage. I then modified it to copy the vmware.log file to a local temp folder, read the last line from the file before deleting the file and return the last line of the log as a PS object.
Note, this is slow and I'm sure my hacks to LucD's script are not elegant, but it does work and I hope if helps someone.
Note: This converts the time value from the log to a PS date object by simple piping the string timestamp from the file into Get-Date. I've read that this does not work as expected for non-US date formatting. For those outside of the US you might want to look into this or just pass the raw timestamp string from the log instead of converting it.
#Examples:
#$lastEventTime = (Get-VM -Name "SomeVM" | Get-VMLogLastEvent).EventTime
#$lastEventTime = Get-VMLogLastEvent -VM "SomeVM" -Path "C:\alternatetemp\"
function Get-VMLogLastEvent{
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)][PSObject[]]$VM,
[string]$Path=$env:TEMP
)
process{
$report = #()
foreach($obj in $VM){
if($obj.GetType().Name -eq "string"){
$obj = Get-VM -Name $obj
}
$logpath = ($obj.ExtensionData.LayoutEx.File | ?{$_.Name -like "*/vmware.log"}).Name
$dsName = $logPath.Split(']')[0].Trim('[')
$vmPath = $logPath.Split(']')[1].Trim(' ')
$ds = Get-Datastore -Name $dsName
$drvName = "MyDS" + (Get-Random)
$localLog = $Path + "\" + $obj.Name + ".vmware.log"
New-PSDrive -Location $ds -Name $drvName -PSProvider VimDatastore -Root '\' | Out-Null
Copy-DatastoreItem -Item ($drvName + ":" + $vmPath) -Destination $localLog -Force:$true
Remove-PSDrive -Name $drvName -Confirm:$false
$lastEvent = Get-Content -Path $localLog -Tail 1
Remove-Item -Path $localLog -Confirm:$false
$row = "" | Select VM, EventType, Event, EventTime
$row.VM = $obj.Name
($row.EventTime, $row.EventType, $row.Event) = $lastEvent.Split("|")
$row.EventTime = $row.EventTime | Get-Date
$report += $row
}
$report
}
}
That should cover your request, but to expound further on why I needed the detail, which reading between the lines may also benefit you, I'll continue.
I inherited hundreds of legacy VMs that have been powered off from various past acquisitions and divestitures and many of which have been moved between vCenter instances losing all event log detail. When I started my cleanup effort in just one datacenter I had over 60TB of powered off VMs. With the legacy nature of these there was also no detail available on who owned or had any knowledge of these old VMs.
For this I hacked another script I found, also from LucD here: https://communities.vmware.com/thread/540397.
This will take in all the powered off VMs, attempt to determine the time powered off via vCenter event history. I modified it to fall back to the above Get-VMLogLastEvent function to get the final poweroff time of the VM if event log detail is not available.
Error catching could be improved - this will error on VMs where for one reason or another there is no vmware.log file. But quick and dirty I've found this to work and provides the detail on what I need for over 90%.
Again this relies on the above function and for me at least the errors just fail through passing through null values. One could probably remove the errors by adding a check for vmware.log existance before attempting to copy it though this would add a touch more latency in execution due to the slow PSDrive interface to datastores.
$Report = #()
$VMs = Get-VM | Where {$_.PowerState -eq "PoweredOff"}
$Datastores = Get-Datastore | Select Name, Id
$PowerOffEvents = Get-VIEvent -Entity $VMs -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} | Group-Object -Property {$_.Vm.Name}
foreach ($VM in $VMs) {
$lastPO = ($PowerOffEvents | Where { $_.Group[0].Vm.Vm -eq $VM.Id }).Group | Sort-Object -Property CreatedTime -Descending | Select -First 1
$lastLogTime = "";
# If no event log detail, revert to vmware.log last entry which takes more time...
if (($lastPO.PoweredOffTime -eq "") -or ($lastPO.PoweredOffTime -eq $null)){
$lastLogTime = (Get-VMLogLastEvent -VM $VM).EventTime
}
$row = "" | select VMName,Powerstate,OS,Host,Cluster,Datastore,NumCPU,MemMb,DiskGb,PoweredOffTime,PoweredOffBy,LastLogTime
$row.VMName = $vm.Name
$row.Powerstate = $vm.Powerstate
$row.OS = $vm.Guest.OSFullName
$row.Host = $vm.VMHost.name
$row.Cluster = $vm.VMHost.Parent.Name
$row.Datastore = $Datastores | Where{$_.Id -eq ($vm.DatastoreIdList | select -First 1)} | Select -ExpandProperty Name
$row.NumCPU = $vm.NumCPU
$row.MemMb = $vm.MemoryMB
$row.DiskGb = Get-HardDisk -VM $vm | Measure-Object -Property CapacityGB -Sum | select -ExpandProperty Sum
$row.PoweredOffTime = $lastPO.CreatedTime
$row.PoweredOffBy = $lastPO.UserName
$row.LastLogTime = $lastLogTime
$report += $row
}
# Output to screen
$report | Sort Cluster, Host, VMName | Select VMName, Cluster, Host, NumCPU, MemMb, #{N='DiskGb';E={[math]::Round($_.DiskGb,2)}}, PoweredOffTime, PoweredOffBy | ft -a
# Output to CSV - change path/filename as appropriate
$report | Sort Cluster, Host, VMName | Export-Csv -Path "output\Powered_Off_VMs_Report.csv" -NoTypeInformation -UseCulture
Cheers!
I pray this pays back some of the karma I've used.
Meyeaard
I have made a script that checks line by line and if string is found changes it to desired format
#example input you can use get-content PATH to txt or any file and assign it to $lines variable
$lines = #"
ernfoewnfnsf
ernfoewnfnsf
Dec 23 10 sgdsgdfgsdadasd
"# -split "\r\n"
#checks line by line and if find anything that maches start of the line, one Big letter two small, space, two digits, space, two digits, space
$lines | ForEach-Object{
if ($_ -match "^[A-Z][a-z]{2}\s\d{2}\s\d{2}\s")
{
$match = [convert]::ToDateTime($matches[0])
$_ -replace $matches[0], "$($match.ToShortDateString()) " | out-file { PATH } -APPEND
}
else
{
$_ | out-file { PATH } -APPEND
}
}
just change {PATH} with a filenamePAth and this should work for you

Get current IP addresses associated with an Azure ARM VM's set of NICs via Powershell

I'm trying to write some Powershell to get a list of Azure ARM vms (Not classic) and the currently associated IP addresses for their NICs.
In classic, this was part of the VM object, but in ARM it's a seperate object, and I'm struggling to get the Powershell to work in the way I want.
I've got the following code segment:
$nic = (((Get-AzureRmVM).NetworkProfile).NetworkInterfaces).Id
ForEach ($i in $nic) {
$nicname = $i.substring($i.LastIndexOf("/")+1)
Get-AzureRmNetworkInterface -Name $nicname -ResourceGroupName RGTEST | Get-AzureRmNetworkInterfaceIpConfig | select-object PrivateIpAddress,PrivateIpAllocationMethod
}
Which works, but only for VMs in the specified resource group 'RGTEST'.
It seems that Get-AzureRmNetworkInterface can only work when passed in the NIC Name and the ResourceGroupName, but I can't seem to get the RGname from the VM to be passed in.
Probably really easy, but I'm struggling with it!
I use this code to get all my ARM VMs, their private IP address and allocation method, it works across resource groups.
$vms = get-azurermvm
$nics = get-azurermnetworkinterface | where VirtualMachine -NE $null #skip Nics with no VM
foreach($nic in $nics)
{
$vm = $vms | where-object -Property Id -EQ $nic.VirtualMachine.id
$prv = $nic.IpConfigurations | select-object -ExpandProperty PrivateIpAddress
$alloc = $nic.IpConfigurations | select-object -ExpandProperty PrivateIpAllocationMethod
Write-Output "$($vm.Name) : $prv , $alloc"
}
Sample Output:
proddc : 10.0.0.4 , Static
stagedc : 10.1.0.4 , Static
Below is the script I used to get the Private and Public IP for an Azure ARM VM. If a VM has more than one NIC or IpConfig it would probably need to use a loop.
$rg = Get-AzureRmResourceGroup -Name "MyResourceGroup01"
$vm = Get-AzureRmVM -ResourceGroupName $rg.ResourceGroupName -Name "MyVM01"
$nic = Get-AzureRmNetworkInterface -ResourceGroupName $rg.ResourceGroupName -Name $(Split-Path -Leaf $VM.NetworkProfile.NetworkInterfaces[0].Id)
$nic | Get-AzureRmNetworkInterfaceIpConfig | Select-Object Name,PrivateIpAddress,#{'label'='PublicIpAddress';Expression={Set-Variable -name pip -scope Global -value $(Split-Path -leaf $_.PublicIpAddress.Id);$pip}}
(Get-AzureRmPublicIpAddress -ResourceGroupName $rg.ResourceGroupName -Name $pip).IpAddress
#Output:
Name PrivateIpAddress PublicIpAddress
---- ---------------- ---------------
ipconfig1 10.0.0.10 MyVM01-pip
40.80.217.1
For those that are looking for a solution that works across multiple subscriptions in a tenant, here's a script that loops through each subscription and reports on each private IP, NIC, VM, Resource Group and associated subscription. The output is in object format and is exported to a CSV file.
<#
.SYNOPSIS
Returns IP addresses and associated network interfaces and virtual machines across all Azure subscriptions the
user has access to.
.DESCRIPTION
This script returns all private IP addresses, the IP configuration resources they are associated with, the network interfaces and virtual
machines across all subscriptions. This script requires:
1. The Azure module to be installed (https://learn.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-2.8.0)
2. The user to be logged in to an Azure account using Connect-AzAccount / Connect-AzureRmAccount
3. The user must have subscription wide read permissions assigned for each subscription being queried
.PARAMETER FileName
Optional. Specify the file name and path for a CSV export.
.EXAMPLE
Get-IpAddressAllocation.ps1 -FileName .\AzureIpAddressReport.csv
#>
<#
.AUTHOR
Michael Wheatfill
.LICENSEURI
https://github.com/mwheatfill/mwheatfill.github.io/blob/master/LICENSE.txt
#>
#region Parameters
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[String]
$FileName
)
#endregion Parameters
#region Initializations
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
#endregion Initializations
#region Functions
function Get-IpAddresses {
param ()
$networkInterfaces = Get-AzNetworkInterface | Where-Object {$_.VirtualMachine -ne $null}
$virtualMachines = Get-AzVM
$results = #()
foreach($interface in $networkInterfaces) {
$ipConfigurations = $interface.IpConfigurations
foreach($ipConfig in $ipConfigurations) {
$vm = $virtualMachines | Where-Object {$_.Id -eq $interface.VirtualMachine.Id}
$ipDetails = [pscustomobject]#{
PrivateIpAddress = $ipConfig.PrivateIpAddress
VMName = $vm.Name
NetworkInterface = $interface.Name
IpConfigName = $ipConfig.Name
Primary = $ipConfig.Primary
ResourceGroup = $vm.ResourceGroupName
Subscription = $subscription.Name
}
$results += $ipDetails
}
}
return $results
}
#endregion Functions
#region Main
$subscriptions = Get-AzSubscription | Select-Object
$ipAddressesInAllSubscriptions = #()
$progressCount = 0
foreach ($subscription in $subscriptions) {
$progressCount++
$progressComplete = ($progressCount / $subscriptions.count * 100)
$progressMessage = "Gathering IP address informtion for subscription $progressCount of $($subscriptions.Count)"
Write-Progress -Activity $progressMessage -Status ($subscription.Name) -PercentComplete $progressComplete
$subscription | Select-AzSubscription > $null
$ipAddressesInSubscription = Get-IpAddresses -SubscriptionObject $subscription
$ipAddressesInAllSubscriptions += $ipAddressesInSubscription
}
$ipAddressesInAllSubscriptions | Sort-Object -Property Subscription, VMName, NetworkInterface, IpConfigName, Primary | Format-Table
$ipAddressesInAllSubscriptions | Export-Csv -Path $FileName -NoTypeInformation
#endregion Main
Since this question was asked back in 2016, Microsoft decided to no longer maintain the AzureRM module past Dec 2020, and along with it, the *-AzureRM* cmdlets. The Az module replaces it going forward.
However, there's a fast alternative for retrieving the list of Azure VMs with their associated IPs (private and public ones), by using Azure Resource Graph (ARG).
Concretely, for several thousand VMs, spread across several hundred Azure subscriptions, it takes mere seconds with ARG as opposed to 20+ minutes using Az's Get-AzVM cmdlet.
The script further down will report correctly even on multiple vmNics and multiple IP configurations per vmNic. It will retrieve all ARM VM data across the Azure subscriptions in the tenant. If running from either a local Powershell session or Cloud Shell, make sure you have the Az.ResourceGraph module installed first.
Sample output on a small test Azure tenant:
The script follows:
function RunARGquery {
param (
[string[]]$SubscriptionIds,
[string]$ARG_query
)
$fullResultSet = #()
$pageSize = 5000
# Subscription batching code below taken
# from https://learn.microsoft.com/en-us/azure/governance/resource-graph/troubleshoot/general#toomanysubscription
# Create a counter, set the batch size, and prepare a variable for the results
$counter = [PSCustomObject] #{ Value = 0 }
$batchSize = 1000
# Group the subscriptions into batches
$subscriptionsBatch = $subscriptionIds | Group -Property { [math]::Floor($counter.Value++ / $batchSize) }
$currentBatchNo = 0
# Run the query for each batch
foreach ($batch in $subscriptionsBatch) {
$pagesProcessedSoFar = 0
do {
$results = #()
if($pagesProcessedSoFar -eq 0) {
$results = Search-AzGraph -Subscription $batch.Group -Query $ARG_query -First $pageSize
}
else {
$results = Search-AzGraph -Subscription $batch.Group -Query $ARG_query -First $pageSize -Skip ($pagesProcessedSoFar * $pageSize)
}
$pagesProcessedSoFar++
Write-Host "Processed $pagesProcessedSoFar pages so far. A number of $(($results | Measure-Object).count) results returned in the last page"
$fullResultSet += $results
} while(($results | Measure-Object).count -eq $pageSize)
Write-Host "Finished subscription batch $currentBatchNo"
$currentBatchNo++
}
return $fullResultSet
}
# Get the date/time now, for timestamping both output files
$currentDateTime = Get-Date -Uformat "%Y%m%d-%H%M%S"
Write-Host "Getting list of Azure subscriptions..."
# Fetch the full array of subscription IDs
$subscriptions = Get-AzSubscription
$subscriptionIds = $subscriptions.Id
Write-Host "Found $(($subscriptionIds | Measure-Object).count) subscriptions"
# ARG query from Listing 23
$ARM_ARG_query = #"
Resources
| where type =~ 'microsoft.compute/virtualmachines'
| project id, vmId = tolower(tostring(id)), vmName = name
| join (Resources
| where type =~ 'microsoft.network/networkinterfaces'
| mv-expand ipconfig=properties.ipConfigurations
| project vmId = tolower(tostring(properties.virtualMachine.id)), privateIp = ipconfig.properties.privateIPAddress, publicIpId = tostring(ipconfig.properties.publicIPAddress.id)
| join kind=leftouter (Resources
| where type =~ 'microsoft.network/publicipaddresses'
| project publicIpId = id, publicIp = properties.ipAddress
) on publicIpId
| project-away publicIpId, publicIpId1
| summarize privateIps = make_list(privateIp), publicIps = make_list(publicIp) by vmId
) on vmId
| project-away vmId, vmId1
| sort by vmName asc
"#
Write-Host "Running ARM ARG query..."
RunARGquery -SubscriptionIds $subscriptionIds -ARG_query $ARM_ARG_query `
| Select-Object -ExcludeProperty ResourceId `
| Sort-Object -Property vmName `
| Export-Csv -NoTypeInformation "AzureVMs_$currentDateTime.csv"
If you're looking to also retrieve the classic Azure VMs (ASM model), using ARG as well, a script is available here. A detailed discussion around the Azure Resource Graph queries for retrieving the VM data, throttling, permissions, etc can be found in this post.
Here's a script that I use to get relevant VM Private/Public IP Address info used for various tasks. It will run from MAC OS or Windows OS as I've a MAC with a Widows 10 Parallels VM running for compatibility. Use it as you wish.
It will export to CSV and attempt to open in Excel or whatever is registered to the CSV extension. In the example below it was saved as PS_AzureRM_Get_VMs.ps1 or just run it as raw code in PowerShell.
#Login to AZURE from PowerShell
#Below works in MAC/Linux PowerShell 6.0.1+ and Windows WMF 4.0+
#pwsh on MAC OS or powershell_ise.exe on Windows
#Connect-AzureRmAccount (Login-AzureRMAcount and Add-AzureRMAccount are the older Azure cmdlets)
# Goto URL https://microsoft.com/devicelogin and the password it provides example Q9KZ3HGN2
# You may need to select-azurermsubscription -subscriptionid $SubscriptionID #Define $SubscriptionID = 'replace everything with your actual subscription xxx-xxxx-xxx'
#Example location using the . way of running a script or just cut and paste to PowerShell
#Example location using the . way of running a script
#MAC PWSH syntax
#. ~/Documents/Scripts/AzureRM/PS_AzureRM_Get_VMs.ps1
#Windows PowerShell.exe/PowerShell_ISE.exe syntax
#. $env:userprofile\Scripts\AzureRM\PS_AzureRM_Get_VMs.ps1
$Project="DevOps"
$clientFilePrefix="AzureRM"
$clientFileCampaign="VMs"
#Get Date Time
$Date = ([DateTime]::Now).ToString("yyyyMMdd")
$Time = ([DateTime]::Now).ToString("HHmmss")
$DateStart=get-date
#Change to Windows Path if running in Windows $env:USERPROFILE
If ($($env:USERPROFILE)) {
$fldrRoot="$($env:USERPROFILE)\"
$fldrPathseparator='\'
} Else {
$fldrRoot="~/"
$fldrPathseparator='/'
}
# Make Directory if not exist
$fldrPath=$fldrRoot+"Documents"+$fldrPathseparator+$Project+$fldrPathseparator+$clientFilePrefix+$fldrPathseparator+$clientFileCampaign
New-Item -ErrorAction Ignore -ItemType directory -Path $fldrPath
#Make Imports Folder
$fldrPathimports=$fldrPath+$fldrPathseparator+"Imports"
New-Item -ErrorAction Ignore -ItemType directory -Path $fldrPathimports
#Make Exports Folder Directory
$fldrPathexports=$fldrPath+$fldrPathseparator+"Exports"
New-Item -ErrorAction Ignore -ItemType directory -Path $fldrPathexports
#Assign the variable to the export file Prefix
$VMInfo_Export=$fldrPathexports+$fldrPathseparator+$clientFilePrefix+"_"+$Project+"_"+$clientFileCampaign+"_"+$Date+"_"+$Time+".csv"
#Create a Table to use for filtering the results
$VMInfo = New-Object System.Data.DataTable
#Now Add some columns for use later
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'ResourceGroup',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'VM',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'Location',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'VM_ID',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'VM_NIC',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'IP',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'Public_IP_Name',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'Public_IP',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'IP_MAC',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'Priv_Dyn',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'Status',([String])))
$VMInfo.Columns.Add((New-Object System.Data.DataColumn 'Date_Time',([String])))
$VMInfo_Array_Count=($VMInfo | Measure-Object | Select Count).Count
#List the Array to show it='s empty
Write-Host "Created Array VMInfo with $VMInfo_Array_Count objects"
$Date_Time=([DateTime]::Now).ToString("yyyy/MM/dd")+" "+([DateTime]::Now).ToString("HH:mm:ss")
#Check the OS type
If ($($ENV:OS)) {$OSTYPE="WINDOWS";Write-Host "The OS is"$OSTYPE" Based"} Else {$OSTYPE="LINUX";Write-Host "The OS is"$OSTYPE" Based"}
#Get the VM's
$VMs = Get-AzureRmVM
$VMstatus = Get-AzureRmVM -Status
#Get the NIC and their properties for matching against the VMs
$NICs = get-azurermnetworkinterface | where VirtualMachine -NE $null #skip NICs with no VM
#Get the Public IPs for matching against the VMs
#Public IPs work only if the naming convention starts with the VM Name used in Azure
$PublicIPs=Get-AzureRmPublicIpAddress | Select-Object Name,ResourceGroupName,IpAddress
#Now Loop through the NICs in Azure and match against the VMs and the Public IPs
ForEach ($nic in $NICs)
{
#Get the VM Info
$VM = $VMs | where-object -Property Id -EQ $nic.VirtualMachine.id
$VM_Name = $($VM.name)
$VM_Location = $($VM.Location)
$VM_Resourcegroup = $($VM.ResourceGroupName)
$VM_ID = $($VM.VMid)
$VM_NIC = $nic.Name -Join ';'
$VM_Status = (($VMstatus | Where {$_.ResourceGroupName -eq $VM_Resourcegroup -and $_.Name -eq $VM_Name}).PowerState).Replace('VM ', '')
$VM_IP = ($nic.IpConfigurations | select-object -ExpandProperty PrivateIpAddress) -Join ';'
$VMPIPName = ($nic.IpConfigurations.PublicIpAddress.Id -Split '/')[-1]
$VM_PublicIP = ($PublicIPs | Where-Object {$_.ResourcegroupName -eq $VM_Resourcegroup -and $_.Name -like "$VMPIPName"} | Select IpAddress).IpAddress
$VM_IP_MAC = (($nic | Select MacAddress).MacAddress) -Join ';'
$VM_Alloc = $nic.IpConfigurations | select-object -ExpandProperty PrivateIpAllocationMethod
#Uncomment this to check the values before going into the Array $VMINFO
#Write-Output "$($VM.ResourceGroupName), $($VM.Name), $($VM.VMid), $($VM.Location), $VM_IP, $VM_PublicIP, $VM_IP_MAC, $VM_Alloc"
#Now populate the $VMInfo array
$row = $VMInfo.NewRow()
$row.'ResourceGroup'=$VM_Resourcegroup
$row.'VM'=$VM_Name
$row.'VM_ID'=$VM_ID
$row.'VM_NIC'=$VM_NIC
$row.'Location'=$VM_Location
$row.'IP'=$VM_IP
$row.'Public_IP_Name'=$VMPIPName
$row.'Public_IP'=$VM_PublicIP
$row.'IP_MAC'=$VM_IP_MAC
$row.'Priv_Dyn'=$VM_Alloc
$row.'Status'=$VM_Status
$row.'Date_Time'=$Date_Time
$VMInfo.Rows.Add($row)
}
cls
$TotalTime=(NEW-TIMESPAN –Start $DateStart –End $(GET-DATE))
Write-Host "Script Ran in $($TotalTime.Hours) hours and $($TotalTime.Minutes) minutes and $($TotalTime.Seconds) seconds"
#Export the Info
Write-Host "Exporting VMINFO Report to `n`t$($VMInfo_Export)"
$VMInfo | Export-CSV -NoTypeInformation -Path $VMInfo_Export
#Depending on OS run the Open/Start command for the CSV Export
If ($OSTYPE -eq "LINUX") {open $VMInfo_Export} `
ElseIf ($OSTYPE -eq "WINDOWS") {start $VMInfo_Export} `
Else {Write-Host "Unknown OS"}
break
##### ###### #####
####### ###### #####
## Extra Tasks to Filter the Exports
##### ###### #####
####### ###### #####
#Get the Array Size
$VMInfo_Array_Count=($VMInfo | Measure-Object | Select Count).Count
#ECHO the Array size
Write-Host "`n`n***** *****"
Write-Host "Array VMInfo has $VMInfo_Array_Count objects"
Write-Host "***** *****"
break
#Shows Configured Resource Group Names
$VMInfo_ResourceGroupNames=($vminfo | Select ResourceGroup -Unique).ResourceGroup
#ECHO Configured Resource Group Names
Write-Host "`n`n***** *****"
Write-Host "***** List of Groups*****"
Write-Host "***** *****"
$($VMInfo_ResourceGroupNames)
break
#Get DC's from resource Group Name
$VM_Environment="dtdaily"
$VMInfo_GetDCs=$vminfo | where {$_.ResourceGroup -eq $VM_Environment -and $_.VM -like "*dc*"}
#ECHO DC's from resource Group Name
Write-Host "`n`n***** *****"
Write-Host "***** List of DC's"
Write-Host "***** *****"
$($VMInfo_GetDCs)
break
#Get Public IP VMs
$VMInfo_PublicIPs=$vminfo | Where {$_.Public_IP -like "*.*"}
#ECHO Public IP VMs
Write-Host "`n`n***** *****"
Write-Host "***** *****"
Write-Host "***** List of Public IP VMs"
Write-Host "***** *****"
$($VMInfo_PublicIPs)
break
#ECHO All VMs
$VMInfo
Break
I have searched a lot and finally succeeded. Using resource group name and azure vm name, you can retrieve private or public ip address:
$Resourcegroup=""
$VmName=""
$VmNetworkdetails= (((Get-AzureRmVM -ResourceGroupName $Resourcegroup -Name $VmName).NetworkProfile).NetworkInterfaces).Id
$nicname = $VmNetworkdetails.substring($VmNetworkdetails.LastIndexOf("/")+1)
$privateIp =(Get-AzureRmNetworkInterface -Name $nicname -ResourceGroupName $Resourcegroup)|Select-Object -ExpandProperty IPConfigurations
write-host $privateIp.PrivateIpAddress