Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions actions_bootstrap.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ $null = $modulesToInstall.Add(([PSCustomObject]@{
# Required dependency of the ALZ module itself.
$null = $modulesToInstall.Add(([PSCustomObject]@{
ModuleName = 'Az.Resources'
ModuleVersion = '5.6.0'
ModuleVersion = '6.5.2'
}))



'Installing PowerShell Modules'
foreach ($module in $modulesToInstall) {
$installSplat = @{
Expand All @@ -52,8 +50,7 @@ foreach ($module in $modulesToInstall) {
Install-Module @installSplat
Import-Module -Name $module.ModuleName -ErrorAction Stop
' - Successfully installed {0}' -f $module.ModuleName
}
catch {
} catch {
$message = 'Failed to install {0}' -f $module.ModuleName
" - $message"
throw
Expand Down
2 changes: 1 addition & 1 deletion src/ALZ/ALZ.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
RequiredModules = @(
@{
ModuleName = 'Az.Resources'
ModuleVersion = '5.6.0'
ModuleVersion = '6.5.2'
}
)

Expand Down
29 changes: 29 additions & 0 deletions src/ALZ/Private/Build-ALZDeploymentEnvFile.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
function Build-ALZDeploymentEnvFile {
param (
[Parameter(Mandatory = $true)]
[PSCustomObject] $configuration,

[Parameter(Mandatory = $false)]
[string] $destination = "."
)
<#
.SYNOPSIS
This function uses configuration to build a .env file for use in the deployment pipeline.
.EXAMPLE
Build-ALZDeploymentEnvFile -configuration configuration
.EXAMPLE
Build-ALZDeploymentEnvFile -configuration configuration -destination "."
.OUTPUTS
N/A
#>

$envFile = Join-Path $destination ".env"

New-Item -Path $envFile -ItemType file -Force | Out-Null

foreach ($configurationValue in $configuration.PsObject.Properties) {
if ($configurationValue.Value.ForEnvironment -eq $true) {
Add-Content -Path $envFile -Value "$($($configurationValue.Name))=`"$($configurationValue.Value.Value)`"" | Out-Null
}
}
}
12 changes: 11 additions & 1 deletion src/ALZ/Private/Edit-ALZConfigurationFilesInPlace.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,17 @@ function Edit-ALZConfigurationFilesInPlace {
foreach ($configKey in $configuration.PsObject.Properties) {
foreach ($name in $configKey.Value.Names) {
if ($null -ne $bicepConfiguration.parameters[$name]) {
$bicepConfiguration.parameters[$name].value = $configKey.Value.Value

# If we've specified a string to replace - and the value contains that string, then replace it.
# Otherwise overwrite the value completely.
if ($null -ne $configKey.Value.Replace) {
$bicepConfiguration.parameters[$name].value = `
$bicepConfiguration.parameters[$name].value -replace $configKey.Value.Replace, $configKey.Value.Value
} else {
$bicepConfiguration.parameters[$name].value = $configKey.Value.Value
}


$modified = $true
}
}
Expand Down
51 changes: 39 additions & 12 deletions src/ALZ/Private/Initialize-ConfigurationObject.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,56 @@ function Initialize-ConfigurationObject {
}

return [pscustomobject]@{
Prefix = [pscustomobject]@{
description = "The prefix that will be added to all resources created by this deployment."
names = @("parTopLevelManagementGroupPrefix", "parCompanyPrefix")
value = "alz"
defaultValue = "alz"
Prefix = [pscustomobject]@{
Type = "Configuration"
Description = "The prefix that will be added to all resources created by this deployment. (e.g. 'alz')"
Names = @("parTopLevelManagementGroupPrefix", "parCompanyPrefix", "parTargetManagementGroupId", "parAssignableScopeManagementGroupId")
Value = "alz"
DefaultValue = "alz"
Valid = "^[a-zA-Z]{3,5}$"
}
Suffix = [pscustomobject]@{
Description = "The suffix that will be added to all resources created by this deployment."
Suffix = [pscustomobject]@{
Type = "Configuration"
Description = "The suffix that will be added to all resources created by this deployment. (e.g. 'test')"
Names = @("parTopLevelManagementGroupSuffix")
Value = ""
DefaultValue = ""
Valid = "^[a-zA-Z]{0,5}$"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this a function rather than a RegEx. We can have more nuanced handling of error.

We can nest the Regex validation for now - but gives us future scope for better validation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I'm finding pscustomobject doesn't house functions all that well - we might need to change to genuine PS classes to get this kind of functionality...

}
Location = [pscustomobject]@{
Location = [pscustomobject]@{
Type = "Configuration"
Description = "Deployment location."
Names = @("parLocation")
AllowedValues = @(Get-AzLocation | Sort-Object Location | Select-Object -ExpandProperty Location )
Names = @("parLocation", "parAutomationAccountLocation", "parLogAnalyticsWorkspaceLocation")
AllowedValues = @(Get-AzLocation | Sort-Object Location | Select-Object -ExpandProperty Location)
Value = ""
}
Environment = [pscustomobject]@{
Description = "The type of environment that will be created . Example: dev, test, qa, staging, prod"
Environment = [pscustomobject]@{
Type = "Configuration"
Description = "The type of environment that will be created. (e.g. 'dev', 'test', 'qa', 'staging', 'prod')"
Names = @("parEnvironment")
DefaultValue = 'prod'
Value = ""
Valid = "^[a-zA-Z0-9]{2,10}$"
}
IdentitySubscriptionId = [pscustomobject]@{
ForEnvironment = $true
Description = "The identifier of the Identity subscription. (e.g '00000000-0000-0000-0000-000000000000')"
Valid = "^( {){0,1}[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(}){0,1}$"
Value = ""
}
ConnectivitySubscriptionId = [pscustomobject]@{
ForEnvironment = $true
Description = "The identifier of the Connectivity subscription. (e.g '00000000-0000-0000-0000-000000000000')"
Valid = "^( {){0,1}[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(}){0,1}$"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a common 'Validate ID or Validate Guid' validation function.

Value = ""
}
ManagementSubscriptionId = [pscustomobject]@{
ForEnvironment = $true
Description = "The identifier of the Management subscription. (e.g 00000000-0000-0000-0000-000000000000)"
Valid = "^( {){0,1}[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(}){0,1}$"
Names = @("parLogAnalyticsWorkspaceResourceId")
Replace = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Value = ""
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/ALZ/Private/Request-ALZEnvironmentConfig.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ function Request-ALZEnvironmentConfig {
.SYNOPSIS
This function uses a template configuration to prompt for and return a user specified/modified configuration object.
.EXAMPLE
New-SlzEnvironmentConfig
Request-ALZEnvironmentConfig
.EXAMPLE
New-SlzEnvironmentConfig -sourceConfigurationFile "orchestration/scripts/parameters/sovereignLandingZone.parameters.json"
Request-ALZEnvironmentConfig -alzIacProvider "bicep"
.OUTPUTS
System.Object. The resultant configuration values.
#>
Expand Down
37 changes: 28 additions & 9 deletions src/ALZ/Private/Request-ConfigurationValue.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@ function Request-ConfigurationValue {
[string] $configName,

[Parameter(Mandatory = $true)]
[object] $configValue
[object] $configValue,

[Parameter(Mandatory = $false)]
[System.Boolean] $withRetries = $true
)

$allowedValues = $configValue.allowedValues
$hasAllowedValues = $null -ne $configValue.allowedValues
$allowedValues = $configValue.AllowedValues
$hasAllowedValues = $null -ne $configValue.AllowedValues

$defaultValue = $configValue.DefaultValue
$hasDefaultValue = $null -ne $configValue.DefaultValue

$defaultValue = $configValue.defaultValue
$hasDefaultValue = $null -ne $configValue.defaultValue
$hasValidator = $null -ne $configValue.Valid

Write-InformationColored $configValue.description -ForegroundColor White -InformationAction Continue
Write-InformationColored $configValue.Description -ForegroundColor White -InformationAction Continue
if ($hasAllowedValues) {
Write-InformationColored "[allowed: $allowedValues] " -ForegroundColor Yellow -InformationAction Continue
}
Expand All @@ -29,13 +34,27 @@ function Request-ConfigurationValue {

$readValue = Read-Host

$previousValue = $configValue.Value

if ($hasDefaultValue -and $readValue -eq "") {
$configValue.value = $configValue.defaultValue
$configValue.Value = $configValue.defaultValue
} else {
$configValue.value = $readValue
$configValue.Value = $readValue
}

$hasNotSpecifiedValue = ($null -eq $configValue.Value -or "" -eq $configValue.Value) -and ($configValue.Value -ne $configValue.DefaultValue)
$isDisallowedValue = $hasAllowedValues -and $allowedValues.Contains($configValue.Value) -eq $false
$isNotValid = $hasValidator -and $configValue.Value -match $configValue.Valid -eq $false

if ($hasNotSpecifiedValue -or $isDisallowedValue -or $isNotValid) {
Write-InformationColored "Please specify a valid value for this field." -ForegroundColor Red -InformationAction Continue
$configValue.Value = $previousValue
$validationError = $true
}

$shouldRetry = $validationError -and $withRetries
}
while ((($null -eq $configValue.value -or "" -eq $configValue.value) -and ($configValue.value -ne $configValue.defaultValue)) -or ($hasAllowedValues -and $allowedValues.Contains($configValue.value) -eq $false))
while (($hasNotSpecifiedValue -or $isDisallowedValue -or $isNotValid) -and $shouldRetry)

Write-InformationColored "" -InformationAction Continue
}
1 change: 1 addition & 0 deletions src/ALZ/Public/New-ALZEnvironment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ function New-ALZEnvironment {
}

Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination $alzEnvironmentDestination -configuration $configuration | Out-Null
Build-ALZDeploymentEnvFile -configuration $configuration -Destination $alzEnvironmentDestination | Out-Null
}

return $true
Expand Down
66 changes: 66 additions & 0 deletions src/Tests/Unit/Private/Build-ALZDeploymentEnvFile.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#-------------------------------------------------------------------------
Set-Location -Path $PSScriptRoot
#-------------------------------------------------------------------------
$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
}
Import-Module $PathToManifest -Force
#-------------------------------------------------------------------------

InModuleScope 'ALZ' {
Describe 'Build-AZLDeploymentEnvFile Private Function Tests' -Tag Unit {
BeforeAll {
$WarningPreference = 'SilentlyContinue'
$ErrorActionPreference = 'SilentlyContinue'
}
Context 'Build-AZLDeploymentEnvFile should create a .env file correctly' {
It 'Creates a config file based on configuration.' {

Mock -CommandName New-Item
Mock -CommandName Add-Content

$configuration = [pscustomobject]@{
Setting1 = [pscustomobject]@{
ForEnvironment = $true
Value = "Test1"
}
Setting2 = [pscustomobject]@{
ForEnvironment = $true
Value = "Test2"
}
}

Build-ALZDeploymentEnvFile -configuration $configuration -destination "test"

Should -Invoke New-Item -ParameterFilter { $Path -match ".env$" } -Scope It -Times 1 -Exactly
Should -Invoke Add-Content -ParameterFilter { $Value -match "^Setting1=`"Test1`"$" } -Scope It -Times 1 -Exactly
Should -Invoke Add-Content -ParameterFilter { $Value -match "^Setting2=`"Test2`"$" } -Scope It -Times 1 -Exactly
}
It 'Omits configuration not intended for the .env file.' {

Mock -CommandName New-Item
Mock -CommandName Add-Content

$configuration = [pscustomobject]@{
Setting1 = [pscustomobject]@{
ForEnvironment = $true
Value = "Test1"
}
Setting2 = [pscustomobject]@{
ForEnvironment = $false
Value = "Test2"
}
}

Build-ALZDeploymentEnvFile -configuration $configuration -destination "test"

Should -Invoke New-Item -ParameterFilter { $Path -match ".env$" } -Scope It -Times 1 -Exactly
Should -Invoke Add-Content -Scope It -Times 1 -Exactly
}
}
}
}
Loading