Export/Public/Get-D365BCLicenseInfo.ps1
function Global:Get-D365BCLicenseInfo { [CmdletBinding()] <# .SYNOPSIS Returns the custom-objects from a license summary file in an easy to process object-format .DESCRIPTION ... .PARAMETER LicensInfoFile [string] The License summary (txt) File or BcLicense file you want to grab the information from (mandatory) .OUTPUTS Array of [PSCustomObject]@{ Type = <ObjectType> Purchased = <NumberPurchased> Assigned = <NumberAssigned> Remaining = <NumberRemaining> Ranges = <Array of [PSCustomObject]@{ Quantity = <Quantity> Start = <StartID> End = <EndID> }> } #> param( [parameter(Mandatory = $true)] [string] $LicensInfoFile ) function Get-D365BCLicenseInfoCustomAreaObjects { param( [parameter(Mandatory = $true)] [string] $SourceString, [parameter(Mandatory = $true)] [ValidateSet("Purchased", "Assigned")] [string] $Area ) $SourceString = $SourceString.Replace($Area, "").Trim() $Type = $SourceString.Substring(0, $SourceString.IndexOf(".")).Trim() $Number = [int]$SourceString.Substring($SourceString.IndexOf(".:")).Replace(".:", "").Trim() $info = [PSCustomObject]@{ Type = $Type Number = $Number } $info } function Update-ArrayCustomAreaObjects { param( [parameter(Mandatory = $true)] [ref] $ObjectArray, [parameter(Mandatory = $true)] [string] $SourceString ) # Get identifier ("Purchased" or "Assigned") from string $Identifier = $SourceString.Substring(0, $SourceString.IndexOf(" ")).Trim() # Get the actual values as an object $info = Get-D365BCLicenseInfoCustomAreaObjects -SourceString $SourceString -Area $Identifier # Find the array-entry and update it $licenseInfo = $ObjectArray.Value | Where-Object { $_.Type -eq $info.Type } if ($Identifier -eq "Purchased") { $licenseInfo.Purchased = $info.Number } else { $licenseInfo.Assigned = $info.Number } $licenseInfo.Remaining = $licenseInfo.Purchased - $licenseInfo.Assigned } function Compress-ObjectIdRanges() { param( [parameter(Mandatory = $true)] [ref] $ObjectArray ) # If there are ID ranges that are practically consecutive, but split over multiple lines, # this function will summarize them into one entry foreach ($objInfo in $ObjectArray.Value) { $newRanges = @() $iNewRanges = -1 foreach ($range in $objInfo.Ranges) { if ($prevRange.End + 1 -eq $range.Start) { $newRanges[$iNewRanges].End = $range.End $newRanges[$iNewRanges].Quantity += $range.Quantity } else { $newRanges += $range $iNewRanges++ } $prevRange = $range } $objInfo.Ranges = $newRanges } } function Get-InfoFromSummaryFile() { param( [parameter(Mandatory = $true)] [string] $LicensInfoFile, [parameter(Mandatory = $true)] $LicenseInfosObject ) # Regular Expression to identify lines in "Custom Area Objects"-block $patternCustomAreaObjects = '(Purchased|Assigned)\s(TableData|Report|Codeunit|Page|XMLPort|Query).*: \d{1,5}' # Regular Expression to identify lines in "Object Assignment"-block $patternObjectAssignment = '(TableData|Report|Codeunit|Page|XMLPort|Query)\s*?\d{1,5}\s*\d{1,5}\s*\d{1,5}' $customAreaObjectsSection = $false $objectAssignmentSection = $false foreach ($s in Get-Content -Path $LicensInfoFile) { if ($s.Trim() -eq 'Custom Area Objects') { $customAreaObjectsSection = $true } if ($s.Trim() -eq 'Object Assignment') { $customAreaObjectsSection = $false $objectAssignmentSection = $true } if ($s.Trim() -eq 'Module Objects and Permissions') { $customAreaObjectsSection = $false $objectAssignmentSection = $false Compress-ObjectIdRanges -ObjectArray ([ref]$LicenseInfosObject) } if ($customAreaObjectsSection -eq $true) { if ($s -match $patternCustomAreaObjects) { Update-ArrayCustomAreaObjects -ObjectArray ([ref]$LicenseInfosObject) -SourceString $s } } if ($objectAssignmentSection -eq $true) { if ($s -match $patternObjectAssignment) { $split = $s -split '\s+' $licenseInfosLocal = $LicenseInfosObject | Where-Object { $_.Type -eq $split[0] } $licenseInfosLocal.Ranges += [PSCustomObject]@{ Quantity = [int]$split[1] Start = [int]$split[2] End = [int]$split[3] } } } } } function Get-InfoFromBcLicenseFile() { param( [parameter(Mandatory = $true)] [string] $LicensInfoFile, [parameter(Mandatory = $true)] $LicenseInfos ) $content = Get-Content -Path $LicensInfoFile -Encoding Unicode $content = $content | Select-Object -Last 1 $content = $content.Substring(2) # Skip some weird unrecognizable chars [xml] $xml = $content foreach ($licenseInfo in $LicenseInfos) { $node = $xml.ChildNodes.PermissionCollections.pl | Where-Object { $_.t -eq $licenseInfo.Type } $node.ps.ChildNodes | Where-Object { $_.pbm -eq "31" } | ForEach-Object { $start = [int]$_.f $end = [int]$_.t $qty = $end - $start + 1 $licenseInfo.Ranges += [PSCustomObject]@{ Quantity = $qty Start = $start End = $end } } } foreach ($licenseInfo in $LicenseInfos) { $licenseInfo.Ranges = $licenseInfo.Ranges | Where-Object { $_.Start -ge 50000 -and $_.End -le 99999 } $licenseInfo.Assigned = ($licenseInfo.Ranges | Measure-Object -Property Quantity -Sum).Sum } } # Prepare array, only with types but without further values $licenseInfos = @() "TableData", "Report", "Codeunit", "Page", "XMLPort", "Query" | ForEach-Object { $licenseInfos += [PSCustomObject]@{ Type = $_ Purchased = 0 Assigned = 0 Remaining = 0 Ranges = @() } } if ($LicensInfoFile.EndsWith(".txt")) { Get-InfoFromSummaryFile -LicensInfoFile $LicensInfoFile -LicenseInfosObject $licenseInfos } else { Get-InfoFromBcLicenseFile -LicensInfoFile $LicensInfoFile -LicenseInfos $licenseInfos } $licenseInfos } Export-ModuleMember Get-D365BCLicenseInfo |