utilities/pipelines/sharedScripts/helper/Get-CrossReferencedModuleList.ps1 (151 lines of code) (raw):
#region helper functions
<#
.SYNOPSIS
Find any nested dependency recursively
.DESCRIPTION
Find any nested dependency recursively
.PARAMETER DependencyMap
Required. The map/dictionary/hashtable of dependencies to search in
.PARAMETER ResourceType
Required. The resource type to search any dependency for
.EXAMPLE
Resolve-DependencyList -DependencyMap @{ a = @('b','c'); b = @('d')} -ResourceType 'a'
Get an array of all dependencies of resource type 'a', as defined in the given DependencyMap. Would return @('b','c','d')
#>
function Resolve-DependencyList {
[CmdletBinding()]
param (
[Parameter()]
[hashtable] $DependencyMap,
[Parameter()]
[string] $ResourceType
)
$resolvedDependencies = @()
if ($DependencyMap.Keys -contains $ResourceType) {
$resolvedDependencies = $DependencyMap[$ResourceType]
foreach ($dependency in $DependencyMap[$ResourceType]) {
$resolvedDependencies += Resolve-DependencyList -DependencyMap $DependencyMap -ResourceType $dependency
}
}
$resolvedDependencies = $resolvedDependencies | Select-Object -Unique
return $resolvedDependencies
}
<#
.SYNOPSIS
Get all references of a given module template
.DESCRIPTION
Get all references of a given module template.
This includes local references, online/remote references & resource deployments
.PARAMETER ModuleTemplateFilePath
Mandatory. The path to the template to search the references for
.PARAMETER TemplateMap
Mandatory. The hashtable of templatePath-templateContent to search in
.EXAMPLE
Get-ReferenceObject -ModuleTemplateFilePath 'C:\dev\key-vault\vault\main.bicep'
Search all references for module 'key-vault\vault'
#>
function Get-ReferenceObject {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $ModuleTemplateFilePath,
[Parameter(Mandatory = $true)]
[hashtable] $TemplateMap
)
. (Join-Path (Get-Item $PSScriptRoot).Parent 'Get-LocallyReferencedFileList.ps1')
$involvedFilePaths = Get-LocallyReferencedFileList -FilePath $ModuleTemplateFilePath -TemplateMap $TemplateMap
$resultSet = @{
resourceReferences = @()
remoteReferences = @()
localPathReferences = $involvedFilePaths | Where-Object {
$involvedFilePath = $_
# We only care about module templates
(Split-Path $involvedFilePath -Leaf) -eq 'main.bicep' -and
# We only care about a direct references and no children when it comes to returning local references
(@(@($involvedFilePaths) + @($ModuleTemplateFilePath)) | Where-Object {
(Split-Path $involvedFilePath) -match ('{0}[\/|\\].+' -f [Regex]::Escape((Split-Path $_ -Parent))) # i.e., if a path has its parent in the list, kick it out
}).count -eq 0
}
}
foreach ($involvedFilePath in (@($ModuleTemplateFilePath) + @($involvedFilePaths))) {
$moduleContent = $TemplateMap[$involvedFilePath]
$resultSet.resourceReferences += @() + $moduleContent | Where-Object { $_ -match "^resource .+ '(.+?)' .+$" } | ForEach-Object { $matches[1] }
$resultSet.remoteReferences += @() + $moduleContent | Where-Object { $_ -match "^module .+ '(.+:.+?)' .+$" -or $_ -match "^import .+ '(.+:.+?)'$" } | ForEach-Object { $matches[1] }
}
return @{
resourceReferences = $resultSet.resourceReferences | Sort-Object -Culture 'en-US' -Unique
remoteReferences = $resultSet.remoteReferences | Sort-Object -Culture 'en-US' -Unique
localPathReferences = $resultSet.localPathReferences | Sort-Object -Culture 'en-US' -Unique
}
}
#endregion
<#
.SYNOPSIS
Get a list of all resource/module references in a given module path
.DESCRIPTION
As an output you will receive a hashtable that (for each provider namespace) lists the
- Directly deployed resources (e.g. via "resource myDeployment 'Microsoft.(..)/(..)@(..)'")
- Linked remote module templates (e.g. via "module rg 'br/modules:(..):(..)'")
.PARAMETER Path
Optional. The path to search in. Defaults to the 'res' folder.
.EXAMPLE
Get-CrossReferencedModuleList
Invoke the function with the default path. Returns an object such as:
{
"Compute/availabilitySets": {
"remoteReferences": [
"avm-res-network-networkinterface"
],
"resourceReferences": [
"Microsoft.Resources/deployments@2021-04-01",
"Microsoft.Compute/availabilitySets@2021-07-01",
"Microsoft.Authorization/locks@2020-05-01",
"Microsoft.Compute/availabilitySets@2021-04-01",
"Microsoft.Authorization/roleAssignments@2020-10-01-preview"
]
},
(...)
}
.EXAMPLE
Get-CrossReferencedModuleList -Path './sql'
Get only the references of the modules in folder path './sql'
#>
function Get-CrossReferencedModuleList {
[CmdletBinding()]
param (
[Parameter()]
[string] $Path = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent
)
$repoRoot = ($Path -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]')[0]
$resultSet = [ordered]@{}
# Collect data
$moduleTemplatePaths = (Get-ChildItem -Path $path -Recurse -File -Filter '*.bicep').FullName | Where-Object {
# No files inthe [/utilities/tools/] folder and none in the [/tests/] folder
$_ -notmatch '.*[\\|\/]tools[\\|\/].*|.*[\\|\/]tests[\\|\/].*'
} | Sort-Object -Culture 'en-US'
$templateMap = @{}
foreach ($moduleTemplatePath in $moduleTemplatePaths) {
$templateMap[$moduleTemplatePath] = Get-Content -Path $moduleTemplatePath
}
# Process data
foreach ($moduleTemplatePath in $moduleTemplatePaths) {
$referenceObject = Get-ReferenceObject -ModuleTemplateFilePath $moduleTemplatePath -TemplateMap $templateMap
# Convert local absolute references to relative references
$referenceObject.localPathReferences = $referenceObject.localPathReferences | ForEach-Object {
$result = $_ -replace ('{0}[\/|\\]' -f [Regex]::Escape($repoRoot)), '' # Remove root
$result = Split-Path $result -Parent # Use only folder name
$result = $result -replace '\\', '/' # Replaces slashes
return $result
}
$moduleFolderPath = Split-Path $moduleTemplatePath -Parent
## avm/res/<provider>/<resourceType>
$resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]')[2] -replace '\\', '/'
# since the moduleTemplatePath can contain folders outside of modules, skip those
if ($resourceTypeIdentifier -ne '') {
$providerNamespace = ($resourceTypeIdentifier -split '[\/|\\]')[0]
$resourceType = $resourceTypeIdentifier.Substring($providerNamespace.Length + 1)
$resultSet["$providerNamespace/$resourceType"] = $referenceObject
}
}
return $resultSet
}