Public/Terraform/Invoke-TerraformPlan.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
function Invoke-TerraformPlan
{
    [CmdletBinding()]
    param
    (
        # The path to the terraform configuration to plan against
        [Parameter(
            Mandatory = $false,
            Position = 0,
            ValueFromPipelineByPropertyName = $true
        )]
        [ValidateNotNullOrEmpty()]
        [string]
        $TerraformConfigPath = $PWD,

        # The Path to the Terraform executable to use
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [ValidateNotNullOrEmpty()]
        [string]
        $TerraformPath = 'terraform',

        # The path to store the output of the Terraform plan
        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            Position = 1
        )]
        [string]
        $OutputPath,

        # A resource to target (useful in testing)
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            Position = 2
        )]
        [string]
        $Target,

        # Whether or not to refresh resources
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [bool]
        $Refresh = $true,

        # Whether or not to use detailed exit codes
        [Parameter(
            Mandatory = $false
        )]
        [bool]
        $DetailedExitCodes = $false,

        # Limit the number of concurrent operation as Terraform walks the graph.
        [Parameter(
            Mandatory = $false
        )]
        [int]
        $Parallelism,

        # Whether or not to enable color output, defaults to false so as not to break CI/CD tools
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [bool]
        $EnableColor = $false
    )
    $ValidExitCodes = @(0)
    $PlanArgs = @('plan')
    if ($EnableColor -eq $false)
    {
        Write-Verbose "Disabling color output"
        $PlanArgs += @("-no-color")
    }
    if ($OutputPath)
    {
        Write-Verbose "Setting output path to $OutputPath"
        $PlanArgs += "-out=$OutputPath"
    }
    if ($Target)
    {
        Write-Verbose "Targetting $target"
        $PlanArgs += "-target='$Target'"
    }
    if ($Refresh -eq $false)
    {
        Write-Verbose "Disabling refresh"
        $PlanArgs += "-refresh=false"
    }
    if ($DetailedExitCodes -eq $true)
    {
        Write-Verbose "Using detailed exit codes"
        # terraform plan may actually return a non-zero exit code when detailed exit codes are used https://www.terraform.io/docs/cli/commands/plan.html#detailed-exitcode
        $ValidExitCodes += 2
        $PlanArgs += "-detailed-exitcode"
    }
    if ($Parallelism)
    {
        Write-Verbose "Setting Parallelism to $Parallelism"
        $PlanArgs += "-parallelism=$Parallelism"
    }
    try
    {
        $TerraformParams = @{
            FilePath = $TerraformPath
            ArgumentList = $PlanArgs
            ExitCodes = $ValidExitCodes
            WorkingDirectory = $TerraformConfigPath
            PassThru = $true
            SuppressOutput = $true
        }
        if ($VerbosePreference -eq 'Continue')
        {
            $TerraformParams.Remove('SuppressOutput')
        }
        $TerraformPlan = Invoke-NativeCommand @TerraformParams | Select-Object -ExpandProperty OutputContent # We want to extract the plan results for later consumption
    }
    catch
    {
        Write-Error "Terraform plan has failed.`n$($_.Exception.Message)"
    } 
    $TerraformResult = @{
        PlanOutput = $TerraformPlan
    }
    if ($OutputPath)
    {
        try
        {
            $AbsoluteOutputPath = Get-Item $OutputPath | Convert-Path
        }
        catch
        {
            Write-Error "Failed to find Terraform plan output at $OutputPath.`n$($_.Exception.Message)"
        }
        # Add the path to the Terraform plan output to the returned object so it can be piped into Invoke-Terraform show if desired
        $TerraformResult.Add('TerraformPlanPath', $AbsoluteOutputPath)
    }
    Return [pscustomobject]$TerraformResult
}