Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement add terraform rerun and destroy #93

Merged
merged 2 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/ALZ/Private/Get-ALZConfig.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ function Get-ALZConfig {
[string] $configFilePath = ""
)

if(!(Test-Path $configFilePath)) {
return $null
}

# Import the config and transform it to a PowerShell object
$extension = (Get-Item -Path $configFilePath).Extension.ToLower()
if($extension -eq ".yml" -or $extension -eq ".yaml") {
Expand Down
27 changes: 22 additions & 5 deletions src/ALZ/Private/Invoke-Terraform.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,33 @@ function Invoke-Terraform {
[string] $tfvarsFileName,

[Parameter(Mandatory = $false)]
[switch] $autoApprove
[switch] $autoApprove,

[Parameter(Mandatory = $false)]
[switch] $destroy
)

if ($PSCmdlet.ShouldProcess("Apply Terraform", "modify")) {
terraform -chdir="$moduleFolderPath" init
Write-InformationColored "Terraform init has completed, now running the apply..." -ForegroundColor Green -InformationAction Continue
if($autoApprove) {
terraform -chdir="$moduleFolderPath" apply -var-file="$tfvarsFileName" -auto-approve
$action = "apply"
if($destroy) {
$action = "destroy"
}

Write-InformationColored "Terraform init has completed, now running the $action..." -ForegroundColor Green -InformationAction Continue

if($destroy) {
if($autoApprove) {
terraform -chdir="$moduleFolderPath" destroy -var-file="$tfvarsFileName" -auto-approve
} else {
terraform -chdir="$moduleFolderPath" destroy -var-file="$tfvarsFileName"
}
} else {
terraform -chdir="$moduleFolderPath" apply -var-file="$tfvarsFileName"
if($autoApprove) {
terraform -chdir="$moduleFolderPath" apply -var-file="$tfvarsFileName" -auto-approve
} else {
terraform -chdir="$moduleFolderPath" apply -var-file="$tfvarsFileName"
}
}
}
}
95 changes: 95 additions & 0 deletions src/ALZ/Private/Invoke-Upgrade.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
function Invoke-Upgrade {
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter(Mandatory = $false)]
[string] $alzEnvironmentDestination,

[Parameter(Mandatory = $false)]
[string] $bootstrapCacheFileName,

[Parameter(Mandatory = $false)]
[string] $starterCacheFileNamePattern,

[Parameter(Mandatory = $false)]
[string] $stateFilePathAndFileName,

[Parameter(Mandatory = $false)]
[string] $currentVersion,

[Parameter(Mandatory = $false)]
[switch] $autoApprove
)

if ($PSCmdlet.ShouldProcess("Upgrade Release", "Operation")) {

$directories = Get-ChildItem -Path $alzEnvironmentDestination -Filter "v*" -Directory
$previousBootstrapCachedValuesPath = $null
$previousStarterCachedValuesPath = $null
$previousStateFilePath = $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
}

$starterCacheFiles = Get-ChildItem -Path $releasePath -Filter $starterCacheFileNamePattern -File

if($starterCacheFiles) {
$previousStarterCachedValuesPath = $starterCacheFiles[0].FullName
}

if(Test-Path $releaseStateFilePath) {
$previousStateFilePath = $releaseStateFilePath
}

if($null -ne $previousStateFilePath) {
if($directory.Name -eq $currentVersion) {
# If the current version has already been run, then skip the upgrade process
break
}

$foundPreviousRelease = $true
$previousVersion = $directory.Name
break
}
}

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 {
$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

# 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
}

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
}
}
}
}
36 changes: 31 additions & 5 deletions src/ALZ/Private/New-ALZEnvironmentTerraform.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ function New-ALZEnvironmentTerraform {
[string] $userInputOverridePath = "",

[Parameter(Mandatory = $false)]
[switch] $autoApprove
[switch] $autoApprove,

[Parameter(Mandatory = $false)]
[switch] $destroy
)

if ($PSCmdlet.ShouldProcess("ALZ-Terraform module configuration", "modify")) {
Expand All @@ -36,10 +39,20 @@ function New-ALZEnvironmentTerraform {
$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"))) {
$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
Expand All @@ -52,8 +65,12 @@ function New-ALZEnvironmentTerraform {

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 -treatEmptyDefaultAsValid $true
$bootstrapConfiguration = Request-ALZEnvironmentConfig -configurationParameters $bootstrapParameters -respectOrdering -userInputOverrides $userInputOverrides -userInputDefaultOverrides $cachedBootstrapConfig -treatEmptyDefaultAsValid $true

# Getting the configuration for the starter module user input
$starterTemplate = $bootstrapConfiguration.PsObject.Properties["starter_module"].Value.Value
Expand All @@ -63,8 +80,13 @@ function New-ALZEnvironmentTerraform {

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 -treatEmptyDefaultAsValid $true
$starterModuleConfiguration = Request-ALZEnvironmentConfig -configurationParameters $starterModuleParameters -respectOrdering -userInputOverrides $userInputOverrides -userInputDefaultOverrides $cachedStarterModuleConfig -treatEmptyDefaultAsValid $true

# Getting subscription ids
Import-SubscriptionData -starterModuleConfiguration $starterModuleConfiguration -bootstrapConfiguration $bootstrapConfiguration
Expand All @@ -75,14 +97,18 @@ function New-ALZEnvironmentTerraform {
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
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"
Invoke-Terraform -moduleFolderPath $bootstrapPath -tfvarsFileName "override.tfvars" -destroy:$destroy.IsPresent
}
}
}
35 changes: 34 additions & 1 deletion src/ALZ/Private/Request-ALZEnvironmentConfig.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ function Request-ALZEnvironmentConfig {
[Parameter(Mandatory = $false)]
[PSCustomObject] $userInputOverrides = $null,
[Parameter(Mandatory = $false)]
[System.Boolean] $treatEmptyDefaultAsValid = $false
[PSCustomObject] $userInputDefaultOverrides = $null,
[Parameter(Mandatory = $false)]
[System.Boolean] $treatEmptyDefaultAsValid = $false,
[Parameter(Mandatory = $false)]
[switch] $autoApprove
)
<#
.SYNOPSIS
Expand All @@ -23,6 +27,21 @@ function Request-ALZEnvironmentConfig {

$configurations = $configurationParameters.PsObject.Properties

$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"
} 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)"
}
if($useDefaults.ToLower() -eq "use") {
$userInputOverrides = $userInputDefaultOverrides
}
}

$hasInputOverrides = $false
if($userInputOverrides -ne $null) {
$hasInputOverrides = $true
Expand All @@ -34,6 +53,20 @@ function Request-ALZEnvironmentConfig {

foreach ($configurationValue in $configurations) {
if ($configurationValue.Value.Type -eq "UserInput") {

# Check for and add cached as default
if($hasDefaultOverrides) {
$defaultOverride = $userInputDefaultOverrides.PsObject.Properties | Where-Object { $_.Name -eq $configurationValue.Name }
if($null -ne $defaultOverride) {
if(!($configurationValue.Value.PSObject.Properties.Name -match "DefaultValue")) {
$configurationValue.Value | Add-Member -NotePropertyName "DefaultValue" -NotePropertyValue $defaultOverride.Value
} else {
$configurationValue.Value.DefaultValue = $defaultOverride.Value
}
}
}

# Check for and use override
if($hasInputOverrides) {
$userInputOverride = $userInputOverrides.PsObject.Properties | Where-Object { $_.Name -eq $configurationValue.Name }
if($null -ne $userInputOverride) {
Expand Down
24 changes: 24 additions & 0 deletions src/ALZ/Private/Write-ConfigurationCache.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
function Write-ConfigurationCache {
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter(Mandatory = $false)]
[string] $filePath,

[Parameter(Mandatory = $false)]
[PSObject] $configuration
)

if ($PSCmdlet.ShouldProcess("Download Terraform Tools", "modify")) {

if(Test-Path $filePath) {
Remove-Item -Path $filePath
}

$cache = [PSCustomObject]@{}
foreach ($configurationItem in $configuration.PSObject.Properties) {
$cache | Add-Member -NotePropertyName $configurationItem.Name -NotePropertyValue $configurationItem.Value.Value
}

$cache | ConvertTo-Json | Out-File -FilePath $filePath
}
}
13 changes: 7 additions & 6 deletions src/ALZ/Public/New-ALZEnvironment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ function New-ALZEnvironment {
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 autoApprove
Automatically approve the terraform apply.
.PARAMETER destroy
Destroy the terraform environment.
.EXAMPLE
New-ALZEnvironment
.EXAMPLE
Expand Down Expand Up @@ -56,7 +58,10 @@ function New-ALZEnvironment {
[string] $userInputOverridePath = "",

[Parameter(Mandatory = $false)]
[switch] $autoApprove
[switch] $autoApprove,

[Parameter(Mandatory = $false)]
[switch] $destroy
)

Write-InformationColored "Getting ready to create a new ALZ environment with you..." -ForegroundColor Green -InformationAction Continue
Expand All @@ -67,11 +72,7 @@ function New-ALZEnvironment {
}

if($alzIacProvider -eq "terraform") {
if($autoApprove) {
New-ALZEnvironmentTerraform -alzEnvironmentDestination $alzEnvironmentDestination -alzVersion $alzVersion -alzCicdPlatform $alzCicdPlatform -userInputOverridePath $userInputOverridePath -autoApprove
} else {
New-ALZEnvironmentTerraform -alzEnvironmentDestination $alzEnvironmentDestination -alzVersion $alzVersion -alzCicdPlatform $alzCicdPlatform -userInputOverridePath $userInputOverridePath
}
New-ALZEnvironmentTerraform -alzEnvironmentDestination $alzEnvironmentDestination -alzVersion $alzVersion -alzCicdPlatform $alzCicdPlatform -userInputOverridePath $userInputOverridePath -autoApprove:$autoApprove.IsPresent -destroy:$destroy.IsPresent
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/Tests/Unit/Public/New-ALZEnvironment.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,13 @@ InModuleScope 'ALZ' {

Mock -CommandName Write-TfvarsFile -MockWith { }

Mock -CommandName Write-ConfigurationCache -MockWith { }

Mock -CommandName Invoke-Terraform -MockWith { }

Mock -CommandName Import-SubscriptionData -MockWith { }

Mock -CommandName Invoke-Upgrade -MockWith { }
}

It 'should return the output directory on completion' {
Expand All @@ -120,7 +123,7 @@ InModuleScope 'ALZ' {
}

It 'should clone the git repo and apply if terraform is selected' {
New-ALZEnvironment -IaC "terraform" -
New-ALZEnvironment -IaC "terraform"
Assert-MockCalled -CommandName Get-ALZGithubRelease -Exactly 1
Assert-MockCalled -CommandName Invoke-Terraform -Exactly 1
}
Expand Down
Loading