Modules/Public/InventoryModules/Hybrid/ARCServers.ps1
|
<#
.Synopsis Inventory for Azure ARC Servers .DESCRIPTION This script consolidates information for all microsoft.hybridcompute/machines and resource provider in $Resources variable. Excel Sheet Name: EvHub .Link https://github.com/thisismydemo/azure-scout/Modules/Public/InventoryModules/Hybrid/ARCServers.ps1 .COMPONENT This PowerShell Module is part of Azure Scout (AZSC). .CATEGORY Hybrid .NOTES Version: 3.6.0 First Release Date: 19th November, 2020 Authors: Claudio Merola and Renato Gregio #> <######## Default Parameters. Don't modify this ########> param($SCPath, $Sub, $Intag, $Resources, $Retirements, $Task ,$File, $SmaResources, $TableStyle, $Unsupported) If ($Task -eq 'Processing') { <######### Insert the resource extraction here ########> $arcservers = $Resources | Where-Object {$_.TYPE -eq 'microsoft.hybridcompute/machines'} <######### Insert the resource Process here ########> if($arcservers) { $tmp = foreach ($1 in $arcservers) { $ResUCount = 1 $sub1 = $SUB | Where-Object { $_.id -eq $1.subscriptionId } $data = $1.PROPERTIES $Tags = if(![string]::IsNullOrEmpty($1.tags.psobject.properties)){$1.tags.psobject.properties}else{'0'} $Retired = Foreach ($Retirement in $Retirements) { if ($Retirement.id -eq $1.id) { $Retirement } } if ($Retired) { $RetiredFeature = foreach ($Retire in $Retired) { $RetiredServiceID = $Unsupported | Where-Object {$_.Id -eq $Retired.ServiceID} $tmp0 = [pscustomobject]@{ 'RetiredFeature' = $RetiredServiceID.RetiringFeature 'RetiredDate' = $RetiredServiceID.RetirementDate } $tmp0 } $RetiringFeature = if ($RetiredFeature.RetiredFeature.count -gt 1) { $RetiredFeature.RetiredFeature | ForEach-Object { $_ + ' ,' } }else { $RetiredFeature.RetiredFeature} $RetiringFeature = [string]$RetiringFeature $RetiringFeature = if ($RetiringFeature -like '* ,*') { $RetiringFeature -replace ".$" }else { $RetiringFeature } $RetiringDate = if ($RetiredFeature.RetiredDate.count -gt 1) { $RetiredFeature.RetiredDate | ForEach-Object { $_ + ' ,' } }else { $RetiredFeature.RetiredDate} $RetiringDate = [string]$RetiringDate $RetiringDate = if ($RetiringDate -like '* ,*') { $RetiringDate -replace ".$" }else { $RetiringDate } } else { $RetiringFeature = $null $RetiringDate = $null } if($data.networkprofile.networkinterfaces.ipaddresses.count -gt 1) { $IPTemp = @() $SubnetTemp = @() foreach ($IPaddresses in $data.networkprofile.networkinterfaces.ipaddresses) { $IPTemp += $IPaddresses.address $SubnetTemp += $IPaddresses.subnet.addressprefix } $IP = if ($IPTemp.count -gt 1) { $IPTemp | ForEach-Object { $_ + ' ,' } }else { $IPTemp } $IP = [string]$IP $IP = if ($IP -like '* ,*') { $IP -replace ".$" }else { $IP } $Subnet = if ($SubnetTemp.count -gt 1) { $SubnetTemp | ForEach-Object { $_ + ' ,' } }else { $SubnetTemp } $Subnet = [string]$Subnet $Subnet = if ($Subnet -like '* ,*') { $Subnet -replace ".$" }else { $Subnet } } else { $IP = $data.networkprofile.networkinterfaces.ipaddresses.address $Subnet = $data.networkprofile.networkinterfaces.ipaddresses.subnet.addressprefix } if(![string]::IsNullOrEmpty($data.laststatuschange)) { $LastStatus = $data.laststatuschange $LastStatus = [datetime]$LastStatus $LastStatus = $LastStatus.ToString("yyyy-MM-dd HH:mm") } else { $LastStatus = $null } if(![string]::IsNullOrEmpty($data.osinstalldate)) { $InstallDate = $data.osinstalldate $InstallDate = [datetime]$InstallDate $InstallDate = $InstallDate.ToString("yyyy-MM-dd HH:mm") } else { $InstallDate = $null } # ── Phase 17.2.6: Policies Applied ─────────────────────────────── $policyCount = 'N/A' $policyCompliant = 'N/A' try { $policyUri = "/subscriptions/$($1.subscriptionId)/providers/Microsoft.PolicyInsights/policyStates/latest/queryResults?api-version=2019-10-01&`$filter=resourceId eq '$($1.id)'&`$top=100" $policyResp = Invoke-AzRestMethod -Path $policyUri -Method POST -Payload '{}' -ErrorAction SilentlyContinue if ($policyResp.StatusCode -eq 200) { $pData = $policyResp.Content | ConvertFrom-Json $states = $pData.value $policyCount = @($states).Count $nonCompliant = @($states | Where-Object { $_.complianceState -eq 'NonCompliant' }).Count $policyCompliant = if ($nonCompliant -eq 0) { 'Compliant' } else { "$nonCompliant NonCompliant" } } } catch {} # ── Phase 17.2.7: Performance Metrics ──────────────────────────── $arcAvgCpu = 'N/A' $arcAvgMem = 'N/A' try { $monEnd2 = (Get-Date).ToUniversalTime().ToString('o') $monStart2 = (Get-Date).AddDays(-7).ToUniversalTime().ToString('o') $arcCpuUri = "/subscriptions/$($1.subscriptionId)/resourceGroups/$($1.RESOURCEGROUP)/providers/Microsoft.HybridCompute/machines/$($1.NAME)/providers/microsoft.insights/metrics?api-version=2019-07-01&metricnames=cpu_usage_active×pan=$monStart2/$monEnd2&interval=P1D&aggregation=Average" $arcCpuResp = Invoke-AzRestMethod -Path $arcCpuUri -Method GET -ErrorAction SilentlyContinue if ($arcCpuResp.StatusCode -eq 200) { $arcCpuData = $arcCpuResp.Content | ConvertFrom-Json $arcCpuVals = $arcCpuData.value[0].timeseries[0].data.average | Where-Object { $_ -ne $null } if ($arcCpuVals) { $arcAvgCpu = [math]::Round(($arcCpuVals | Measure-Object -Average).Average, 1) } } } catch {} # ── Phase 17.2.9: Cost / ESU Tracking ───────────────────────────── $esuEnabled = if ($data.licenseprofile.esuprofile.servertype) { $true } else { $false } $arcMonthlyCost = 'N/A' try { $arcCostUri = "/subscriptions/$($1.subscriptionId)/providers/Microsoft.CostManagement/query?api-version=2023-03-01" $arcCostBody = @{ type = 'Usage' timeframe = 'MonthToDate' dataset = @{ granularity = 'None' filter = @{ dimensions = @{ name = 'ResourceId'; operator = 'In'; values = @($1.id) } } aggregation = @{ totalCost = @{ name = 'PreTaxCost'; function = 'Sum' } } } } | ConvertTo-Json -Depth 10 $arcCostResp = Invoke-AzRestMethod -Path $arcCostUri -Method POST -Payload $arcCostBody -ErrorAction SilentlyContinue if ($arcCostResp.StatusCode -eq 200) { $arcCostData = $arcCostResp.Content | ConvertFrom-Json $rawArcCost = $arcCostData.properties.rows[0][0] if ($rawArcCost -ne $null) { $arcMonthlyCost = [math]::Round([double]$rawArcCost, 2) } } } catch {} # ── Phase 17.2.10: Hybrid Connectivity ───────────────────────────── $proxyConfigured = if ($data.agentConfiguration.proxy.url) { $true } else { $false } $privateLinkScope = if ($data.privateLinkScopeResourceId) { ($data.privateLinkScopeResourceId -split '/')[-1] } else { 'None' } $connStatus = if ($data.status) { $data.status } else { 'N/A' } foreach ($Tag in $Tags) { $obj = @{ 'ID' = $1.id; 'Subscription' = $sub1.name; 'Resource Group' = $1.RESOURCEGROUP; 'Location' = $1.LOCATION; 'Name' = $1.NAME; 'Retiring Feature' = $RetiringFeature; 'Retiring Date' = $RetiringDate; 'Display Name' = $data.displayname; 'Domain' = $data.domainname; 'AD FQDN' = $data.adfqdn; 'DNS FQDN' = $data.dnsfqdn; 'Cloud Provider' = $data.cloudmetadata.provider; 'Manufacturer' = $data.detectedproperties.manufacturer; 'Model' = $data.detectedProperties.model; 'Processor' = $data.detectedproperties.processornames; 'Processor Count' = $data.detectedproperties.processorcount; 'Logical Core Count' = $data.detectedproperties.logicalcorecount; 'Memory (GB)' = $data.detectedproperties.totalphysicalmemoryingigabytes; 'Serial Number' = $data.detectedproperties.serialnumber; 'Asset Tag' = $data.detectedproperties.smbiosassettag; 'MS SQL Server' = $data.mssqldiscovered; 'Agent Version' = $data.agentversion; 'Status' = $data.status; 'Last Status Change' = $LastStatus; 'IP Address' = $IP; 'Subnet' = $Subnet; 'OS Name' = $data.osName; 'OS Version' = $data.osVersion; 'OS Install Date' = $InstallDate; 'Operating System' = $data.osSku; 'License Status' = $data.licenseprofile.licensestatus; 'License Channel' = $data.licenseprofile.licensechannel; 'License Type' = $data.licenseprofile.esuprofile.servertype; 'ESU Enabled' = $esuEnabled; 'Est. Monthly Cost (USD)' = $arcMonthlyCost; 'Policy Assignments' = $policyCount; 'Policy Compliance' = $policyCompliant; 'Avg CPU % (7d)' = $arcAvgCpu; 'Proxy Configured' = $proxyConfigured; 'Private Link Scope' = $privateLinkScope; 'Resource U' = $ResUCount; 'Tag Name' = [string]$Tag.Name; 'Tag Value' = [string]$Tag.Value } $obj if ($ResUCount -eq 1) { $ResUCount = 0 } } } $tmp } } <######## Resource Excel Reporting Begins Here ########> Else { <######## $SmaResources.(RESOURCE FILE NAME) ##########> if($SmaResources) { $TableName = ('ARCServer_'+(($SmaResources.'Resource U' | Measure-Object -Sum).Sum)) $Style = New-ExcelStyle -HorizontalAlignment Center -AutoSize -NumberFormat '0' $condtxt = @() #Retirement $condtxt += New-ConditionalText -Range G2:G100 -ConditionalType ContainsText $Exc = New-Object System.Collections.Generic.List[System.Object] $Exc.Add('Subscription') $Exc.Add('Resource Group') $Exc.Add('Location') $Exc.Add('Name') $Exc.Add('Display Name') $Exc.Add('Domain') $Exc.Add('Retiring Feature') $Exc.Add('Retiring Date') $Exc.Add('AD FQDN') $Exc.Add('DNS FQDN') $Exc.Add('Cloud Provider') $Exc.Add('Manufacturer') $Exc.Add('Model') $Exc.Add('Processor') $Exc.Add('Processor Count') $Exc.Add('Logical Core Count') $Exc.Add('Memory (GB)') $Exc.Add('Serial Number') $Exc.Add('Asset Tag') $Exc.Add('MS SQL Server') $Exc.Add('Agent Version') $Exc.Add('Status') $Exc.Add('Last Status Change') $Exc.Add('IP Address') $Exc.Add('Subnet') $Exc.Add('OS Name') $Exc.Add('OS Version') $Exc.Add('OS Install Date') $Exc.Add('License Status') $Exc.Add('License Channel') $Exc.Add('License Type') $Exc.Add('ESU Enabled') $Exc.Add('Est. Monthly Cost (USD)') $Exc.Add('Policy Assignments') $Exc.Add('Policy Compliance') $Exc.Add('Avg CPU % (7d)') $Exc.Add('Proxy Configured') $Exc.Add('Private Link Scope') if($InTag) { $Exc.Add('Tag Name') $Exc.Add('Tag Value') } $Exc.Add('Resource U') [PSCustomObject]$SmaResources | ForEach-Object { $_ } | Select-Object $Exc | Export-Excel -Path $File -WorksheetName 'ARC Servers' -AutoSize -MaxAutoSizeRows 100 -TableName $TableName -TableStyle $tableStyle -ConditionalText $condtxt -Style $Style } } |