Adding values by duplicated name - azure

I want to generate Azure log analytics query using powershell.
But I'm stuck on a strange problem. Not sure why, but it shows duplicated entries for few VMs.
My goal is to generate report of up time values for each VM. Here's my code:
$startime = '2019-08-01';
$endtime = '2019-08-31';
$queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId "..." -Query "let start_time=startofday(datetime('$startime'));
let end_time=endofday(datetime('$endtime'));
Heartbeat
| where TimeGenerated > start_time and TimeGenerated < end_time
| summarize heartbeat_per_hour=count() by bin_at(TimeGenerated, 1h, start_time), Computer
| extend available_per_hour=iff(heartbeat_per_hour>0, true, false)
| summarize total_available_hours=countif(available_per_hour==true) by Computer
"
$output = $QueryResults.Results | foreach-object {
[PSCustomObject]
#{
"VM Name" = ($_.Computer.split('.')[0]).ToUpper()
"VM Uptime (in Hours)" = $_.total_available_hours
}
}
In $output variable I see multiple entries for few VMs. It would be easy to just remove the duplicates, but I want to sum the values of up time for each VM occurence in order to avoid duplicate entries, but having them added at the same time.
Duplicates happen because in query result, some VMs are shown both w/out and with FQDN with different up time values, hence the split command in foreach-object command. Not sure why that happens too.

Please correct me if I'm misunderstanding the issue.
According to your code, seems that if the computer_1 name is pc_1, and computer_2 name is pc_1.azure.com, then you think them as the same computer? so you want to sum the uptime of the 2 computers together? Like pc_1 uptime is 10, pc_1.azure.com uptime is 20, the result should be vmName:pc_1 => uptime:30? right?
If that's the case, you have 2 ways to achieve that. The first way is re-write the query in kusto, the 2nd way is re-write powershell code.
Here, I just re-write your kusto query by split the computer name, like below:
let start_time=startofday(datetime('$startime'));
let end_time=endofday(datetime('$endtime'));
Heartbeat
| where TimeGenerated > start_time and TimeGenerated < end_time
| extend aa=split(Computer, ".")
| extend bb=array_slice(aa, 0, 0)
| extend my_Computer=strcat_array(bb,"")
| summarize heartbeat_per_hour=count() by bin_at(TimeGenerated, 1h, start_time), my_Computer
| extend available_per_hour=iff(heartbeat_per_hour>0, true, false)
| summarize total_available_hours=countif(available_per_hour==true) by my_Computer
The screenshot below shows the effect of split computer name:
The 2nd way to use powershell, like op mentioned in the comment:
$output = $output | Group-Object -Property "VM Name" | Select-Object -unique Name, #{L="VM Uptime (in Hours)";E={($_.group | Measure-Object -Property "VM Uptime (in Hours)" -Sum).sum}}

Related

Query number of cores per vm using AZ GRAPH

as of this moment in the organization, an automatic report is running on all resources. The report is running in POWERSHELL
and uses FORECH on FORECH to reach each machine to collect information
I was asked to optimize and refactor the script in order for it to be use the az graph search.
I manage to collect all the data, except for the number of cores per machine
Resources
| join kind=leftouter (ResourceContainers | where type=='microsoft.resources/subscriptions' | project subscriptionName=name, subscriptionId) on subscriptionId
| where type =~ 'Microsoft.Compute/virtualMachines'
| extend HUBEnabled = iff(isempty(tostring(properties.['licenseType'])), "No", "Yes")
| extend osType = tostring(properties.storageProfile.osDisk.osType)
| extend VMSize = tostring(properties.hardwareProfile.vmSize)
| extend VMStatus = tostring(properties.extended.instanceView.powerState.displayStatus)
| extend nics=array_length(properties.networkProfile.networkInterfaces)
| extend vCPUs = tostring(properties.hardwareProfile.vmSizeProperties.minVCPUCount)
| extend OSName = tostring(properties.osProfile.computerName)
| extend Location = tostring(location)
| extend vCPU = tostring(properties.hardwareProfile.vmSizeProperties)
| mv-expand nic=properties.networkProfile.networkInterfaces
| where nics == 1 or nic.properties.primary =~ 'true' or isempty(nic)
| extend nicId = tostring(nic.id)
| join kind=leftouter (
Resources
| where type =~ 'microsoft.network/networkinterfaces'
| extend ipConfigsCount=array_length(properties.ipConfigurations)
| mv-expand ipconfig=properties.ipConfigurations
| where ipConfigsCount == 1 or ipconfig.properties.primary =~ 'true'
| project nicId = id, privateIpAddress = tostring(ipconfig.properties.privateIPAddress))
| join kind=leftouter (
Resources
| where type == "microsoft.compute/virtualmachinescalesets"
)
on nicId
| project VMName = name, computer_name=OSName, VMSize, vCPU , Location, osType , VMStatus , Private_IP=privateIpAddress , HUBEnabled , subscriptionName , subscriptionId , resourceGroup
for some reason | extend vCPU = tostring(properties.hardwareProfile.vmSizeProperties) return empty value
I tried to reproduce the same in my environment.
when tried with query
vmCore=tostring(properties.hardwareProfile.vmSizeProperties.NumberOfCores)
But not able to get number of cores .
Tried with properties.hardwareProfile.vmSizeProperties
Graph query:
Resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| project name, properties.vmId, properties.storageProfile.osDisk.osType,properties.hardwareProfile.vmSize,properties.hardwareProfile.vmSizeProperties
| order by ['name'] asc
Everything like hardware profile is resulting in output except vm size properties are empty which includes number of cores property:
If i checked the VM in my environment. Its json doesn't have property separately for vmsize properties where it has to have number of cores.
So there is not a straight forward way for it through the graph query.
Try Initially check the properties and then call the number of cores for each VM.
Below command gives VM size particularly.
Command:
$Myvm= Get-AzureRmVM -ResourceGroupName myrg -Name myacctvm
Reference:
Command:
$MyVmZizeProperties = Get-AzureRmVMSize -ResourceGroupName myrg -VMName myacctVM
$NumberOfCores = $MyVmZizeProperties.NumberOfCores
Reference: get-number-of-cores-of-vm-in-azure | SO & https://learn.microsoft.com/en-us/azure/virtual-machines/resource-graph-samples?tabs=azure-cli

Combining & matching output from Get-AzureADUser, Get-AzureADSubscribedSku , Get-AzureADUserManager

Problem & what i have now
The script
comments are in norwegian btw, if they look strange lol
Connect-AzureAD
#variabel
$Users = Get-AzureADUser -All:$true | where-object { $null -ne $_.AssignedLicenses.SkuId } | Sort-Object CompanyName, UserPrincipalName| Select-Object -Property CompanyName, DisplayName, UserPrincipalName, Department, Mobile, TelephoneNumber
#formatting
$userlistTable = $Users | Format-Table
$userlistHTML = $Users | ConvertTo-Html
#outputs
$userlistHTML > out.html # ut som HTML
$userlistTable > out.txt # ut som Tabell i .txt
$userlistTable # ut som Tabell i terminal
My output as it stands right now:
CompanyName DisplayName UserPrincipalName Department Mobile TelephoneNumber
----------- ----------- ----------------- ---------- ------ ---------------
Company inc Usser Name username#website.com Callsenter 12345678 87654321
What i would like. is a table that has all the info in the output of $Users to inclide the users "SkuPartNumber".
The field u get by running the command Get-AzureADSubscribedSku | Select -Property SkuPartNumber
I would also like to get the users "manager", that u get by running Get-AzureADUserManager.
that last command uses the users Object ID to find their manager.
And to be honest, im very lost on how to combine these commands into one table.
its not the end of the world as it is right now. i could of just have multiple tables but having to manually cross reference these takes some time.
Im really not sure why these things are split into different commands to be honest. i get that a license is via 365 and not azure. but it seems a little backwards that i cant see the licenses from the command showing me all the user information. when a user class in powershell DOES infact show the sku ID. its burried within AssignedLicenses from running the command:
Get-AzureADUser | where-object -property UserPrincipalName -eq "emailhere#domain.com" | FL
This will give you among other things, this info:
AssignedLicenses : {class AssignedLicense {
DisabledPlans: System.Collections.Generic.List`1[System.String]
SkuId: 3b555118-da6a-4418-894f-7df1e2096870
}
conclusion
I know this was a long read. and if u made it this far im sorry.
any help with this would be amazing. This might be super easy to do, but im very far from a powershell wiz. thanks again for reading, and any help.
You can add additional properties to selected objects with calculated properties like Select #{label='name';expression={foo}}
$Users = Get-AzureADUser -All:$true
$Users | Where-Object { $_.AssignedLicenses.SkuId } |
Select-Object -Property UserPrincipalName, ## other properties here...
#{l='ManagerUPN';e={($_ | Get-AzureADUserManager).UserPrincipalName}},
#{l='AssignedSKUs';e={$_.AssignedLicenses.SkuId -join ';'}}
UserPrincipalName ManagerUPN AssignedSKUs
----------------- ---------- ------------
user#domain.com manager#domain.com 00000000-0000-0000-0000-000000000000;11111111-1111-1111-1111-111111111111
It can be slow to run Get-AzureADUserManager for every user, but that's how azure stores the relationships.
When you have a lot of users, it can be slightly faster to get the manager users first, then use Get-AzureADUserDirectReport -all $true to expand all the directreports in one call. The Microsoft.Graph.Users module is also a bit more lightweight

Getting Distribution Groups / Owners w/ PowerShell, but removing entries that have a null or empty owner

I have the lovely job of getting a list of Distribution Groups and their owners from Exchange Online. So far this is working great, but I need to fine tune my output to exclude groups that don't have any owners; and groups that have multiple owners (by returning only the first).
So far this has been fairly difficult as this is sort of my first foray into PShell.
Here's my code:
$job = Get-DistributionGroup | select Name,PrimarySmtpAddress, #{n= "ManagedBy"; e={$_.ManagedBy | Select-Object -First 1 | Where-Object {$_.ManagedBy.Count -eq 0} |foreach {(Get-Mailbox $_).PrimarySMTPAddress}}}
Write-Output $job | ConvertTo-Json
Here's an example of my output:
I basically only want a single string address to be returned. So a single owner of a distro group, no nulls, and only the first address in the collections. (Right now they are blank, I'm probably nuking them with my code- but usually they return 4 or so email addresses but I just want the first person)
Thanks!
By collection you mean the ManagedBy property right? If so, try with { ($_.ManagedBy | Select-Object -First 1 | Get-Mailbox).PrimarySMTPAddress } –
Santiago Squarzon
1 hour ago

Get-AzureRmConsumptionUsageDetail limited response to 1000 items

I've discovered this azure PS command Get-AzureRmConsumptionUsageDetail that as per docs is able to "get usage details of the subscription".
I performed some tests on my MSDN account with success. When tried with the Enterprise account (with many more resources) I've seen that it is always returning a max of 1000 items in all cases.
I tried to set -MaxCount to a higher than 1000 value without success.
The only workaround I see is try to identify all the resources in my subscription and query each one with the hope that no one individually has more than a thousand entries. Bad news is that I cannot do that for deleted items.
This behavior is not mentioned on the MS docs page so, any idea how this command should be properly used?
To partially solve my problem I developed a quick and dirty script that is able to ask for all the current subscription resources and then iterate through all them to get the billing details. It should be strange for a single resource to overpass the 1000 rows limit as resources are usually billed per day.
The drawbacks are the amount of api calls it has to perform and thus the time as well as that deleted resources are not included.
$resources = Get-AzureRmResource
Write-Output "Found $($resources.Count) in the subscription"
$consumption = #()
$i = $resources.Count
foreach($resource in $resources) {
$consumption += Get-AzureRmConsumptionUsageDetail -InstanceId $resource.ResourceId
$i--
Write-Output "$i - $($resource.ResourceId)"
}
$consumption | Group-Object InstanceId | %{
New-Object psobject -Property #{
ResourceGroup = ([regex]::Match($_.Name, ".*\/resource[gG]roups\/(.*?)\/.*$")).Groups[1].Value.ToUpper();
Cost = ($_.Group | Measure-Object PretaxCost -Sum).Sum
}
} | Group-Object ResourceGroup | %{
New-Object psobject -Property #{
ResourceGroup = $_.Name;
Total = ($_.Group | Measure-Object Cost -Sum).Sum
}
} | Export-Csv cost_report.csv

Removing text in a string between two characters using Powershell

I have a powershell script that runs and collects information and puts it in a .csv file. A sample of the information looks like what is listed below, with each line starting with a unique server name followed by a random unique identifier in contained a pair of ( ).
"GDR01W01SQ004 (e785754f-eeb1)","1","4","63","NY-TER-PLN-P-5N"
"GDR01L02D001 (4b889a4d-d281)","4","12","129","CO-FDP-STE-NP-5N"
I have a second powershell script that runs and takes this .csv file and its information and formats it into a report with a header and proper spacing.
Can someone please assist me with removing the text in between the ( ) as well as the ( )?
I would like the entries for each line to look like the following:
"GDR01W01SQ004","1","4","63","NY-TER-PLN-P-5N"
Thank you very much in advance!
Here is the script I have been using.
####################PowerCLI Check####################
# Verify whether the PowerCLI module is loaded, if not load it.
if ( (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -eq $null )
{
Add-PsSnapin VMware.VimAutomation.Core -ErrorAction Stop
}
################### Run Time Section ####################
#This script should be run from a server that has DNS records for all entries in vcenters.txt
$file = get-Content c:\reports\vcenter\vcenters.txt
foreach ( $server in $file) {
Connect-VIserver -Server $server
Get-VM | select Name, NumCpu, MemoryGB, ProvisionedSpaceGB, Notes | Out-Null
}
# Command for Custom Annotations.
Get-VM | select Name, NumCpu, MemoryGB, ProvisionedSpaceGB, Notes -expandproperty customfields | Export-Csv -path “c:\reports\vcenter\vcenall.csv” -NoTypeInformation
# Takes vcenall.csv and sorts only the Name and Notes columns and selects all but the custom fields. Capacity Reporting script caprep.ps1 runs against this csv.
Import-csv c:\reports\vcenter\vcenall.csv | Sort-Object Notes, Name | Select-Object Name, NumCpu, MemoryGB, ProvisionedSpaceGB, Notes |Export-csv capacity.csv -NoTypeInformation
#Used to remove domain from server name
(Get-Content capacity.csv) | ForEach-Object { $_ -replace ".domain.local", "" } | Set-Content capacity.csv
# Takes vcenall.csv and sorts only the Notes column and selects only the Name and Notes columns. Nimsoft comparison script nimcomp.ps1 runs against this csv.
Import-csv c:\reports\vcenter\vcenall.csv | Sort-Object Notes | Select-Object Name, Notes | Export-csv nimsoft.csv -NoTypeInformation
# Takes vcenall.csv and sorts only the Name columns and exports all fields. Backup/Restore comparison script bure.ps1 runs against this csv.
Import-csv c:\reports\vcenter\vcenall.csv | Sort-Object Name | Export-csv bure.csv -NoTypeInformation
I think you need to add more information but just using what you have let try this one approach
Import-Csv C:\temp\test.csv -Header server,1,2,3,4 | ForEach-Object{
$_.server = (($_.server).split("(")[0]).Trim()
$_
}
We import the csv data and assign a header. If you already have one then this parameter can be omitted.
Then we examine each row of data as an object. Change the server data by splitting it up by its spaces. If this data is for server names then it is safe to assume that that everything before the first space is the server name. This approach is dependent on the space being there. We could also use the same logic with the ( but this would be easier if the space was a guarantee.
So we update the server and then send the data back down the pipe with $_.
Sample Output
server 1 2 3 4
------ - - - -
GDR01W01SQ004 1 4 63 NY-TER-PLN-P-5N
GDR01L02D001 4 12 129 CO-FDP-STE-NP-5N
Edit based on comments
Since it is a server display name I changed the logic to split based on the "(". Also using the Split() method instead of the -split operator.

Resources