Skip to content
Merged
139 changes: 139 additions & 0 deletions utilities/tools/REST2CARML/private/module/Expand-DeploymentBlock.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<#
.SYNOPSIS
Expand the given module/resource block content with details about contained params/properties

.DESCRIPTION
Expand the given module/resource block content with details about contained params/properties

.PARAMETER DeclarationBlock
Mandatory. The declaration block to expand upon. If available, the object will get the 2 new properties 'topLevelElements' & 'nestedElements'

.PARAMETER NestedType
Mandatory. The key word that indicates nested-elements. Can be 'params' or 'properties'

.EXAMPLE
Expand-DeploymentBlock -DeclarationBlock @{ startIndex = 173; endIndex = 183; content = @( 'resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {', ' name: name', ' location: location', (..)) } -NestedType 'properties'
#>
function Expand-DeploymentBlock {

[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[hashtable] $DeclarationBlock,

[Parameter(Mandatory = $true)]
[ValidateSet('properties', 'params')]
[string] $NestedType
)

$topLevelIndent = Get-LineIndentation -Line $DeclarationBlock.content[1]
$relevantProperties = $DeclarationBlock.content | Where-Object { (Get-LineIndentation $_) -eq $topLevelIndent -and $_ -notlike "*$($NestedType): {*" -and $_ -like '*:*' }
$topLevelElementNames = $relevantProperties | ForEach-Object { ($_ -split ':')[0].Trim() }

####################################
## Collect top level elements ##
####################################
$topLevelElements = @()
foreach ($topLevelElementName in $topLevelElementNames) {

# Find start index of element
$relativeElementStartIndex = 1
for ($index = $relativeElementStartIndex; $index -lt $DeclarationBlock.content.Count; $index++) {
if ($DeclarationBlock.content[$index] -match ("^\s{$($topLevelIndent)}$($topLevelElementName):.+$" )) {
$relativeElementStartIndex = $index
break
}
}

# Find end index of element
$isPropertyOrClosing = "^\s{$($topLevelIndent)}\w+:.+$|^}$"
if ($DeclarationBlock.content[$index + 1] -notmatch $isPropertyOrClosing) {
# If the next line is not another element/property/param, it's a multi-line declaration
$relativeElementEndIndex = $relativeElementStartIndex
while ($DeclarationBlock.content[($relativeElementEndIndex + 1)] -notmatch $isPropertyOrClosing) {
$relativeElementEndIndex++
}
} else {
$relativeElementEndIndex = $relativeElementStartIndex
}

# Build result
$topLevelElements += @{
name = $topLevelElementName
content = $DeclarationBlock.content[$relativeElementStartIndex..$relativeElementEndIndex]
}
}

$DeclarationBlock['topLevelElements'] = $topLevelElements

#################################
## Collect nested elements ##
#################################
if (($DeclarationBlock.content | Where-Object { $_ -match "^\s*$($NestedType): \{\s*$" }).count -gt 0) {

# Find start index of nested block
# --------------------------------
$propertiesStartIndex = 1
for ($index = $propertiesStartIndex; $index -lt $DeclarationBlock.content.Count; $index++) {
if ($DeclarationBlock.Content[$index] -match "^\s*$($NestedType): \{\s*$") {
$propertiesStartIndex = $index
break
}
}

# Find end index of nested block
# ------------------------------
$propertiesEndIndex = $propertiesStartIndex
for ($index = $propertiesEndIndex; $index -lt $DeclarationBlock.content.Count; $index++) {
if ((Get-LineIndentation -Line $DeclarationBlock.Content[$index]) -eq $topLevelIndent -and $DeclarationBlock.Content[$index].Trim() -eq '}') {
$propertiesEndIndex = $index
break
}
}

# Process nested block
# --------------------
if ($DeclarationBlock.content[$propertiesStartIndex] -like '*{*}*' -or $DeclarationBlock.content[$propertiesStartIndex + 1].Trim() -eq '}') {
# Empty properties block. Can be skipped.
$DeclarationBlock['nestedElements'] = @()
} else {
$nestedIndent = Get-LineIndentation -Line $DeclarationBlock.content[($propertiesStartIndex + 1)]
$relevantNestedProperties = $DeclarationBlock.content[($propertiesStartIndex + 1) .. ($propertiesEndIndex - 1)] | Where-Object { (Get-LineIndentation $_) -eq $nestedIndent -and $_ -match '^\s*\w+:.*' }
$nestedPropertyNames = $relevantNestedProperties | ForEach-Object { ($_ -split ':')[0].Trim() }

# Collect full data block
$nestedElements = @()
foreach ($nestedPropertyName in $nestedPropertyNames) {

# Find start index of poperty
$relativeElementStartIndex = 1
for ($index = $relativeElementStartIndex; $index -lt $DeclarationBlock.content.Count; $index++) {
if ($DeclarationBlock.content[$index] -match ("^\s{$($nestedIndent)}$($nestedPropertyName):.+$" )) {
$relativeElementStartIndex = $index
break
}
}

# Find end index of poperty
$isPropertyOrClosing = "^\s{$($nestedIndent)}\w+:.+$|^\s{$($topLevelIndent)}}$"
if ($DeclarationBlock.content[$index + 1] -notmatch $isPropertyOrClosing) {
# If the next line is not another element/property/param, it's a multi-line declaration
$relativeElementEndIndex = $relativeElementStartIndex
while ($DeclarationBlock.content[($relativeElementEndIndex + 1)] -notmatch $isPropertyOrClosing) {
$relativeElementEndIndex++
}
} else {
$relativeElementEndIndex = $relativeElementStartIndex
}

# Build result
$nestedElements += @{
name = $nestedPropertyName
content = $DeclarationBlock.content[$relativeElementStartIndex..$relativeElementEndIndex]
}
}

$DeclarationBlock['nestedElements'] = $nestedElements
}
}
}
111 changes: 111 additions & 0 deletions utilities/tools/REST2CARML/private/module/Read-DeclarationBlock.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<#
.SYNOPSIS
Find all instances of a Bicep delcaration block (e.g. 'param') in the given template file

.DESCRIPTION
Find all instances of a Bicep delcaration block (e.g. 'param') in the given template file. Returns an array of all ocurrences, including their content, start- & end-index

.PARAMETER DeclarationContent
Mandatory. The Bicep template content to search in

.PARAMETER DeclarationType
Mandatory. The declaration type to search for. Can be 'param', 'var', 'resource', 'module', or 'output'

.EXAMPLE
Read-DeclarationBlock -DeclarationContent @('targetScope = 'subscription', '', '@description('Some Description'), param description string, '', (..)) -DeclarationType 'param

Get all 'param' declaration blocks of the given declaration content.
#>
function Read-DeclarationBlock {

[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[object[]] $DeclarationContent,

[Parameter(Mandatory = $true)]
[ValidateSet('param', 'var', 'resource', 'module', 'output')]
[string] $DeclarationType
)

######################################################################################################################################
## Define which key-words indicate a new declaration (depending on whether you'd move up in the template, or down line by line) ##
######################################################################################################################################

# Any keyword when moving from the current line up to indicate that it's a new declaration
$newLogicIdentifiersUp = @(
'^targetScope.+$',
'^param .+$',
'^var .+$',
'^resource .+$',
'^module .+$',
'^output .+$',
'^//.*$'
'^\s*$'
)
# Any keyword when moving from the current line down to indicate that it's a new declaration
$newLogicIdentifiersDown = @(
'^param .+$',
'^var .+$',
'^resource .+$',
'^module .+$',
'^output .+$',
'^//.*$',
'^@.+$',
'^\s*$'
)

#########################################
## Find all indexes to investigate ##
#########################################
$existingBlocks = @()

$declarationIndexes = @()
for ($index = 0; $index -lt $DeclarationContent.Count; $index++) {
if ($DeclarationContent[$index] -like "$DeclarationType *") {
$declarationIndexes += $index
}
}

########################################################################################################################
## Process each found index and find its element's start- & end-index (i.e., where its declaration starts & ends) ##
########################################################################################################################
foreach ($declarationIndex in $declarationIndexes) {
switch ($DeclarationType) {
{ $PSItem -in @('param', 'output') } {
# Let's go 'up' until the declarations end
$declarationStartIndex = $declarationIndex
while ($DeclarationContent[$declarationStartIndex] -eq $DeclarationContent[$declarationIndex] -or (($newLogicIdentifiersUp | Where-Object { $DeclarationContent[$declarationStartIndex] -match $_ }).Count -eq 0 -and $declarationStartIndex -ne 0)) {
$declarationStartIndex--
}
# Logic always counts one too far
$declarationStartIndex++

# The declaration line is always the last line of the block
$declarationEndIndex = $declarationIndex
break
}
{ $PSItem -in @('var', 'resource', 'module') } {
# The declaration line is always the first line of the block
$declarationStartIndex = $declarationIndex

# Let's go 'down' until the var declarations end
$declarationEndIndex = $declarationIndex
while ($DeclarationContent[$declarationEndIndex] -eq $DeclarationContent[$declarationIndex] -or (($newLogicIdentifiersDown | Where-Object { $DeclarationContent[$declarationEndIndex] -match $_ }).Count -eq 0 -and $declarationEndIndex -ne $DeclarationContent.Count)) {
$declarationEndIndex++
}
# Logic always counts one too far
$declarationEndIndex--
break
}
}

$existingBlocks += @{
content = $templateContent[$declarationStartIndex .. $declarationEndIndex]
startIndex = $declarationStartIndex
endIndex = $declarationEndIndex
}
}

return $existingBlocks
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function Set-ModuleTemplate {
begin {
Write-Debug ('{0} entered' -f $MyInvocation.MyCommand)

$templatePath = Join-Path $script:repoRoot 'modules' $FullResourceType 'deploy.bicep'
$templateFilePath = Join-Path $script:repoRoot 'modules' $FullResourceType 'deploy.bicep'
$providerNamespace = ($FullResourceType -split '/')[0]
$resourceType = $FullResourceType -replace "$providerNamespace/", ''
}
Expand All @@ -59,6 +59,9 @@ function Set-ModuleTemplate {
## Collect Data #
#####################

# Existing template (if any)
$existingTemplateContent = Resolve-ExistingTemplateContent -TemplateFilePath $templateFilePath

# Collect child-resource information
$linkedChildren = $fullmoduleData | Where-Object {
# Is nested
Expand Down Expand Up @@ -172,6 +175,7 @@ function Set-ModuleTemplate {

# Create collected parameters
# ---------------------------
# TODO: Only update parameters that are not already defines
# First the required
foreach ($parameter in ($parametersToAdd | Where-Object { $_.required } | Sort-Object -Property 'Name')) {
$templateContent += Get-FormattedModuleParameter -ParameterData $parameter
Expand Down Expand Up @@ -385,7 +389,7 @@ function Set-ModuleTemplate {

# Update file
# -----------
Set-Content -Path $templatePath -Value ($templateContent | Out-String).TrimEnd() -Force
Set-Content -Path $templateFilePath -Value ($templateContent | Out-String).TrimEnd() -Force
}

end {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<#
.SYNOPSIS
Retrieve indentation of a line.

.DESCRIPTION
Retrieve indentation of a line.

.PARAMETER Line
Mandatory. The line to analyse for indentation.

.EXAMPLE
Get-LineIndentation -Line ' Test'

Retrieve indentation of line ' Test'. Would return 4.
#>
function Get-LineIndentation {

[CmdletBinding()]
param (
[Parameter(Mandatory = $false)]
[string] $Line
)

begin {
Write-Debug ('{0} entered' -f $MyInvocation.MyCommand)
}

process {
$indentation = 0
for ($i = 0; $i -lt $Line.Length; $i++) {
$Char = $Line[$i]
switch -regex ($Char) {
'`t' {
$indentation += 2
}
' ' {
$indentation += 1
}
default {
return $indentation
}
}
}
return $indentation
}

end {
Write-Debug ('{0} exited' -f $MyInvocation.MyCommand)
}
}
Loading