From 5728aee051f0f5447b65da39f30534ec651f970c Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 07:17:29 +0100 Subject: [PATCH 01/14] Update to latest --- .../Resolve-ExistingTemplateContent.ps1 | 19 +++++++++++++++++++ .../private/module/Set-ModuleTemplate.ps1 | 8 ++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 diff --git a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 new file mode 100644 index 0000000000..df4b545345 --- /dev/null +++ b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 @@ -0,0 +1,19 @@ +function Resolve-ExistingTemplateContent { + + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string] $TemplateFilePath + ) + + if (-not (Test-Path $TemplateFilePath)) { + return + } + + $existingTemplateContent = Get-Content -Path $TemplateFilePath + + $existingParametersBlock = @() + $existingVariablesBlock = @() + $existingdeploymentBlock = @() + $existingOutputsBlock = @() +} diff --git a/utilities/tools/REST2CARML/private/module/Set-ModuleTemplate.ps1 b/utilities/tools/REST2CARML/private/module/Set-ModuleTemplate.ps1 index 46973d6a0d..0dc5b4a72a 100644 --- a/utilities/tools/REST2CARML/private/module/Set-ModuleTemplate.ps1 +++ b/utilities/tools/REST2CARML/private/module/Set-ModuleTemplate.ps1 @@ -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/", '' } @@ -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 @@ -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 @@ -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 { From b7bc8fbd1889912bbcf2042ef8bb442509ee7c56 Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 09:11:19 +0100 Subject: [PATCH 02/14] Update to latest --- .../Resolve-ExistingTemplateContent.ps1 | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 index df4b545345..02bbb9220d 100644 --- a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 +++ b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 @@ -10,10 +10,68 @@ return } - $existingTemplateContent = Get-Content -Path $TemplateFilePath + $templateContent = Get-Content -Path $TemplateFilePath + # 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+$' + ) + + ############################ + ## Extract Parameters ## + ############################ $existingParametersBlock = @() + + $paramIndexes = @() + for ($index = 0; $index -lt $templateContent.Count; $index++) { + if ($templateContent[$index] -like 'param *') { + $paramIndexes += $index + } + } + + foreach ($paramIndex in $paramIndexes) { + + # Let's go 'up' until the param declarations end + $paramStartIndex = $paramIndex + while ($templateContent[$paramStartIndex] -eq $templateContent[$paramIndex] -or (($newLogicIdentifiersUp | Where-Object { $templateContent[$paramStartIndex] -match $_ }).Count -eq 0 -and $paramStartIndex -ne 0)) { + $paramStartIndex-- + } + # Logic always counts one too far + $paramStartIndex++ + + # The param line is always the last line of a param + $paramEndIndex = $paramIndex + + $existingParametersBlock += @{ + content = $templateContent[$paramStartIndex .. $paramIndex] + startIndex = $paramStartIndex + endIndex = $paramEndIndex + } + } + + ########################### + ## Extract Variables ## + ########################### $existingVariablesBlock = @() $existingdeploymentBlock = @() $existingOutputsBlock = @() } +Resolve-ExistingTemplateContent -TemplateFilePath 'C:\dev\ip\Azure-ResourceModules\ResourceModules\modules\Microsoft.Storage\storageAccounts\deploy.bicep' From d51ca049307f911b0b41a66957c9b5f992b90317 Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 09:40:24 +0100 Subject: [PATCH 03/14] Added var extraction --- .../Resolve-ExistingTemplateContent.ps1 | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 index 02bbb9220d..608d894ba0 100644 --- a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 +++ b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 @@ -32,7 +32,7 @@ '^output .+$', '^//.*$', '^@.+$', - '^\s+$' + '^\s*$' ) ############################ @@ -61,7 +61,7 @@ $paramEndIndex = $paramIndex $existingParametersBlock += @{ - content = $templateContent[$paramStartIndex .. $paramIndex] + content = $templateContent[$paramStartIndex .. $paramEndIndex] startIndex = $paramStartIndex endIndex = $paramEndIndex } @@ -71,7 +71,44 @@ ## Extract Variables ## ########################### $existingVariablesBlock = @() + + $varIndexes = @() + for ($index = 0; $index -lt $templateContent.Count; $index++) { + if ($templateContent[$index] -like 'var *') { + $varIndexes += $index + } + } + + foreach ($varIndex in $varIndexes) { + + # The var line is always the first line of a var + $varStartIndex = $varIndex + + + # Let's go 'down' until the var declarations end + $varEndIndex = $varIndex + while ($templateContent[$varEndIndex] -eq $templateContent[$varIndex] -or (($newLogicIdentifiersDown | Where-Object { $templateContent[$varEndIndex] -match $_ }).Count -eq 0 -and $varEndIndex -ne $templateContent.Count)) { + $varEndIndex++ + } + # Logic always counts one too far + $varEndIndex-- + + $existingVariablesBlock += @{ + content = $templateContent[$varStartIndex .. $varEndIndex] + startIndex = $varStartIndex + endIndex = $varEndIndex + } + } + + ########################### + ## Extract Resources ## + ########################### $existingdeploymentBlock = @() + + + ######################### + ## Extract Outputs ## + ######################### $existingOutputsBlock = @() } Resolve-ExistingTemplateContent -TemplateFilePath 'C:\dev\ip\Azure-ResourceModules\ResourceModules\modules\Microsoft.Storage\storageAccounts\deploy.bicep' From d75c230ad1b3aa36f87b35237aee9999fced4879 Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 09:48:01 +0100 Subject: [PATCH 04/14] Added outputs extraction --- .../Resolve-ExistingTemplateContent.ps1 | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 index 608d894ba0..701a984f24 100644 --- a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 +++ b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 @@ -38,7 +38,7 @@ ############################ ## Extract Parameters ## ############################ - $existingParametersBlock = @() + $existingParameterBlocks = @() $paramIndexes = @() for ($index = 0; $index -lt $templateContent.Count; $index++) { @@ -60,7 +60,7 @@ # The param line is always the last line of a param $paramEndIndex = $paramIndex - $existingParametersBlock += @{ + $existingParameterBlocks += @{ content = $templateContent[$paramStartIndex .. $paramEndIndex] startIndex = $paramStartIndex endIndex = $paramEndIndex @@ -70,7 +70,7 @@ ########################### ## Extract Variables ## ########################### - $existingVariablesBlock = @() + $existingVariableBlocks = @() $varIndexes = @() for ($index = 0; $index -lt $templateContent.Count; $index++) { @@ -93,7 +93,7 @@ # Logic always counts one too far $varEndIndex-- - $existingVariablesBlock += @{ + $existingVariableBlocks += @{ content = $templateContent[$varStartIndex .. $varEndIndex] startIndex = $varStartIndex endIndex = $varEndIndex @@ -109,6 +109,40 @@ ######################### ## Extract Outputs ## ######################### - $existingOutputsBlock = @() + $existingOutputBlocks = @() + + $outputIndexes = @() + for ($index = 0; $index -lt $templateContent.Count; $index++) { + if ($templateContent[$index] -like 'output *') { + $outputIndexes += $index + } + } + + foreach ($outputIndex in $outputIndexes) { + + # Let's go 'up' until the output declarations end + $outputStartIndex = $outputIndex + while ($templateContent[$outputStartIndex] -eq $templateContent[$outputIndex] -or (($newLogicIdentifiersUp | Where-Object { $templateContent[$outputStartIndex] -match $_ }).Count -eq 0 -and $outputStartIndex -ne 0)) { + $outputStartIndex-- + } + # Logic always counts one too far + $outputStartIndex++ + + # The output line is always the last line of a output + $outputEndIndex = $outputIndex + + $existingOutputBlocks += @{ + content = $templateContent[$outputStartIndex .. $outputEndIndex] + startIndex = $outputStartIndex + endIndex = $outputEndIndex + } + } + + return @{ + parameters = $existingParameterBlocks + variables = $existingVariableBlocks + resources = $existingVariableBlocks + outputs = $existingOutputBlocks + } } Resolve-ExistingTemplateContent -TemplateFilePath 'C:\dev\ip\Azure-ResourceModules\ResourceModules\modules\Microsoft.Storage\storageAccounts\deploy.bicep' From 571cb6ff43203804b0201f225ca57274140eb18d Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 12:20:35 +0100 Subject: [PATCH 05/14] Update to latest --- .../private/module/Read-DeclarationBlock.ps1 | 101 +++++++++++++++ .../Resolve-ExistingTemplateContent.ps1 | 120 ++---------------- 2 files changed, 111 insertions(+), 110 deletions(-) create mode 100644 utilities/tools/REST2CARML/private/module/Read-DeclarationBlock.ps1 diff --git a/utilities/tools/REST2CARML/private/module/Read-DeclarationBlock.ps1 b/utilities/tools/REST2CARML/private/module/Read-DeclarationBlock.ps1 new file mode 100644 index 0000000000..ab67702eaf --- /dev/null +++ b/utilities/tools/REST2CARML/private/module/Read-DeclarationBlock.ps1 @@ -0,0 +1,101 @@ +<# +.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 + ) + + # 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*$' + ) + + $existingBlocks = @() + + $declarationIndexes = @() + for ($index = 0; $index -lt $DeclarationContent.Count; $index++) { + if ($DeclarationContent[$index] -like "$DeclarationType *") { + $declarationIndexes += $index + } + } + + 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 +} diff --git a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 index 701a984f24..b9401138ff 100644 --- a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 +++ b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 @@ -12,136 +12,36 @@ $templateContent = Get-Content -Path $TemplateFilePath - # 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*$' - ) - ############################ ## Extract Parameters ## ############################ - $existingParameterBlocks = @() - - $paramIndexes = @() - for ($index = 0; $index -lt $templateContent.Count; $index++) { - if ($templateContent[$index] -like 'param *') { - $paramIndexes += $index - } - } - - foreach ($paramIndex in $paramIndexes) { - - # Let's go 'up' until the param declarations end - $paramStartIndex = $paramIndex - while ($templateContent[$paramStartIndex] -eq $templateContent[$paramIndex] -or (($newLogicIdentifiersUp | Where-Object { $templateContent[$paramStartIndex] -match $_ }).Count -eq 0 -and $paramStartIndex -ne 0)) { - $paramStartIndex-- - } - # Logic always counts one too far - $paramStartIndex++ - - # The param line is always the last line of a param - $paramEndIndex = $paramIndex - - $existingParameterBlocks += @{ - content = $templateContent[$paramStartIndex .. $paramEndIndex] - startIndex = $paramStartIndex - endIndex = $paramEndIndex - } - } + $existingParameterBlocks = Read-DeclarationBlock -DeclarationContent $templateContent -DeclarationType 'param' ########################### ## Extract Variables ## ########################### - $existingVariableBlocks = @() - - $varIndexes = @() - for ($index = 0; $index -lt $templateContent.Count; $index++) { - if ($templateContent[$index] -like 'var *') { - $varIndexes += $index - } - } - - foreach ($varIndex in $varIndexes) { - - # The var line is always the first line of a var - $varStartIndex = $varIndex - - - # Let's go 'down' until the var declarations end - $varEndIndex = $varIndex - while ($templateContent[$varEndIndex] -eq $templateContent[$varIndex] -or (($newLogicIdentifiersDown | Where-Object { $templateContent[$varEndIndex] -match $_ }).Count -eq 0 -and $varEndIndex -ne $templateContent.Count)) { - $varEndIndex++ - } - # Logic always counts one too far - $varEndIndex-- - - $existingVariableBlocks += @{ - content = $templateContent[$varStartIndex .. $varEndIndex] - startIndex = $varStartIndex - endIndex = $varEndIndex - } - } + $existingVariableBlocks = Read-DeclarationBlock -DeclarationContent $templateContent -DeclarationType 'var' ########################### ## Extract Resources ## ########################### - $existingdeploymentBlock = @() + $existingResourceBlocks = Read-DeclarationBlock -DeclarationContent $templateContent -DeclarationType 'resource' + ######################### + ## Extract Modules ## + ######################### + $existingModuleBlocks = Read-DeclarationBlock -DeclarationContent $templateContent -DeclarationType 'module' ######################### ## Extract Outputs ## ######################### - $existingOutputBlocks = @() - - $outputIndexes = @() - for ($index = 0; $index -lt $templateContent.Count; $index++) { - if ($templateContent[$index] -like 'output *') { - $outputIndexes += $index - } - } - - foreach ($outputIndex in $outputIndexes) { - - # Let's go 'up' until the output declarations end - $outputStartIndex = $outputIndex - while ($templateContent[$outputStartIndex] -eq $templateContent[$outputIndex] -or (($newLogicIdentifiersUp | Where-Object { $templateContent[$outputStartIndex] -match $_ }).Count -eq 0 -and $outputStartIndex -ne 0)) { - $outputStartIndex-- - } - # Logic always counts one too far - $outputStartIndex++ - - # The output line is always the last line of a output - $outputEndIndex = $outputIndex - - $existingOutputBlocks += @{ - content = $templateContent[$outputStartIndex .. $outputEndIndex] - startIndex = $outputStartIndex - endIndex = $outputEndIndex - } - } + $existingOutputBlocks = Read-DeclarationBlock -DeclarationContent $templateContent -DeclarationType 'output' return @{ parameters = $existingParameterBlocks variables = $existingVariableBlocks - resources = $existingVariableBlocks + resources = $existingResourceBlocks + modules = $existingModuleBlocks outputs = $existingOutputBlocks } } From 16caaf8f1fa724f2afba601f61601d5821456b97 Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 13:03:58 +0100 Subject: [PATCH 06/14] Extended resource extraction function --- .../Resolve-ExistingTemplateContent.ps1 | 123 +++++++++++++++++- .../private/shared/Get-LineIndentation.ps1 | 39 ++++++ 2 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 diff --git a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 index b9401138ff..8dbec83f31 100644 --- a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 +++ b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 @@ -1,8 +1,64 @@ -function Resolve-ExistingTemplateContent { +<# +.SYNOPSIS +Get details about all parameters, variables, resources, modules & outputs in the given template + +.DESCRIPTION +Get details about all parameters, variables, resources, modules & outputs in the given template. Depending on the type of declaration, you further get information like names, types, nested properties, etc. + +.PARAMETER TemplateFilePath +Mandatory. The path of the template to extract the data from. + +.EXAMPLE +Resolve-ExistingTemplateContent -TemplateFilePath 'C:/dev/Microsoft.Storage/storageAccounts/deploy.bicep' + +Get all the requested information from the template in path 'C:/dev/Microsoft.Storage/storageAccounts/deploy.bicep'. Returns an object like: + +Name Value +---- ----- +outputs {resourceId, name, resourceGroupName, primaryBlobEndpoint…} +modules {System.Collections.Hashtable, System.Collections.Hashtable, System.Collections.Hashtable, System.Collections.Hashtable…} +variables {diagnosticsMetrics, supportsBlobService, supportsFileService, identityType…} +resources {System.Collections.Hashtable, System.Collections.Hashtable, System.Collections.Hashtable, System.Collections.Hashtable…} +parameters {name, location, roleAssignments, systemAssignedIdentity…} + +And if you drill down, for resources an array like: + +Name Value +---- ----- +startIndex 173 +endIndex 183 +content {resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) {, name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment… +nestedProperties {mode, template} +topLevelProperties name + +startIndex 185 +endIndex 188 +content {resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = if (!empty(cMKKeyVaultResourceId)) {, name: last(split(cMKKeyVaultResourceId, '/')), scope: resourc… +topLevelProperties {name, scope} + +startIndex 190 +endIndex 240 +content {resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {, name: name, location: location, kind: storageAccountKind…} +nestedProperties {encryption, accessTier, supportsHttpsTrafficOnly, isHnsEnabled…} +topLevelProperties {name, location, kind, sku…} + +startIndex 242 +endIndex 252 +content {resource storageAccount_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId))… +nestedProperties {storageAccountId, workspaceId, eventHubAuthorizationRuleId, eventHubName…} +topLevelProperties {name, scope} + +startIndex 254 +endIndex 261 +content {resource storageAccount_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) {, name: '${storageAccount.name}-${lock}-lock', properties: {, level: any(lock)… +nestedProperties {level, notes} +topLevelProperties {name, scope} +#> +function Resolve-ExistingTemplateContent { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $TemplateFilePath ) @@ -17,26 +73,88 @@ ############################ $existingParameterBlocks = Read-DeclarationBlock -DeclarationContent $templateContent -DeclarationType 'param' + # Analyze content + foreach ($block in $existingParameterBlocks) { + $block['name'] = (($block.content | Where-Object { $_ -like 'param *' }) -split ' ')[1] + $block['type'] = (($block.content | Where-Object { $_ -like 'param *' }) -split ' ')[2] + } + ########################### ## Extract Variables ## ########################### $existingVariableBlocks = Read-DeclarationBlock -DeclarationContent $templateContent -DeclarationType 'var' + # Analyze content + foreach ($block in $existingVariableBlocks) { + $block['name'] = (($block.content | Where-Object { $_ -like 'var *' }) -split ' ')[1] + } + ########################### ## Extract Resources ## ########################### $existingResourceBlocks = Read-DeclarationBlock -DeclarationContent $templateContent -DeclarationType 'resource' + # Analyze content + foreach ($block in $existingResourceBlocks) { + + $topLevelIndent = Get-LineIndentation -Line $block.content[1] + $relevantProperties = $block.content | Where-Object { (Get-LineIndentation $_) -eq $topLevelIndent -and $_ -notlike '*properties: {*' -and $_ -like '*:*' } + + $block['topLevelProperties'] = $relevantProperties | ForEach-Object { ($_ -split ':')[0].Trim() } + + if (($block.content | Where-Object { $_ -match '\s*properties:.+' }).count -gt 0) { + $propertiesStartIndex = 1 + for ($index = $propertiesStartIndex; $index -lt $block.content.Count; $index++) { + if ($block.Content[$index] -match '^\s*properties: {.*') { + $propertiesStartIndex = $index + break + } + } + + $propertiesEndIndex = $propertiesStartIndex + for ($index = $propertiesEndIndex; $index -lt $block.content.Count; $index++) { + if ((Get-LineIndentation -Line $block.Content[$index]) -eq $topLevelIndent -and $block.Content[$index].Trim() -eq '}') { + $propertiesEndIndex = $index + break + } + } + + if ($block.content[$propertiesStartIndex] -like '*{*}*' -or $block.content[$propertiesStartIndex + 1].Trim() -eq '}') { + # Empty properties block. Can be skipped + $block['nestedProperties'] = @() + } else { + $nestedIndent = Get-LineIndentation -Line $block.content[($propertiesStartIndex + 1)] + $relevantNestedProperties = $block.content[($propertiesStartIndex + 1) .. ($propertiesEndIndex - 1)] | Where-Object { (Get-LineIndentation $_) -eq $nestedIndent -and $_ -match '^\s*\w+:.*' } + $block['nestedProperties'] = $relevantNestedProperties | ForEach-Object { ($_ -split ':')[0].Trim() } + } + } + } + ######################### ## Extract Modules ## ######################### $existingModuleBlocks = Read-DeclarationBlock -DeclarationContent $templateContent -DeclarationType 'module' + # Analyze content + foreach ($block in $existingModuleBlocks) { + $block['params'] = $block.content[3..($block.content.count - 3)] | ForEach-Object { ($_ -split ':')[0].Trim() } + } + + ######################### ## Extract Outputs ## ######################### $existingOutputBlocks = Read-DeclarationBlock -DeclarationContent $templateContent -DeclarationType 'output' + foreach ($block in $existingOutputBlocks) { + $block['name'] = (($block.content | Where-Object { $_ -like 'output *' }) -split ' ')[1] + $block['type'] = (($block.content | Where-Object { $_ -like 'output *' }) -split ' ')[2] + } + + + ####################### + ## Return result ## + ####################### return @{ parameters = $existingParameterBlocks variables = $existingVariableBlocks @@ -45,4 +163,3 @@ outputs = $existingOutputBlocks } } -Resolve-ExistingTemplateContent -TemplateFilePath 'C:\dev\ip\Azure-ResourceModules\ResourceModules\modules\Microsoft.Storage\storageAccounts\deploy.bicep' diff --git a/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 b/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 new file mode 100644 index 0000000000..b600e75732 --- /dev/null +++ b/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 @@ -0,0 +1,39 @@ +<# +.SYNOPSIS +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 {} + 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 {} +} From aee3c4908690b737b394aeab667001e41bdba208 Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 13:48:24 +0100 Subject: [PATCH 07/14] Update to latest --- .../private/module/Resolve-ExistingTemplateContent.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 index 8dbec83f31..e173271f27 100644 --- a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 +++ b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 @@ -99,8 +99,8 @@ function Resolve-ExistingTemplateContent { $topLevelIndent = Get-LineIndentation -Line $block.content[1] $relevantProperties = $block.content | Where-Object { (Get-LineIndentation $_) -eq $topLevelIndent -and $_ -notlike '*properties: {*' -and $_ -like '*:*' } - - $block['topLevelProperties'] = $relevantProperties | ForEach-Object { ($_ -split ':')[0].Trim() } + $topLevelPropertyNames = $relevantProperties | ForEach-Object { ($_ -split ':')[0].Trim() } + $block['topLevelProperties'] = $topLevelPropertyNames if (($block.content | Where-Object { $_ -match '\s*properties:.+' }).count -gt 0) { $propertiesStartIndex = 1 @@ -125,7 +125,8 @@ function Resolve-ExistingTemplateContent { } else { $nestedIndent = Get-LineIndentation -Line $block.content[($propertiesStartIndex + 1)] $relevantNestedProperties = $block.content[($propertiesStartIndex + 1) .. ($propertiesEndIndex - 1)] | Where-Object { (Get-LineIndentation $_) -eq $nestedIndent -and $_ -match '^\s*\w+:.*' } - $block['nestedProperties'] = $relevantNestedProperties | ForEach-Object { ($_ -split ':')[0].Trim() } + $nestedPropertyNames = $relevantNestedProperties | ForEach-Object { ($_ -split ':')[0].Trim() } + $block['nestedProperties'] = $nestedPropertyNames } } } From 68a5798ee52072852c82cfb0bf260b1867e3ec76 Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 15:54:29 +0100 Subject: [PATCH 08/14] Extended resources evaluation --- .../Resolve-ExistingTemplateContent.ps1 | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 index e173271f27..f3c709957e 100644 --- a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 +++ b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 @@ -100,7 +100,40 @@ function Resolve-ExistingTemplateContent { $topLevelIndent = Get-LineIndentation -Line $block.content[1] $relevantProperties = $block.content | Where-Object { (Get-LineIndentation $_) -eq $topLevelIndent -and $_ -notlike '*properties: {*' -and $_ -like '*:*' } $topLevelPropertyNames = $relevantProperties | ForEach-Object { ($_ -split ':')[0].Trim() } - $block['topLevelProperties'] = $topLevelPropertyNames + + # Collect full data block + $topLevelProperties = @() + foreach ($topLevelPropertyName in $topLevelPropertyNames) { + + # Find start index of poperty + $relativePropertyStartIndex = 1 + for ($index = $relativePropertyStartIndex; $index -lt $block.content.Count; $index++) { + if ($block.content[$index] -match ("^\s{$($topLevelIndent)}$($topLevelPropertyName):.+$" )) { + $relativePropertyStartIndex = $index + break + } + } + + # Find end index of poperty + $isPropertyOrClosing = "^\s{$($topLevelIndent)}\w+:.+$|^}$" + if ($block.content[$index + 1] -notmatch $isPropertyOrClosing) { + # If the next line is not another property, it's a multi-line declaration + $relativePropertyEndIndex = $relativePropertyStartIndex + while ($block.content[($relativePropertyEndIndex + 1)] -notmatch $isPropertyOrClosing) { + $relativePropertyEndIndex++ + } + } else { + $relativePropertyEndIndex = $relativePropertyStartIndex + } + + # Build result + $topLevelProperties += @{ + name = $topLevelPropertyName + content = $block.content[$relativePropertyStartIndex..$relativePropertyEndIndex] + } + } + + $block['topLevelProperties'] = $topLevelProperties if (($block.content | Where-Object { $_ -match '\s*properties:.+' }).count -gt 0) { $propertiesStartIndex = 1 @@ -126,7 +159,40 @@ function Resolve-ExistingTemplateContent { $nestedIndent = Get-LineIndentation -Line $block.content[($propertiesStartIndex + 1)] $relevantNestedProperties = $block.content[($propertiesStartIndex + 1) .. ($propertiesEndIndex - 1)] | Where-Object { (Get-LineIndentation $_) -eq $nestedIndent -and $_ -match '^\s*\w+:.*' } $nestedPropertyNames = $relevantNestedProperties | ForEach-Object { ($_ -split ':')[0].Trim() } - $block['nestedProperties'] = $nestedPropertyNames + + # Collect full data block + $nestedProperties = @() + foreach ($nestedPropertyName in $nestedPropertyNames) { + + # Find start index of poperty + $relativePropertyStartIndex = 1 + for ($index = $relativePropertyStartIndex; $index -lt $block.content.Count; $index++) { + if ($block.content[$index] -match ("^\s{$($nestedIndent)}$($nestedPropertyName):.+$" )) { + $relativePropertyStartIndex = $index + break + } + } + + # Find end index of poperty + $isPropertyOrClosing = "^\s{$($nestedIndent)}\w+:.+$|^\s{$($topLevelIndent)}}$" + if ($block.content[$index + 1] -notmatch $isPropertyOrClosing) { + # If the next line is not another property, it's a multi-line declaration + $relativePropertyEndIndex = $relativePropertyStartIndex + while ($block.content[($relativePropertyEndIndex + 1)] -notmatch $isPropertyOrClosing) { + $relativePropertyEndIndex++ + } + } else { + $relativePropertyEndIndex = $relativePropertyStartIndex + } + + # Build result + $nestedProperties += @{ + name = $nestedPropertyName + content = $block.content[$relativePropertyStartIndex..$relativePropertyEndIndex] + } + } + + $block['nestedProperties'] = $nestedProperties } } } From e54a432470f07ffdb7f379ac56042c8b8b7d8f1b Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 19:39:52 +0100 Subject: [PATCH 09/14] Enabled detailed content for modules --- .../Resolve-ExistingTemplateContent.ps1 | 83 ++++++++++++++++++- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 index f3c709957e..ebcd87aac7 100644 --- a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 +++ b/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 @@ -102,6 +102,7 @@ function Resolve-ExistingTemplateContent { $topLevelPropertyNames = $relevantProperties | ForEach-Object { ($_ -split ':')[0].Trim() } # Collect full data block + ## Top level properties $topLevelProperties = @() foreach ($topLevelPropertyName in $topLevelPropertyNames) { @@ -135,10 +136,11 @@ function Resolve-ExistingTemplateContent { $block['topLevelProperties'] = $topLevelProperties - if (($block.content | Where-Object { $_ -match '\s*properties:.+' }).count -gt 0) { + ## Nested properties + if (($block.content | Where-Object { $_ -match '^\s*properties: \{\s*$' }).count -gt 0) { $propertiesStartIndex = 1 for ($index = $propertiesStartIndex; $index -lt $block.content.Count; $index++) { - if ($block.Content[$index] -match '^\s*properties: {.*') { + if ($block.Content[$index] -match '^\s*properties: \{\s*$') { $propertiesStartIndex = $index break } @@ -204,7 +206,82 @@ function Resolve-ExistingTemplateContent { # Analyze content foreach ($block in $existingModuleBlocks) { - $block['params'] = $block.content[3..($block.content.count - 3)] | ForEach-Object { ($_ -split ':')[0].Trim() } + + $topLevelIndent = Get-LineIndentation -Line $block.content[1] + $relevantProperties = $block.content | Where-Object { (Get-LineIndentation $_) -eq $topLevelIndent -and $_ -notlike '*params: {*' -and $_ -like '*:*' } + $topLevelPropertyNames = $relevantProperties | ForEach-Object { ($_ -split ':')[0].Trim() } + + # Collect full data block + ## Top level properties + $block['topLevelProperties'] = @( + @{ + name = 'name' + content = $block.content[1] + } + ) + ## Nested params + # $block['params'] = $block.content[3..($block.content.count - 3)] | ForEach-Object { ($_ -split ':')[0].Trim() } + if (($block.content | Where-Object { $_ -match '^\s*params: {\s*$' }).count -gt 0) { + $paramsStartIndex = 1 + for ($index = $paramsStartIndex; $index -lt $block.content.Count; $index++) { + if ($block.Content[$index] -match '^\s*params: {\s*$') { + $paramsStartIndex = $index + break + } + } + + $paramsEndIndex = $paramsStartIndex + for ($index = $paramsEndIndex; $index -lt $block.content.Count; $index++) { + if ((Get-LineIndentation -Line $block.Content[$index]) -eq $topLevelIndent -and $block.Content[$index].Trim() -eq '}') { + $paramsEndIndex = $index + break + } + } + $paramsEndIndex + + if ($block.content[$paramsStartIndex] -like '*{*}*' -or $block.content[$paramsStartIndex + 1].Trim() -eq '}') { + # Empty properties block. Can be skipped + $block['nestedParams'] = @() + } else { + $nestedIndent = Get-LineIndentation -Line $block.content[($paramsStartIndex + 1)] + $relevantNestedParams = $block.content[($paramsStartIndex + 1) .. ($paramsEndIndex - 1)] | Where-Object { (Get-LineIndentation $_) -eq $nestedIndent -and $_ -match '^\s*\w+:.*' } + $nestedParamNames = $relevantNestedParams | ForEach-Object { ($_ -split ':')[0].Trim() } + + # Collect full data block + $nestedparams = @() + foreach ($nestedParamName in $nestedParamNames) { + + # Find start index of poperty + $relativeParamStartIndex = 1 + for ($index = $relativeParamStartIndex; $index -lt $block.content.Count; $index++) { + if ($block.content[$index] -match ("^\s{$($nestedIndent)}$($nestedParamName):.+$" )) { + $relativeParamStartIndex = $index + break + } + } + + # Find end index of poperty + $isParamOrClosing = "^\s{$($nestedIndent)}\w+:.+$|^\s{$($topLevelIndent)}}$" + if ($block.content[$index + 1] -notmatch $isParamOrClosing) { + # If the next line is not another param, it's a multi-line declaration + $relativeParamEndIndex = $relativeParamStartIndex + while ($block.content[($relativeParamEndIndex + 1)] -notmatch $isParamOrClosing) { + $relativeParamEndIndex++ + } + } else { + $relativeParamEndIndex = $relativeParamStartIndex + } + + # Build result + $nestedParams += @{ + name = $nestedParamName + content = $block.content[$relativeParamStartIndex..$relativeParamEndIndex] + } + } + + $block['nestedParams'] = $nestedParams + } + } } From 5456f15246ceaa4f353a2b4d3f6c5a45cefa3cbf Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 19:40:11 +0100 Subject: [PATCH 10/14] Moved function --- .../module => public}/Resolve-ExistingTemplateContent.ps1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename utilities/tools/REST2CARML/{private/module => public}/Resolve-ExistingTemplateContent.ps1 (100%) diff --git a/utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/public/Resolve-ExistingTemplateContent.ps1 similarity index 100% rename from utilities/tools/REST2CARML/private/module/Resolve-ExistingTemplateContent.ps1 rename to utilities/tools/REST2CARML/public/Resolve-ExistingTemplateContent.ps1 From bb341f75fc09fe0f69d0b64a9fbd4cdf407ba2a4 Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 20:05:36 +0100 Subject: [PATCH 11/14] Simplified code --- .../private/module/Expand-DeploymentBlock.ps1 | 129 +++++++++++++ .../Resolve-ExistingTemplateContent.ps1 | 180 +----------------- 2 files changed, 131 insertions(+), 178 deletions(-) create mode 100644 utilities/tools/REST2CARML/private/module/Expand-DeploymentBlock.ps1 diff --git a/utilities/tools/REST2CARML/private/module/Expand-DeploymentBlock.ps1 b/utilities/tools/REST2CARML/private/module/Expand-DeploymentBlock.ps1 new file mode 100644 index 0000000000..32d4c468a0 --- /dev/null +++ b/utilities/tools/REST2CARML/private/module/Expand-DeploymentBlock.ps1 @@ -0,0 +1,129 @@ +<# +.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 full data block + ## Top level elements + $topLevelElements = @() + foreach ($topLevelElementName in $topLevelElementNames) { + + # Find start index of poperty + $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 poperty + $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 + + ## Nested elements + if (($DeclarationBlock.content | Where-Object { $_ -match "^\s*$($NestedType): \{\s*$" }).count -gt 0) { + $propertiesStartIndex = 1 + for ($index = $propertiesStartIndex; $index -lt $DeclarationBlock.content.Count; $index++) { + if ($DeclarationBlock.Content[$index] -match "^\s*$($NestedType): \{\s*$") { + $propertiesStartIndex = $index + break + } + } + + $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 + } + } + + 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 + } + } +} diff --git a/utilities/tools/REST2CARML/public/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/public/Resolve-ExistingTemplateContent.ps1 index ebcd87aac7..1ee6904c76 100644 --- a/utilities/tools/REST2CARML/public/Resolve-ExistingTemplateContent.ps1 +++ b/utilities/tools/REST2CARML/public/Resolve-ExistingTemplateContent.ps1 @@ -96,107 +96,7 @@ function Resolve-ExistingTemplateContent { # Analyze content foreach ($block in $existingResourceBlocks) { - - $topLevelIndent = Get-LineIndentation -Line $block.content[1] - $relevantProperties = $block.content | Where-Object { (Get-LineIndentation $_) -eq $topLevelIndent -and $_ -notlike '*properties: {*' -and $_ -like '*:*' } - $topLevelPropertyNames = $relevantProperties | ForEach-Object { ($_ -split ':')[0].Trim() } - - # Collect full data block - ## Top level properties - $topLevelProperties = @() - foreach ($topLevelPropertyName in $topLevelPropertyNames) { - - # Find start index of poperty - $relativePropertyStartIndex = 1 - for ($index = $relativePropertyStartIndex; $index -lt $block.content.Count; $index++) { - if ($block.content[$index] -match ("^\s{$($topLevelIndent)}$($topLevelPropertyName):.+$" )) { - $relativePropertyStartIndex = $index - break - } - } - - # Find end index of poperty - $isPropertyOrClosing = "^\s{$($topLevelIndent)}\w+:.+$|^}$" - if ($block.content[$index + 1] -notmatch $isPropertyOrClosing) { - # If the next line is not another property, it's a multi-line declaration - $relativePropertyEndIndex = $relativePropertyStartIndex - while ($block.content[($relativePropertyEndIndex + 1)] -notmatch $isPropertyOrClosing) { - $relativePropertyEndIndex++ - } - } else { - $relativePropertyEndIndex = $relativePropertyStartIndex - } - - # Build result - $topLevelProperties += @{ - name = $topLevelPropertyName - content = $block.content[$relativePropertyStartIndex..$relativePropertyEndIndex] - } - } - - $block['topLevelProperties'] = $topLevelProperties - - ## Nested properties - if (($block.content | Where-Object { $_ -match '^\s*properties: \{\s*$' }).count -gt 0) { - $propertiesStartIndex = 1 - for ($index = $propertiesStartIndex; $index -lt $block.content.Count; $index++) { - if ($block.Content[$index] -match '^\s*properties: \{\s*$') { - $propertiesStartIndex = $index - break - } - } - - $propertiesEndIndex = $propertiesStartIndex - for ($index = $propertiesEndIndex; $index -lt $block.content.Count; $index++) { - if ((Get-LineIndentation -Line $block.Content[$index]) -eq $topLevelIndent -and $block.Content[$index].Trim() -eq '}') { - $propertiesEndIndex = $index - break - } - } - - if ($block.content[$propertiesStartIndex] -like '*{*}*' -or $block.content[$propertiesStartIndex + 1].Trim() -eq '}') { - # Empty properties block. Can be skipped - $block['nestedProperties'] = @() - } else { - $nestedIndent = Get-LineIndentation -Line $block.content[($propertiesStartIndex + 1)] - $relevantNestedProperties = $block.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 - $nestedProperties = @() - foreach ($nestedPropertyName in $nestedPropertyNames) { - - # Find start index of poperty - $relativePropertyStartIndex = 1 - for ($index = $relativePropertyStartIndex; $index -lt $block.content.Count; $index++) { - if ($block.content[$index] -match ("^\s{$($nestedIndent)}$($nestedPropertyName):.+$" )) { - $relativePropertyStartIndex = $index - break - } - } - - # Find end index of poperty - $isPropertyOrClosing = "^\s{$($nestedIndent)}\w+:.+$|^\s{$($topLevelIndent)}}$" - if ($block.content[$index + 1] -notmatch $isPropertyOrClosing) { - # If the next line is not another property, it's a multi-line declaration - $relativePropertyEndIndex = $relativePropertyStartIndex - while ($block.content[($relativePropertyEndIndex + 1)] -notmatch $isPropertyOrClosing) { - $relativePropertyEndIndex++ - } - } else { - $relativePropertyEndIndex = $relativePropertyStartIndex - } - - # Build result - $nestedProperties += @{ - name = $nestedPropertyName - content = $block.content[$relativePropertyStartIndex..$relativePropertyEndIndex] - } - } - - $block['nestedProperties'] = $nestedProperties - } - } + Expand-DeploymentBlock -DeclarationBlock $block -NestedType 'properties' } ######################### @@ -206,85 +106,9 @@ function Resolve-ExistingTemplateContent { # Analyze content foreach ($block in $existingModuleBlocks) { - - $topLevelIndent = Get-LineIndentation -Line $block.content[1] - $relevantProperties = $block.content | Where-Object { (Get-LineIndentation $_) -eq $topLevelIndent -and $_ -notlike '*params: {*' -and $_ -like '*:*' } - $topLevelPropertyNames = $relevantProperties | ForEach-Object { ($_ -split ':')[0].Trim() } - - # Collect full data block - ## Top level properties - $block['topLevelProperties'] = @( - @{ - name = 'name' - content = $block.content[1] - } - ) - ## Nested params - # $block['params'] = $block.content[3..($block.content.count - 3)] | ForEach-Object { ($_ -split ':')[0].Trim() } - if (($block.content | Where-Object { $_ -match '^\s*params: {\s*$' }).count -gt 0) { - $paramsStartIndex = 1 - for ($index = $paramsStartIndex; $index -lt $block.content.Count; $index++) { - if ($block.Content[$index] -match '^\s*params: {\s*$') { - $paramsStartIndex = $index - break - } - } - - $paramsEndIndex = $paramsStartIndex - for ($index = $paramsEndIndex; $index -lt $block.content.Count; $index++) { - if ((Get-LineIndentation -Line $block.Content[$index]) -eq $topLevelIndent -and $block.Content[$index].Trim() -eq '}') { - $paramsEndIndex = $index - break - } - } - $paramsEndIndex - - if ($block.content[$paramsStartIndex] -like '*{*}*' -or $block.content[$paramsStartIndex + 1].Trim() -eq '}') { - # Empty properties block. Can be skipped - $block['nestedParams'] = @() - } else { - $nestedIndent = Get-LineIndentation -Line $block.content[($paramsStartIndex + 1)] - $relevantNestedParams = $block.content[($paramsStartIndex + 1) .. ($paramsEndIndex - 1)] | Where-Object { (Get-LineIndentation $_) -eq $nestedIndent -and $_ -match '^\s*\w+:.*' } - $nestedParamNames = $relevantNestedParams | ForEach-Object { ($_ -split ':')[0].Trim() } - - # Collect full data block - $nestedparams = @() - foreach ($nestedParamName in $nestedParamNames) { - - # Find start index of poperty - $relativeParamStartIndex = 1 - for ($index = $relativeParamStartIndex; $index -lt $block.content.Count; $index++) { - if ($block.content[$index] -match ("^\s{$($nestedIndent)}$($nestedParamName):.+$" )) { - $relativeParamStartIndex = $index - break - } - } - - # Find end index of poperty - $isParamOrClosing = "^\s{$($nestedIndent)}\w+:.+$|^\s{$($topLevelIndent)}}$" - if ($block.content[$index + 1] -notmatch $isParamOrClosing) { - # If the next line is not another param, it's a multi-line declaration - $relativeParamEndIndex = $relativeParamStartIndex - while ($block.content[($relativeParamEndIndex + 1)] -notmatch $isParamOrClosing) { - $relativeParamEndIndex++ - } - } else { - $relativeParamEndIndex = $relativeParamStartIndex - } - - # Build result - $nestedParams += @{ - name = $nestedParamName - content = $block.content[$relativeParamStartIndex..$relativeParamEndIndex] - } - } - - $block['nestedParams'] = $nestedParams - } - } + Expand-DeploymentBlock -DeclarationBlock $block -NestedType 'params' } - ######################### ## Extract Outputs ## ######################### From d53f5b814a3aa0faa48f44b0e556f9488c0dde33 Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 20:28:07 +0100 Subject: [PATCH 12/14] Added documentation --- .../private/module/Expand-DeploymentBlock.ps1 | 22 ++++++++++++++----- .../private/module/Read-DeclarationBlock.ps1 | 10 +++++++++ .../Resolve-ExistingTemplateContent.ps1 | 21 +++++++++--------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/utilities/tools/REST2CARML/private/module/Expand-DeploymentBlock.ps1 b/utilities/tools/REST2CARML/private/module/Expand-DeploymentBlock.ps1 index 32d4c468a0..6ddf91616b 100644 --- a/utilities/tools/REST2CARML/private/module/Expand-DeploymentBlock.ps1 +++ b/utilities/tools/REST2CARML/private/module/Expand-DeploymentBlock.ps1 @@ -30,12 +30,13 @@ function Expand-DeploymentBlock { $relevantProperties = $DeclarationBlock.content | Where-Object { (Get-LineIndentation $_) -eq $topLevelIndent -and $_ -notlike "*$($NestedType): {*" -and $_ -like '*:*' } $topLevelElementNames = $relevantProperties | ForEach-Object { ($_ -split ':')[0].Trim() } - # Collect full data block - ## Top level elements + #################################### + ## Collect top level elements ## + #################################### $topLevelElements = @() foreach ($topLevelElementName in $topLevelElementNames) { - # Find start index of poperty + # Find start index of element $relativeElementStartIndex = 1 for ($index = $relativeElementStartIndex; $index -lt $DeclarationBlock.content.Count; $index++) { if ($DeclarationBlock.content[$index] -match ("^\s{$($topLevelIndent)}$($topLevelElementName):.+$" )) { @@ -44,7 +45,7 @@ function Expand-DeploymentBlock { } } - # Find end index of poperty + # 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 @@ -65,8 +66,13 @@ function Expand-DeploymentBlock { $DeclarationBlock['topLevelElements'] = $topLevelElements - ## Nested elements + ################################# + ## 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*$") { @@ -75,6 +81,8 @@ function Expand-DeploymentBlock { } } + # 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 '}') { @@ -83,8 +91,10 @@ function Expand-DeploymentBlock { } } + # Process nested block + # -------------------- if ($DeclarationBlock.content[$propertiesStartIndex] -like '*{*}*' -or $DeclarationBlock.content[$propertiesStartIndex + 1].Trim() -eq '}') { - # Empty properties block. Can be skipped + # Empty properties block. Can be skipped. $DeclarationBlock['nestedElements'] = @() } else { $nestedIndent = Get-LineIndentation -Line $DeclarationBlock.content[($propertiesStartIndex + 1)] diff --git a/utilities/tools/REST2CARML/private/module/Read-DeclarationBlock.ps1 b/utilities/tools/REST2CARML/private/module/Read-DeclarationBlock.ps1 index ab67702eaf..33742fae5c 100644 --- a/utilities/tools/REST2CARML/private/module/Read-DeclarationBlock.ps1 +++ b/utilities/tools/REST2CARML/private/module/Read-DeclarationBlock.ps1 @@ -28,6 +28,10 @@ function Read-DeclarationBlock { [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.+$', @@ -51,6 +55,9 @@ function Read-DeclarationBlock { '^\s*$' ) + ######################################### + ## Find all indexes to investigate ## + ######################################### $existingBlocks = @() $declarationIndexes = @() @@ -60,6 +67,9 @@ function Read-DeclarationBlock { } } + ######################################################################################################################## + ## 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') } { diff --git a/utilities/tools/REST2CARML/public/Resolve-ExistingTemplateContent.ps1 b/utilities/tools/REST2CARML/public/Resolve-ExistingTemplateContent.ps1 index 1ee6904c76..d9744076f9 100644 --- a/utilities/tools/REST2CARML/public/Resolve-ExistingTemplateContent.ps1 +++ b/utilities/tools/REST2CARML/public/Resolve-ExistingTemplateContent.ps1 @@ -28,31 +28,31 @@ Name Value startIndex 173 endIndex 183 content {resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) {, name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment… -nestedProperties {mode, template} -topLevelProperties name +nestedElements {mode, template} +topLevelElements name startIndex 185 endIndex 188 content {resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = if (!empty(cMKKeyVaultResourceId)) {, name: last(split(cMKKeyVaultResourceId, '/')), scope: resourc… -topLevelProperties {name, scope} +topLevelElements {name, scope} startIndex 190 endIndex 240 content {resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {, name: name, location: location, kind: storageAccountKind…} -nestedProperties {encryption, accessTier, supportsHttpsTrafficOnly, isHnsEnabled…} -topLevelProperties {name, location, kind, sku…} +nestedElements {encryption, accessTier, supportsHttpsTrafficOnly, isHnsEnabled…} +topLevelElements {name, location, kind, sku…} startIndex 242 endIndex 252 content {resource storageAccount_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId))… -nestedProperties {storageAccountId, workspaceId, eventHubAuthorizationRuleId, eventHubName…} -topLevelProperties {name, scope} +nestedElements {storageAccountId, workspaceId, eventHubAuthorizationRuleId, eventHubName…} +topLevelElements {name, scope} startIndex 254 endIndex 261 -content {resource storageAccount_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) {, name: '${storageAccount.name}-${lock}-lock', properties: {, level: any(lock)… -nestedProperties {level, notes} -topLevelProperties {name, scope} +content {resource storageAccount_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) {, name: '${storageAccount.name}-${lock}-lock', Elements: {, level: any(lock)… +nestedElements {level, notes} +topLevelElements {name, scope} #> function Resolve-ExistingTemplateContent { @@ -119,7 +119,6 @@ function Resolve-ExistingTemplateContent { $block['type'] = (($block.content | Where-Object { $_ -like 'output *' }) -split ' ')[2] } - ####################### ## Return result ## ####################### From a0cce3df78df570acac0d05b4477cc5156eece9c Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 20:29:09 +0100 Subject: [PATCH 13/14] Update to latest --- .../private/shared/Get-LineIndentation.ps1 | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 b/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 index b600e75732..daf35929ee 100644 --- a/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 +++ b/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 @@ -11,12 +11,17 @@ Get-LineIndentation -Line ' Test' Retrieve indentation of line ' Test'. Would return 4. #> function Get-LineIndentation { + [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $Line ) - begin {} + + begin { + Write-Debug ('{0} entered' -f $MyInvocation.MyCommand) + } + process { $indentation = 0 for ($i = 0; $i -lt $Line.Length; $i++) { @@ -35,5 +40,8 @@ function Get-LineIndentation { } return $indentation } - end {} + + end { + Write-Debug ('{0} exited' -f $MyInvocation.MyCommand) + } } From 914c98964de68066b634f5592fbcc6cbf320e51b Mon Sep 17 00:00:00 2001 From: MrMCake Date: Sun, 30 Oct 2022 20:29:34 +0100 Subject: [PATCH 14/14] Update to latest --- .../tools/REST2CARML/private/shared/Get-LineIndentation.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 b/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 index daf35929ee..9168cc2042 100644 --- a/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 +++ b/utilities/tools/REST2CARML/private/shared/Get-LineIndentation.ps1 @@ -2,6 +2,9 @@ .SYNOPSIS Retrieve indentation of a line. +.DESCRIPTION +Retrieve indentation of a line. + .PARAMETER Line Mandatory. The line to analyse for indentation.