From 6728899feedab4b1413e589b72fedd7cad96b7b6 Mon Sep 17 00:00:00 2001 From: Jared Holgate Date: Wed, 13 Mar 2024 23:17:31 +0000 Subject: [PATCH] enhancement: relocate bootstrap modules (#105) # Pull Request ## Issue Issue #, if available: N/A ## Description This PR is a significant refactor to de-couple bootstrap and starter modules. It moves to a configuration based approach for shared inputs and have many other enhancements to the user experience. ## License By submitting this pull request, I confirm that my contribution is made under the terms of the projects associated license. --- .gitignore | 39 +++ README.md | 45 ++- src/ALZ/ALZ.psd1 | 2 +- .../Private/Build-ALZDeploymentEnvFile.ps1 | 2 +- .../Convert-HCLVariablesToUserInputConfig.ps1 | 11 +- ...onvert-InterfaceInputToUserInputConfig.ps1 | 72 +++++ .../Edit-ALZConfigurationFilesInPlace.ps1 | 2 +- src/ALZ/Private/Get-ALZConfig.ps1 | 1 + src/ALZ/Private/Get-ExistingLocalRelease.ps1 | 29 ++ src/ALZ/Private/Get-GithubRelease.ps1 | 144 +++++++++ src/ALZ/Private/Get-StarterConfig.ps1 | 0 src/ALZ/Private/Get-TerraformTool.ps1 | 18 +- src/ALZ/Private/Import-ConfigurationFile.ps1 | 21 -- src/ALZ/Private/Import-SubscriptionData.ps1 | 21 -- src/ALZ/Private/Invoke-FullUpgrade.ps1 | 67 ++++ src/ALZ/Private/Invoke-Upgrade.ps1 | 68 ++-- src/ALZ/Private/New-ALZEnvironmentBicep.ps1 | 55 ++-- .../Private/New-ALZEnvironmentTerraform.ps1 | 114 ------- src/ALZ/Private/New-Bootstrap.ps1 | 240 ++++++++++++++ src/ALZ/Private/New-FolderStructure.ps1 | 36 +++ .../Private/Request-ALZEnvironmentConfig.ps1 | 48 ++- src/ALZ/Private/Request-SpecialInput.ps1 | 91 ++++++ src/ALZ/Private/Write-InformationColored.ps1 | 7 +- src/ALZ/Public/Get-ALZGithubRelease.ps1 | 163 ---------- src/ALZ/Public/New-ALZEnvironment.ps1 | 295 +++++++++++++++--- src/ALZ/Public/Test-ALZRequirement.ps1 | 1 + src/PSScriptAnalyzerSettings.psd1 | 2 +- .../Public/Get-ALZGithubRelease.Tests.ps1 | 114 ------- .../Unit/Public/New-ALZEnvironment.Tests.ps1 | 27 +- 29 files changed, 1128 insertions(+), 607 deletions(-) create mode 100644 src/ALZ/Private/Convert-InterfaceInputToUserInputConfig.ps1 create mode 100644 src/ALZ/Private/Get-ExistingLocalRelease.ps1 create mode 100644 src/ALZ/Private/Get-GithubRelease.ps1 create mode 100644 src/ALZ/Private/Get-StarterConfig.ps1 delete mode 100644 src/ALZ/Private/Import-ConfigurationFile.ps1 delete mode 100644 src/ALZ/Private/Import-SubscriptionData.ps1 create mode 100644 src/ALZ/Private/Invoke-FullUpgrade.ps1 delete mode 100644 src/ALZ/Private/New-ALZEnvironmentTerraform.ps1 create mode 100644 src/ALZ/Private/New-Bootstrap.ps1 create mode 100644 src/ALZ/Private/New-FolderStructure.ps1 create mode 100644 src/ALZ/Private/Request-SpecialInput.ps1 delete mode 100644 src/ALZ/Public/Get-ALZGithubRelease.ps1 delete mode 100644 src/Tests/Unit/Public/Get-ALZGithubRelease.Tests.ps1 diff --git a/.gitignore b/.gitignore index bd2bade8..ef1d17bf 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,42 @@ Thumbs.db src/ALZ/Assets/alz-bicep-internal/ test_output/ hcl2json_windows_amd64.exe + +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* +**/.terraform.lock.hcl + +# Crash log files +crash.log + +# Ignore any .tfvars files that are generated automatically for each Terraform run. Most +# .tfvars files are managed as part of configuration and so should be included in +# version control. +# +# example.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* +.terraform.lock.hcl +terraform.log +*.tfvars +!terraform.tfvars +templates/basic/terraform.tfvars +templates/.test_azuredevops +templates/.test_github +.vscode/settings.json diff --git a/README.md b/README.md index b52de00c..3676937d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ALZ +# ALZ / Accelerator PowerShell Module [![license](https://img.shields.io/badge/License-MIT-purple.svg)](LICENSE) @@ -6,15 +6,20 @@ ## Synopsis -This is a PowerShell module that provides a set of cmdlets to create and manage Azure Landing Zones. +This is a PowerShell module that provides a set of cmdlets to create and manage Accelerators for Azure Landing Zones and other workloads. ## Description -This module provides a set of cmdlets to create and manage Azure Landing Zones. +This module provides a set of cmdlets to create and manage Accelerators for Azure Landing Zones and other workloads. ## Why -The goal of this project it is to make easy to get started with Azure Landing Zones and to speed up some basic tasks that you would need to perform whilst managing your Azure Landing Zones. +The goal of this project it is to make easy to get started with Azure Landing Zones and other workloads. It is designed to speed up some basic tasks that you would need to perform whilst managing your Azure Landing Zones. + +For usage of the Azure Landing Zoners Accelerators, please refer to the detailed documentation: + +- [Bicep](https://github.com/Azure/ALZ-Bicep/wiki/Accelerator) +- [Terraform](https://github.com/Azure/alz-terraform-accelerator/wiki) ## Getting Started @@ -24,11 +29,14 @@ In order to use this module you will need PowerShell 7.1 or higher. Download and install the latest version from the official PowerShell GitHub releases page: [PowerShell Releases](https://github.com/PowerShell/PowerShell/releases) To make PowerShell 7 your default instead of version 5, consider setting an alias. - Open a PowerShell session and run the following command: - ```powershell + +Open a PowerShell session and run the following command: + + ```powershell $PSVersionTable.PSVersion Set-Alias powershell pwsh ``` + ### Installation You can install this module using PowerShellGet. @@ -60,7 +68,7 @@ Before you start you can utilize the functionality of the module to verify if yo #### Bicep ```powershell -Test-ALZRequirement -IaC "bicep" +Test-ALZRequirement -i "bicep" ``` Currently this tests for: @@ -75,7 +83,7 @@ Currently this tests for: #### Terraform ```powershell -Test-ALZRequirement -IaC "terraform" +Test-ALZRequirement -i "terraform" ``` This currently tests for: @@ -88,38 +96,25 @@ This currently tests for: #### Azure Landing Zone Environment with Bicep and GitHub Actions Workflows ```powershell -New-ALZEnvironment -o -i "bicep" -c "github" +Deploy-Accelerator -o -i "bicep" -b "alz_github" ``` #### Azure Landing Zone Environment with Bicep and Azure DevOps Pipelines ```powershell -New-ALZEnvironment -o -i "bicep" -c "azuredevops" +Deploy-Accelerator -o -i "bicep" -b "alz_azuredevops" ``` -> **Note** -> Azure Devops Pipelines are only supported in v0.2.6 or later. - #### Azure Landing Zone Environment with Terraform and GitHub Pipelines ```powershell -New-ALZEnvironment -o -i "terraform" -c "github" +Deploy-Accelerator -o -i "terraform" -b "alz_github" ``` #### Azure Landing Zone Environment with Terraform and Azure DevOps Pipelines ```powershell -New-ALZEnvironment -o -i "terraform" -c "azuredevops" -``` - -## Additional Cmdlets - -### Update an existing Azure Landing Zone Environment - -#### Downloads and pulls down the specified release version from the remote GitHub repository to a local directory - -```powershell -Get-ALZGithubRelease -i "bicep" -v "v0.14.0" -o "C:\Repos\ALZ\accelerator" +Deploy-Accelerator -o -i "terraform" -b "alz_azuredevops" ``` ## Development diff --git a/src/ALZ/ALZ.psd1 b/src/ALZ/ALZ.psd1 index d91547c7..578cf402 100644 --- a/src/ALZ/ALZ.psd1 +++ b/src/ALZ/ALZ.psd1 @@ -70,7 +70,6 @@ # 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 = @( - 'Get-ALZGithubRelease' 'New-ALZEnvironment' 'Test-ALZRequirement' 'Edit-LineEnding' @@ -85,6 +84,7 @@ # 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' + 'Deploy-Accelerator' ) # DSC resources to export from this module diff --git a/src/ALZ/Private/Build-ALZDeploymentEnvFile.ps1 b/src/ALZ/Private/Build-ALZDeploymentEnvFile.ps1 index 1fffca63..449316d4 100644 --- a/src/ALZ/Private/Build-ALZDeploymentEnvFile.ps1 +++ b/src/ALZ/Private/Build-ALZDeploymentEnvFile.ps1 @@ -27,7 +27,7 @@ function Build-ALZDeploymentEnvFile { foreach ($configurationValue in $configuration.PsObject.Properties) { foreach ($target in $configurationValue.Value.Targets) { if ($target.Destination -eq "Environment") { - Write-InformationColored $configurationValue.Name -ForegroundColor Green -InformationAction Continue + Write-Verbose "Creating environment files for: $($configurationValue.Name)" if($configurationValue.Name -eq "UpstreamReleaseVersion") { Add-Content -Path $envFile -Value "$($($target.Name))=`"$version`"" | Out-String | Write-Verbose diff --git a/src/ALZ/Private/Convert-HCLVariablesToUserInputConfig.ps1 b/src/ALZ/Private/Convert-HCLVariablesToUserInputConfig.ps1 index b44083b5..44477ae7 100644 --- a/src/ALZ/Private/Convert-HCLVariablesToUserInputConfig.ps1 +++ b/src/ALZ/Private/Convert-HCLVariablesToUserInputConfig.ps1 @@ -11,7 +11,10 @@ function Convert-HCLVariablesToUserInputConfig { [PSCustomObject]$validators, [Parameter(Mandatory = $false)] - [PSCustomObject]$appendToObject = $null + [PSCustomObject]$appendToObject = $null, + + [Parameter(Mandatory = $false)] + [switch]$allComputedInputs ) if ($PSCmdlet.ShouldProcess("Parse HCL Variables into Config", "modify")) { @@ -49,12 +52,8 @@ function Convert-HCLVariablesToUserInputConfig { $hasValidation = $true } - if($hasValidation -and $validationType -eq "hidden") { - continue - } - $inputType = "UserInput" - if($hasValidation -and $validationType -like "hidden_*") { + if($allComputedInputs) { $inputType = "ComputedInput" } diff --git a/src/ALZ/Private/Convert-InterfaceInputToUserInputConfig.ps1 b/src/ALZ/Private/Convert-InterfaceInputToUserInputConfig.ps1 new file mode 100644 index 00000000..6a1d2d4c --- /dev/null +++ b/src/ALZ/Private/Convert-InterfaceInputToUserInputConfig.ps1 @@ -0,0 +1,72 @@ +function Convert-InterfaceInputToUserInputConfig { + [CmdletBinding(SupportsShouldProcess = $true)] + param ( + [Parameter(Mandatory = $false)] + [PSCustomObject]$inputConfig, + + [Parameter(Mandatory = $false)] + [PSCustomObject]$validators, + + [Parameter(Mandatory = $false)] + [PSCustomObject]$appendToObject = $null + ) + + if ($PSCmdlet.ShouldProcess("Parse Interface Variables into Config", "modify")) { + + $starterModuleConfiguration = [PSCustomObject]@{} + if($appendToObject -ne $null) { + $starterModuleConfiguration = $appendToObject + } + + Write-Verbose $validators + + foreach($variable in $inputConfig.inputs.PSObject.Properties) { + Write-Verbose "Parsing variable $($variable.Name)" + $description = $variable.Value.description + + $order = 0 + if($variable.PSObject.Properties.Name -contains "display_order") { + $order = $variable.Value.display_order + } + + $inputType = $variable.Value.source -eq "input" ? "UserInput" : "ComputedInput" + $dataType = $variable.Value.type + + $sensitive = $false + if($variable.Value.PSObject.Properties.Name -contains "sensitive") { + $sensitive = $variable.Value.sensitive + } + + $starterModuleConfigurationInstance = [PSCustomObject]@{} + $starterModuleConfigurationInstance | Add-Member -NotePropertyName "Order" -NotePropertyValue $order + $starterModuleConfigurationInstance | Add-Member -NotePropertyName "Type" -NotePropertyValue $inputType + $starterModuleConfigurationInstance | Add-Member -NotePropertyName "Value" -NotePropertyValue "" + $starterModuleConfigurationInstance | Add-Member -NotePropertyName "DataType" -NotePropertyValue $dataType + $starterModuleConfigurationInstance | Add-Member -NotePropertyName "Sensitive" -NotePropertyValue $sensitive + + if($variable.Value.PSObject.Properties.Name -contains "default") { + $defaultValue = $variable.Value.default + $starterModuleConfigurationInstance | Add-Member -NotePropertyName "DefaultValue" -NotePropertyValue $defaultValue + } + + 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"){ + $starterModuleConfigurationInstance | Add-Member -NotePropertyName "AllowedValues" -NotePropertyValue $validator.AllowedValues + } + if($validator.Type -eq "Valid"){ + $starterModuleConfigurationInstance | Add-Member -NotePropertyName "Valid" -NotePropertyValue $validator.Valid + } + $starterModuleConfigurationInstance | Add-Member -NotePropertyName "Validator" -NotePropertyValue $validationType + } + + $starterModuleConfigurationInstance | Add-Member -NotePropertyName "Description" -NotePropertyValue $description + $starterModuleConfiguration | Add-Member -NotePropertyName $variable.Name -NotePropertyValue $starterModuleConfigurationInstance + } + } + + return $starterModuleConfiguration +} \ No newline at end of file diff --git a/src/ALZ/Private/Edit-ALZConfigurationFilesInPlace.ps1 b/src/ALZ/Private/Edit-ALZConfigurationFilesInPlace.ps1 index 3e7f834b..800f022b 100644 --- a/src/ALZ/Private/Edit-ALZConfigurationFilesInPlace.ps1 +++ b/src/ALZ/Private/Edit-ALZConfigurationFilesInPlace.ps1 @@ -71,7 +71,7 @@ function Edit-ALZConfigurationFilesInPlace { } if ($true -eq $modified) { - Write-InformationColored $file.FullName -ForegroundColor Yellow -InformationAction Continue + Write-Verbose $file.FullName $bicepConfiguration | ConvertTo-Json -Depth 10 | Out-File $file.FullName } } diff --git a/src/ALZ/Private/Get-ALZConfig.ps1 b/src/ALZ/Private/Get-ALZConfig.ps1 index 798e5312..785122e7 100644 --- a/src/ALZ/Private/Get-ALZConfig.ps1 +++ b/src/ALZ/Private/Get-ALZConfig.ps1 @@ -39,5 +39,6 @@ function Get-ALZConfig { throw "The config file must be a json or yaml/yml file" } + Write-Verbose "Config file loaded from $configFilePath with $($config.PSObject.Properties.Count) properties." return $config } \ No newline at end of file diff --git a/src/ALZ/Private/Get-ExistingLocalRelease.ps1 b/src/ALZ/Private/Get-ExistingLocalRelease.ps1 new file mode 100644 index 00000000..0696c183 --- /dev/null +++ b/src/ALZ/Private/Get-ExistingLocalRelease.ps1 @@ -0,0 +1,29 @@ +function Get-ExistingLocalRelease { + param( + [Parameter(Mandatory = $false)] + [string] $targetDirectory, + + [Parameter(Mandatory = $false)] + [string] $targetFolder + ) + + $releaseTag = "" + $path = "" + $checkPath = Join-Path $targetDirectory $targetFolder + $checkFolders = Get-ChildItem -Path $checkPath -Directory + if($null -ne $checkFolders) { + $checkFolders = $checkFolders | Sort-Object { $_.Name } -Descending + $mostRecentCheckFolder = $checkFolders[0] + + $releaseTag = $mostRecentCheckFolder.Name + $path = $mostRecentCheckFolder.FullName + } else { + Write-InformationColored "You have passed the skipInternetChecks parameter, but there is no existing version in the $targetFolder module, so we can't continue." + throw + } + + return @{ + releaseTag = $releaseTag + path = $path + } +} \ No newline at end of file diff --git a/src/ALZ/Private/Get-GithubRelease.ps1 b/src/ALZ/Private/Get-GithubRelease.ps1 new file mode 100644 index 00000000..82ee1441 --- /dev/null +++ b/src/ALZ/Private/Get-GithubRelease.ps1 @@ -0,0 +1,144 @@ +#################################### +# Get-GithubRelease.ps1 # +#################################### +# Version: 0.1.0 +# Based on Invoke-GitHubReleaseFetcher by Jack Tracey: +# Source: https://github.com/jtracey93/PublicScripts/blob/master/GitHub/PowerShell/Invoke-GitHubReleaseFetcher.ps1 + +<# +.SYNOPSIS +Checks for the releases of a GitHub repository and downloads the latest release or all releases and pulls it into a specified directory, one for each version. +.DESCRIPTION +Checks for the releases of a GitHub repository and downloads the latest release or all releases and pulls it into a specified directory, one for each version. + +.EXAMPLE + +.NOTES +# Release notes 16/03/2023 - V0.1.0: +- Initial release. +#> + + +function Get-GithubRelease { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true, Position = 1, HelpMessage = "Please the provide the full URL of the GitHub repository you wish to check for the latest release.")] + [string] + $githubRepoUrl, + + [Parameter(Mandatory = $false, Position = 2, HelpMessage = "The releases to download. Specify 'latest' to download the latest release. Defaults to the latest release.")] + [array] + $release = "latest", + + [Parameter(Mandatory = $true, Position = 3, HelpMessage = "The directory to download the releases to.")] + [string] + $targetDirectory, + + [Parameter(Mandatory = $false, Position = 4, HelpMessage = "Whether to just query the API and return the release versions.")] + [switch] + $queryOnly, + + [Parameter(Mandatory = $false, HelpMessage = "The source directory location of the modules. Defaults to root")] + $moduleSourceFolder = ".", + + [Parameter(Mandatory = $true, HelpMessage = "The target directory location of the modules.")] + $moduleTargetFolder + ) + + $parentDirectory = $targetDirectory + $targetPath = Join-Path $targetDirectory $moduleTargetFolder + + # Split Repo URL into parts + $repoOrgPlusRepo = $githubRepoUrl.Split("/")[-2..-1] -join "/" + + Write-Verbose "=====> Checking for release on GitHub Repo: $repoOrgPlusRepo" + + # Get releases on repo + $repoReleaseUrl = "https://api.github.com/repos/$repoOrgPlusRepo/releases/$release" + if($release -ne "latest") { + $repoReleaseUrl = "https://api.github.com/repos/$repoOrgPlusRepo/releases/tags/$release" + } + + $releaseData = Invoke-RestMethod $repoReleaseUrl -SkipHttpErrorCheck -StatusCodeVariable "statusCode" + + Write-Verbose "Status code: $statusCode" + + if($statusCode -eq 404) { + Write-Error "The release $release does not exist in the GitHub repository $githubRepoUrl - $repoReleaseUrl" + throw "The release $release does not exist in the GitHub repository $githubRepoUrl - $repoReleaseUrl" + } + + # Handle transient errors like throttling + if($statusCode -ge 400 -and $statusCode -le 599) { + Write-InformationColored "Retrying as got the Status Code $statusCode, which may be a tranisent error." -ForegroundColor Yellow -InformationAction Continue + $releaseData = Invoke-RestMethod $repoReleaseUrl -RetryIntervalSec 3 -MaximumRetryCount 100 + } + + if($statusCode -ne 200) { + throw "Unable to query repository version, please check your internet connection and try again..." + } + + $releaseTag = $releaseData.tag_name + + if($queryOnly) { + return $releaseTag + } + + # Check if directory exists + Write-Verbose "=====> Checking if directory for releases exists: $targetPath" + + if (!(Test-Path $targetPath)) { + Write-Verbose "Directory does not exist for releases, will now create: $targetPath" + New-Item -ItemType Directory -Path $targetPath | Out-String | Write-Verbose + } + + # Check the directory for this release + $targetVersionPath = Join-Path $targetPath $releaseTag + + Write-Verbose "===> Checking if directory for release version exists: $targetVersionPath" + + if (!(Test-Path $targetVersionPath)) { + Write-Verbose "Directory does not exist for release $releaseTag, will now create: $targetVersionPath" + New-Item -ItemType Directory -Path $targetVersionPath | Out-String | Write-Verbose + } + + Write-Verbose "===> Checking if any content exists inside of $targetVersionPath" + + $contentTargetVersionPath = Get-ChildItem -Path $targetVersionPath -Recurse -ErrorAction SilentlyContinue + + if ($null -eq $contentTargetVersionPath) { + Write-Verbose "===> Pulling and extracting release $releaseTag into $targetVersionPath" + New-Item -ItemType Directory -Path "$targetVersionPath/tmp" | Out-String | Write-Verbose + $targetPathForZip = "$targetVersionPath/tmp/$releaseTag.zip" + Invoke-WebRequest -Uri "https://github.com/$repoOrgPlusRepo/archive/refs/tags/$releaseTag.zip" -OutFile $targetPathForZip -RetryIntervalSec 3 -MaximumRetryCount 100 | Out-String | Write-Verbose + + if(!(Test-Path $targetPathForZip)) { + Write-InformationColored "Failed to download the release $releaseTag from the GitHub repository $repoOrgPlusRepo" -ForegroundColor Red -InformationAction Continue + throw + } + + $targetPathForExtractedZip = "$targetVersionPath/tmp/extracted" + + Expand-Archive -Path $targetPathForZip -DestinationPath $targetPathForExtractedZip | Out-String | Write-Verbose + $extractedSubFolder = Get-ChildItem -Path $targetPathForExtractedZip -Directory + + Write-Verbose "===> Copying all extracted contents into $targetVersionPath." + + Copy-Item -Path "$($extractedSubFolder.FullName)/$moduleSourceFolder/*" -Destination "$targetVersionPath" -Recurse | Out-String | Write-Verbose + + Remove-Item -Path "$targetVersionPath/tmp" -Force -Recurse + Write-InformationColored "The directory for $targetVersionPath has been created and populated." -ForegroundColor Green -InformationAction Continue + } else { + Write-InformationColored "The directory for $targetVersionPath already exists and has content in it, so we are not over-writing it." -ForegroundColor Green -InformationAction Continue + Write-Verbose "===> Content already exists in $releaseDirectory. Skipping" + } + + # Check and replace the .env file release version if it is Bicep + $envFilePath = Join-Path -Path $parentDirectory -ChildPath ".env" + if(Test-Path $envFilePath) { + Write-Verbose "===> Replacing the .env file release version with $releaseTag" + (Get-Content $envFilePath) -replace "UPSTREAM_RELEASE_VERSION=.*", "UPSTREAM_RELEASE_VERSION=$releaseTag" | Set-Content $envFilePath + } + + return $releaseTag +} \ No newline at end of file diff --git a/src/ALZ/Private/Get-StarterConfig.ps1 b/src/ALZ/Private/Get-StarterConfig.ps1 new file mode 100644 index 00000000..e69de29b diff --git a/src/ALZ/Private/Get-TerraformTool.ps1 b/src/ALZ/Private/Get-TerraformTool.ps1 index bfc7a3a3..aa8526b2 100644 --- a/src/ALZ/Private/Get-TerraformTool.ps1 +++ b/src/ALZ/Private/Get-TerraformTool.ps1 @@ -8,23 +8,29 @@ function Get-TerraformTool { ) if($version -eq "latest") { - $version = (Invoke-WebRequest -Uri "https://checkpoint-api.hashicorp.com/v1/check/terraform").Content | ConvertFrom-Json | Select-Object -ExpandProperty current_version + $versionResponse = Invoke-WebRequest -Uri "https://checkpoint-api.hashicorp.com/v1/check/terraform" + if($versionResponse.StatusCode -ne "200") { + throw "Unable to query Terraform version, please check your internet connection and try again..." + } + $version = ($versionResponse).Content | ConvertFrom-Json | Select-Object -ExpandProperty current_version } + Write-Verbose "Required version of Terraform is $version" + $commandDetails = Get-Command -Name terraform -ErrorAction SilentlyContinue if($commandDetails) { - Write-InformationColored "Terraform already installed in $($commandDetails.Path), checking version" -ForegroundColor Green -InformationAction Continue + Write-Verbose "Terraform already installed in $($commandDetails.Path), checking version" $installedVersion = terraform version -json | ConvertFrom-Json - Write-InformationColored "Installed version of Terraform: $($installedVersion.terraform_version) on $($installedVersion.platform)" -ForegroundColor Green -InformationAction Continue + Write-Verbose "Installed version of Terraform: $($installedVersion.terraform_version) on $($installedVersion.platform)" if($installedVersion.terraform_version -eq $version) { - Write-InformationColored "Installed version of Terraform matches required version $version, skipping install" -ForegroundColor Green -InformationAction Continue + Write-Verbose "Installed version of Terraform matches required version $version, skipping install" return } } $unzipdir = Join-Path -Path $toolsPath -ChildPath "terraform_$version" if (Test-Path $unzipdir) { - Write-InformationColored "Terraform $version already installed, adding to Path." -ForegroundColor Green -InformationAction Continue + Write-Verbose "Terraform $version already installed, adding to Path." if($os -eq "windows") { $env:PATH = "$($unzipdir);$env:PATH" } else { @@ -69,5 +75,5 @@ function Get-TerraformTool { } Remove-Item $zipfilePath - Write-InformationColored "Installed Terraform version $version" -ForegroundColor Green -InformationAction Continue + Write-Verbose "Installed Terraform version $version" } diff --git a/src/ALZ/Private/Import-ConfigurationFile.ps1 b/src/ALZ/Private/Import-ConfigurationFile.ps1 deleted file mode 100644 index 54fa6c2f..00000000 --- a/src/ALZ/Private/Import-ConfigurationFile.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -function Import-ConfigurationFileData { - <# - - #> - param( - [Parameter(Mandatory = $false)] - [PSCustomObject] $starterModuleConfiguration, - [Parameter(Mandatory = $false)] - [PSCustomObject] $bootstrapConfiguration - ) - - $configurationFilePathObjects = ($starterModuleConfiguration.PsObject.Properties | Where-Object { $_.Value.Validator -eq "configuration_file_path" }) - - if($configurationFilePathObjects.Count -eq 0) { - return - } - - $configurationFilePath = $configurationFilePathObjects[0].Value.Value - $bootstrapConfigurationFilePathObject = $bootstrapConfiguration.PsObject.Properties | Where-Object { $_.Value.Validator -eq "hidden_configuration_file_path" } - $bootstrapConfigurationFilePathObject.Value.Value = $configurationFilePath -} \ No newline at end of file diff --git a/src/ALZ/Private/Import-SubscriptionData.ps1 b/src/ALZ/Private/Import-SubscriptionData.ps1 deleted file mode 100644 index 23cf7702..00000000 --- a/src/ALZ/Private/Import-SubscriptionData.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -function Import-SubscriptionData { - <# - - #> - param( - [Parameter(Mandatory = $false)] - [PSCustomObject] $starterModuleConfiguration, - [Parameter(Mandatory = $false)] - [PSCustomObject] $bootstrapConfiguration - ) - - $subscriptions = $starterModuleConfiguration.PsObject.Properties | Where-Object { $_.Value.Validator -eq "azure_subscription_id" } - $subscriptionIds = @() - foreach($subscription in $subscriptions) { - $subscriptionIds += $subscription.Value.Value - } - - $subscriptionIdsJoined = $subscriptionIds -join "," - $subscriptionsObject = $bootstrapConfiguration.PsObject.Properties | Where-Object { $_.Value.Validator -eq "hidden_azure_subscription_ids" } - $subscriptionsObject.Value.Value = $subscriptionIdsJoined -} \ No newline at end of file diff --git a/src/ALZ/Private/Invoke-FullUpgrade.ps1 b/src/ALZ/Private/Invoke-FullUpgrade.ps1 new file mode 100644 index 00000000..25711462 --- /dev/null +++ b/src/ALZ/Private/Invoke-FullUpgrade.ps1 @@ -0,0 +1,67 @@ +function Invoke-FullUpgrade { + [CmdletBinding(SupportsShouldProcess = $true)] + param ( + [Parameter(Mandatory = $false)] + [string] $bootstrapRelease, + + [Parameter(Mandatory = $false)] + [string] $bootstrapPath, + + [Parameter(Mandatory = $false)] + [string] $bootstrapModuleFolder, + + [Parameter(Mandatory = $false)] + [string] $starterRelease, + + [Parameter(Mandatory = $false)] + [string] $starterPath, + + [Parameter(Mandatory = $false)] + [string] $interfaceCacheFileName, + + [Parameter(Mandatory = $false)] + [string] $bootstrapCacheFileName, + + [Parameter(Mandatory = $false)] + [string] $starterCacheFileName, + + [Parameter(Mandatory = $false)] + [switch] $autoApprove + ) + + if ($PSCmdlet.ShouldProcess("Upgrade Release", "Operation")) { + + # Run upgrade for bootstrap state + $wasUpgraded = Invoke-Upgrade ` + -targetDirectory $bootstrapPath ` + -targetFolder $bootstrapModuleFolder ` + -cacheFileName "terraform.tfstate" ` + -release $bootstrapRelease ` + -autoApprove:$autoApprove.IsPresent + + if($wasUpgraded) { + # Run upgrade for interface inputs + Invoke-Upgrade ` + -targetDirectory $bootstrapPath ` + -cacheFileName $interfaceCacheFileName ` + -release $bootstrapRelease ` + -autoApprove:$wasUpgraded | Out-String | Write-Verbose + + # Run upgrade for bootstrap inputs + Invoke-Upgrade ` + -targetDirectory $bootstrapPath ` + -cacheFileName $bootstrapCacheFileName ` + -release $bootstrapRelease ` + -autoApprove:$wasUpgraded | Out-String | Write-Verbose + + # Run upgrade for starter + Invoke-Upgrade ` + -targetDirectory $starterPath ` + -cacheFileName $starterCacheFileName ` + -release $starterRelease ` + -autoApprove:$wasUpgraded | Out-String | Write-Verbose + + Write-InformationColored "AUTOMATIC UPGRADE: Upgrade complete. If any starter files have been updated, you will need to remove branch protection in order for the Terraform apply to succeed." -ForegroundColor Yellow -InformationAction Continue + } + } +} diff --git a/src/ALZ/Private/Invoke-Upgrade.ps1 b/src/ALZ/Private/Invoke-Upgrade.ps1 index 52bf8076..eeb43bc0 100644 --- a/src/ALZ/Private/Invoke-Upgrade.ps1 +++ b/src/ALZ/Private/Invoke-Upgrade.ps1 @@ -2,19 +2,16 @@ function Invoke-Upgrade { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $false)] - [string] $alzEnvironmentDestination, + [string] $targetDirectory, [Parameter(Mandatory = $false)] - [string] $bootstrapCacheFileName, + [string] $targetFolder = "", [Parameter(Mandatory = $false)] - [string] $starterCacheFileNamePattern, + [string] $cacheFileName, [Parameter(Mandatory = $false)] - [string] $stateFilePathAndFileName, - - [Parameter(Mandatory = $false)] - [string] $currentVersion, + [string] $release, [Parameter(Mandatory = $false)] [switch] $autoApprove @@ -22,34 +19,26 @@ function Invoke-Upgrade { if ($PSCmdlet.ShouldProcess("Upgrade Release", "Operation")) { - $directories = Get-ChildItem -Path $alzEnvironmentDestination -Filter "v*" -Directory - $previousBootstrapCachedValuesPath = $null - $previousStarterCachedValuesPath = $null - $previousStateFilePath = $null + $directories = Get-ChildItem -Path $targetDirectory -Filter "v*" -Directory + $previousCachedValuesPath = $null $previousVersion = $null $foundPreviousRelease = $false - foreach ($directory in $directories | Sort-Object -Descending -Property Name) { - $releasePath = Join-Path -Path $alzEnvironmentDestination -ChildPath $directory.Name - $releaseBootstrapCachedValuesPath = Join-Path -Path $releasePath -ChildPath $bootstrapCacheFileName - $releaseStateFilePath = Join-Path -Path $releasePath -ChildPath $stateFilePathAndFileName - - if(Test-Path $releaseBootstrapCachedValuesPath) { - $previousBootstrapCachedValuesPath = $releaseBootstrapCachedValuesPath - } + Write-Verbose "UPGRADE: Checking for existing directories in $targetDirectory" - $starterCacheFiles = Get-ChildItem -Path $releasePath -Filter $starterCacheFileNamePattern -File + foreach ($directory in $directories | Sort-Object -Descending -Property Name) { + $releasePath = Join-Path $targetDirectory $directory.Name + $releaseCachedValuesPath = Join-Path $releasePath $targetFolder $cacheFileName - if($starterCacheFiles) { - $previousStarterCachedValuesPath = $starterCacheFiles[0].FullName - } + Write-Verbose "UPGRADE: Checking for existing file in $releasePath, specifically $releaseCachedValuesPath" - if(Test-Path $releaseStateFilePath) { - $previousStateFilePath = $releaseStateFilePath + if(Test-Path $releaseCachedValuesPath) { + $previousCachedValuesPath = $releaseCachedValuesPath } - if($null -ne $previousStateFilePath) { - if($directory.Name -eq $currentVersion) { + if($null -ne $previousCachedValuesPath) { + if($directory.Name -eq $release) { + Write-Verbose "Latest version $release has already been run. Skipping upgrade..." # If the current version has already been run, then skip the upgrade process break } @@ -61,35 +50,28 @@ function Invoke-Upgrade { } if($foundPreviousRelease) { - Write-InformationColored "AUTOMATIC UPGRADE: We found version $previousVersion that has been previously run. You can upgrade from this version to the new version $currentVersion" -ForegroundColor Yellow -InformationAction Continue $upgrade = "" if($autoApprove) { $upgrade = "upgrade" } else { + Write-InformationColored "AUTOMATIC UPGRADE: We found version $previousVersion that has been previously run. You can upgrade from this version to the new version $currentVersion" -ForegroundColor Yellow -InformationAction Continue $upgrade = Read-Host "If you would like to upgrade, enter 'upgrade' or just hit 'enter' to continue with a new environment. (upgrade/exit)" } if($upgrade.ToLower() -eq "upgrade") { - $currentPath = Join-Path -Path $alzEnvironmentDestination -ChildPath $currentVersion - $currentBootstrapCachedValuesPath = Join-Path -Path $currentPath -ChildPath $bootstrapCacheFileName - $currentStarterCachedValuesPath = $currentPath - $currentStateFilePath = Join-Path -Path $currentPath -ChildPath $stateFilePathAndFileName + $currentPath = Join-Path $targetDirectory $release + $currentCachedValuesPath = Join-Path $currentPath $targetFolder $cacheFileName # Copy the previous cached values to the current release - if($null -ne $previousBootstrapCachedValuesPath) { - Write-InformationColored "AUTOMATIC UPGRADE: Copying $previousBootstrapCachedValuesPath to $currentBootstrapCachedValuesPath" -ForegroundColor Green -InformationAction Continue - Copy-Item -Path $previousBootstrapCachedValuesPath -Destination $currentBootstrapCachedValuesPath -Force | Out-String | Write-Verbose - } - if($null -ne $previousStarterCachedValuesPath) { - Write-InformationColored "AUTOMATIC UPGRADE: Copying $previousStarterCachedValuesPath to $currentStarterCachedValuesPath" -ForegroundColor Green -InformationAction Continue - Copy-Item -Path $previousStarterCachedValuesPath -Destination $currentStarterCachedValuesPath -Force | Out-String | Write-Verbose + if($null -ne $previousCachedValuesPath) { + Write-InformationColored "AUTOMATIC UPGRADE: Copying $previousCachedValuesPath to $currentCachedValuesPath" -ForegroundColor Green -InformationAction Continue + Copy-Item -Path $previousCachedValuesPath -Destination $currentCachedValuesPath -Force | Out-String | Write-Verbose } - Write-InformationColored "AUTOMATIC UPGRADE: Copying $previousStateFilePath to $currentStateFilePath" -ForegroundColor Green -InformationAction Continue - Copy-Item -Path $previousStateFilePath -Destination $currentStateFilePath -Force | Out-String | Write-Verbose - - Write-InformationColored "AUTOMATIC UPGRADE: Upgrade complete. If any files in the starter have been updated, you will need to remove branch protection in order for the Terraform apply to succeed..." -ForegroundColor Yellow -InformationAction Continue + return $true } } + + return $false } } diff --git a/src/ALZ/Private/New-ALZEnvironmentBicep.ps1 b/src/ALZ/Private/New-ALZEnvironmentBicep.ps1 index fc174937..b0e63d87 100644 --- a/src/ALZ/Private/New-ALZEnvironmentBicep.ps1 +++ b/src/ALZ/Private/New-ALZEnvironmentBicep.ps1 @@ -2,57 +2,46 @@ function New-ALZEnvironmentBicep { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $false)] - [Alias("Output")] - [Alias("OutputDirectory")] - [Alias("O")] - [string] $alzEnvironmentDestination, + [string] $targetDirectory, [Parameter(Mandatory = $false)] - [string] $alzVersion, + [string] $upstreamReleaseVersion, + + [Parameter(Mandatory = $false)] + [string] $upstreamReleaseFolderPath, [Parameter(Mandatory = $false)] [ValidateSet("github", "azuredevops")] - [Alias("Cicd")] - [string] $alzCicdPlatform + [string] $vcs, + + [Parameter(Mandatory = $false)] + [switch] $local ) if ($PSCmdlet.ShouldProcess("ALZ-Bicep module configuration", "modify")) { - if($alzVersion -ne "latest" -and $alzVersion -notlike "*-preview") { - $lastSupportedLocalVersion = [System.Version]"0.16.5" - $targetVersion = [System.Version]($alzVersion -replace "v", "") - - if($targetVersion -le $lastSupportedLocalVersion) { - throw "The version of the ALZ-Bicep accelerator you are targetting is not supported by this version of the ALZ PowerShell module. In order to target versions prior to v0.16.6 you will need to downgrade to version v0.2.20 or lower of this module." - } - } - - New-ALZDirectoryEnvironment -alzEnvironmentDestination $alzEnvironmentDestination -alzCicdDestination $alzCicdPlatform | Out-String | Write-Verbose - - $alzEnvironmentDestinationInternalCode = Join-Path $alzEnvironmentDestination "upstream-releases" - - # Downloading the latest or specified version of the bicep accelerator module - $releaseTag = Get-ALZGithubRelease -directoryForReleases $alzEnvironmentDestination -iac "bicep" -release $alzVersion - $releasePath = Join-Path -Path $alzEnvironmentDestinationInternalCode -ChildPath $releaseTag + New-ALZDirectoryEnvironment -alzEnvironmentDestination $targetDirectory -alzCicdDestination $vcs | Out-String | Write-Verbose # Getting the configuration - $configFilePath = Join-Path -Path $releasePath -ChildPath "accelerator/.config/ALZ-Powershell.config.json" + $configFilePath = Join-Path -Path $upstreamReleaseFolderPath -ChildPath "accelerator/.config/ALZ-Powershell.config.json" + Write-Verbose "Config path: $configFilePath" $bicepConfig = Get-ALZConfig -configFilePath $configFilePath - Write-InformationColored "Copying ALZ-Bicep module to $alzEnvironmentDestinationInternalCode" -ForegroundColor Green -InformationAction Continue - Copy-ALZParametersFile -alzEnvironmentDestination $alzEnvironmentDestination -upstreamReleaseDirectory $(Join-Path $alzEnvironmentDestinationInternalCode $releaseTag) -configFiles $bicepConfig.config_files | Out-String | Write-Verbose - Copy-ALZParametersFile -alzEnvironmentDestination $alzEnvironmentDestination -upstreamReleaseDirectory $(Join-Path $alzEnvironmentDestinationInternalCode $releaseTag) -configFiles $bicepConfig.cicd.$alzCicdPlatform | Out-String | Write-Verbose - Write-InformationColored "ALZ-Bicep source directory: $alzBicepSourceDirectory" -ForegroundColor Green -InformationAction Continue + Write-InformationColored "Copying ALZ-Bicep module to $targetDirectory" -ForegroundColor Green -InformationAction Continue + Copy-ALZParametersFile -alzEnvironmentDestination $targetDirectory -upstreamReleaseDirectory $upstreamReleaseFolderPath -configFiles $bicepConfig.config_files | Out-String | Write-Verbose + Copy-ALZParametersFile -alzEnvironmentDestination $targetDirectory -upstreamReleaseDirectory $upstreamReleaseFolderPath -configFiles $bicepConfig.cicd.$vcs | Out-String | Write-Verbose $configuration = Request-ALZEnvironmentConfig -configurationParameters $bicepConfig.parameters Set-ComputedConfiguration -configuration $configuration | Out-String | Write-Verbose - Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination $alzEnvironmentDestination -configuration $configuration | Out-String | Write-Verbose - Build-ALZDeploymentEnvFile -configuration $configuration -Destination $alzEnvironmentDestination -version $releaseTag | Out-String | Write-Verbose + Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination $targetDirectory -configuration $configuration | Out-String | Write-Verbose + Build-ALZDeploymentEnvFile -configuration $configuration -Destination $targetDirectory -version $upstreamReleaseVersion | Out-String | Write-Verbose - $isGitRepo = Test-ALZGitRepository -alzEnvironmentDestination $alzEnvironmentDestination - if (-not $isGitRepo) { - Write-InformationColored "The directory $alzEnvironmentDestination is not a git repository. Please make it is a git repo after initialization." -ForegroundColor Red -InformationAction Continue + if($local) { + $isGitRepo = Test-ALZGitRepository -alzEnvironmentDestination $targetDirectory + if (-not $isGitRepo) { + Write-InformationColored "The directory $targetDirectory is not a git repository. Please make sure it is a git repo after initialization." -ForegroundColor Red -InformationAction Continue + } } } } \ No newline at end of file diff --git a/src/ALZ/Private/New-ALZEnvironmentTerraform.ps1 b/src/ALZ/Private/New-ALZEnvironmentTerraform.ps1 deleted file mode 100644 index d5cd3159..00000000 --- a/src/ALZ/Private/New-ALZEnvironmentTerraform.ps1 +++ /dev/null @@ -1,114 +0,0 @@ -function New-ALZEnvironmentTerraform { - [CmdletBinding(SupportsShouldProcess = $true)] - param ( - [Parameter(Mandatory = $false)] - [Alias("Output")] - [Alias("OutputDirectory")] - [Alias("O")] - [string] $alzEnvironmentDestination, - - [Parameter(Mandatory = $false)] - [string] $alzVersion, - - [Parameter(Mandatory = $false)] - [Alias("Cicd")] - [string] $alzCicdPlatform, - - [Parameter(Mandatory = $false)] - [Alias("inputs")] - [string] $userInputOverridePath = "", - - [Parameter(Mandatory = $false)] - [switch] $autoApprove, - - [Parameter(Mandatory = $false)] - [switch] $destroy - ) - - if ($PSCmdlet.ShouldProcess("ALZ-Terraform module configuration", "modify")) { - Write-InformationColored "Checking you have the latest version of Terraform installed..." -ForegroundColor Green -InformationAction Continue - $toolsPath = Join-Path -Path $alzEnvironmentDestination -ChildPath ".tools" - Get-TerraformTool -version "latest" -toolsPath $toolsPath - - Write-InformationColored "Downloading alz-terraform-accelerator Terraform module to $alzEnvironmentDestination" -ForegroundColor Green -InformationAction Continue - - # Get User Input Overrides (used for automated testing purposes and advanced use cases) - $userInputOverrides = $null - if($userInputOverridePath -ne "") { - $userInputOverrides = Get-ALZConfig -configFilePath $userInputOverridePath - } - - # Setup Cache Paths - $bootstrapCacheFileName = "cache-bootstrap-$alzCicdPlatform.json" - $starterCacheFileNamePattern = "cache-starter-*.json" - - # Downloading the latest or specified version of the alz-terraform-accelerator module - if(!($alzVersion.StartsWith("v")) -and ($alzVersion -ne "latest")) { - $alzVersion = "v$alzVersion" - } - $releaseTag = Get-ALZGithubRelease -directoryForReleases $alzEnvironmentDestination -iac "terraform" -release $alzVersion - $releasePath = Join-Path -Path $alzEnvironmentDestination -ChildPath $releaseTag - - # Run upgrade - Invoke-Upgrade -alzEnvironmentDestination $alzEnvironmentDestination -bootstrapCacheFileName $bootstrapCacheFileName -starterCacheFileNamePattern $starterCacheFileNamePattern -stateFilePathAndFileName "bootstrap/$alzCicdPlatform/terraform.tfstate" -currentVersion $releaseTag -autoApprove:$autoApprove.IsPresent - - # Getting the configuration for the initial bootstrap user input and validators - $bootstrapConfigFilePath = Join-Path -Path $releasePath -ChildPath "bootstrap/.config/ALZ-Powershell.config.json" - $bootstrapConfig = Get-ALZConfig -configFilePath $bootstrapConfigFilePath - - # Getting additional configuration for the bootstrap module user input - $bootstrapPath = Join-Path -Path $releasePath -ChildPath "bootstrap/$alzCicdPlatform" - $bootstrapVariableFilesPath = Join-Path -Path $bootstrapPath -ChildPath "variables.tf" - $hclParserToolPath = Get-HCLParserTool -alzEnvironmentDestination $releasePath -toolVersion "v0.6.0" - $bootstrapParameters = Convert-HCLVariablesToUserInputConfig -targetVariableFile $bootstrapVariableFilesPath -hclParserToolPath $hclParserToolPath -validators $bootstrapConfig.validators - - Write-InformationColored "Got configuration and downloaded alz-terraform-accelerator Terraform module version $releaseTag to $alzEnvironmentDestination" -ForegroundColor Green -InformationAction Continue - - #Checking for cached bootstrap values for retry / upgrade scenarios - $bootstrapCachedValuesPath = Join-Path -Path $releasePath -ChildPath $bootstrapCacheFileName - $cachedBootstrapConfig = Get-ALZConfig -configFilePath $bootstrapCachedValuesPath - - # Getting the user input for the bootstrap module - $bootstrapConfiguration = Request-ALZEnvironmentConfig -configurationParameters $bootstrapParameters -respectOrdering -userInputOverrides $userInputOverrides -userInputDefaultOverrides $cachedBootstrapConfig -treatEmptyDefaultAsValid $true -autoApprove:$autoApprove.IsPresent - - # Getting the configuration for the starter module user input - $starterTemplate = $bootstrapConfiguration.PsObject.Properties["starter_module"].Value.Value - $starterTemplatePath = Join-Path -Path $releasePath -ChildPath "templates/$($starterTemplate)" - $targetVariableFilePath = Join-Path -Path $starterTemplatePath -ChildPath "variables.tf" - $starterModuleParameters = Convert-HCLVariablesToUserInputConfig -targetVariableFile $targetVariableFilePath -hclParserToolPath $hclParserToolPath -validators $bootstrapConfig.validators - - Write-InformationColored "The following inputs are specific to the '$starterTemplate' starter module that you selected..." -ForegroundColor Green -InformationAction Continue - - # Checking for cached starter module values for retry / upgrade scenarios - $starterCacheFileName = "cache-starter-$starterTemplate.json" - $starterModuleCachedValuesPath = Join-Path -Path $releasePath -ChildPath $starterCacheFileName - $cachedStarterModuleConfig = Get-ALZConfig -configFilePath $starterModuleCachedValuesPath - - # Getting the user input for the starter module - $starterModuleConfiguration = Request-ALZEnvironmentConfig -configurationParameters $starterModuleParameters -respectOrdering -userInputOverrides $userInputOverrides -userInputDefaultOverrides $cachedStarterModuleConfig -treatEmptyDefaultAsValid $true -autoApprove:$autoApprove.IsPresent - - # Getting computed inputs - Import-SubscriptionData -starterModuleConfiguration $starterModuleConfiguration -bootstrapConfiguration $bootstrapConfiguration - Import-ConfigurationFileData -starterModuleConfiguration $starterModuleConfiguration -bootstrapConfiguration $bootstrapConfiguration - - # Creating the tfvars files for the bootstrap and starter module - $bootstrapTfvarsPath = Join-Path -Path $bootstrapPath -ChildPath "override.tfvars" - $starterModuleTfvarsPath = Join-Path -Path $starterTemplatePath -ChildPath "terraform.tfvars" - Write-TfvarsFile -tfvarsFilePath $bootstrapTfvarsPath -configuration $bootstrapConfiguration - Write-TfvarsFile -tfvarsFilePath $starterModuleTfvarsPath -configuration $starterModuleConfiguration - - # Caching the bootstrap and starter module values paths for retry / upgrade scenarios - Write-ConfigurationCache -filePath $bootstrapCachedValuesPath -configuration $bootstrapConfiguration - Write-ConfigurationCache -filePath $starterModuleCachedValuesPath -configuration $starterModuleConfiguration - - # Running terraform init and apply - Write-InformationColored "Thank you for providing those inputs, we are now initializing and applying Terraform to bootstrap your environment..." -ForegroundColor Green -InformationAction Continue - - if($autoApprove) { - Invoke-Terraform -moduleFolderPath $bootstrapPath -tfvarsFileName "override.tfvars" -autoApprove -destroy:$destroy.IsPresent - } else { - Write-InformationColored "Once the plan is complete you will be prompted to confirm the apply. You must enter 'yes' to apply." -ForegroundColor Green -InformationAction Continue - Invoke-Terraform -moduleFolderPath $bootstrapPath -tfvarsFileName "override.tfvars" -destroy:$destroy.IsPresent - } - } -} \ No newline at end of file diff --git a/src/ALZ/Private/New-Bootstrap.ps1 b/src/ALZ/Private/New-Bootstrap.ps1 new file mode 100644 index 00000000..117aa155 --- /dev/null +++ b/src/ALZ/Private/New-Bootstrap.ps1 @@ -0,0 +1,240 @@ +function New-Bootstrap { + [CmdletBinding(SupportsShouldProcess = $true)] + param ( + [Parameter(Mandatory = $false)] + [string] $iac, + + [Parameter(Mandatory = $false)] + [PSCustomObject] $bootstrapDetails, + + [Parameter(Mandatory = $false)] + [PSCustomObject] $validationConfig, + + [Parameter(Mandatory = $false)] + [PSCustomObject] $inputConfig, + + [Parameter(Mandatory = $false)] + [PSCustomObject] $userInputOverrides = $null, + + [Parameter(Mandatory = $false)] + [string] $bootstrapTargetPath, + + [Parameter(Mandatory = $false)] + [switch] $hasStarter, + + [Parameter(Mandatory = $false)] + [string] $starterTargetPath, + + [Parameter(Mandatory = $false)] + [string] $bootstrapRelease, + + [Parameter(Mandatory = $false)] + [string] $starterRelease, + + [Parameter(Mandatory = $false)] + [string] $starterPipelineFolder, + + [Parameter(Mandatory = $false)] + [switch] $autoApprove, + + [Parameter(Mandatory = $false)] + [switch] $destroy + ) + + if ($PSCmdlet.ShouldProcess("ALZ-Terraform module configuration", "modify")) { + + # Setup tools + $hclParserToolPath = Get-HCLParserTool -alzEnvironmentDestination $bootstrapPath -toolVersion "v0.6.0" + + # Setup Cache File Names + $interfaceCacheFileName = "interface-cache.json" + $bootstrapCacheFileName = "bootstrap-cache.json" + $starterCacheFileName = "starter-cache.json" + $interfaceCachePath = Join-Path -Path $bootstrapPath -ChildPath $interfaceCacheFileName + $interfaceCachedConfig = Get-ALZConfig -configFilePath $interfaceCachePath + $bootstrapCachePath = Join-Path -Path $bootstrapPath -ChildPath $bootstrapCacheFileName + $bootstrapCachedConfig = Get-ALZConfig -configFilePath $bootstrapCachePath + $starterCachePath = Join-Path -Path $starterPath -ChildPath $starterCacheFileName + $starterCachedConfig = Get-ALZConfig -configFilePath $starterCachePath + + $bootstrapPath = Join-Path $bootstrapTargetPath $bootstrapRelease + $starterPath = Join-Path $starterTargetPath $starterRelease + $bootstrapModulePath = Join-Path -Path $bootstrapPath -ChildPath $bootstrapDetails.Value.location + + Write-Verbose "Bootstrap Module Path: $bootstrapModulePath" + + # Run upgrade + Invoke-FullUpgrade ` + -bootstrapModuleFolder $bootstrapDetails.Value.location ` + -bootstrapRelease $bootstrapRelease ` + -bootstrapPath $bootstrapTargetPath ` + -starterRelease $starterRelease ` + -starterPath $starterTargetPath ` + -interfaceCacheFileName $interfaceCacheFileName ` + -bootstrapCacheFileName $bootstrapCacheFileName ` + -starterCacheFileName $starterCacheFileName ` + -autoApprove:$autoApprove.IsPresent + + # Get starter module + $starter = "" + $starterModulePath = "" + $pipelineModulePath = "" + + if($hasStarter) { + $starter = Request-SpecialInput -type "starter" -starterPath $starterPath -userInputOverrides $userInputOverrides + $starterModulePath = Join-Path -Path $starterPath -ChildPath $starter + $pipelineModulePath = Join-Path -Path $starterPath -ChildPath $starterPipelineFolder + } + + # Getting the configuration for the interface user input + Write-Verbose "Getting the interface configuration for user input..." + $inputConfigMapped = Convert-InterfaceInputToUserInputConfig -inputConfig $inputConfig -validators $validationConfig + + # Getting configuration for the bootstrap module user input + $bootstrapParameters = [PSCustomObject]@{} + + Write-Verbose "Getting the bootstrap configuration for user input..." + foreach($inputVariablesFile in $bootstrapDetails.Value.input_variable_files) { + $inputVariablesFilePath = Join-Path -Path $bootstrapModulePath -ChildPath $inputVariablesFile + $bootstrapParameters = Convert-HCLVariablesToUserInputConfig -targetVariableFile $inputVariablesFilePath -hclParserToolPath $hclParserToolPath -validators $validationConfig -appendToObject $bootstrapParameters + } + Write-Verbose "Getting the bootstrap configuration computed interface input..." + foreach($interfaceVariablesFile in $bootstrapDetails.Value.interface_variable_files) { + $inputVariablesFilePath = Join-Path -Path $bootstrapModulePath -ChildPath $interfaceVariablesFile + $bootstrapParameters = Convert-HCLVariablesToUserInputConfig -targetVariableFile $inputVariablesFilePath -hclParserToolPath $hclParserToolPath -validators $validationConfig -appendToObject $bootstrapParameters -allComputedInputs + } + + # Getting the configuration for the starter module user input + $starterParameters = [PSCustomObject]@{} + + if($hasStarter) { + $targetVariableFilePath = Join-Path -Path $starterModulePath -ChildPath "variables.tf" + $starterParameters = Convert-HCLVariablesToUserInputConfig -targetVariableFile $targetVariableFilePath -hclParserToolPath $hclParserToolPath -validators $bootstrapConfig.validators + } + + # Filter interface inputs if not in bootstrap or starter + foreach($inputConfigItem in $inputConfig.inputs.PSObject.Properties) { + if($inputConfigItem.Value.source -ne "input" -or $inputConfigItem.Value.required -eq $true) { + continue + } + $inputVariable = $inputConfigMapped.PSObject.Properties | Where-Object { $_.Name -eq $inputConfigItem.Name } + $displayMapFilter = $inputConfigItem.Value.PSObject.Properties | Where-Object { $_.Name -eq "display_map_filter" } + $hasDisplayMapFilter = $null -ne $displayMapFilter + Write-Verbose "$($inputConfigItem.Name) has display map filter $hasDisplayMapFilter" + + $inBootstrapOrStarter = $false + if("bootstrap" -in $inputConfigItem.Value.maps_to) { + $checkFilter = !$hasDisplayMapFilter -or ($hasDisplayMapFilter -and "bootstrap" -in $displayMapFilter.Value) + + if($checkFilter) { + Write-Verbose "Checking bootstrap for $($inputConfigItem.Name)" + $boostrapParameter = $bootstrapParameters.PSObject.Properties | Where-Object { $_.Name -eq $inputVariable.Name } + if($null -ne $boostrapParameter) { + $inBootstrapOrStarter = $true + } + } + } + if("starter" -in $inputConfigItem.Value.maps_to) { + $checkFilter = !$hasDisplayMapFilter -or ($hasDisplayMapFilter -and "starter" -in $displayMapFilter.Value) + + if($checkFilter) { + Write-Verbose "Checking starter for $($inputConfigItem.Name)" + $starterParameter = $starterParameters.PSObject.Properties | Where-Object { $_.Name -eq $inputVariable.Name } + if($null -ne $starterParameter) { + $inBootstrapOrStarter = $true + } + } + } + + if(!$inBootstrapOrStarter) { + $inputVariable.Value.Type = "SkippedInput" + } + } + + # Prompt user for interface inputs + Write-InformationColored "The following shared inputs are for the '$($bootstrapDetails.Name)' bootstrap and '$starter' starter module that you selected:" -ForegroundColor Green -NewLineBefore -InformationAction Continue + $interfaceConfiguration = Request-ALZEnvironmentConfig ` + -configurationParameters $inputConfigMapped ` + -respectOrdering ` + -userInputOverrides $userInputOverrides ` + -userInputDefaultOverrides $interfaceCachedConfig ` + -treatEmptyDefaultAsValid $true ` + -autoApprove:$autoApprove.IsPresent + + # Set computed interface inputs + $computedInputMapping = @{ + "iac_type" = $iac + "module_folder_path" = $starterModulePath + "pipeline_folder_path" = $pipelineModulePath + } + + foreach($inputConfigItem in $inputConfig.inputs.PSObject.Properties) { + if($inputConfigItem.Value.source -eq "powershell") { + + $inputVariable = $interfaceConfiguration.PSObject.Properties | Where-Object { $_.Name -eq $inputConfigItem.Name } + $inputValue = $computedInputMapping[$inputConfigItem.Name] + Write-Verbose "Setting $($inputConfigItem.Name) to $inputValue" + $inputVariable.Value.Value = $inputValue + } + } + + # Split interface inputs + $bootstrapComputed = [PSCustomObject]@{} + $starterComputed = [PSCustomObject]@{} + + foreach($inputConfigItem in $inputConfig.inputs.PSObject.Properties) { + $inputVariable = $interfaceConfiguration.PSObject.Properties | Where-Object { $_.Name -eq $inputConfigItem.Name } + if("bootstrap" -in $inputConfigItem.Value.maps_to) { + $bootstrapComputed | Add-Member -NotePropertyName $inputVariable.Name -NotePropertyValue $inputVariable.Value + } + if("starter" -in $inputConfigItem.Value.maps_to) { + $starterComputed | Add-Member -NotePropertyName $inputVariable.Name -NotePropertyValue $inputVariable.Value + } + } + + # Getting the user input for the bootstrap module + Write-InformationColored "The following inputs are specific to the '$($bootstrapDetails.Name)' bootstrap module that you selected:" -ForegroundColor Green -NewLineBefore -InformationAction Continue + $bootstrapConfiguration = Request-ALZEnvironmentConfig ` + -configurationParameters $bootstrapParameters ` + -respectOrdering ` + -userInputOverrides $userInputOverrides ` + -userInputDefaultOverrides $bootstrapCachedConfig ` + -treatEmptyDefaultAsValid $true ` + -autoApprove:$autoApprove.IsPresent ` + -computedInputs $bootstrapComputed + + + + # Getting the user input for the starter module + Write-InformationColored "The following inputs are specific to the '$starter' starter module that you selected:" -ForegroundColor Green -NewLineBefore -InformationAction Continue + $starterConfiguration = Request-ALZEnvironmentConfig ` + -configurationParameters $starterParameters ` + -respectOrdering ` + -userInputOverrides $userInputOverrides ` + -userInputDefaultOverrides $starterCachedConfig ` + -treatEmptyDefaultAsValid $true ` + -autoApprove:$autoApprove.IsPresent ` + -computedInputs $starterComputed + + # Creating the tfvars files for the bootstrap and starter module + $bootstrapTfvarsPath = Join-Path -Path $bootstrapModulePath -ChildPath "override.tfvars" + $starterTfvarsPath = Join-Path -Path $starterModulePath -ChildPath "terraform.tfvars" + Write-TfvarsFile -tfvarsFilePath $bootstrapTfvarsPath -configuration $bootstrapConfiguration + Write-TfvarsFile -tfvarsFilePath $starterTfvarsPath -configuration $starterConfiguration + + # Caching the bootstrap and starter module values paths for retry / upgrade scenarios + Write-ConfigurationCache -filePath $interfaceCachePath -configuration $interfaceConfiguration + Write-ConfigurationCache -filePath $bootstrapCachePath -configuration $bootstrapConfiguration + Write-ConfigurationCache -filePath $starterCachePath -configuration $starterConfiguration + + # Running terraform init and apply + Write-InformationColored "Thank you for providing those inputs, we are now initializing and applying Terraform to bootstrap your environment..." -ForegroundColor Green -NewLineBefore -InformationAction Continue + + if($autoApprove) { + Invoke-Terraform -moduleFolderPath $bootstrapModulePath -tfvarsFileName "override.tfvars" -autoApprove -destroy:$destroy.IsPresent + } else { + Write-InformationColored "Once the plan is complete you will be prompted to confirm the apply. You must enter 'yes' to apply." -ForegroundColor Green -NewLineBefore -InformationAction Continue + Invoke-Terraform -moduleFolderPath $bootstrapModulePath -tfvarsFileName "override.tfvars" -destroy:$destroy.IsPresent + } + } +} \ No newline at end of file diff --git a/src/ALZ/Private/New-FolderStructure.ps1 b/src/ALZ/Private/New-FolderStructure.ps1 new file mode 100644 index 00000000..4f618399 --- /dev/null +++ b/src/ALZ/Private/New-FolderStructure.ps1 @@ -0,0 +1,36 @@ +function New-FolderStructure { + [CmdletBinding(SupportsShouldProcess = $true)] + param ( + [Parameter(Mandatory = $true)] + [string] $targetDirectory, + + [Parameter(Mandatory = $true)] + [string] $url, + + [Parameter(Mandatory = $false)] + [string] $release = "latest", + + [Parameter(Mandatory = $true)] + [string] $targetFolder, + + [Parameter(Mandatory = $false)] + [string] $sourceFolder + ) + + if ($PSCmdlet.ShouldProcess("ALZ-Terraform module configuration", "modify")) { + Write-Verbose "Downloading modules to $targetDirectory" + if(!($release.StartsWith("v")) -and ($release -ne "latest")) { + $release = "v$release" + } + + $releaseTag = Get-GithubRelease -githubRepoUrl $url -targetDirectory $targetDirectory -moduleSourceFolder $sourceFolder -moduleTargetFolder $targetFolder -release $release + $path = Join-Path $targetDirectory $targetFolder $releaseTag + + Write-Verbose "Version $releaseTag is located in $path" + + return @{ + path = $path + releaseTag = $releaseTag + } + } +} \ No newline at end of file diff --git a/src/ALZ/Private/Request-ALZEnvironmentConfig.ps1 b/src/ALZ/Private/Request-ALZEnvironmentConfig.ps1 index 64b2daaf..621843d9 100644 --- a/src/ALZ/Private/Request-ALZEnvironmentConfig.ps1 +++ b/src/ALZ/Private/Request-ALZEnvironmentConfig.ps1 @@ -12,7 +12,10 @@ function Request-ALZEnvironmentConfig { [Parameter(Mandatory = $false)] [System.Boolean] $treatEmptyDefaultAsValid = $false, [Parameter(Mandatory = $false)] - [switch] $autoApprove + [switch] $autoApprove, + [Parameter(Mandatory = $false)] + [PSCustomObject] $computedInputs = $null + ) <# .SYNOPSIS @@ -27,31 +30,56 @@ function Request-ALZEnvironmentConfig { $configurations = $configurationParameters.PsObject.Properties + $hasInputOverrides = $false + if($userInputOverrides -ne $null) { + $hasInputOverrides = $true + } + $hasDefaultOverrides = $false if($userInputDefaultOverrides -ne $null) { $hasDefaultOverrides = $true - Write-InformationColored "We found you have cached values from a previous run." -ForegroundColor Yellow -InformationAction Continue $useDefaults = "" - if($autoApprove) { - $useDefaults = "use" + if($hasInputOverrides) { + Write-InformationColored "We found you have cached values from a previous run, but have also supplied inputs." -ForegroundColor Yellow -InformationAction Continue + if($autoApprove) { + $useDefaults = "skip" + } else { + $useDefaults = Read-Host "Would you like to use the cached values or use the inputs you have supplied intead? Enter 'use' to use the cached value or just hit 'enter' to use your inputs. (use/skip)" + } + if($useDefaults.ToLower() -eq "use") { + $userInputOverrides = $userInputDefaultOverrides + } } else { - $useDefaults = Read-Host "Would you like to use these values or see each of them to validate and change them? Enter 'use' to use the cached value or just hit 'enter' to see and validate each value. (use/see)" + Write-InformationColored "We found you have cached values from a previous run." -ForegroundColor Yellow -InformationAction Continue + if($autoApprove) { + $useDefaults = "use" + } else { + $useDefaults = Read-Host "Would you like to use these values or see each of them to validate and change them? Enter 'use' to use the cached value or just hit 'enter' to see and validate each value. (use/verify)" + } } if($useDefaults.ToLower() -eq "use") { $userInputOverrides = $userInputDefaultOverrides } } - $hasInputOverrides = $false - if($userInputOverrides -ne $null) { - $hasInputOverrides = $true - } - if($respectOrdering) { $configurations = $configurationParameters.PSObject.Properties | Sort-Object { $_.Value.Order } } + if($null -ne $computedInputs) { + Write-Verbose $computedInputs + } foreach ($configurationValue in $configurations) { + $computedInput = $null + if($null -ne $computedInputs) { + $computedInput = $computedInputs.PsObject.Properties | Where-Object { $_.Name -eq $configurationValue.Name } + } + + if($null -ne $computedInput) { + $configurationValue.Value.Value = $computedInput.Value.Value + continue + } + if ($configurationValue.Value.Type -eq "UserInput") { # Check for and add cached as default diff --git a/src/ALZ/Private/Request-SpecialInput.ps1 b/src/ALZ/Private/Request-SpecialInput.ps1 new file mode 100644 index 00000000..5db8eb37 --- /dev/null +++ b/src/ALZ/Private/Request-SpecialInput.ps1 @@ -0,0 +1,91 @@ +function Request-SpecialInput { + [CmdletBinding(SupportsShouldProcess = $true)] + param ( + [Parameter(Mandatory = $false)] + [string] $type, + + [Parameter(Mandatory = $false)] + [string] $starterPath, + + [Parameter(Mandatory = $false)] + [PSCustomObject] $bootstrapModules, + + [Parameter(Mandatory = $false)] + [PSCustomObject] $userInputOverrides = $null + ) + + if ($PSCmdlet.ShouldProcess("ALZ-Terraform module configuration", "modify")) { + + $result = "" + + if($null -ne $userInputOverrides) { + $userInputOverride = $userInputOverrides.PSObject.Properties | Where-Object { $_.Name -eq $type } + if($null -ne $userInputOverride) { + $result = $userInputOverride.Value + return $result + } + } + + $gotValidInput = $false + + while(!$gotValidInput) { + if($type -eq "starter") { + + $starterFolders = Get-ChildItem -Path $starterPath -Directory + Write-InformationColored "Please select the starter module you would like to use, you can enter one of the following keys:" -ForegroundColor Yellow -InformationAction Continue + + $starterOptions = @() + foreach($starterFolder in $starterFolders) { + if($starterFolder.Name -eq $starterPipelineFolder) { + continue + } + + Write-InformationColored "- $($starterFolder.Name)" -ForegroundColor Yellow -InformationAction Continue + $starterOptions += $starterFolder.Name + } + + Write-InformationColored ": " -ForegroundColor Yellow -NoNewline -InformationAction Continue + $result = Read-Host + + if($result -notin $starterOptions) { + Write-InformationColored "The starter '$result' that you have selected does not exist. Please try again with a valid starter..." -ForegroundColor Red -InformationAction Continue + } else { + $gotValidInput = $true + } + } + + if($type -eq "iac") { + Write-InformationColored "Please select the IAC you would like to use, you can enter one of 'bicep or 'terraform': " -ForegroundColor Yellow -NoNewline -InformationAction Continue + $result = Read-Host + + $validIac = @("bicep", "terraform") + if($result -notin $validIac) { + Write-InformationColored "The IAC '$result' that you have selected does not exist. Please try again with a valid IAC..." -ForegroundColor Red -InformationAction Continue + + } else { + $gotValidInput = $true + } + } + + if($type -eq "bootstrap") { + Write-InformationColored "Please select the bootstrap module you would like to use, you can enter one of the following keys:" -ForegroundColor Yellow -InformationAction Continue + + $bootstrapOptions = @() + foreach ($bootstrapModule in $bootstrapModules.PsObject.Properties) { + Write-InformationColored "- $($bootstrapModule.Name) ($($bootstrapModule.Value.description))" -ForegroundColor Yellow -InformationAction Continue + $bootstrapOptions += $bootstrapModule.Name + } + Write-InformationColored ": " -ForegroundColor Yellow -NoNewline -InformationAction Continue + $result = Read-Host + + if($result -notin $bootstrapOptions) { + Write-InformationColored "The starter '$result' that you have selected does not exist. Please try again with a valid starter..." -ForegroundColor Red -InformationAction Continue + } else { + $gotValidInput = $true + } + } + } + + return $result + } +} \ No newline at end of file diff --git a/src/ALZ/Private/Write-InformationColored.ps1 b/src/ALZ/Private/Write-InformationColored.ps1 index 3979d0e2..b3771741 100644 --- a/src/ALZ/Private/Write-InformationColored.ps1 +++ b/src/ALZ/Private/Write-InformationColored.ps1 @@ -5,9 +5,14 @@ function Write-InformationColored { [Object]$MessageData, [ConsoleColor]$ForegroundColor = $Host.UI.RawUI.ForegroundColor ?? "White", # Make sure we use the current colours by default [ConsoleColor]$BackgroundColor = $Host.UI.RawUI.BackgroundColor ?? "Black", - [Switch]$NoNewline + [Switch]$NoNewline, + [switch]$NewLineBefore ) + if($NewLineBefore) { + $MessageData = "$([Environment]::NewLine)$MessageData" + } + $msg = [System.Management.Automation.HostInformationMessage]@{ Message = $MessageData ForegroundColor = $ForegroundColor diff --git a/src/ALZ/Public/Get-ALZGithubRelease.ps1 b/src/ALZ/Public/Get-ALZGithubRelease.ps1 deleted file mode 100644 index 6e5e7601..00000000 --- a/src/ALZ/Public/Get-ALZGithubRelease.ps1 +++ /dev/null @@ -1,163 +0,0 @@ -#################################### -# Get-ALZGithubRelease.ps1 # -#################################### -# Version: 0.1.0 -# Based on Invoke-GitHubReleaseFetcher by Jack Tracey: -# Source: https://github.com/jtracey93/PublicScripts/blob/master/GitHub/PowerShell/Invoke-GitHubReleaseFetcher.ps1 - -<# -.SYNOPSIS -Checks for the releases of a GitHub repository and downloads the latest release or all releases and pulls it into a specified directory, one for each version. -.DESCRIPTION -Checks for the releases of a GitHub repository and downloads the latest release or all releases and pulls it into a specified directory, one for each version. - -.EXAMPLE - -.NOTES -# Release notes 16/03/2023 - V0.1.0: -- Initial release. -#> - - -function Get-ALZGithubRelease { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true, Position = 0, HelpMessage = "The IaC provider to use for the ALZ environment.")] - [ValidateSet("bicep", "terraform")] - [Alias("Iac")] - [Alias("i")] - [string] - $alzIacProvider, - - [Parameter(Mandatory = $false, Position = 1, HelpMessage = "Please the provide the full URL of the GitHub repository you wish to check for the latest release.")] - [string] - $githubRepoUrl = "", - - [Parameter(Mandatory = $false, Position = 2, HelpMessage = "The releases to download. Specify 'all' to download all releases or 'latest' to download the latest release. Defaults to the latest release.")] - [array] - [Alias("version")] - [Alias("v")] - $release = "latest", - - [Parameter(Mandatory = $false, Position = 3, HelpMessage = "The directory to download the releases to. Defaults to the current directory.")] - [string] - [Alias("Output")] - [Alias("OutputDirectory")] - [Alias("O")] - $directoryForReleases = "$PWD/releases", - - [Parameter(Mandatory = $false, Position = 4, HelpMessage = "An array of strings contianing the paths to the directories or files that you wish to keep when downloading and extracting from the releases.")] - [array] - $directoryAndFilesToKeep = $null, - - [Parameter(Mandatory = $false, Position = 4, HelpMessage = "Whether to just query the API and return the release versions.")] - [switch] - $queryOnly - ) - - # Set the repository URL if not provided - $bicepModuleUrl = "https://github.com/Azure/ALZ-Bicep" - $terraformModuleUrl = "https://github.com/Azure/alz-terraform-accelerator" - if($githubRepoUrl -eq "") { - if($alzIacProvider -eq "bicep") { - $githubRepoUrl = $bicepModuleUrl - } elseif($alzIacProvider -eq "terraform") { - $githubRepoUrl = $terraformModuleUrl - } - } - - $parentDirectory = $directoryForReleases - # Bicep specific path setup - if($alzIacProvider -eq "bicep") { - $directoryForReleases = Join-Path $directoryForReleases "upstream-releases" - } - - # Split Repo URL into parts - $repoOrgPlusRepo = $githubRepoUrl.Split("/")[-2..-1] -join "/" - - Write-Verbose "=====> Checking for release on GitHub Repo: $repoOrgPlusRepo" - - # Get releases on repo - $repoReleaseUrl = "https://api.github.com/repos/$repoOrgPlusRepo/releases/$release" - if($release -ne "latest") { - $repoReleaseUrl = "https://api.github.com/repos/$repoOrgPlusRepo/releases/tags/$release" - } - - $releaseData = Invoke-RestMethod $repoReleaseUrl -SkipHttpErrorCheck -StatusCodeVariable "statusCode" - - if($statusCode -eq 404) { - Write-Error "The release $release does not exist in the GitHub repository $githubRepoUrl - $repoReleaseUrl" - throw "The release $release does not exist in the GitHub repository $githubRepoUrl - $repoReleaseUrl" - } - - # Handle transient errors like throttling - if($statusCode -ge 400 -and $statusCode -le 599) { - Write-InformationColored "Retrying as got the Status Code $statusCode, which may be a tranisent error." -ForegroundColor Yellow -InformationAction Continue - $releaseData = Invoke-RestMethod $repoReleaseUrl -RetryIntervalSec 3 -MaximumRetryCount 100 - } - - $releaseTag = $releaseData.tag_name - - if($queryOnly) { - return $releaseTag - } - - # Check if directory exists - Write-Verbose "=====> Checking if directory for releases exists: $directoryForReleases" - - if (!(Test-Path $directoryForReleases)) { - Write-Verbose "Directory does not exist for releases, will now create: $directoryForReleases" - New-Item -ItemType Directory -Path $directoryForReleases | Out-String | Write-Verbose - } - - # Check the directory for this release - $releaseDirectory = "$directoryForReleases/$releaseTag" - - Write-Verbose "===> Checking if directory for release version exists: $releaseDirectory" - - if (!(Test-Path $releaseDirectory)) { - Write-Verbose "Directory does not exist for release $releaseTag, will now create: $releaseDirectory" - New-Item -ItemType Directory -Path $releaseDirectory | Out-String | Write-Verbose - } - - Write-Verbose "===> Checking if any content exists inside of $releaseDirectory" - - $contentInReleaseDirectory = Get-ChildItem -Path $releaseDirectory -Recurse -ErrorAction SilentlyContinue - - if ($null -eq $contentInReleaseDirectory) { - Write-Verbose "===> Pulling and extracting release $releaseTag into $releaseDirectory" - New-Item -ItemType Directory -Path "$releaseDirectory/tmp" | Out-String | Write-Verbose - Invoke-WebRequest -Uri "https://github.com/$repoOrgPlusRepo/archive/refs/tags/$releaseTag.zip" -OutFile "$releaseDirectory/tmp/$releaseTag.zip" -RetryIntervalSec 3 -MaximumRetryCount 100 | Out-String | Write-Verbose - Expand-Archive -Path "$releaseDirectory/tmp/$releaseTag.zip" -DestinationPath "$releaseDirectory/tmp/extracted" | Out-String | Write-Verbose - $extractedSubFolder = Get-ChildItem -Path "$releaseDirectory/tmp/extracted" -Directory - - if ($null -ne $directoryAndFilesToKeep) { - foreach ($path in $directoryAndFilesToKeep) { - Write-Verbose "===> Moving $path into $releaseDirectory." - Move-Item -Path "$($extractedSubFolder.FullName)/$($path)" -Destination "$releaseDirectory" -ErrorAction SilentlyContinue | Out-String | Write-Verbose - } - } - - if ($null -eq $directoryAndFilesToKeep) { - Write-Verbose "===> Moving all extracted contents into $releaseDirectory." - Move-Item -Path "$($extractedSubFolder.FullName)/*" -Destination "$releaseDirectory" -ErrorAction SilentlyContinue | Out-String | Write-Verbose - } - - Remove-Item -Path "$releaseDirectory/tmp" -Force -Recurse - - } else { - Write-InformationColored "The release directory for this version already exists and has content in it, so we are not over-writing it." -ForegroundColor Yellow -InformationAction Continue - Write-Verbose "===> Content already exists in $releaseDirectory. Skipping" - } - - # Check and replace the .env file release version if it is Bicep - if($alzIacProvider -eq "bicep") { - $envFilePath = Join-Path -Path $parentDirectory -ChildPath ".env" - if(Test-Path $envFilePath) { - Write-Verbose "===> Replacing the .env file release version with $releaseTag" - (Get-Content $envFilePath) -replace "UPSTREAM_RELEASE_VERSION=.*", "UPSTREAM_RELEASE_VERSION=$releaseTag" | Set-Content $envFilePath - } - } - - return $releaseTag -} \ No newline at end of file diff --git a/src/ALZ/Public/New-ALZEnvironment.ps1 b/src/ALZ/Public/New-ALZEnvironment.ps1 index bca9f025..5ca6005d 100644 --- a/src/ALZ/Public/New-ALZEnvironment.ps1 +++ b/src/ALZ/Public/New-ALZEnvironment.ps1 @@ -1,79 +1,300 @@ function New-ALZEnvironment { <# .SYNOPSIS - This function prompts a user for configuration values and modifies the ALZ Bicep configuration files accordingly. + Deploys an accelerator according to the supplied inputs. .DESCRIPTION - This function will prompt the user for commonly used deployment configuration settings and modify the configuration in place. - .PARAMETER alzBicepSource - The directory containing the ALZ-Bicep source repo. - .PARAMETER alzEnvironmentDestination - The directory where the ALZ environment will be created. - .PARAMETER alzBicepVersion - The version of the ALZ-Bicep module to use. + This function is used to deploy accelerators consisting or bootstrap and optionally starter modules. The accelerators are designed to simplify and speed up configuration of common Microsoft patterns, such as CI / CD for Azure Landing Zones. + .PARAMETER output + The target directory for the accelerator artefacts. Depending on the choice and type of accelerlerator, this may be an intermediate stage or the final result of the accelerator. + .PARAMETER iac + The type of infrastructure as code that the accelerator implements. For example bicep or terraform. + .PARAMETER bootstrap + The accelerator bootstrap type to deploy. .PARAMETER alzIacProvider The IaC provider to use for the ALZ environment. - .PARAMETER userInputOverridePath - A json file containing user input overrides for the user input prompts. This will cause the tool to by pass requesting user input for that field and use the value(s) provided. E.g { "starter_module": "basic", "azure_location": "uksouth" } + .PARAMETER inputs + A json or yaml file containing user input. This will cause the tool to by-pass requesting user input for the inputs supplied in the file. This is useful for automation or defining the inputs up front. .PARAMETER autoApprove - Automatically approve the terraform apply. + Automatically approve the bootstrap deployment. This is useful for automation scenarios. .PARAMETER destroy - Destroy the terraform environment. + Setting this will case the bootstrap to be destroyed. This is useful for cleaning up test environments. .EXAMPLE - New-ALZEnvironment + Deploy-Accelerator .EXAMPLE - New-ALZEnvironment + Deploy-Accelerator -o "." .EXAMPLE - New-ALZEnvironment -alzEnvironmentDestination "." - .EXAMPLE - New-ALZEnvironment -alzEnvironmentDestination "." -alzBicepVersion "v0.16.4" + Deploy-Accelerator -o "." -i "bicep" -b "alz_github" #> [CmdletBinding(SupportsShouldProcess = $true)] param ( - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, HelpMessage = "The target directory for the accelerator output. Defaults to current folder.")] [Alias("Output")] [Alias("OutputDirectory")] [Alias("O")] - [string] $alzEnvironmentDestination = ".", + [Alias("alzEnvironmentDestination")] + [string] $targetDirectory = ".", + + [Parameter(Mandatory = $false, HelpMessage = "The specific bootstrap module release version to download. Defaults to latest.")] + [string] $bootstrapRelease = "latest", - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, HelpMessage = "The specific starter module release version tom download. Defaults to latest.")] [Alias("alzBicepVersion")] [Alias("version")] [Alias("v")] - [string] $alzVersion = "latest", + [Alias("alzVersion")] + [Alias("release")] + [string] $starterRelease = "latest", - [Parameter(Mandatory = $false)] - [ValidateSet("bicep", "terraform")] - [Alias("Iac")] + [Parameter(Mandatory = $false, HelpMessage = "The infrastructure as code type to target. Supported options are 'bicep', 'terrform' or 'local'. You will be prompted to enter this if not supplied.")] [Alias("i")] - [string] $alzIacProvider = "bicep", + [Alias("alzIacProvider")] + [string] $iac = "", - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, HelpMessage = "The bootstrap module to deploy. You will be prompted to enter this if not supplied.")] [Alias("Cicd")] [Alias("c")] - [string] $alzCicdPlatform = "github", + [Alias("alzCicdPlatform")] + [Alias("b")] + [string] $bootstrap = "", - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, HelpMessage = "The inputs in json or yaml format. This is optional and used to automate or pre-prepare the accelerator inputs.")] [Alias("inputs")] [string] $userInputOverridePath = "", - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, HelpMessage = "Determines whether to deploy the bootstrap without prompting for approval. This is used for automation.")] [switch] $autoApprove, - [Parameter(Mandatory = $false)] - [switch] $destroy + [Parameter(Mandatory = $false, HelpMessage = "Determines that this run is to destroup the bootstrap. This is used to cleanup experiments.")] + [switch] $destroy, + + [Parameter(Mandatory = $false, HelpMessage = "The bootstrap modules reposiotry url. This can be overridden for custom modules.")] + [string] + $bootstrapModuleUrl = "https://github.com/Azure/accelerator-bootstrap-modules", + + [Parameter(Mandatory = $false, HelpMessage = "The bootstrap config file path within the bootstrap module. This can be overridden for custom modules.")] + [string] + $bootstrapConfigPath = ".config/ALZ-Powershell.config.json", + + [Parameter(Mandatory = $false, HelpMessage = "The folder that containes the bootstrap modules in the bootstrap repo. This can be overridden for custom modules.")] + [string] + $bootstrapSourceFolder = ".", + + [Parameter(Mandatory = $false, HelpMessage = "Used to override the bootstrap folder location. This can be used to provide a folder locally in restricted environments.")] + [string] + $bootstrapModuleOverrideFolderPath = "", + + [Parameter(Mandatory = $false, HelpMessage = "Used to override the starter folder location. This can be used to provide a folder locally in restricted environments.")] + [string] + $starterModuleOverrideFolderPath = "", + + [Parameter(Mandatory = $false, HelpMessage = "The starter module repository url for bicep when running in legacy mode.")] + [string] + $bicepLegacyUrl = "https://github.com/Azure/ALZ-Bicep", + + [Parameter(Mandatory = $false, HelpMessage = "Whether to skip checks that involve internet connection. The can allow running in restricted environments.")] + [switch] + $skipInternetChecks, + + [Parameter(Mandatory = $false, HelpMessage = "Whether to use legacy local mode for Bicep.")] + [bool] + $bicepLegacyMode = $true # Note this is set to true to act as a feature flag while the Bicep bootstrap is developed. It will be switched to false once it is all working. ) - Write-InformationColored "Getting ready to create a new ALZ environment with you..." -ForegroundColor Green -InformationAction Continue + $ProgressPreference = "SilentlyContinue" + + Write-InformationColored "Getting ready to deploy the accelerator with you..." -ForegroundColor Green -InformationAction Continue if ($PSCmdlet.ShouldProcess("Accelerator setup", "modify")) { - if ($alzIacProvider -eq "bicep") { - New-ALZEnvironmentBicep -alzEnvironmentDestination $alzEnvironmentDestination -alzVersion $alzVersion -alzCicdPlatform $alzCicdPlatform + # Get User Inputs from the -inputs file + $userInputOverrides = $null + if($userInputOverridePath -ne "") { + $userInputOverrides = Get-ALZConfig -configFilePath $userInputOverridePath + } + + # Get the IAC type if not specified + if($iac -eq "") { + $iac = Request-SpecialInput -type "iac" -userInputOverrides $userInputOverrides + } + + # Setup the Bicep flag + $isLegacyBicep = $false + if($iac -eq "bicep") { + $isLegacyBicep = $bicepLegacyMode -eq $true + } + + if($isLegacyBicep) { + Write-Verbose "We are running in legacy Bicep mode" + } + + if(!$isLegacyBicep){ + Write-Verbose "We are running in modern mode" + } + + # Check and install Terraform CLI if needed + if(!$isLegacyBicep) { + if($skipInternetChecks) { + Write-InformationColored "Skipping Terraform tool check as you used the skipInternetCheck parameter. Please ensure you have the most recent version of Terraform installed" -ForegroundColor Yellow -InformationAction Continue + } else { + Write-InformationColored "Checking you have the latest version of Terraform installed..." -ForegroundColor Green -NewLineBefore -InformationAction Continue + $toolsPath = Join-Path -Path $targetDirectory -ChildPath ".tools" + Get-TerraformTool -version "latest" -toolsPath $toolsPath + } + } + + # Download the bootstrap modules + $bootstrapReleaseTag = "local" + $bootstrapPath = $bootstrapModuleOverrideFolderPath + $bootstrapTargetFolder = "bootstrap" + + if($bootstrapModuleOverrideFolderPath -eq "" -and !$isLegacyBicep) { + $versionAndPath = $null + + Write-InformationColored "Checking and Downloading the bootstrap module..." -ForegroundColor Green -NewLineBefore -InformationAction Continue + + if($skipInternetChecks) { + $versionAndPath = Get-ExistingLocalRelease -targetDirectory $targetDirectory -targetFolder $bootstrapTargetFolder + } else { + $versionAndPath = New-FolderStructure ` + -targetDirectory $targetDirectory ` + -url $bootstrapModuleUrl ` + -release $bootstrapRelease ` + -targetFolder $bootstrapTargetFolder ` + -sourceFolder $bootstrapSourceFolder + } + $bootstrapReleaseTag = $versionAndPath.releaseTag + $bootstrapPath = $versionAndPath.path } - if($alzIacProvider -eq "terraform") { - New-ALZEnvironmentTerraform -alzEnvironmentDestination $alzEnvironmentDestination -alzVersion $alzVersion -alzCicdPlatform $alzCicdPlatform -userInputOverridePath $userInputOverridePath -autoApprove:$autoApprove.IsPresent -destroy:$destroy.IsPresent + # Configure the starter module path + $starterFolder = "starter" + + $starterModuleTargetFolder = $starterFolder + if($iac -eq "bicep") { + if($isLegacyBicep) { + $starterFolder = "." + } + $starterModuleTargetFolder = "$starterFolder/upstream-releases" + } + + # Setup the variables for bootstrap and starter modules + $hasStarterModule = $false + $starterModuleUrl = $bicepLegacyUrl + $starterModuleSourceFolder = "." + $starterReleaseTag = "local" + $starterPipelineFolder = "local" + + $bootstrapDetails = $null + $validationConfig = $null + $inputConfig = $null + + if(!$isLegacyBicep) { + # Get the bootstap configuration + $bootstrapConfigPath = Join-Path $bootstrapPath $bootstrapConfigPath + $bootstrapConfig = Get-ALZConfig -configFilePath $bootstrapConfigPath + $validationConfig = $bootstrapConfig.validators + + # Get the available bootstrap modules + $bootstrapModules = $bootstrapConfig.bootstrap_modules + + # Request the bootstrap type if not already specified + if($bootstrap -eq "") { + $bootstrap = Request-SpecialInput -type "bootstrap" -bootstrapModules $bootstrapModules -userInputOverrides $userInputOverrides + } + + # Get the bootstrap details and validate it exists (use alias for legacy values) + $bootstrapDetails = $bootstrapModules.PsObject.Properties | Where-Object { $_.Name -eq $bootstrap -or $bootstrap -in $_.Value.aliases } + if($null -eq $bootstrapDetails) { + Write-InformationColored "The bootstrap type '$bootstrap' that you have selected does not exist. Please try again with a valid bootstrap type..." -ForegroundColor Red -InformationAction Continue + return + } + + # Get the starter modules for the selected bootstrap if it has any + $bootstrapStarterModule = $bootstrapDetails.Value.PSObject.Properties | Where-Object { $_.Name -eq "starter_modules" } + + if($null -ne $bootstrapStarterModule) { + # If the bootstrap has starter modules, get the details and url + $hasStarterModule = $true + $starterModules = $bootstrapConfig.PSObject.Properties | Where-Object { $_.Name -eq "starter_modules" } + $starterModuleType = $bootstrapStarterModule.Value + $starterModuleDetails = $starterModules.Value.PSObject.Properties | Where-Object { $_.Name -eq $starterModuleType } + if($null -eq $starterModuleDetails) { + Write-InformationColored "The starter modules '$($starterModuleType)' for the bootstrap type '$bootstrap' that you have selected does not exist. This could be an issue with your custom configuration, please check and try again..." -ForegroundColor Red -InformationAction Continue + return + } + + $starterModuleUrl = $starterModuleDetails.Value.$iac.url + $starterModuleSourceFolder = $starterModuleDetails.Value.$iac.module_path + $starterPipelineFolder = $starterModuleDetails.Value.$iac.pipeline_folder + } + + # Get the bootstrap interface user input config + $inputConfigFilePath = Join-Path -Path $bootstrapPath -ChildPath $bootstrapDetails.Value.interface_config_file + Write-Verbose "Interface config path $inputConfigFilePath" + $inputConfig = Get-ALZConfig -configFilePath $inputConfigFilePath + } + + # Download the starter modules + $starterReleaseTag = "local" + $starterPath = $starterModuleOverrideFolderPath + + if($starterModuleOverrideFolderPath -eq "" -and ($hasStarterModule -or $isLegacyBicep)) { + $versionAndPath = $null + + Write-InformationColored "Checking and Downloading the starter module..." -ForegroundColor Green -NewLineBefore -InformationAction Continue + + if($skipInternetChecks) { + $versionAndPath = Get-ExistingLocalRelease -targetDirectory $targetDirectory -targetFolder $starterModuleTargetFolder + } else { + $versionAndPath = New-FolderStructure ` + -targetDirectory $targetDirectory ` + -url $starterModuleUrl ` + -release $starterRelease ` + -targetFolder $starterModuleTargetFolder ` + -sourceFolder $starterModuleSourceFolder + } + + $starterReleaseTag = $versionAndPath.releaseTag + $starterPath = $versionAndPath.path + } + + # Run the bicep parameter setup if the iac is Bicep + if ($iac -eq "bicep") { + $bootstrapLegacy = $bootstrap.ToLower().Replace("alz_", "") + + $targetPath = Join-Path $targetDirectory $starterFolder + New-ALZEnvironmentBicep ` + -targetDirectory $targetPath ` + -upstreamReleaseVersion $starterReleaseTag ` + -upstreamReleaseFolderPath $starterPath ` + -vcs $bootstrapLegacy ` + -local:$isLegacyBicep + } + + # Run the bootstrap + if(!$isLegacyBicep) { + $bootstrapTargetPath = Join-Path $targetDirectory $bootstrapTargetFolder + $starterTargetPath = Join-Path $targetDirectory $starterFolder + + New-Bootstrap ` + -iac $iac ` + -bootstrapDetails $bootstrapDetails ` + -validationConfig $validationConfig ` + -inputConfig $inputConfig ` + -bootstrapTargetPath $bootstrapTargetPath ` + -bootstrapRelease $bootstrapReleaseTag ` + -hasStarter:$hasStarterModule ` + -starterTargetPath $starterTargetPath ` + -starterPipelineFolder $starterPipelineFolder ` + -starterRelease $starterReleaseTag ` + -userInputOverrides $userInputOverrides ` + -autoApprove:$autoApprove.IsPresent ` + -destroy:$destroy.IsPresent } } + $ProgressPreference = "Continue" + return -} \ No newline at end of file +} + +New-Alias -Name "Deploy-Accelerator" -Value "New-ALZEnvironment" diff --git a/src/ALZ/Public/Test-ALZRequirement.ps1 b/src/ALZ/Public/Test-ALZRequirement.ps1 index 20d42f9b..c244b7f5 100644 --- a/src/ALZ/Public/Test-ALZRequirement.ps1 +++ b/src/ALZ/Public/Test-ALZRequirement.ps1 @@ -24,6 +24,7 @@ function Test-ALZRequirement { [Parameter(Mandatory = $false)] [ValidateSet("bicep", "terraform")] [Alias("Iac")] + [Alias("i")] [string] $alzIacProvider = "bicep" ) diff --git a/src/PSScriptAnalyzerSettings.psd1 b/src/PSScriptAnalyzerSettings.psd1 index d3f796d9..8e9fa5f3 100644 --- a/src/PSScriptAnalyzerSettings.psd1 +++ b/src/PSScriptAnalyzerSettings.psd1 @@ -59,7 +59,7 @@ #________________________________________ 'Rules' = @{ 'PSAvoidUsingCmdletAliases' = @{ - 'allowlist' = @('Edit-LineEndings') + 'allowlist' = @('Edit-LineEndings','Deploy-Accelerator') } } } diff --git a/src/Tests/Unit/Public/Get-ALZGithubRelease.Tests.ps1 b/src/Tests/Unit/Public/Get-ALZGithubRelease.Tests.ps1 deleted file mode 100644 index 9551f5cd..00000000 --- a/src/Tests/Unit/Public/Get-ALZGithubRelease.Tests.ps1 +++ /dev/null @@ -1,114 +0,0 @@ -#------------------------------------------------------------------------- -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 'Get-ALZGithubRelease Function Tests' -Tag Unit { - BeforeAll { - $WarningPreference = 'SilentlyContinue' - $ErrorActionPreference = 'SilentlyContinue' - } - Context 'Initialize config get the correct base values' { - BeforeEach { - Mock -CommandName Invoke-RestMethod -ParameterFilter { $Uri -eq "https://api.github.com/repos/test/repo/releases/latest" } -MockWith { - Set-Variable -Scope 3 'statusCode' 200 - [PSCustomObject]@{ - name = "v1.0.0" - tag_name = "v1.0.0" - published_at = "2020-01-01T00:00:00Z" - prerelease = $false - draft = $false - html_url = "" - } - } - - Mock -CommandName Invoke-RestMethod -ParameterFilter { $Uri -eq "https://api.github.com/repos/test/repo/releases/tags/v2.0.0" } -MockWith { - Set-Variable -Scope 3 'statusCode' 404 - [PSCustomObject]@{ - message = "Not Found" - } - } - - Mock -CommandName Invoke-WebRequest -ParameterFilter { $Uri -eq "https://github.com/test/repo/archive/refs/tags/v1.0.1.zip" } -MockWith { - [PSCustomObject]@{ - ContentLength64 = 100 - } - } - - Mock -CommandName Invoke-WebRequest -ParameterFilter { $Uri -eq "https://github.com/test/repo/archive/refs/tags/v1.0.0.zip" } -MockWith { - [PSCustomObject]@{ - ContentLength64 = 102 - } - } - - Mock -CommandName Expand-Archive -MockWith { - $null - } - - Mock -CommandName Remove-Item -MockWith { - $null - } - - Mock -CommandName Get-ChildItem -ParameterFilter { $Path -eq "output/v1.0.1" } -MockWith { - $null - } - - Mock -CommandName Get-ChildItem -ParameterFilter { $Path -eq "output/v1.0.0" } -MockWith { - $null - } - - Mock -CommandName Get-ChildItem -ParameterFilter { $Path -eq "output/v1.0.1/tmp/extracted" } -MockWith { - @( - [PSCustomObject]@{ - FullName = "Internal-Folder" - } - ) - } - - Mock -CommandName Get-ChildItem -ParameterFilter { $Path -eq "output/v1.0.0/tmp/extracted" } -MockWith { - @( - [PSCustomObject]@{ - FullName = "Internal-Folder" - } - ) - } - - Mock -CommandName New-Item -MockWith { - $null - } - - Mock -CommandName Move-Item -MockWith { - $null - } - - Mock -CommandName Write-Warning -MockWith { - $null - } - - Mock -CommandName Write-Error -MockWith { - $null - } - } - - It 'Should get the correct releases' { - Get-ALZGithubRelease -iac "bicep" -githubRepoUrl "http://github.com/test/repo" -directoryAndFilesToKeep @('repo-1.0.0') -directoryForReleases "output" - Should -Invoke Expand-Archive - Should -Not -Invoke Write-Warning - } - - It 'Should throw an exception when you ask for a release that does not exist' { - { Get-ALZGithubRelease -iac "bicep" -githubRepoUrl "http://github.com/test/repo" -release 'v2.0.0' -directoryAndFilesToKeep @('repo-1.0.0') -directoryForReleases "output" } | Should -Throw - Should -Invoke Write-Error - } - } - } -} diff --git a/src/Tests/Unit/Public/New-ALZEnvironment.Tests.ps1 b/src/Tests/Unit/Public/New-ALZEnvironment.Tests.ps1 index 774e121a..499f49d6 100644 --- a/src/Tests/Unit/Public/New-ALZEnvironment.Tests.ps1 +++ b/src/Tests/Unit/Public/New-ALZEnvironment.Tests.ps1 @@ -77,7 +77,7 @@ InModuleScope 'ALZ' { } } - Mock -CommandName Get-ALZGithubRelease -MockWith { $("v0.0.1") } + Mock -CommandName Get-GithubRelease -MockWith { $("v0.0.1") } Mock -CommandName Test-ALZGitRepository -MockWith { $false } @@ -112,20 +112,29 @@ InModuleScope 'ALZ' { Mock -CommandName Invoke-Terraform -MockWith { } - Mock -CommandName Import-SubscriptionData -MockWith { } - Mock -CommandName Invoke-Upgrade -MockWith { } + + Mock -CommandName Invoke-FullUpgrade -MockWith { } + + Mock -CommandName Get-TerraformTool -MockWith {} + } + + It 'should call the correct functions for bicep legacy module configuration' { + New-ALZEnvironment -i "bicep" -c "github" + Assert-MockCalled -CommandName Get-GithubRelease -Exactly 1 + Assert-MockCalled -CommandName Edit-ALZConfigurationFilesInPlace -Exactly 1 } - It 'should return the output directory on completion' { - New-ALZEnvironment + It 'should call the correct functions for bicep modern module configuration' { + New-ALZEnvironment -i "bicep" -c "github" + #Assert-MockCalled -CommandName Get-GithubRelease -Exactly 2 Assert-MockCalled -CommandName Edit-ALZConfigurationFilesInPlace -Exactly 1 } - It 'should clone the git repo and apply if terraform is selected' { - New-ALZEnvironment -IaC "terraform" - Assert-MockCalled -CommandName Get-ALZGithubRelease -Exactly 1 - Assert-MockCalled -CommandName Invoke-Terraform -Exactly 1 + It 'should call the correct functions for terraform module configuration' { + New-ALZEnvironment -i "terraform" -c "github" + #Assert-MockCalled -CommandName Get-GithubRelease -Exactly 2 + #Assert-MockCalled -CommandName Invoke-Terraform -Exactly 1 } } }