Skip to content

Commit 176ec96

Browse files
Enhancement add terraform rerun and destroy (#93)
# Pull Request ## Issue Issue #, if available: Azure/alz-terraform-accelerator#70 ## Description Description of changes: This PR adds re-run, upgrade and destroy features for Terraform. These features are designed to simplify usage of the accelerator, provide any easy way to apply changes, fix issues and upgrade to a newer version. The destroy will make it much easier to clean up test environments. ## License By submitting this pull request, I confirm that my contribution is made under the terms of the projects associated license.
1 parent c5bd475 commit 176ec96

8 files changed

+221
-18
lines changed

src/ALZ/Private/Get-ALZConfig.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ function Get-ALZConfig {
77
[string] $configFilePath = ""
88
)
99

10+
if(!(Test-Path $configFilePath)) {
11+
return $null
12+
}
13+
1014
# Import the config and transform it to a PowerShell object
1115
$extension = (Get-Item -Path $configFilePath).Extension.ToLower()
1216
if($extension -eq ".yml" -or $extension -eq ".yaml") {

src/ALZ/Private/Invoke-Terraform.ps1

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,33 @@ function Invoke-Terraform {
88
[string] $tfvarsFileName,
99

1010
[Parameter(Mandatory = $false)]
11-
[switch] $autoApprove
11+
[switch] $autoApprove,
12+
13+
[Parameter(Mandatory = $false)]
14+
[switch] $destroy
1215
)
1316

1417
if ($PSCmdlet.ShouldProcess("Apply Terraform", "modify")) {
1518
terraform -chdir="$moduleFolderPath" init
16-
Write-InformationColored "Terraform init has completed, now running the apply..." -ForegroundColor Green -InformationAction Continue
17-
if($autoApprove) {
18-
terraform -chdir="$moduleFolderPath" apply -var-file="$tfvarsFileName" -auto-approve
19+
$action = "apply"
20+
if($destroy) {
21+
$action = "destroy"
22+
}
23+
24+
Write-InformationColored "Terraform init has completed, now running the $action..." -ForegroundColor Green -InformationAction Continue
25+
26+
if($destroy) {
27+
if($autoApprove) {
28+
terraform -chdir="$moduleFolderPath" destroy -var-file="$tfvarsFileName" -auto-approve
29+
} else {
30+
terraform -chdir="$moduleFolderPath" destroy -var-file="$tfvarsFileName"
31+
}
1932
} else {
20-
terraform -chdir="$moduleFolderPath" apply -var-file="$tfvarsFileName"
33+
if($autoApprove) {
34+
terraform -chdir="$moduleFolderPath" apply -var-file="$tfvarsFileName" -auto-approve
35+
} else {
36+
terraform -chdir="$moduleFolderPath" apply -var-file="$tfvarsFileName"
37+
}
2138
}
2239
}
2340
}

src/ALZ/Private/Invoke-Upgrade.ps1

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
function Invoke-Upgrade {
2+
[CmdletBinding(SupportsShouldProcess = $true)]
3+
param (
4+
[Parameter(Mandatory = $false)]
5+
[string] $alzEnvironmentDestination,
6+
7+
[Parameter(Mandatory = $false)]
8+
[string] $bootstrapCacheFileName,
9+
10+
[Parameter(Mandatory = $false)]
11+
[string] $starterCacheFileNamePattern,
12+
13+
[Parameter(Mandatory = $false)]
14+
[string] $stateFilePathAndFileName,
15+
16+
[Parameter(Mandatory = $false)]
17+
[string] $currentVersion,
18+
19+
[Parameter(Mandatory = $false)]
20+
[switch] $autoApprove
21+
)
22+
23+
if ($PSCmdlet.ShouldProcess("Upgrade Release", "Operation")) {
24+
25+
$directories = Get-ChildItem -Path $alzEnvironmentDestination -Filter "v*" -Directory
26+
$previousBootstrapCachedValuesPath = $null
27+
$previousStarterCachedValuesPath = $null
28+
$previousStateFilePath = $null
29+
$previousVersion = $null
30+
$foundPreviousRelease = $false
31+
32+
foreach ($directory in $directories | Sort-Object -Descending -Property Name) {
33+
$releasePath = Join-Path -Path $alzEnvironmentDestination -ChildPath $directory.Name
34+
$releaseBootstrapCachedValuesPath = Join-Path -Path $releasePath -ChildPath $bootstrapCacheFileName
35+
$releaseStateFilePath = Join-Path -Path $releasePath -ChildPath $stateFilePathAndFileName
36+
37+
if(Test-Path $releaseBootstrapCachedValuesPath) {
38+
$previousBootstrapCachedValuesPath = $releaseBootstrapCachedValuesPath
39+
}
40+
41+
$starterCacheFiles = Get-ChildItem -Path $releasePath -Filter $starterCacheFileNamePattern -File
42+
43+
if($starterCacheFiles) {
44+
$previousStarterCachedValuesPath = $starterCacheFiles[0].FullName
45+
}
46+
47+
if(Test-Path $releaseStateFilePath) {
48+
$previousStateFilePath = $releaseStateFilePath
49+
}
50+
51+
if($null -ne $previousStateFilePath) {
52+
if($directory.Name -eq $currentVersion) {
53+
# If the current version has already been run, then skip the upgrade process
54+
break
55+
}
56+
57+
$foundPreviousRelease = $true
58+
$previousVersion = $directory.Name
59+
break
60+
}
61+
}
62+
63+
if($foundPreviousRelease) {
64+
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
65+
$upgrade = ""
66+
if($autoApprove) {
67+
$upgrade = "upgrade"
68+
} else {
69+
$upgrade = Read-Host "If you would like to upgrade, enter 'upgrade' or just hit 'enter' to continue with a new environment. (upgrade/exit)"
70+
}
71+
72+
if($upgrade.ToLower() -eq "upgrade") {
73+
$currentPath = Join-Path -Path $alzEnvironmentDestination -ChildPath $currentVersion
74+
$currentBootstrapCachedValuesPath = Join-Path -Path $currentPath -ChildPath $bootstrapCacheFileName
75+
$currentStarterCachedValuesPath = $currentPath
76+
$currentStateFilePath = Join-Path -Path $currentPath -ChildPath $stateFilePathAndFileName
77+
78+
# Copy the previous cached values to the current release
79+
if($null -ne $previousBootstrapCachedValuesPath) {
80+
Write-InformationColored "AUTOMATIC UPGRADE: Copying $previousBootstrapCachedValuesPath to $currentBootstrapCachedValuesPath" -ForegroundColor Green -InformationAction Continue
81+
Copy-Item -Path $previousBootstrapCachedValuesPath -Destination $currentBootstrapCachedValuesPath -Force | Out-String | Write-Verbose
82+
}
83+
if($null -ne $previousStarterCachedValuesPath) {
84+
Write-InformationColored "AUTOMATIC UPGRADE: Copying $previousStarterCachedValuesPath to $currentStarterCachedValuesPath" -ForegroundColor Green -InformationAction Continue
85+
Copy-Item -Path $previousStarterCachedValuesPath -Destination $currentStarterCachedValuesPath -Force | Out-String | Write-Verbose
86+
}
87+
88+
Write-InformationColored "AUTOMATIC UPGRADE: Copying $previousStateFilePath to $currentStateFilePath" -ForegroundColor Green -InformationAction Continue
89+
Copy-Item -Path $previousStateFilePath -Destination $currentStateFilePath -Force | Out-String | Write-Verbose
90+
91+
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
92+
}
93+
}
94+
}
95+
}

src/ALZ/Private/New-ALZEnvironmentTerraform.ps1

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ function New-ALZEnvironmentTerraform {
2020
[string] $userInputOverridePath = "",
2121

2222
[Parameter(Mandatory = $false)]
23-
[switch] $autoApprove
23+
[switch] $autoApprove,
24+
25+
[Parameter(Mandatory = $false)]
26+
[switch] $destroy
2427
)
2528

2629
if ($PSCmdlet.ShouldProcess("ALZ-Terraform module configuration", "modify")) {
@@ -36,10 +39,20 @@ function New-ALZEnvironmentTerraform {
3639
$userInputOverrides = Get-ALZConfig -configFilePath $userInputOverridePath
3740
}
3841

42+
# Setup Cache Paths
43+
$bootstrapCacheFileName = "cache-bootstrap-$alzCicdPlatform.json"
44+
$starterCacheFileNamePattern = "cache-starter-*.json"
45+
3946
# Downloading the latest or specified version of the alz-terraform-accelerator module
47+
if(!($alzVersion.StartsWith("v"))) {
48+
$alzVersion = "v$alzVersion"
49+
}
4050
$releaseTag = Get-ALZGithubRelease -directoryForReleases $alzEnvironmentDestination -iac "terraform" -release $alzVersion
4151
$releasePath = Join-Path -Path $alzEnvironmentDestination -ChildPath $releaseTag
4252

53+
# Run upgrade
54+
Invoke-Upgrade -alzEnvironmentDestination $alzEnvironmentDestination -bootstrapCacheFileName $bootstrapCacheFileName -starterCacheFileNamePattern $starterCacheFileNamePattern -stateFilePathAndFileName "bootstrap/$alzCicdPlatform/terraform.tfstate" -currentVersion $releaseTag -autoApprove:$autoApprove.IsPresent
55+
4356
# Getting the configuration for the initial bootstrap user input and validators
4457
$bootstrapConfigFilePath = Join-Path -Path $releasePath -ChildPath "bootstrap/.config/ALZ-Powershell.config.json"
4558
$bootstrapConfig = Get-ALZConfig -configFilePath $bootstrapConfigFilePath
@@ -52,8 +65,12 @@ function New-ALZEnvironmentTerraform {
5265

5366
Write-InformationColored "Got configuration and downloaded alz-terraform-accelerator Terraform module version $releaseTag to $alzEnvironmentDestination" -ForegroundColor Green -InformationAction Continue
5467

68+
#Checking for cached bootstrap values for retry / upgrade scenarios
69+
$bootstrapCachedValuesPath = Join-Path -Path $releasePath -ChildPath $bootstrapCacheFileName
70+
$cachedBootstrapConfig = Get-ALZConfig -configFilePath $bootstrapCachedValuesPath
71+
5572
# Getting the user input for the bootstrap module
56-
$bootstrapConfiguration = Request-ALZEnvironmentConfig -configurationParameters $bootstrapParameters -respectOrdering -userInputOverrides $userInputOverrides -treatEmptyDefaultAsValid $true
73+
$bootstrapConfiguration = Request-ALZEnvironmentConfig -configurationParameters $bootstrapParameters -respectOrdering -userInputOverrides $userInputOverrides -userInputDefaultOverrides $cachedBootstrapConfig -treatEmptyDefaultAsValid $true
5774

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

6481
Write-InformationColored "The following inputs are specific to the '$starterTemplate' starter module that you selected..." -ForegroundColor Green -InformationAction Continue
6582

83+
# Checking for cached starter module values for retry / upgrade scenarios
84+
$starterCacheFileName = "cache-starter-$starterTemplate.json"
85+
$starterModuleCachedValuesPath = Join-Path -Path $releasePath -ChildPath $starterCacheFileName
86+
$cachedStarterModuleConfig = Get-ALZConfig -configFilePath $starterModuleCachedValuesPath
87+
6688
# Getting the user input for the starter module
67-
$starterModuleConfiguration = Request-ALZEnvironmentConfig -configurationParameters $starterModuleParameters -respectOrdering -userInputOverrides $userInputOverrides -treatEmptyDefaultAsValid $true
89+
$starterModuleConfiguration = Request-ALZEnvironmentConfig -configurationParameters $starterModuleParameters -respectOrdering -userInputOverrides $userInputOverrides -userInputDefaultOverrides $cachedStarterModuleConfig -treatEmptyDefaultAsValid $true
6890

6991
# Getting subscription ids
7092
Import-SubscriptionData -starterModuleConfiguration $starterModuleConfiguration -bootstrapConfiguration $bootstrapConfiguration
@@ -75,14 +97,18 @@ function New-ALZEnvironmentTerraform {
7597
Write-TfvarsFile -tfvarsFilePath $bootstrapTfvarsPath -configuration $bootstrapConfiguration
7698
Write-TfvarsFile -tfvarsFilePath $starterModuleTfvarsPath -configuration $starterModuleConfiguration
7799

100+
# Caching the bootstrap and starter module values paths for retry / upgrade scenarios
101+
Write-ConfigurationCache -filePath $bootstrapCachedValuesPath -configuration $bootstrapConfiguration
102+
Write-ConfigurationCache -filePath $starterModuleCachedValuesPath -configuration $starterModuleConfiguration
103+
78104
# Running terraform init and apply
79105
Write-InformationColored "Thank you for providing those inputs, we are now initializing and applying Terraform to bootstrap your environment..." -ForegroundColor Green -InformationAction Continue
80106

81107
if($autoApprove) {
82-
Invoke-Terraform -moduleFolderPath $bootstrapPath -tfvarsFileName "override.tfvars" -autoApprove
108+
Invoke-Terraform -moduleFolderPath $bootstrapPath -tfvarsFileName "override.tfvars" -autoApprove -destroy:$destroy.IsPresent
83109
} else {
84110
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
85-
Invoke-Terraform -moduleFolderPath $bootstrapPath -tfvarsFileName "override.tfvars"
111+
Invoke-Terraform -moduleFolderPath $bootstrapPath -tfvarsFileName "override.tfvars" -destroy:$destroy.IsPresent
86112
}
87113
}
88114
}

src/ALZ/Private/Request-ALZEnvironmentConfig.ps1

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ function Request-ALZEnvironmentConfig {
88
[Parameter(Mandatory = $false)]
99
[PSCustomObject] $userInputOverrides = $null,
1010
[Parameter(Mandatory = $false)]
11-
[System.Boolean] $treatEmptyDefaultAsValid = $false
11+
[PSCustomObject] $userInputDefaultOverrides = $null,
12+
[Parameter(Mandatory = $false)]
13+
[System.Boolean] $treatEmptyDefaultAsValid = $false,
14+
[Parameter(Mandatory = $false)]
15+
[switch] $autoApprove
1216
)
1317
<#
1418
.SYNOPSIS
@@ -23,6 +27,21 @@ function Request-ALZEnvironmentConfig {
2327

2428
$configurations = $configurationParameters.PsObject.Properties
2529

30+
$hasDefaultOverrides = $false
31+
if($userInputDefaultOverrides -ne $null) {
32+
$hasDefaultOverrides = $true
33+
Write-InformationColored "We found you have cached values from a previous run." -ForegroundColor Yellow -InformationAction Continue
34+
$useDefaults = ""
35+
if($autoApprove) {
36+
$useDefaults = "use"
37+
} else {
38+
$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)"
39+
}
40+
if($useDefaults.ToLower() -eq "use") {
41+
$userInputOverrides = $userInputDefaultOverrides
42+
}
43+
}
44+
2645
$hasInputOverrides = $false
2746
if($userInputOverrides -ne $null) {
2847
$hasInputOverrides = $true
@@ -34,6 +53,20 @@ function Request-ALZEnvironmentConfig {
3453

3554
foreach ($configurationValue in $configurations) {
3655
if ($configurationValue.Value.Type -eq "UserInput") {
56+
57+
# Check for and add cached as default
58+
if($hasDefaultOverrides) {
59+
$defaultOverride = $userInputDefaultOverrides.PsObject.Properties | Where-Object { $_.Name -eq $configurationValue.Name }
60+
if($null -ne $defaultOverride) {
61+
if(!($configurationValue.Value.PSObject.Properties.Name -match "DefaultValue")) {
62+
$configurationValue.Value | Add-Member -NotePropertyName "DefaultValue" -NotePropertyValue $defaultOverride.Value
63+
} else {
64+
$configurationValue.Value.DefaultValue = $defaultOverride.Value
65+
}
66+
}
67+
}
68+
69+
# Check for and use override
3770
if($hasInputOverrides) {
3871
$userInputOverride = $userInputOverrides.PsObject.Properties | Where-Object { $_.Name -eq $configurationValue.Name }
3972
if($null -ne $userInputOverride) {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
function Write-ConfigurationCache {
2+
[CmdletBinding(SupportsShouldProcess = $true)]
3+
param (
4+
[Parameter(Mandatory = $false)]
5+
[string] $filePath,
6+
7+
[Parameter(Mandatory = $false)]
8+
[PSObject] $configuration
9+
)
10+
11+
if ($PSCmdlet.ShouldProcess("Download Terraform Tools", "modify")) {
12+
13+
if(Test-Path $filePath) {
14+
Remove-Item -Path $filePath
15+
}
16+
17+
$cache = [PSCustomObject]@{}
18+
foreach ($configurationItem in $configuration.PSObject.Properties) {
19+
$cache | Add-Member -NotePropertyName $configurationItem.Name -NotePropertyValue $configurationItem.Value.Value
20+
}
21+
22+
$cache | ConvertTo-Json | Out-File -FilePath $filePath
23+
}
24+
}

src/ALZ/Public/New-ALZEnvironment.ps1

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ function New-ALZEnvironment {
1616
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" }
1717
.PARAMETER autoApprove
1818
Automatically approve the terraform apply.
19+
.PARAMETER destroy
20+
Destroy the terraform environment.
1921
.EXAMPLE
2022
New-ALZEnvironment
2123
.EXAMPLE
@@ -56,7 +58,10 @@ function New-ALZEnvironment {
5658
[string] $userInputOverridePath = "",
5759

5860
[Parameter(Mandatory = $false)]
59-
[switch] $autoApprove
61+
[switch] $autoApprove,
62+
63+
[Parameter(Mandatory = $false)]
64+
[switch] $destroy
6065
)
6166

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

6974
if($alzIacProvider -eq "terraform") {
70-
if($autoApprove) {
71-
New-ALZEnvironmentTerraform -alzEnvironmentDestination $alzEnvironmentDestination -alzVersion $alzVersion -alzCicdPlatform $alzCicdPlatform -userInputOverridePath $userInputOverridePath -autoApprove
72-
} else {
73-
New-ALZEnvironmentTerraform -alzEnvironmentDestination $alzEnvironmentDestination -alzVersion $alzVersion -alzCicdPlatform $alzCicdPlatform -userInputOverridePath $userInputOverridePath
74-
}
75+
New-ALZEnvironmentTerraform -alzEnvironmentDestination $alzEnvironmentDestination -alzVersion $alzVersion -alzCicdPlatform $alzCicdPlatform -userInputOverridePath $userInputOverridePath -autoApprove:$autoApprove.IsPresent -destroy:$destroy.IsPresent
7576
}
7677
}
7778

src/Tests/Unit/Public/New-ALZEnvironment.Tests.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,13 @@ InModuleScope 'ALZ' {
108108

109109
Mock -CommandName Write-TfvarsFile -MockWith { }
110110

111+
Mock -CommandName Write-ConfigurationCache -MockWith { }
112+
111113
Mock -CommandName Invoke-Terraform -MockWith { }
112114

113115
Mock -CommandName Import-SubscriptionData -MockWith { }
114116

117+
Mock -CommandName Invoke-Upgrade -MockWith { }
115118
}
116119

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

122125
It 'should clone the git repo and apply if terraform is selected' {
123-
New-ALZEnvironment -IaC "terraform" -
126+
New-ALZEnvironment -IaC "terraform"
124127
Assert-MockCalled -CommandName Get-ALZGithubRelease -Exactly 1
125128
Assert-MockCalled -CommandName Invoke-Terraform -Exactly 1
126129
}

0 commit comments

Comments
 (0)