From 9930cab3ee49fcf328a270aa0a32de7bdf8cbc43 Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Fri, 28 Mar 2025 14:56:46 +0000 Subject: [PATCH 01/10] feat: Add Edit-LineEnding function and Invoke-EABillingSPNPermissionsSetup script --- src/ALZ/ALZ.psd1 | 39 ++-- src/ALZ/Public/Edit-LineEnding.ps1 | 43 ++++ .../Invoke-EABillingSPNPermissionsSetup.ps1 | 184 ++++++++++++++++++ 3 files changed, 250 insertions(+), 16 deletions(-) create mode 100644 src/ALZ/Public/Edit-LineEnding.ps1 create mode 100644 src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 diff --git a/src/ALZ/ALZ.psd1 b/src/ALZ/ALZ.psd1 index 0f0f27b..9c31070 100644 --- a/src/ALZ/ALZ.psd1 +++ b/src/ALZ/ALZ.psd1 @@ -9,33 +9,33 @@ @{ # Script module or binary module file associated with this manifest. - RootModule = 'ALZ.psm1' + RootModule = 'ALZ.psm1' # Version number of this module. - ModuleVersion = '0.1.0' + ModuleVersion = '0.1.0' # Supported PSEditions # CompatiblePSEditions = @() # ID used to uniquely identify this module - GUID = '74a4385f-281e-4776-bd7c-3b6a09d6ba63' + GUID = '74a4385f-281e-4776-bd7c-3b6a09d6ba63' # Author of this module - Author = 'Microsoft Corporation' + Author = 'Microsoft Corporation' # Company or vendor of this module - CompanyName = 'Microsoft Corporation' + CompanyName = 'Microsoft Corporation' # Copyright statement for this module - Copyright = '(c) Microsoft Corporation. All rights reserved.' + Copyright = '(c) Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module - Description = 'Azure Landing Zones Powershell Module' + Description = 'Azure Landing Zones Powershell Module' CompatiblePSEditions = 'Core' # Minimum version of the PowerShell engine required by this module - PowerShellVersion = '7.4' + PowerShellVersion = '7.4' # Name of the PowerShell host required by this module # PowerShellHostName = '' @@ -53,7 +53,10 @@ # ProcessorArchitecture = '' # Modules that must be imported into the global environment prior to importing this module - RequiredModules = @() + RequiredModules = @( + @{ ModuleName = "Az.Accounts"; ModuleVersion = "2.10.4" } + @{ ModuleName = "Az.Resources"; ModuleVersion = "6.5.0" } + ) # Assemblies that must be loaded prior to importing this module # RequiredAssemblies = @() @@ -71,19 +74,23 @@ # NestedModules = @() # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. - FunctionsToExport = @( + FunctionsToExport = @( 'Test-AcceleratorRequirement', - 'Deploy-Accelerator' + 'Deploy-Accelerator', + 'Edit-LineEnding', + 'Invoke-EABillingSPNPermissionsSetup' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. - CmdletsToExport = @() + CmdletsToExport = @() # Variables to export from this module - VariablesToExport = '*' + VariablesToExport = '*' # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. - AliasesToExport = @() + AliasesToExport = @( + 'Edit-LineEndings' + ) # DSC resources to export from this module # DscResourcesToExport = @() @@ -95,7 +102,7 @@ # FileList = @() # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. - PrivateData = @{ + PrivateData = @{ PSData = @{ @@ -112,7 +119,7 @@ LicenseUri = 'https://github.com/Azure/ALZ-PowerShell-Module/blob/main/LICENSE' # A URL to the main website for this project. - ProjectUri = 'https://github.com/Azure/ALZ-Powershell-Module' + ProjectUri = 'https://github.com/Azure/ALZ-PowerShell-Module' # A URL to an icon representing this module. IconUri = 'https://raw.githubusercontent.com/Azure/ALZ-PowerShell-Module/main/docs/rsz_alzlogo.png' diff --git a/src/ALZ/Public/Edit-LineEnding.ps1 b/src/ALZ/Public/Edit-LineEnding.ps1 new file mode 100644 index 0000000..7a4776c --- /dev/null +++ b/src/ALZ/Public/Edit-LineEnding.ps1 @@ -0,0 +1,43 @@ +enum LineEndingTypes { + Darwin + Unix + Win +} + +function Edit-LineEnding { + [CmdletBinding(SupportsShouldProcess = $true)] + [OutputType([String[]])] + param ( + [Parameter(ValueFromPipeline = $true)] + [String[]]$InputText, + [Parameter()][LineEndingTypes]$LineEnding = "Unix" + ) + + Begin { + + Switch ("$LineEnding".ToLower()) { + "darwin" { $eol = "`r" } + "unix" { $eol = "`n" } + "win" { $eol = "`r`n" } + } + + } + + Process { + + [String[]]$outputText += $InputText | + ForEach-Object { $_ -replace "`r`n", "`n" } | + ForEach-Object { $_ -replace "`r", "`n" } | + ForEach-Object { $_ -replace "`n", "$eol" } + + } + + End { + + return $outputText + + } + +} + +New-Alias -Name "Edit-LineEndings" -Value "Edit-LineEnding" \ No newline at end of file diff --git a/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 b/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 new file mode 100644 index 0000000..50a126c --- /dev/null +++ b/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 @@ -0,0 +1,184 @@ +function Invoke-EABillingSPNPermissionsSetup { + <# +.SYNOPSIS +Creates a new SPN, or uses an existing SPN/MI, and assigns it the 'SubscriptionCreator' role to it to allow it to create subscriptions in the specified EA billing enrollment account. + +.DESCRIPTION +Creates a new SPN, or uses an existing SPN/MI, and assigns it the 'SubscriptionCreator' role to it to allow it to create subscriptions in the specified EA billing enrollment account. + +.EXAMPLE +# Create a new SPN and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters +/Invoke-EABillingSPNPermissionsSetup.ps1 -eaEnrollmentNumber "123456" -eaEnrollmentAccountNumber "987654" + +# Create a new SPN and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaBillingAccountResourceId' parameter +./Invoke-EABillingSPNPermissionsSetup.ps1 -eaBillingAccountResourceId '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' + +# Create a new SPN, with a custom name, and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters +./Invoke-EABillingSPNPermissionsSetup.ps1 -eaEnrollmentNumber "123456" -eaEnrollmentAccountNumber "987654" -newSpnDisplayName 'spn-lz-sub-vending-custom-name' + +# Create a new SPN, with a custom name, and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaBillingAccountResourceId' parameter +./Invoke-EABillingSPNPermissionsSetup.ps1 -eaBillingAccountResourceId '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' -newSpnDisplayName 'spn-lz-sub-vending-custom-name' + +# Use an existing SPN/MI and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters +./Invoke-EABillingSPNPermissionsSetup.ps1 -eaEnrollmentNumber "123456" -eaEnrollmentAccountNumber "987654" -existingSpnMiObjectId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + +# Use an existing SPN/MI and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaBillingAccountResourceId' parameter +./Invoke-EABillingSPNPermissionsSetup.ps1 -eaBillingAccountResourceId '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' -existingSpnMiObjectId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' +.NOTES +# Release notes 22/12/2022 - V1.0.0: +- Initial release. + +# Release notes 23/12/2022 - V1.1.0: +- Added simplified inputs for the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters to form the 'eaBillingAccountResourceId' parameter value, instead of having to provide the full resource ID in the 'eaBillingAccountResourceId' parameter. +#> + + # Check for pre-reqs + #Requires -PSEdition Core + #Requires -Modules @{ ModuleName="Az.Accounts"; ModuleVersion="2.10.4" } + #Requires -Modules @{ ModuleName="Az.Resources"; ModuleVersion="6.5.0" } + + [CmdletBinding(DefaultParameterSetName = "Default")] + param ( + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 1, HelpMessage = "Provide the EA enrollment number that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '1234567'. This parameter is only used if the 'eaBillingAccountResourceId' parameter is not provided. It's value is used to create the 'eaBillingAccountResourceId' parameter value, that looks like this: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' (this parameter value is the middle numerical value).")] + [string] + $eaEnrollmentNumber, + + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 2, HelpMessage = "Provide the EA enrollment Account Number/ID that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '987654'. This parameter is only used if the 'eaBillingAccountResourceId' parameter is not provided. It's value is used to create the 'eaBillingAccountResourceId' parameter value, that looks like this: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' (this parameter value is the middle numerical value).")] + [string] + $eaEnrollmentAccountNumber, + + [Parameter(ParameterSetName = "Advanced", Mandatory = $false, Position = 4, HelpMessage = "Provide the EA enrollment/billing account ID that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/123456'")] + [string] + $eaBillingAccountResourceId, + + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 3, HelpMessage = "(Optional) Provide an existing Service Principal Name (SPN) (aka Enterprise Application) 'Object ID' to grant the 'SubscriptionCreator' role to on the specified billing account instead of creating a new one. If left blank a new SPN will be created. This can also be the object ID of a Managed Identity's SPN.")] + [string] + $existingSpnMiObjectId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 5, HelpMessage = "(Optional) Provide a Display Name for the new Service Principal (SPN) (aka Enterprise Application) to be created. If left blank the default value of 'spn-lz-sub-vending' will be used.")] + [string] + $newSpnDisplayName = "spn-lz-sub-vending" + ) + + # Checks + Write-Host "Checking inputs..." -ForegroundColor Cyan + Write-Host "" + + # Check $eaBillingAccountResourceId is valid and populate from $eaEnrollmentNumber and $eaEnrollmentAccountNumber if not provided + if ($eaBillingAccountResourceId -eq $null -or $eaBillingAccountResourceId -eq "") { + Write-Host "eaBillingAccountResourceId paramter value not set, forming parameter value from eaEnrollmentNumber and eaEnrollmentAccountNumber parameters..." -ForegroundColor Magenta + if ($eaEnrollmentNumber -eq $null -or $eaEnrollmentAccountNumber -eq $null -or $eaEnrollmentNumber -eq '' -or $eaEnrollmentAccountNumber -eq '') { + throw "No values provdided for the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters. These parameters are required if the 'eaBillingAccountResourceId' parameter is not provided. Please provide values for these parameters and try again." + } + $eaBillingAccountResourceId = "/providers/Microsoft.Billing/billingAccounts/$eaEnrollmentNumber/enrollmentAccounts/$eaEnrollmentAccountNumber" + Write-Host "eaBillingAccountResourceId parameter value set to '$($eaBillingAccountResourceId)'" -ForegroundColor Green + Write-Host "" + } + + # Check $eaBillingAccountResourceId is valid and exists + Write-Host "EA billing account parameters provided..." -ForegroundColor Cyan + Write-Host "Checking the specified EA billing account ID '$($eaBillingAccountResourceId)' exists..." -ForegroundColor Yellow + + if ($null -ne $eaBillingAccountResourceId -and $eaBillingAccountResourceId -ne "") { + $geteaBillingAccountResourceId = Invoke-AzRestMethod -Method GET -Path "$($eaBillingAccountResourceId)?api-version=2019-10-01-preview" -ErrorAction SilentlyContinue + + if ($geteaBillingAccountResourceId.StatusCode -ne 200) { + Write-Error "HTTP Status Code: $($geteaBillingAccountResourceId.StatusCode)" + Write-Error "HTTP Repsone Content: $($geteaBillingAccountResourceId.Content)" + throw "The specified EA billing account ID '$($eaBillingAccountResourceId)' does not exist. Please check the value and try again. Also ensure you are logged in as the EA Account Owner for the specified EA billing account." + } else { + Write-Host "The specified EA billing account ID '$($eaBillingAccountResourceId)' exists. Continuing..." -ForegroundColor Green + Write-Host "" + } + } + + # Check $existingSpnMiObjectId is valid and exists + if ($existingSpnMiObjectId -ne $null -and $existingSpnMiObjectId -ne "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") { + Write-Host "Existing SPN/MI provided..." -ForegroundColor Cyan + Write-Host "Checking the specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' exists..." -ForegroundColor Yellow + $getexistingSpnMiObjectId = Get-AzADServicePrincipal -ObjectId $existingSpnMiObjectId -ErrorAction SilentlyContinue + + if ($null -eq $getexistingSpnMiObjectId) { + throw "The specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' does not exist. Please check the value and try again." + } else { + Write-Host "The specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' exists with a Display Name of: '$($getexistingSpnMiObjectId.DisplayName)' with a Type of: '$($getexistingSpnMiObjectId.ServicePrincipalType)'. Continuing..." -ForegroundColor Green + Write-Host "" + $finalSpnMiObjectId = $getexistingSpnMiObjectId.Id + $finalSpnMiAppId = $getexistingSpnMiObjectId.AppId + $finalSpnMiDisplayName = $getexistingSpnMiObjectId.DisplayName + $finalSpnMiType = $getexistingSpnMiObjectId.ServicePrincipalType + } + } else { + # Create a new SPN (aka Enterprise Application) as no existing SPN/MI was provided via the $existingSpnMiObjectId parameter + Write-Host "No Existing SPN/MI provided. Proceeding to create a new SPN (aka Enterprise Application)..." -ForegroundColor Cyan + Write-Host "Creating a new SPN (aka Enterprise Application) with a Display Name of '$($newSpnDisplayName)'..." -ForegroundColor Yellow + + $newSpn = New-AzADServicePrincipal -DisplayName $newSpnDisplayName -Description "Service Principal Name (SPN) for the Landing Zone Subscription Vending. See https://aka.ms/lz-vending/bicep or https://aka.ms/lz-vending/tf for more information." -ErrorAction Stop + Write-Host "New SPN (aka Enterprise Application) created with a Display Name of '$($newSpn.DisplayName)' and an Object ID of '$($newSpn.Id)'." -ForegroundColor Green + Write-Host "" + + $finalSpnMiObjectId = $newSpn.Id + $finalSpnMiDisplayName = $newSpn.DisplayName + $finalSpnMiType = $newSpn.ServicePrincipalType + $finalSpnMiAppId = $newSpn.AppId + } + + ## convert this to a retry loop + # Start-Sleep -Seconds 15 + + # Grant SPN/MI access to the specified EA billing account + Write-Host "Pre-reqs passed and complete..." -ForegroundColor Cyan + Write-Host "Granting the 'SubscriptionCreator' role (ID: 'a0bcee42-bf30-4d1b-926a-48d21664ef71') on the EA Billing Account ID of: '$($eaBillingAccountResourceId)' to the AAD Object ID of: '$($finalSpnMiObjectId)' which has the Display Name of: '$($finalSpnMiDisplayName)'..." -ForegroundColor Yellow + + # Get the current AAD Tenant ID + $currentTenant = Get-AzTenant -ErrorAction Stop + + # Create GUID for role assignment name + $roleAssignmentName = New-Guid + + $roleAssignmentHashTable = [ordered]@{ + "properties" = @{ + "principalId" = "$finalSpnMiObjectId" + "roleDefinitionId" = "$eaBillingAccountResourceId/billingRoleDefinitions/a0bcee42-bf30-4d1b-926a-48d21664ef71" + "principalTenantId" = "$($currentTenant.TenantId)" + } + } + $roleAssignmentPayloadJson = $roleAssignmentHashTable | ConvertTo-Json -Depth 100 + + $grantRbac = Invoke-AzRestMethod -Method PUT -Path "$($eaBillingAccountResourceId)/billingRoleAssignments/$($roleAssignmentName)?api-version=2019-10-01-preview" -Payload $roleAssignmentPayloadJson -ErrorAction SilentlyContinue + + # Create variables for retry loop + $retryCount = 0 + $retryLimit = 10 + $retryDelay = 5 + + + if ($grantRbac.StatusCode -eq 400 -and $grantRbac.Content.Contains("are not valid")) { + while ($retryCount -le $retryLimit) { + Write-Host "The 'SubscriptionCreator' role has not been granted to the SPN/MI. Retrying in $retryDelay seconds to allow platform replication to occur..." -ForegroundColor Magenta + + Start-Sleep -Seconds $retryDelay + $retryCount++ + + $grantRbac = Invoke-AzRestMethod -Method PUT -Path "$($eaBillingAccountResourceId)/billingRoleAssignments/$($roleAssignmentName)?api-version=2019-10-01-preview" -Payload $roleAssignmentPayloadJson -ErrorAction SilentlyContinue + + if ($grantRbac.StatusCode -eq 200) { + break + } + } + } + if ($grantRbac.StatusCode -ne 200) { + Write-Error "HTTP Status Code: $($grantRbac.StatusCode)" + Write-Error "HTTP Repsone Content: $($grantRbac.Content)" + throw "An error occurred while attempting to grant the 'SubscriptionCreator' role to the SPN/MI. Please check the error message above and try again." + } else { + Write-Host "The 'SubscriptionCreator' role has been granted to the SPN/MI." -ForegroundColor Green + Write-Host "" + Write-Host "The SPN/MI 'Object ID' is: '$($finalSpnMiObjectId)'" -ForegroundColor Green + Write-Host "The SPN/MI 'App ID' is: '$($finalSpnMiAppId)'" -ForegroundColor Green + Write-Host "The SPN/MI 'Display Name' is: '$($finalSpnMiDisplayName)'" -ForegroundColor Green + Write-Host "The SPN/MI 'Type' is: '$($finalSpnMiType)'" -ForegroundColor Green + } + + return +} \ No newline at end of file From c1c701f1054daec41e8d04610577fa52dadfe447 Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Fri, 28 Mar 2025 15:01:10 +0000 Subject: [PATCH 02/10] fix: Update module requirements in ALZ.psd1 and remove redundant Requires statements in Invoke-EABillingSPNPermissionsSetup.ps1 --- src/ALZ/ALZ.psd1 | 4 ++-- src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ALZ/ALZ.psd1 b/src/ALZ/ALZ.psd1 index 9c31070..54c9197 100644 --- a/src/ALZ/ALZ.psd1 +++ b/src/ALZ/ALZ.psd1 @@ -54,8 +54,8 @@ # Modules that must be imported into the global environment prior to importing this module RequiredModules = @( - @{ ModuleName = "Az.Accounts"; ModuleVersion = "2.10.4" } - @{ ModuleName = "Az.Resources"; ModuleVersion = "6.5.0" } + @{ModuleName = 'Az.Accounts'; ModuleVersion = '2.10.4' }, + @{ModuleName = 'Az.Resources'; ModuleVersion = '6.5.0' } ) # Assemblies that must be loaded prior to importing this module diff --git a/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 b/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 index 50a126c..c37b3d9 100644 --- a/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 +++ b/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 @@ -34,8 +34,6 @@ Creates a new SPN, or uses an existing SPN/MI, and assigns it the 'SubscriptionC # Check for pre-reqs #Requires -PSEdition Core - #Requires -Modules @{ ModuleName="Az.Accounts"; ModuleVersion="2.10.4" } - #Requires -Modules @{ ModuleName="Az.Resources"; ModuleVersion="6.5.0" } [CmdletBinding(DefaultParameterSetName = "Default")] param ( From 536d3f8807d2b840de7b0d443832d2435aa4b140 Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Fri, 28 Mar 2025 15:58:13 +0000 Subject: [PATCH 03/10] fix: Format RequiredModules for better readability in ALZ.psd1 --- src/ALZ/ALZ.psd1 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ALZ/ALZ.psd1 b/src/ALZ/ALZ.psd1 index 54c9197..521ef50 100644 --- a/src/ALZ/ALZ.psd1 +++ b/src/ALZ/ALZ.psd1 @@ -54,8 +54,14 @@ # Modules that must be imported into the global environment prior to importing this module RequiredModules = @( - @{ModuleName = 'Az.Accounts'; ModuleVersion = '2.10.4' }, - @{ModuleName = 'Az.Resources'; ModuleVersion = '6.5.0' } + @{ + ModuleName = 'Az.Accounts' + ModuleVersion = '2.10.4' + }, + @{ + ModuleName = 'Az.Resources' + ModuleVersion = '6.5.0' + } ) # Assemblies that must be loaded prior to importing this module From 16dcab3ff524eb18a786a4c97fe09bb584d92fb4 Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Fri, 28 Mar 2025 15:59:59 +0000 Subject: [PATCH 04/10] fix: Update PowerShell version matrix in PullRequest.yml to include 7.5.0 --- .github/workflows/PullRequest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/PullRequest.yml b/.github/workflows/PullRequest.yml index 0b6aa08..e1b0750 100644 --- a/.github/workflows/PullRequest.yml +++ b/.github/workflows/PullRequest.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - pwsh: ['7.1.3'] + pwsh: ['7.1.3', '7.5.0'] steps: - name: Check out repository uses: actions/checkout@v3 From 2afe4cb2065825d81c3e449f05094206ed652a21 Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:13:33 +0000 Subject: [PATCH 05/10] fix: Update required PowerShell version to 7.1.3 and remove Edit-LineEnding function --- src/ALZ.Settings.ps1 | 2 +- src/ALZ/ALZ.psd1 | 5 +--- src/ALZ/Public/Edit-LineEnding.ps1 | 43 ------------------------------ 3 files changed, 2 insertions(+), 48 deletions(-) delete mode 100644 src/ALZ/Public/Edit-LineEnding.ps1 diff --git a/src/ALZ.Settings.ps1 b/src/ALZ.Settings.ps1 index 93817bf..b8cb7db 100644 --- a/src/ALZ.Settings.ps1 +++ b/src/ALZ.Settings.ps1 @@ -1,2 +1,2 @@ # specify the minimum required major PowerShell version that the build script should validate -[version]$script:requiredPSVersion = '5.1.0' \ No newline at end of file +[version]$script:requiredPSVersion = '7.1.3' \ No newline at end of file diff --git a/src/ALZ/ALZ.psd1 b/src/ALZ/ALZ.psd1 index 521ef50..e23e66e 100644 --- a/src/ALZ/ALZ.psd1 +++ b/src/ALZ/ALZ.psd1 @@ -83,7 +83,6 @@ FunctionsToExport = @( 'Test-AcceleratorRequirement', 'Deploy-Accelerator', - 'Edit-LineEnding', 'Invoke-EABillingSPNPermissionsSetup' ) @@ -94,9 +93,7 @@ VariablesToExport = '*' # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. - AliasesToExport = @( - 'Edit-LineEndings' - ) + AliasesToExport = @() # DSC resources to export from this module # DscResourcesToExport = @() diff --git a/src/ALZ/Public/Edit-LineEnding.ps1 b/src/ALZ/Public/Edit-LineEnding.ps1 deleted file mode 100644 index 7a4776c..0000000 --- a/src/ALZ/Public/Edit-LineEnding.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -enum LineEndingTypes { - Darwin - Unix - Win -} - -function Edit-LineEnding { - [CmdletBinding(SupportsShouldProcess = $true)] - [OutputType([String[]])] - param ( - [Parameter(ValueFromPipeline = $true)] - [String[]]$InputText, - [Parameter()][LineEndingTypes]$LineEnding = "Unix" - ) - - Begin { - - Switch ("$LineEnding".ToLower()) { - "darwin" { $eol = "`r" } - "unix" { $eol = "`n" } - "win" { $eol = "`r`n" } - } - - } - - Process { - - [String[]]$outputText += $InputText | - ForEach-Object { $_ -replace "`r`n", "`n" } | - ForEach-Object { $_ -replace "`r", "`n" } | - ForEach-Object { $_ -replace "`n", "$eol" } - - } - - End { - - return $outputText - - } - -} - -New-Alias -Name "Edit-LineEndings" -Value "Edit-LineEnding" \ No newline at end of file From 49f1c01e8c728b219dc724e858fc1a2e041a9d46 Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:19:17 +0000 Subject: [PATCH 06/10] fix: Add Az.Accounts and Az.Resources modules to the installation list --- actions_bootstrap.ps1 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/actions_bootstrap.ps1 b/actions_bootstrap.ps1 index 71665a4..4df355f 100644 --- a/actions_bootstrap.ps1 +++ b/actions_bootstrap.ps1 @@ -29,6 +29,15 @@ $null = $modulesToInstall.Add(([PSCustomObject]@{ ModuleName = 'platyPS' ModuleVersion = '0.12.0' })) +# Required for Invoke-EABillingSPNPermissionsSetup to work +$null = $modulesToInstall.Add(([PSCustomObject]@{ + ModuleName = 'Az.Accounts' + ModuleVersion = '2.10.4' + })) +$null = $modulesToInstall.Add(([PSCustomObject]@{ + ModuleName = 'Az.Resources' + ModuleVersion = '6.5.0' + })) 'Installing PowerShell Modules' foreach ($module in $modulesToInstall) { From 182701763cef9c3dff719aae1bfcad47286b2d85 Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:28:20 +0000 Subject: [PATCH 07/10] fix: Add EditorConfig for consistent coding styles and formatting fix: Enable EditorConfig validation in super-linter.yml --- .editorconfig | 12 + .github/workflows/super-linter.yml | 1 + .../Invoke-EABillingSPNPermissionsSetup.ps1 | 254 +++++++++--------- 3 files changed, 140 insertions(+), 127 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4877a6f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{ps1,psm1,psd1}] +indent_style = space +indent_size = 4 + + diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index dceae28..49517b3 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -42,6 +42,7 @@ jobs: # VALIDATE_TERRAFORM_TERRASCAN: true # disabled for now as does not support TF 1.3 optional(type, default) VALIDATE_TERRAFORM_TFLINT: true VALIDATE_YAML: true + VALIDATE_EDITORCONFIG: true # VALIDATE_GO: true # Disabled because it down not work :( # Additional settings: # If a shell script is not executable, the bash-exec diff --git a/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 b/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 index c37b3d9..c6ae702 100644 --- a/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 +++ b/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 @@ -1,5 +1,5 @@ function Invoke-EABillingSPNPermissionsSetup { - <# + <# .SYNOPSIS Creates a new SPN, or uses an existing SPN/MI, and assigns it the 'SubscriptionCreator' role to it to allow it to create subscriptions in the specified EA billing enrollment account. @@ -32,151 +32,151 @@ Creates a new SPN, or uses an existing SPN/MI, and assigns it the 'SubscriptionC - Added simplified inputs for the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters to form the 'eaBillingAccountResourceId' parameter value, instead of having to provide the full resource ID in the 'eaBillingAccountResourceId' parameter. #> - # Check for pre-reqs - #Requires -PSEdition Core - - [CmdletBinding(DefaultParameterSetName = "Default")] - param ( - [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 1, HelpMessage = "Provide the EA enrollment number that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '1234567'. This parameter is only used if the 'eaBillingAccountResourceId' parameter is not provided. It's value is used to create the 'eaBillingAccountResourceId' parameter value, that looks like this: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' (this parameter value is the middle numerical value).")] - [string] - $eaEnrollmentNumber, - - [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 2, HelpMessage = "Provide the EA enrollment Account Number/ID that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '987654'. This parameter is only used if the 'eaBillingAccountResourceId' parameter is not provided. It's value is used to create the 'eaBillingAccountResourceId' parameter value, that looks like this: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' (this parameter value is the middle numerical value).")] - [string] - $eaEnrollmentAccountNumber, - - [Parameter(ParameterSetName = "Advanced", Mandatory = $false, Position = 4, HelpMessage = "Provide the EA enrollment/billing account ID that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/123456'")] - [string] - $eaBillingAccountResourceId, - - [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 3, HelpMessage = "(Optional) Provide an existing Service Principal Name (SPN) (aka Enterprise Application) 'Object ID' to grant the 'SubscriptionCreator' role to on the specified billing account instead of creating a new one. If left blank a new SPN will be created. This can also be the object ID of a Managed Identity's SPN.")] - [string] - $existingSpnMiObjectId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - - [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 5, HelpMessage = "(Optional) Provide a Display Name for the new Service Principal (SPN) (aka Enterprise Application) to be created. If left blank the default value of 'spn-lz-sub-vending' will be used.")] - [string] - $newSpnDisplayName = "spn-lz-sub-vending" - ) - - # Checks - Write-Host "Checking inputs..." -ForegroundColor Cyan - Write-Host "" - - # Check $eaBillingAccountResourceId is valid and populate from $eaEnrollmentNumber and $eaEnrollmentAccountNumber if not provided - if ($eaBillingAccountResourceId -eq $null -or $eaBillingAccountResourceId -eq "") { - Write-Host "eaBillingAccountResourceId paramter value not set, forming parameter value from eaEnrollmentNumber and eaEnrollmentAccountNumber parameters..." -ForegroundColor Magenta - if ($eaEnrollmentNumber -eq $null -or $eaEnrollmentAccountNumber -eq $null -or $eaEnrollmentNumber -eq '' -or $eaEnrollmentAccountNumber -eq '') { - throw "No values provdided for the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters. These parameters are required if the 'eaBillingAccountResourceId' parameter is not provided. Please provide values for these parameters and try again." - } - $eaBillingAccountResourceId = "/providers/Microsoft.Billing/billingAccounts/$eaEnrollmentNumber/enrollmentAccounts/$eaEnrollmentAccountNumber" - Write-Host "eaBillingAccountResourceId parameter value set to '$($eaBillingAccountResourceId)'" -ForegroundColor Green - Write-Host "" - } + # Check for pre-reqs + #Requires -PSEdition Core - # Check $eaBillingAccountResourceId is valid and exists - Write-Host "EA billing account parameters provided..." -ForegroundColor Cyan - Write-Host "Checking the specified EA billing account ID '$($eaBillingAccountResourceId)' exists..." -ForegroundColor Yellow + [CmdletBinding(DefaultParameterSetName = "Default")] + param ( + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 1, HelpMessage = "Provide the EA enrollment number that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '1234567'. This parameter is only used if the 'eaBillingAccountResourceId' parameter is not provided. It's value is used to create the 'eaBillingAccountResourceId' parameter value, that looks like this: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' (this parameter value is the middle numerical value).")] + [string] + $eaEnrollmentNumber, - if ($null -ne $eaBillingAccountResourceId -and $eaBillingAccountResourceId -ne "") { - $geteaBillingAccountResourceId = Invoke-AzRestMethod -Method GET -Path "$($eaBillingAccountResourceId)?api-version=2019-10-01-preview" -ErrorAction SilentlyContinue + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 2, HelpMessage = "Provide the EA enrollment Account Number/ID that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '987654'. This parameter is only used if the 'eaBillingAccountResourceId' parameter is not provided. It's value is used to create the 'eaBillingAccountResourceId' parameter value, that looks like this: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' (this parameter value is the middle numerical value).")] + [string] + $eaEnrollmentAccountNumber, - if ($geteaBillingAccountResourceId.StatusCode -ne 200) { - Write-Error "HTTP Status Code: $($geteaBillingAccountResourceId.StatusCode)" - Write-Error "HTTP Repsone Content: $($geteaBillingAccountResourceId.Content)" - throw "The specified EA billing account ID '$($eaBillingAccountResourceId)' does not exist. Please check the value and try again. Also ensure you are logged in as the EA Account Owner for the specified EA billing account." - } else { - Write-Host "The specified EA billing account ID '$($eaBillingAccountResourceId)' exists. Continuing..." -ForegroundColor Green - Write-Host "" - } - } + [Parameter(ParameterSetName = "Advanced", Mandatory = $false, Position = 4, HelpMessage = "Provide the EA enrollment/billing account ID that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/123456'")] + [string] + $eaBillingAccountResourceId, - # Check $existingSpnMiObjectId is valid and exists - if ($existingSpnMiObjectId -ne $null -and $existingSpnMiObjectId -ne "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") { - Write-Host "Existing SPN/MI provided..." -ForegroundColor Cyan - Write-Host "Checking the specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' exists..." -ForegroundColor Yellow - $getexistingSpnMiObjectId = Get-AzADServicePrincipal -ObjectId $existingSpnMiObjectId -ErrorAction SilentlyContinue + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 3, HelpMessage = "(Optional) Provide an existing Service Principal Name (SPN) (aka Enterprise Application) 'Object ID' to grant the 'SubscriptionCreator' role to on the specified billing account instead of creating a new one. If left blank a new SPN will be created. This can also be the object ID of a Managed Identity's SPN.")] + [string] + $existingSpnMiObjectId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - if ($null -eq $getexistingSpnMiObjectId) { - throw "The specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' does not exist. Please check the value and try again." - } else { - Write-Host "The specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' exists with a Display Name of: '$($getexistingSpnMiObjectId.DisplayName)' with a Type of: '$($getexistingSpnMiObjectId.ServicePrincipalType)'. Continuing..." -ForegroundColor Green - Write-Host "" - $finalSpnMiObjectId = $getexistingSpnMiObjectId.Id - $finalSpnMiAppId = $getexistingSpnMiObjectId.AppId - $finalSpnMiDisplayName = $getexistingSpnMiObjectId.DisplayName - $finalSpnMiType = $getexistingSpnMiObjectId.ServicePrincipalType - } - } else { - # Create a new SPN (aka Enterprise Application) as no existing SPN/MI was provided via the $existingSpnMiObjectId parameter - Write-Host "No Existing SPN/MI provided. Proceeding to create a new SPN (aka Enterprise Application)..." -ForegroundColor Cyan - Write-Host "Creating a new SPN (aka Enterprise Application) with a Display Name of '$($newSpnDisplayName)'..." -ForegroundColor Yellow + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 5, HelpMessage = "(Optional) Provide a Display Name for the new Service Principal (SPN) (aka Enterprise Application) to be created. If left blank the default value of 'spn-lz-sub-vending' will be used.")] + [string] + $newSpnDisplayName = "spn-lz-sub-vending" + ) - $newSpn = New-AzADServicePrincipal -DisplayName $newSpnDisplayName -Description "Service Principal Name (SPN) for the Landing Zone Subscription Vending. See https://aka.ms/lz-vending/bicep or https://aka.ms/lz-vending/tf for more information." -ErrorAction Stop - Write-Host "New SPN (aka Enterprise Application) created with a Display Name of '$($newSpn.DisplayName)' and an Object ID of '$($newSpn.Id)'." -ForegroundColor Green + # Checks + Write-Host "Checking inputs..." -ForegroundColor Cyan Write-Host "" - $finalSpnMiObjectId = $newSpn.Id - $finalSpnMiDisplayName = $newSpn.DisplayName - $finalSpnMiType = $newSpn.ServicePrincipalType - $finalSpnMiAppId = $newSpn.AppId - } + # Check $eaBillingAccountResourceId is valid and populate from $eaEnrollmentNumber and $eaEnrollmentAccountNumber if not provided + if ($eaBillingAccountResourceId -eq $null -or $eaBillingAccountResourceId -eq "") { + Write-Host "eaBillingAccountResourceId paramter value not set, forming parameter value from eaEnrollmentNumber and eaEnrollmentAccountNumber parameters..." -ForegroundColor Magenta + if ($eaEnrollmentNumber -eq $null -or $eaEnrollmentAccountNumber -eq $null -or $eaEnrollmentNumber -eq '' -or $eaEnrollmentAccountNumber -eq '') { + throw "No values provdided for the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters. These parameters are required if the 'eaBillingAccountResourceId' parameter is not provided. Please provide values for these parameters and try again." + } + $eaBillingAccountResourceId = "/providers/Microsoft.Billing/billingAccounts/$eaEnrollmentNumber/enrollmentAccounts/$eaEnrollmentAccountNumber" + Write-Host "eaBillingAccountResourceId parameter value set to '$($eaBillingAccountResourceId)'" -ForegroundColor Green + Write-Host "" + } + + # Check $eaBillingAccountResourceId is valid and exists + Write-Host "EA billing account parameters provided..." -ForegroundColor Cyan + Write-Host "Checking the specified EA billing account ID '$($eaBillingAccountResourceId)' exists..." -ForegroundColor Yellow + + if ($null -ne $eaBillingAccountResourceId -and $eaBillingAccountResourceId -ne "") { + $geteaBillingAccountResourceId = Invoke-AzRestMethod -Method GET -Path "$($eaBillingAccountResourceId)?api-version=2019-10-01-preview" -ErrorAction SilentlyContinue + + if ($geteaBillingAccountResourceId.StatusCode -ne 200) { + Write-Error "HTTP Status Code: $($geteaBillingAccountResourceId.StatusCode)" + Write-Error "HTTP Repsone Content: $($geteaBillingAccountResourceId.Content)" + throw "The specified EA billing account ID '$($eaBillingAccountResourceId)' does not exist. Please check the value and try again. Also ensure you are logged in as the EA Account Owner for the specified EA billing account." + } else { + Write-Host "The specified EA billing account ID '$($eaBillingAccountResourceId)' exists. Continuing..." -ForegroundColor Green + Write-Host "" + } + } + + # Check $existingSpnMiObjectId is valid and exists + if ($existingSpnMiObjectId -ne $null -and $existingSpnMiObjectId -ne "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") { + Write-Host "Existing SPN/MI provided..." -ForegroundColor Cyan + Write-Host "Checking the specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' exists..." -ForegroundColor Yellow + $getexistingSpnMiObjectId = Get-AzADServicePrincipal -ObjectId $existingSpnMiObjectId -ErrorAction SilentlyContinue + + if ($null -eq $getexistingSpnMiObjectId) { + throw "The specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' does not exist. Please check the value and try again." + } else { + Write-Host "The specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' exists with a Display Name of: '$($getexistingSpnMiObjectId.DisplayName)' with a Type of: '$($getexistingSpnMiObjectId.ServicePrincipalType)'. Continuing..." -ForegroundColor Green + Write-Host "" + $finalSpnMiObjectId = $getexistingSpnMiObjectId.Id + $finalSpnMiAppId = $getexistingSpnMiObjectId.AppId + $finalSpnMiDisplayName = $getexistingSpnMiObjectId.DisplayName + $finalSpnMiType = $getexistingSpnMiObjectId.ServicePrincipalType + } + } else { + # Create a new SPN (aka Enterprise Application) as no existing SPN/MI was provided via the $existingSpnMiObjectId parameter + Write-Host "No Existing SPN/MI provided. Proceeding to create a new SPN (aka Enterprise Application)..." -ForegroundColor Cyan + Write-Host "Creating a new SPN (aka Enterprise Application) with a Display Name of '$($newSpnDisplayName)'..." -ForegroundColor Yellow + + $newSpn = New-AzADServicePrincipal -DisplayName $newSpnDisplayName -Description "Service Principal Name (SPN) for the Landing Zone Subscription Vending. See https://aka.ms/lz-vending/bicep or https://aka.ms/lz-vending/tf for more information." -ErrorAction Stop + Write-Host "New SPN (aka Enterprise Application) created with a Display Name of '$($newSpn.DisplayName)' and an Object ID of '$($newSpn.Id)'." -ForegroundColor Green + Write-Host "" + + $finalSpnMiObjectId = $newSpn.Id + $finalSpnMiDisplayName = $newSpn.DisplayName + $finalSpnMiType = $newSpn.ServicePrincipalType + $finalSpnMiAppId = $newSpn.AppId + } - ## convert this to a retry loop - # Start-Sleep -Seconds 15 + ## convert this to a retry loop + # Start-Sleep -Seconds 15 - # Grant SPN/MI access to the specified EA billing account - Write-Host "Pre-reqs passed and complete..." -ForegroundColor Cyan - Write-Host "Granting the 'SubscriptionCreator' role (ID: 'a0bcee42-bf30-4d1b-926a-48d21664ef71') on the EA Billing Account ID of: '$($eaBillingAccountResourceId)' to the AAD Object ID of: '$($finalSpnMiObjectId)' which has the Display Name of: '$($finalSpnMiDisplayName)'..." -ForegroundColor Yellow + # Grant SPN/MI access to the specified EA billing account + Write-Host "Pre-reqs passed and complete..." -ForegroundColor Cyan + Write-Host "Granting the 'SubscriptionCreator' role (ID: 'a0bcee42-bf30-4d1b-926a-48d21664ef71') on the EA Billing Account ID of: '$($eaBillingAccountResourceId)' to the AAD Object ID of: '$($finalSpnMiObjectId)' which has the Display Name of: '$($finalSpnMiDisplayName)'..." -ForegroundColor Yellow - # Get the current AAD Tenant ID - $currentTenant = Get-AzTenant -ErrorAction Stop + # Get the current AAD Tenant ID + $currentTenant = Get-AzTenant -ErrorAction Stop - # Create GUID for role assignment name - $roleAssignmentName = New-Guid + # Create GUID for role assignment name + $roleAssignmentName = New-Guid - $roleAssignmentHashTable = [ordered]@{ - "properties" = @{ - "principalId" = "$finalSpnMiObjectId" - "roleDefinitionId" = "$eaBillingAccountResourceId/billingRoleDefinitions/a0bcee42-bf30-4d1b-926a-48d21664ef71" - "principalTenantId" = "$($currentTenant.TenantId)" + $roleAssignmentHashTable = [ordered]@{ + "properties" = @{ + "principalId" = "$finalSpnMiObjectId" + "roleDefinitionId" = "$eaBillingAccountResourceId/billingRoleDefinitions/a0bcee42-bf30-4d1b-926a-48d21664ef71" + "principalTenantId" = "$($currentTenant.TenantId)" + } } - } - $roleAssignmentPayloadJson = $roleAssignmentHashTable | ConvertTo-Json -Depth 100 + $roleAssignmentPayloadJson = $roleAssignmentHashTable | ConvertTo-Json -Depth 100 - $grantRbac = Invoke-AzRestMethod -Method PUT -Path "$($eaBillingAccountResourceId)/billingRoleAssignments/$($roleAssignmentName)?api-version=2019-10-01-preview" -Payload $roleAssignmentPayloadJson -ErrorAction SilentlyContinue + $grantRbac = Invoke-AzRestMethod -Method PUT -Path "$($eaBillingAccountResourceId)/billingRoleAssignments/$($roleAssignmentName)?api-version=2019-10-01-preview" -Payload $roleAssignmentPayloadJson -ErrorAction SilentlyContinue - # Create variables for retry loop - $retryCount = 0 - $retryLimit = 10 - $retryDelay = 5 + # Create variables for retry loop + $retryCount = 0 + $retryLimit = 10 + $retryDelay = 5 - if ($grantRbac.StatusCode -eq 400 -and $grantRbac.Content.Contains("are not valid")) { - while ($retryCount -le $retryLimit) { - Write-Host "The 'SubscriptionCreator' role has not been granted to the SPN/MI. Retrying in $retryDelay seconds to allow platform replication to occur..." -ForegroundColor Magenta + if ($grantRbac.StatusCode -eq 400 -and $grantRbac.Content.Contains("are not valid")) { + while ($retryCount -le $retryLimit) { + Write-Host "The 'SubscriptionCreator' role has not been granted to the SPN/MI. Retrying in $retryDelay seconds to allow platform replication to occur..." -ForegroundColor Magenta - Start-Sleep -Seconds $retryDelay - $retryCount++ + Start-Sleep -Seconds $retryDelay + $retryCount++ - $grantRbac = Invoke-AzRestMethod -Method PUT -Path "$($eaBillingAccountResourceId)/billingRoleAssignments/$($roleAssignmentName)?api-version=2019-10-01-preview" -Payload $roleAssignmentPayloadJson -ErrorAction SilentlyContinue + $grantRbac = Invoke-AzRestMethod -Method PUT -Path "$($eaBillingAccountResourceId)/billingRoleAssignments/$($roleAssignmentName)?api-version=2019-10-01-preview" -Payload $roleAssignmentPayloadJson -ErrorAction SilentlyContinue - if ($grantRbac.StatusCode -eq 200) { - break - } + if ($grantRbac.StatusCode -eq 200) { + break + } + } } - } - if ($grantRbac.StatusCode -ne 200) { - Write-Error "HTTP Status Code: $($grantRbac.StatusCode)" - Write-Error "HTTP Repsone Content: $($grantRbac.Content)" - throw "An error occurred while attempting to grant the 'SubscriptionCreator' role to the SPN/MI. Please check the error message above and try again." - } else { - Write-Host "The 'SubscriptionCreator' role has been granted to the SPN/MI." -ForegroundColor Green - Write-Host "" - Write-Host "The SPN/MI 'Object ID' is: '$($finalSpnMiObjectId)'" -ForegroundColor Green - Write-Host "The SPN/MI 'App ID' is: '$($finalSpnMiAppId)'" -ForegroundColor Green - Write-Host "The SPN/MI 'Display Name' is: '$($finalSpnMiDisplayName)'" -ForegroundColor Green - Write-Host "The SPN/MI 'Type' is: '$($finalSpnMiType)'" -ForegroundColor Green - } - - return -} \ No newline at end of file + if ($grantRbac.StatusCode -ne 200) { + Write-Error "HTTP Status Code: $($grantRbac.StatusCode)" + Write-Error "HTTP Repsone Content: $($grantRbac.Content)" + throw "An error occurred while attempting to grant the 'SubscriptionCreator' role to the SPN/MI. Please check the error message above and try again." + } else { + Write-Host "The 'SubscriptionCreator' role has been granted to the SPN/MI." -ForegroundColor Green + Write-Host "" + Write-Host "The SPN/MI 'Object ID' is: '$($finalSpnMiObjectId)'" -ForegroundColor Green + Write-Host "The SPN/MI 'App ID' is: '$($finalSpnMiAppId)'" -ForegroundColor Green + Write-Host "The SPN/MI 'Display Name' is: '$($finalSpnMiDisplayName)'" -ForegroundColor Green + Write-Host "The SPN/MI 'Type' is: '$($finalSpnMiType)'" -ForegroundColor Green + } + + return +} From 0c76c95ab883e929085966e633aeaac57f3b688f Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:35:01 +0000 Subject: [PATCH 08/10] editorconfig fixes --- .gitignore | 2 +- docs/CHANGELOG.md | 39 -------------- src/ALZ/ALZ.psm1 | 2 +- .../Convert-BicepConfigToInputConfig.ps1 | 22 ++++---- .../Convert-HCLVariablesToInputConfig.ps1 | 22 ++++---- .../Convert-ParametersToInputConfig.ps1 | 16 +++--- .../Format-TokenizedConfigurationString.ps1 | 2 +- .../Private/Config-Helpers/Get-ALZConfig.ps1 | 14 ++--- .../Get-AvailabilityZonesSupport.ps1 | 2 +- .../Config-Helpers/Get-AzureRegionData.ps1 | 52 +++++++++---------- .../Remove-TerraformMetaFileSet.ps1 | 10 ++-- .../Remove-UnrequiredFileSet.ps1 | 30 +++++------ .../Set-ComputedConfiguration.ps1 | 4 +- .../Private/Config-Helpers/Write-JsonFile.ps1 | 6 +-- .../Config-Helpers/Write-TfvarsJsonFile.ps1 | 12 ++--- .../Get-ExistingLocalRelease.ps1 | 4 +- .../Invoke-Terraform.ps1 | 38 +++++++------- .../New-Bootstrap.ps1 | 2 +- .../New-FolderStructure.ps1 | 10 ++-- .../Private/Shared/Test-Utility-Functions.ps1 | 2 +- .../Shared/Write-InformationColored.ps1 | 4 +- src/PSScriptAnalyzerSettings.psd1 | 6 +-- .../Set-ComputedConfiguration.Tests.ps1 | 32 ++++++------ 23 files changed, 147 insertions(+), 186 deletions(-) delete mode 100644 docs/CHANGELOG.md diff --git a/.gitignore b/.gitignore index 4e4bfe1..efa7b79 100644 --- a/.gitignore +++ b/.gitignore @@ -54,4 +54,4 @@ templates/.test_azuredevops templates/.test_github .vscode/settings.json /ALZ -.tools \ No newline at end of file +.tools diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md deleted file mode 100644 index a1d84e3..0000000 --- a/docs/CHANGELOG.md +++ /dev/null @@ -1,39 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.2.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [0.2.4] - -- Add support for Terraform - -## [0.2.3] - -- fix min prefix length from 3 to 2 - -## [0.2.2] - -- Add fix for resource group name reference for LAW id -- Change supported ALZ version to v0.14.0 - -## [0.2.1] - -- Fixed the issue by adding in two new computed values to correlate to -each parameter -- Changed alz bicep config for the soon to be new release - -## [0.2.0] - -- Need to adjust the policy assignment param file as switched to the ALZ -defaults. -- Need to switch to orchestration version of management diagnostic -setting module param file. -- Switching custom modules directory to match naming convention of -custom parameters directory. - -## [0.1.4] - -- Adding Computed values to Environment -- Adding file targeted config to ALZ-BICEP-CONFIG \ No newline at end of file diff --git a/src/ALZ/ALZ.psm1 b/src/ALZ/ALZ.psm1 index 430dd75..f676807 100644 --- a/src/ALZ/ALZ.psm1 +++ b/src/ALZ/ALZ.psm1 @@ -28,4 +28,4 @@ foreach ($file in @($public + $private)) { # export all public functions -Export-ModuleMember -Function $public.Basename -Alias * \ No newline at end of file +Export-ModuleMember -Function $public.Basename -Alias * diff --git a/src/ALZ/Private/Config-Helpers/Convert-BicepConfigToInputConfig.ps1 b/src/ALZ/Private/Config-Helpers/Convert-BicepConfigToInputConfig.ps1 index 2980f54..f589c38 100644 --- a/src/ALZ/Private/Config-Helpers/Convert-BicepConfigToInputConfig.ps1 +++ b/src/ALZ/Private/Config-Helpers/Convert-BicepConfigToInputConfig.ps1 @@ -14,13 +14,13 @@ function Convert-BicepConfigToInputConfig { if ($PSCmdlet.ShouldProcess("Parse Interface Variables into Config", "modify")) { $configItems = [PSCustomObject]@{} - if($appendToObject -ne $null) { + if ($appendToObject -ne $null) { $configItems = $appendToObject } Write-Verbose $validators - foreach($variable in $bicepConfig.inputs.PSObject.Properties) { + foreach ($variable in $bicepConfig.inputs.PSObject.Properties) { Write-Verbose "Parsing variable $($variable.Name)" $description = $variable.Value.description @@ -28,38 +28,38 @@ function Convert-BicepConfigToInputConfig { $configItem | Add-Member -NotePropertyName "Source" -NotePropertyValue $variable.Value.source $configItem | Add-Member -NotePropertyName "Value" -NotePropertyValue "" - if($variable.Value.PSObject.Properties.Name -contains "sourceInput") { + if ($variable.Value.PSObject.Properties.Name -contains "sourceInput") { $configItem | Add-Member -NotePropertyName "SourceInput" -NotePropertyValue $variable.Value.sourceInput } - if($variable.Value.PSObject.Properties.Name -contains "pattern") { + if ($variable.Value.PSObject.Properties.Name -contains "pattern") { $configItem | Add-Member -NotePropertyName "Pattern" -NotePropertyValue $variable.Value.pattern } - if($variable.Value.PSObject.Properties.Name -contains "process") { + if ($variable.Value.PSObject.Properties.Name -contains "process") { $configItem | Add-Member -NotePropertyName "Process" -NotePropertyValue $variable.Value.process } - if($variable.Value.PSObject.Properties.Name -contains "default") { + if ($variable.Value.PSObject.Properties.Name -contains "default") { $defaultValue = $variable.Value.default $configItem | Add-Member -NotePropertyName "DefaultValue" -NotePropertyValue $defaultValue } - if($variable.Value.PSObject.Properties.Name -contains "validation") { + if ($variable.Value.PSObject.Properties.Name -contains "validation") { $validationType = $variable.Value.validation $validator = $validators.PSObject.Properties[$validationType].Value $description = "$description ($($validator.Description))" Write-Verbose "Adding $($variable.Value.validation) validation for $($variable.Name). Validation type: $($validator.Type)" - if($validator.Type -eq "AllowedValues"){ + if ($validator.Type -eq "AllowedValues") { $configItem | Add-Member -NotePropertyName "AllowedValues" -NotePropertyValue $validator.AllowedValues } - if($validator.Type -eq "Valid"){ + if ($validator.Type -eq "Valid") { $configItem | Add-Member -NotePropertyName "Valid" -NotePropertyValue $validator.Valid } $configItem | Add-Member -NotePropertyName "Validator" -NotePropertyValue $validationType } - if($variable.Value.PSObject.Properties.Name -contains "targets") { + if ($variable.Value.PSObject.Properties.Name -contains "targets") { $configItem | Add-Member -NotePropertyName "targets" -NotePropertyValue $variable.Value.targets } @@ -69,4 +69,4 @@ function Convert-BicepConfigToInputConfig { } return $configItems -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Convert-HCLVariablesToInputConfig.ps1 b/src/ALZ/Private/Config-Helpers/Convert-HCLVariablesToInputConfig.ps1 index dfd3e42..dfde3d7 100644 --- a/src/ALZ/Private/Config-Helpers/Convert-HCLVariablesToInputConfig.ps1 +++ b/src/ALZ/Private/Config-Helpers/Convert-HCLVariablesToInputConfig.ps1 @@ -17,7 +17,7 @@ function Convert-HCLVariablesToInputConfig { if ($PSCmdlet.ShouldProcess("Parse HCL Variables into Config", "modify")) { $terraformVariables = & $hclParserToolPath $targetVariableFile | ConvertFrom-Json - if($terraformVariables.PSObject.Properties.Name -notcontains "variable") { + if ($terraformVariables.PSObject.Properties.Name -notcontains "variable") { Write-Verbose "No variables found in $targetVariableFile, skipping..." return $appendToObject } @@ -25,22 +25,22 @@ function Convert-HCLVariablesToInputConfig { Write-Verbose "Variables found in $targetVariableFile, processing..." $configItems = [PSCustomObject]@{} - if($appendToObject -ne $null) { + if ($appendToObject -ne $null) { $configItems = $appendToObject } - foreach($variable in $terraformVariables.variable.PSObject.Properties) { - if($variable.Value[0].PSObject.Properties.Name -contains "description") { + foreach ($variable in $terraformVariables.variable.PSObject.Properties) { + if ($variable.Value[0].PSObject.Properties.Name -contains "description") { $description = $variable.Value[0].description $validationTypeSplit = $description -split "\|" $hasValidation = $false - if($validationTypeSplit.Length -gt 1) { + if ($validationTypeSplit.Length -gt 1) { $description = $validationTypeSplit[0].Trim() } - if($validationTypeSplit.Length -eq 2) { + if ($validationTypeSplit.Length -eq 2) { $splitItem = $validationTypeSplit[1].Trim() $validationType = $splitItem $hasValidation = $true @@ -51,17 +51,17 @@ function Convert-HCLVariablesToInputConfig { $configItem | Add-Member -NotePropertyName "Value" -NotePropertyValue "" $configItem | Add-Member -NotePropertyName "Source" -NotePropertyValue "input" - if($variable.Value[0].PSObject.Properties.Name -contains "default") { + if ($variable.Value[0].PSObject.Properties.Name -contains "default") { $configItem | Add-Member -NotePropertyName "DefaultValue" -NotePropertyValue $variable.Value[0].default } - if($hasValidation) { + if ($hasValidation) { $validator = $validators.PSObject.Properties[$validationType].Value $description = "$description ($($validator.Description))" - if($validator.Type -eq "AllowedValues"){ + if ($validator.Type -eq "AllowedValues") { $configItem | Add-Member -NotePropertyName "AllowedValues" -NotePropertyValue $validator.AllowedValues } - if($validator.Type -eq "Valid"){ + if ($validator.Type -eq "Valid") { $configItem | Add-Member -NotePropertyName "Valid" -NotePropertyValue $validator.Valid } $configItem | Add-Member -NotePropertyName "Validator" -NotePropertyValue $validationType @@ -75,4 +75,4 @@ function Convert-HCLVariablesToInputConfig { } return $configItems -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Convert-ParametersToInputConfig.ps1 b/src/ALZ/Private/Config-Helpers/Convert-ParametersToInputConfig.ps1 index 4ba8458..3f65b04 100644 --- a/src/ALZ/Private/Config-Helpers/Convert-ParametersToInputConfig.ps1 +++ b/src/ALZ/Private/Config-Helpers/Convert-ParametersToInputConfig.ps1 @@ -6,12 +6,12 @@ function Convert-ParametersToInputConfig { [hashtable] $parameters ) - foreach($parameterKey in $parameters.Keys) { + foreach ($parameterKey in $parameters.Keys) { $parameter = $parameters[$parameterKey] Write-Verbose "Processing parameter $parameterKey $(ConvertTo-Json $parameter -Depth 100)" - foreach($parameterAlias in $parameter.aliases) { - if($inputConfig.PsObject.Properties.Name -contains $parameterAlias) { + foreach ($parameterAlias in $parameter.aliases) { + if ($inputConfig.PsObject.Properties.Name -contains $parameterAlias) { Write-Verbose "Alias $parameterAlias exists in input config, renaming..." $configItem = $inputConfig.PSObject.Properties | Where-Object { $_.Name -eq $parameterAlias } $inputConfig | Add-Member -NotePropertyName $parameterKey -NotePropertyValue @{ @@ -23,17 +23,17 @@ function Convert-ParametersToInputConfig { } } - if($inputConfig.PsObject.Properties.Name -notcontains $parameterKey) { + if ($inputConfig.PsObject.Properties.Name -notcontains $parameterKey) { $variableValue = [Environment]::GetEnvironmentVariable("ALZ_$($parameterKey)") - if($null -eq $variableValue) { - if($parameter.type -eq "SwitchParameter") { + if ($null -eq $variableValue) { + if ($parameter.type -eq "SwitchParameter") { $variableValue = $parameter.value.IsPresent } else { $variableValue = $parameter.value } } - if($parameter.type -eq "SwitchParameter") { + if ($parameter.type -eq "SwitchParameter") { $variableValue = [bool]::Parse($variableValue) } Write-Verbose "Adding parameter $parameterKey with value $variableValue" @@ -45,4 +45,4 @@ function Convert-ParametersToInputConfig { } return $inputConfig -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Format-TokenizedConfigurationString.ps1 b/src/ALZ/Private/Config-Helpers/Format-TokenizedConfigurationString.ps1 index 1755d8b..ee05dd6 100644 --- a/src/ALZ/Private/Config-Helpers/Format-TokenizedConfigurationString.ps1 +++ b/src/ALZ/Private/Config-Helpers/Format-TokenizedConfigurationString.ps1 @@ -23,4 +23,4 @@ function Format-TokenizedConfigurationString { } return $returnValue -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Get-ALZConfig.ps1 b/src/ALZ/Private/Config-Helpers/Get-ALZConfig.ps1 index 67ed1c5..90b1217 100644 --- a/src/ALZ/Private/Config-Helpers/Get-ALZConfig.ps1 +++ b/src/ALZ/Private/Config-Helpers/Get-ALZConfig.ps1 @@ -8,19 +8,19 @@ function Get-ALZConfig { [string] $hclParserToolPath = "" ) - if(!(Test-Path $configFilePath)) { + if (!(Test-Path $configFilePath)) { Write-Error "The config file does not exist at $configFilePath" throw "The config file does not exist at $configFilePath" } - if($null -eq $inputConfig) { + if ($null -eq $inputConfig) { $inputConfig = [PSCustomObject]@{} } # Import the config and transform it to a PowerShell object $extension = (Get-Item -Path $configFilePath).Extension.ToLower() $config = $null - if($extension -eq ".yml" -or $extension -eq ".yaml") { + if ($extension -eq ".yml" -or $extension -eq ".yaml") { if (!(Get-Module -ListAvailable -Name powershell-Yaml)) { Write-Host "Installing YAML module" Install-Module powershell-Yaml -Force @@ -33,7 +33,7 @@ function Get-ALZConfig { throw $errorMessage } - } elseif($extension -eq ".json") { + } elseif ($extension -eq ".json") { try { $config = [PSCustomObject](Get-Content -Path $configFilePath | ConvertFrom-Json) } catch { @@ -41,7 +41,7 @@ function Get-ALZConfig { Write-Error $errorMessage throw $errorMessage } - } elseif($extension -eq ".tfvars") { + } elseif ($extension -eq ".tfvars") { try { $config = [PSCustomObject](& $hclParserToolPath $configFilePath | ConvertFrom-Json) } catch { @@ -55,7 +55,7 @@ function Get-ALZConfig { Write-Verbose "Config file loaded from $configFilePath with $($config.PSObject.Properties.Name.Count) properties." - foreach($property in $config.PSObject.Properties) { + foreach ($property in $config.PSObject.Properties) { $inputConfig | Add-Member -NotePropertyName $property.Name -NotePropertyValue @{ Value = $property.Value Source = $extension @@ -63,4 +63,4 @@ function Get-ALZConfig { } return $inputConfig -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Get-AvailabilityZonesSupport.ps1 b/src/ALZ/Private/Config-Helpers/Get-AvailabilityZonesSupport.ps1 index 4095ff0..80eebd6 100644 --- a/src/ALZ/Private/Config-Helpers/Get-AvailabilityZonesSupport.ps1 +++ b/src/ALZ/Private/Config-Helpers/Get-AvailabilityZonesSupport.ps1 @@ -12,4 +12,4 @@ function Get-AvailabilityZonesSupport { $jsonZones = ConvertTo-Json $zone.zones -Depth 10 Write-Verbose "Zones for $region are $jsonZones" return $zone.zones -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Get-AzureRegionData.ps1 b/src/ALZ/Private/Config-Helpers/Get-AzureRegionData.ps1 index 384e1ee..baf8737 100644 --- a/src/ALZ/Private/Config-Helpers/Get-AzureRegionData.ps1 +++ b/src/ALZ/Private/Config-Helpers/Get-AzureRegionData.ps1 @@ -5,38 +5,38 @@ function Get-AzureRegionData { ) $terraformCode = @' -terraform { - required_providers { - azapi = { - source = "azure/azapi" - version = "~> 2.0" + terraform { + required_providers { + azapi = { + source = "azure/azapi" + version = "~> 2.0" + } + } } - } -} -module "regions" { - source = "Azure/avm-utl-regions/azurerm" - version = "0.3.0" - use_cached_data = false - availability_zones_filter = false - recommended_filter = false -} + module "regions" { + source = "Azure/avm-utl-regions/azurerm" + version = "0.3.0" + use_cached_data = false + availability_zones_filter = false + recommended_filter = false + } -locals { - regions = { for region in module.regions.regions_by_name : region.name => { - display_name = region.display_name - zones = region.zones == null ? [] : [for zone in region.zones : tostring(zone)] + locals { + regions = { for region in module.regions.regions_by_name : region.name => { + display_name = region.display_name + zones = region.zones == null ? [] : [for zone in region.zones : tostring(zone)] + } + } } - } -} -output "regions_and_zones" { - value = local.regions -} + output "regions_and_zones" { + value = local.regions + } '@ $regionFolder = Join-Path $toolsPath "azure-regions" - if(Test-Path $regionFolder) { + if (Test-Path $regionFolder) { Remove-Item $regionFolder -Recurse -Force } @@ -55,7 +55,7 @@ output "regions_and_zones" { $zonesSupport = @() $supportedRegions = @() - foreach($region in $regionsAndZones.PSObject.Properties) { + foreach ($region in $regionsAndZones.PSObject.Properties) { $supportedRegions += $region.Name $zonesSupport += @{ region = $region.Name @@ -67,4 +67,4 @@ output "regions_and_zones" { zonesSupport = $zonesSupport supportedRegions = $supportedRegions } -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Remove-TerraformMetaFileSet.ps1 b/src/ALZ/Private/Config-Helpers/Remove-TerraformMetaFileSet.ps1 index d2f0d03..72023f5 100644 --- a/src/ALZ/Private/Config-Helpers/Remove-TerraformMetaFileSet.ps1 +++ b/src/ALZ/Private/Config-Helpers/Remove-TerraformMetaFileSet.ps1 @@ -18,8 +18,8 @@ function Remove-TerraformMetaFileSet { [switch]$writeVerboseLogs ) - if($PSCmdlet.ShouldProcess("Remove files", "modify")) { - if($terraformFilesOrFoldersToRemove.Length -eq 0 ) { + if ($PSCmdlet.ShouldProcess("Remove files", "modify")) { + if ($terraformFilesOrFoldersToRemove.Length -eq 0 ) { Write-Verbose "No folders or files specified, so not removing aything from $path" return } @@ -27,12 +27,12 @@ function Remove-TerraformMetaFileSet { $filesAndFolders = Get-ChildItem -Path $path -Force foreach ($fileOrFolder in $filesAndFolders) { - if($terraformFilesOrFoldersToRemove -contains $fileOrFolder.Name) { - if($writeVerboseLogs) { + if ($terraformFilesOrFoldersToRemove -contains $fileOrFolder.Name) { + if ($writeVerboseLogs) { Write-Verbose "Exact Match - Removing: $($fileOrFolder.FullName)" } Remove-Item -Path $fileOrFolder.FullName -Force -Recurse | Out-Null } } } -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Remove-UnrequiredFileSet.ps1 b/src/ALZ/Private/Config-Helpers/Remove-UnrequiredFileSet.ps1 index ebb72a5..b6f3b46 100644 --- a/src/ALZ/Private/Config-Helpers/Remove-UnrequiredFileSet.ps1 +++ b/src/ALZ/Private/Config-Helpers/Remove-UnrequiredFileSet.ps1 @@ -11,8 +11,8 @@ function Remove-UnrequiredFileSet { [switch]$writeVerboseLogs ) - if($PSCmdlet.ShouldProcess("Remove files", "modify")) { - if($foldersOrFilesToRetain.Length -eq 0 -and $subFoldersOrFilesToRemove.Length -eq 0) { + if ($PSCmdlet.ShouldProcess("Remove files", "modify")) { + if ($foldersOrFilesToRetain.Length -eq 0 -and $subFoldersOrFilesToRemove.Length -eq 0) { Write-Verbose "No folders or files to retain specified, so not removing aything from $path" return } @@ -25,8 +25,8 @@ function Remove-UnrequiredFileSet { $folderRelativePath = $file.Directory.FullName.Replace($path, "").Replace("\", "/").TrimStart("/") foreach ($folderOrFileToRetain in $foldersOrFilesToRetain) { # If we have an exact match of the file name and path, always retain it. - if($folderOrFileToRetain.TrimStart("./") -eq $fileRelativePath) { - if($writeVerboseLogs) { + if ($folderOrFileToRetain.TrimStart("./") -eq $fileRelativePath) { + if ($writeVerboseLogs) { Write-Verbose "Exact Match - Retaining: $fileRelativePath at $($file.FullName)" } $filesToRetain += $file.FullName @@ -36,14 +36,14 @@ function Remove-UnrequiredFileSet { # If we match on a pattern, take into account the subfolders or files to remove. if ($fileRelativePath -like "$folderOrFileToRetain*") { $skipFile = $false - foreach($subfolderOrFileToRemove in $subFoldersOrFilesToRemove) { + foreach ($subfolderOrFileToRemove in $subFoldersOrFilesToRemove) { if ($file.Name -eq $subfolderOrFileToRemove -or $file.Directory.Name -eq $subfolderOrFileToRemove -or $fileRelativePath.EndsWith("/$subfolderOrFileToRemove") -or $folderRelativePath.EndsWith("/$subfolderOrFileToRemove")) { $skipFile = $true } } - if(!$skipFile) { - if($writeVerboseLogs) { + if (!$skipFile) { + if ($writeVerboseLogs) { Write-Verbose "Pattern Match - Retaining: $fileRelativePath at $($file.FullName)" } $filesToRetain += $file.FullName @@ -52,9 +52,9 @@ function Remove-UnrequiredFileSet { } } - foreach($file in $files) { - if($filesToRetain -notcontains $file.FullName) { - if($writeVerboseLogs) { + foreach ($file in $files) { + if ($filesToRetain -notcontains $file.FullName) { + if ($writeVerboseLogs) { Write-Verbose "Removing: $($file.FullName)" } Remove-Item -Path $file.FullName -Force | Out-Null @@ -62,11 +62,11 @@ function Remove-UnrequiredFileSet { } $folders = Get-ChildItem -Path $path -Directory -Recurse -Force - foreach($folder in $folders) { - if(Test-Path $folder.FullName) { + foreach ($folder in $folders) { + if (Test-Path $folder.FullName) { $folderItems = Get-ChildItem -Path $folder.FullName -Recurse -File -Force - if($folderItems.Count -eq 0) { - if($writeVerboseLogs) { + if ($folderItems.Count -eq 0) { + if ($writeVerboseLogs) { Write-Verbose "Removing empty folder: $($folder.FullName)" } Remove-Item -Path $folder.FullName -Force -Recurse | Out-Null @@ -74,4 +74,4 @@ function Remove-UnrequiredFileSet { } } } -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Set-ComputedConfiguration.ps1 b/src/ALZ/Private/Config-Helpers/Set-ComputedConfiguration.ps1 index d009502..a7b1277 100644 --- a/src/ALZ/Private/Config-Helpers/Set-ComputedConfiguration.ps1 +++ b/src/ALZ/Private/Config-Helpers/Set-ComputedConfiguration.ps1 @@ -14,7 +14,7 @@ function Set-ComputedConfiguration { if ($configKey.Value.Value -is [array]) { $formattedValues = @() - foreach($formatString in $configKey.Value.Value) { + foreach ($formatString in $configKey.Value.Value) { $formattedValues += Format-TokenizedConfigurationString -tokenizedString $formatString -configuration $configuration } @@ -37,4 +37,4 @@ function Set-ComputedConfiguration { } } } -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Write-JsonFile.ps1 b/src/ALZ/Private/Config-Helpers/Write-JsonFile.ps1 index fba6e22..e3910b4 100644 --- a/src/ALZ/Private/Config-Helpers/Write-JsonFile.ps1 +++ b/src/ALZ/Private/Config-Helpers/Write-JsonFile.ps1 @@ -10,7 +10,7 @@ function Write-JsonFile { if ($PSCmdlet.ShouldProcess("Download Terraform Tools", "modify")) { - if(Test-Path $jsonFilePath) { + if (Test-Path $jsonFilePath) { Remove-Item -Path $jsonFilePath } @@ -18,7 +18,7 @@ function Write-JsonFile { foreach ($configKey in $configuration.PsObject.Properties | Sort-Object Name) { foreach ($target in $configKey.Value.Targets) { - if($target.Destination -eq "Environment") { + if ($target.Destination -eq "Environment") { $environmentVariables.$($target.Name) = $configKey.Value.Value } } @@ -27,4 +27,4 @@ function Write-JsonFile { $json = ConvertTo-Json -InputObject $environmentVariables -Depth 100 $json | Out-File -FilePath $jsonFilePath } -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Config-Helpers/Write-TfvarsJsonFile.ps1 b/src/ALZ/Private/Config-Helpers/Write-TfvarsJsonFile.ps1 index 65ca0b5..dac277a 100644 --- a/src/ALZ/Private/Config-Helpers/Write-TfvarsJsonFile.ps1 +++ b/src/ALZ/Private/Config-Helpers/Write-TfvarsJsonFile.ps1 @@ -13,26 +13,26 @@ function Write-TfvarsJsonFile { if ($PSCmdlet.ShouldProcess("Download Terraform Tools", "modify")) { - if(Test-Path $tfvarsFilePath) { + if (Test-Path $tfvarsFilePath) { Remove-Item -Path $tfvarsFilePath } $jsonObject = [ordered]@{} - foreach($configurationProperty in $configuration.PSObject.Properties | Sort-Object Name) { - if($skipItems -contains $configurationProperty.Name) { + foreach ($configurationProperty in $configuration.PSObject.Properties | Sort-Object Name) { + if ($skipItems -contains $configurationProperty.Name) { Write-Verbose "Skipping configuration property: $($configurationProperty.Name)" continue } - + $configurationValue = $configurationProperty.Value.Value - if($null -ne $configurationValue -and $configurationValue.ToString() -eq "sourced-from-env") { + if ($null -ne $configurationValue -and $configurationValue.ToString() -eq "sourced-from-env") { Write-Verbose "Sourced from env var: $($configurationProperty.Name)" continue } - if($configurationProperty.Value.Validator -eq "configuration_file_path") { + if ($configurationProperty.Value.Validator -eq "configuration_file_path") { $configurationValue = [System.IO.Path]::GetFileName($configurationValue) } diff --git a/src/ALZ/Private/Deploy-Accelerator-Helpers/Get-ExistingLocalRelease.ps1 b/src/ALZ/Private/Deploy-Accelerator-Helpers/Get-ExistingLocalRelease.ps1 index 0696c18..701f7f7 100644 --- a/src/ALZ/Private/Deploy-Accelerator-Helpers/Get-ExistingLocalRelease.ps1 +++ b/src/ALZ/Private/Deploy-Accelerator-Helpers/Get-ExistingLocalRelease.ps1 @@ -11,7 +11,7 @@ function Get-ExistingLocalRelease { $path = "" $checkPath = Join-Path $targetDirectory $targetFolder $checkFolders = Get-ChildItem -Path $checkPath -Directory - if($null -ne $checkFolders) { + if ($null -ne $checkFolders) { $checkFolders = $checkFolders | Sort-Object { $_.Name } -Descending $mostRecentCheckFolder = $checkFolders[0] @@ -26,4 +26,4 @@ function Get-ExistingLocalRelease { releaseTag = $releaseTag path = $path } -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Deploy-Accelerator-Helpers/Invoke-Terraform.ps1 b/src/ALZ/Private/Deploy-Accelerator-Helpers/Invoke-Terraform.ps1 index 4d96d8d..8d5fd1b 100644 --- a/src/ALZ/Private/Deploy-Accelerator-Helpers/Invoke-Terraform.ps1 +++ b/src/ALZ/Private/Deploy-Accelerator-Helpers/Invoke-Terraform.ps1 @@ -26,10 +26,10 @@ function Invoke-Terraform { if ($PSCmdlet.ShouldProcess("Apply Terraform", "modify")) { # Check and Set Subscription ID $removeSubscriptionId = $false - if($null -eq $env:ARM_SUBSCRIPTION_ID -or $env:ARM_SUBSCRIPTION_ID -eq "") { + if ($null -eq $env:ARM_SUBSCRIPTION_ID -or $env:ARM_SUBSCRIPTION_ID -eq "") { Write-Verbose "Setting environment variable ARM_SUBSCRIPTION_ID" $subscriptionId = $(az account show --query id -o tsv) - if($null -eq $subscriptionId -or $subscriptionId -eq "") { + if ($null -eq $subscriptionId -or $subscriptionId -eq "") { Write-Error "Subscription ID not found. Please ensure you are logged in to Azure and have selected a subscription. Use 'az account show' to check." return } @@ -40,11 +40,11 @@ function Invoke-Terraform { terraform -chdir="$moduleFolderPath" init $action = "apply" - if($destroy) { + if ($destroy) { $action = "destroy" } - if(!$silent) { + if (!$silent) { Write-InformationColored "Terraform init has completed, now running the $action..." -ForegroundColor Green -NewLineBefore -InformationAction Continue } @@ -60,7 +60,7 @@ function Invoke-Terraform { $arguments += "plan" $arguments += "-out=$planFileName" $arguments += "-input=false" - if($tfvarsFileName -ne "") { + if ($tfvarsFileName -ne "") { $arguments += "-var-file=$tfvarsFileName" } @@ -68,7 +68,7 @@ function Invoke-Terraform { $arguments += "-destroy" } - if(!$silent) { + if (!$silent) { Write-InformationColored "Running Plan Command for $action : $command $arguments" -ForegroundColor Green -NewLineBefore -InformationAction Continue & $command $arguments } else { @@ -79,23 +79,23 @@ function Invoke-Terraform { # Stop and display timer $StopWatch.Stop() - if(!$silent) { + if (!$silent) { Write-InformationColored "Time taken to complete Terraform plan:" -ForegroundColor Green -NewLineBefore -InformationAction Continue } $StopWatch.Elapsed | Format-Table - if($exitCode -ne 0) { + if ($exitCode -ne 0) { Write-InformationColored "Terraform plan for $action failed with exit code $exitCode. Please review the error and try again or raise an issue." -ForegroundColor Red -NewLineBefore -InformationAction Continue throw "Terraform plan failed with exit code $exitCode. Please review the error and try again or raise an issue." } - if(!$autoApprove) { + if (!$autoApprove) { Write-InformationColored "Terraform plan has completed, please review the plan and confirm you wish to continue." -ForegroundColor Yellow -NewLineBefore -InformationAction Continue $choices = [System.Management.Automation.Host.ChoiceDescription[]] @("&Yes", "&No") $message = "Please confirm you wish to apply the plan." $title = "Confirm Terraform plan" $resultIndex = $host.ui.PromptForChoice($title, $message, $choices, 0) - if($resultIndex -eq 1) { + if ($resultIndex -eq 1) { Write-InformationColored "You have chosen not to apply the plan. Exiting..." -ForegroundColor Red -NewLineBefore -InformationAction Continue return } @@ -113,7 +113,7 @@ function Invoke-Terraform { $arguments += "-input=false" $arguments += "$planFileName" - if(!$silent) { + if (!$silent) { Write-InformationColored "Running Apply Command for $action : $command $arguments" -ForegroundColor Green -NewLineBefore -InformationAction Continue & $command $arguments } else { @@ -125,7 +125,7 @@ function Invoke-Terraform { $currentAttempt = 0 $maxAttempts = 5 - while($exitCode -ne 0 -and $currentAttempt -lt $maxAttempts) { + while ($exitCode -ne 0 -and $currentAttempt -lt $maxAttempts) { Write-InformationColored "Terraform $action failed with exit code $exitCode. This is likely a transient issue, so we are retrying..." -ForegroundColor Yellow -NewLineBefore -InformationAction Continue $currentAttempt++ $command = "terraform" @@ -134,7 +134,7 @@ function Invoke-Terraform { $arguments += "apply" $arguments += "-auto-approve" $arguments += "-input=false" - if($tfvarsFileName -ne "") { + if ($tfvarsFileName -ne "") { $arguments += "-var-file=$tfvarsFileName" } if ($destroy) { @@ -146,24 +146,24 @@ function Invoke-Terraform { $exitCode = $LASTEXITCODE } - if($removeSubscriptionId) { + if ($removeSubscriptionId) { Write-Verbose "Removing environment variable ARM_SUBSCRIPTION_ID that was set prior to this run" $env:ARM_SUBSCRIPTION_ID = $null } # Stop and display timer $StopWatch.Stop() - if(!$silent) { + if (!$silent) { Write-InformationColored "Time taken to complete Terraform apply:" -ForegroundColor Green -NewLineBefore -InformationAction Continue } $StopWatch.Elapsed | Format-Table - if($exitCode -ne 0) { + if ($exitCode -ne 0) { Write-InformationColored "Terraform $action failed with exit code $exitCode after $maxAttempts attempts. Please review the error and try again or raise an issue." -ForegroundColor Red -NewLineBefore -InformationAction Continue throw "Terraform $action failed with exit code $exitCode after $maxAttempts attempts. Please review the error and try again or raise an issue." } else { - if($output -ne "") { - if($outputFilePath -eq "") { + if ($output -ne "") { + if ($outputFilePath -eq "") { $outputFilePath = Join-Path $moduleFolderPath "output.json" } $command = "terraform" @@ -179,4 +179,4 @@ function Invoke-Terraform { } } } -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Deploy-Accelerator-Helpers/New-Bootstrap.ps1 b/src/ALZ/Private/Deploy-Accelerator-Helpers/New-Bootstrap.ps1 index 7b70bd9..7281411 100644 --- a/src/ALZ/Private/Deploy-Accelerator-Helpers/New-Bootstrap.ps1 +++ b/src/ALZ/Private/Deploy-Accelerator-Helpers/New-Bootstrap.ps1 @@ -281,4 +281,4 @@ function New-Bootstrap { Write-InformationColored "Bootstrap has completed successfully! Thanks for using our tool. Head over to Phase 3 in the documentation to continue..." -ForegroundColor Green -NewLineBefore -InformationAction Continue } -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Deploy-Accelerator-Helpers/New-FolderStructure.ps1 b/src/ALZ/Private/Deploy-Accelerator-Helpers/New-FolderStructure.ps1 index a3eedc4..3cffdf7 100644 --- a/src/ALZ/Private/Deploy-Accelerator-Helpers/New-FolderStructure.ps1 +++ b/src/ALZ/Private/Deploy-Accelerator-Helpers/New-FolderStructure.ps1 @@ -29,22 +29,22 @@ function New-FolderStructure { if ($PSCmdlet.ShouldProcess("ALZ-Terraform module configuration", "modify")) { Write-Verbose "Downloading modules to $targetDirectory" - if(!($release.StartsWith("v")) -and ($release -ne "latest")) { + if (!($release.StartsWith("v")) -and ($release -ne "latest")) { $release = "v$release" } $releaseTag = "" $path = "" - if($overrideSourceDirectoryPath -ne "") { + if ($overrideSourceDirectoryPath -ne "") { $releaseTag = "local" $path = Join-Path $targetDirectory $targetFolder $releaseTag - if((Test-Path $path) -and !$replaceFiles) { + if ((Test-Path $path) -and !$replaceFiles) { Write-Verbose "Folder $path already exists, so not copying files." } else { Write-InformationColored "Copying files from $overrideSourceDirectoryPath to $path" -ForegroundColor Green -InformationAction Continue - if(!(Test-Path $path)) { + if (!(Test-Path $path)) { New-Item -Path $path -ItemType "Directory" } Copy-Item -Path "$overrideSourceDirectoryPath/$sourceFolder/*" -Destination "$path" -Recurse -Force | Out-String | Write-Verbose @@ -62,4 +62,4 @@ function New-FolderStructure { releaseTag = $releaseTag } } -} \ No newline at end of file +} diff --git a/src/ALZ/Private/Shared/Test-Utility-Functions.ps1 b/src/ALZ/Private/Shared/Test-Utility-Functions.ps1 index 414771b..69ca75f 100644 --- a/src/ALZ/Private/Shared/Test-Utility-Functions.ps1 +++ b/src/ALZ/Private/Shared/Test-Utility-Functions.ps1 @@ -5,4 +5,4 @@ function Get-PSVersion { $PSVersionTable } function Get-ScriptRoot { $PSScriptRoot } # Used to allow mocking of the Get-Module AZ -function Get-AZVersion { Get-Module -Name Az -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1 } \ No newline at end of file +function Get-AZVersion { Get-Module -Name Az -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1 } diff --git a/src/ALZ/Private/Shared/Write-InformationColored.ps1 b/src/ALZ/Private/Shared/Write-InformationColored.ps1 index b377174..43c22c8 100644 --- a/src/ALZ/Private/Shared/Write-InformationColored.ps1 +++ b/src/ALZ/Private/Shared/Write-InformationColored.ps1 @@ -9,7 +9,7 @@ function Write-InformationColored { [switch]$NewLineBefore ) - if($NewLineBefore) { + if ($NewLineBefore) { $MessageData = "$([Environment]::NewLine)$MessageData" } @@ -21,4 +21,4 @@ function Write-InformationColored { } Write-Information $msg -} \ No newline at end of file +} diff --git a/src/PSScriptAnalyzerSettings.psd1 b/src/PSScriptAnalyzerSettings.psd1 index 90f2ac2..b626d6e 100644 --- a/src/PSScriptAnalyzerSettings.psd1 +++ b/src/PSScriptAnalyzerSettings.psd1 @@ -18,9 +18,9 @@ #________________________________________ #ExcludeRules #Specify ExcludeRules when you want to exclude a certain rule from the the default set of rules. - ExcludeRules = @( - 'PSAvoidUsingWriteHost', - 'PSReviewUnusedParameter' + ExcludeRules = @( + 'PSAvoidUsingWriteHost', + 'PSReviewUnusedParameter' ) #________________________________________ #Rules diff --git a/src/Tests/Unit/Private/Set-ComputedConfiguration.Tests.ps1 b/src/Tests/Unit/Private/Set-ComputedConfiguration.Tests.ps1 index bc742ed..28f2fc2 100644 --- a/src/Tests/Unit/Private/Set-ComputedConfiguration.Tests.ps1 +++ b/src/Tests/Unit/Private/Set-ComputedConfiguration.Tests.ps1 @@ -34,8 +34,8 @@ InModuleScope 'ALZ' { Name = "Setting2" Destination = "Environment" }) - Source = "calculated" - Value = "{%Setting1%}" + Source = "calculated" + Value = "{%Setting1%}" } } @@ -45,11 +45,11 @@ InModuleScope 'ALZ' { It 'Computed, Processed array values replace values correctly' { $configuration = [pscustomobject]@{ - Nested = [pscustomobject]@{ + Nested = [pscustomobject]@{ Source = "calculated" Description = "A Test Value" - Process = '@($args | Select-Object -Unique)' - Value = @( + Process = '@($args | Select-Object -Unique)' + Value = @( "1", "1", "3" @@ -68,11 +68,11 @@ InModuleScope 'ALZ' { It 'Computed, Processed array values replace values correctly in a case insensitive deduplication.' { $configuration = [pscustomobject]@{ - Nested = [pscustomobject]@{ + Nested = [pscustomobject]@{ Source = "calculated" Description = "A Test Value" - Process = '@($args | ForEach-Object { $_.ToLower() } | Select-Object -Unique)' - Value = @( + Process = '@($args | ForEach-Object { $_.ToLower() } | Select-Object -Unique)' + Value = @( "A", "a", "A", @@ -92,11 +92,11 @@ InModuleScope 'ALZ' { It 'Computed, Processed array values replace values correctly and keep array type when only one item remains.' { $configuration = [pscustomobject]@{ - Nested = [pscustomobject]@{ + Nested = [pscustomobject]@{ Source = "calculated" Description = "A Test Value" - Process = '@($args | Select-Object -Unique)' - Value = @( + Process = '@($args | Select-Object -Unique)' + Value = @( "1", "1", "1" @@ -115,10 +115,10 @@ InModuleScope 'ALZ' { It 'Computed, Processed values replace values correctly' { $configuration = [pscustomobject]@{ - Nested = [pscustomobject]@{ + Nested = [pscustomobject]@{ Source = "calculated" Description = "A Test Value" - Process = '($args[0] -eq "eastus") ? "eastus2" : ($args[0] -eq "eastus2") ? "eastus" : $args[0]' + Process = '($args[0] -eq "eastus") ? "eastus2" : ($args[0] -eq "eastus2") ? "eastus" : $args[0]' Value = "eastus" Targets = @( [pscustomobject]@{ @@ -134,10 +134,10 @@ InModuleScope 'ALZ' { It 'Computed, Processed values replace values correctly' { $configuration = [pscustomobject]@{ - Nested = [pscustomobject]@{ + Nested = [pscustomobject]@{ Source = "calculated" Description = "A Test Value" - Process = '($args[0] -eq "goodbye") ? "Hello" : "Goodbye"' + Process = '($args[0] -eq "goodbye") ? "Hello" : "Goodbye"' Value = "goodbye" Targets = @( [pscustomobject]@{ @@ -152,4 +152,4 @@ InModuleScope 'ALZ' { } } } -} \ No newline at end of file +} From 54bba5dce619d301932e1f3d22dde5c3e34c0266 Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:37:47 +0000 Subject: [PATCH 09/10] fix: Ensure consistent formatting and add missing newlines in scripts --- src/ALZ.Settings.ps1 | 2 +- .../Copy-ParameterFileCollection.ps1 | 2 +- src/Tests/Unit/Private/Set-Config.Tests.ps1 | 30 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ALZ.Settings.ps1 b/src/ALZ.Settings.ps1 index b8cb7db..cb20843 100644 --- a/src/ALZ.Settings.ps1 +++ b/src/ALZ.Settings.ps1 @@ -1,2 +1,2 @@ # specify the minimum required major PowerShell version that the build script should validate -[version]$script:requiredPSVersion = '7.1.3' \ No newline at end of file +[version]$script:requiredPSVersion = '7.1.3' diff --git a/src/ALZ/Private/Deploy-Accelerator-Helpers/Copy-ParameterFileCollection.ps1 b/src/ALZ/Private/Deploy-Accelerator-Helpers/Copy-ParameterFileCollection.ps1 index f730dc0..99f48a2 100644 --- a/src/ALZ/Private/Deploy-Accelerator-Helpers/Copy-ParameterFileCollection.ps1 +++ b/src/ALZ/Private/Deploy-Accelerator-Helpers/Copy-ParameterFileCollection.ps1 @@ -23,4 +23,4 @@ function Copy-ParametersFileCollection { Write-Warning "The file $sourcePath does not exist." } } -} \ No newline at end of file +} diff --git a/src/Tests/Unit/Private/Set-Config.Tests.ps1 b/src/Tests/Unit/Private/Set-Config.Tests.ps1 index ee75efd..83fc748 100644 --- a/src/Tests/Unit/Private/Set-Config.Tests.ps1 +++ b/src/Tests/Unit/Private/Set-Config.Tests.ps1 @@ -5,22 +5,22 @@ $ModuleName = 'ALZ' $PathToManifest = [System.IO.Path]::Combine('..', '..', '..', $ModuleName, "$ModuleName.psd1") #------------------------------------------------------------------------- if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') { - #if the module is already in memory, remove it - Remove-Module -Name $ModuleName -Force + #if the module is already in memory, remove it + Remove-Module -Name $ModuleName -Force } Import-Module $PathToManifest -Force #------------------------------------------------------------------------- InModuleScope 'ALZ' { - Describe 'Set-Config Private Function Tests' -Tag Unit { - BeforeAll { - $WarningPreference = 'SilentlyContinue' - $ErrorActionPreference = 'SilentlyContinue' - } - Context 'Set-Config should request CLI input for configuration.' { - It 'Based on the configuration object' { + Describe 'Set-Config Private Function Tests' -Tag Unit { + BeforeAll { + $WarningPreference = 'SilentlyContinue' + $ErrorActionPreference = 'SilentlyContinue' + } + Context 'Set-Config should request CLI input for configuration.' { + It 'Based on the configuration object' { - $config = @' + $config = @' { "parameters":{ "Prefix":{ @@ -39,9 +39,9 @@ InModuleScope 'ALZ' { } '@ | ConvertFrom-Json - Set-Config -configurationParameters $config.Parameters - } + Set-Config -configurationParameters $config.Parameters + } - } - } -} \ No newline at end of file + } + } +} From 754da215c0a5f5f0ac025897bfdff1885a8a446b Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:40:47 +0000 Subject: [PATCH 10/10] fix: Improve JSON formatting in Set-Config.Tests.ps1 for better readability --- src/Tests/Unit/Private/Set-Config.Tests.ps1 | 28 ++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Tests/Unit/Private/Set-Config.Tests.ps1 b/src/Tests/Unit/Private/Set-Config.Tests.ps1 index 83fc748..22e5044 100644 --- a/src/Tests/Unit/Private/Set-Config.Tests.ps1 +++ b/src/Tests/Unit/Private/Set-Config.Tests.ps1 @@ -22,21 +22,21 @@ InModuleScope 'ALZ' { $config = @' { - "parameters":{ - "Prefix":{ - "Type":"UserInput", - "Description":"The prefix that will be added to all resources created by this deployment. (e.g. 'alz')", - "Targets":[ - { - "Name":"parTopLevelManagementGroupPrefix", - "Destination":"Parameters" - } - ], - "DefaultValue":"alz", - "Value":"" - } + "parameters": { + "Prefix": { + "Type": "UserInput", + "Description": "The prefix that will be added to all resources created by this deployment. (e.g. 'alz')", + "Targets": [ + { + "Name": "parTopLevelManagementGroupPrefix", + "Destination": "Parameters" + } + ], + "DefaultValue": "alz", + "Value": "" + } } - } + } '@ | ConvertFrom-Json Set-Config -configurationParameters $config.Parameters