tfe.psm1
function NewTarballFile { [CmdletBinding()] [OutputType([Boolean])] Param ( # The input file(s) [Parameter(Mandatory=$true)][ValidateScript({Test-Path $_})][String]$source, [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][String]$output, [Parameter(Mandatory=$false)][Switch]$overwrite ) if ($overwrite) { if (test-path $output) { write-verbose "output file '$output' already exists. It will be deleted." Remove-Item $output } } #Get the source from file system $fsObject = Get-item $source If (test-path $source -PathType Leaf) { #individual file $parentDir = $fsObject.Directory $childName = $fsObject.Name & tar -C "$parentDir" -czf $output $childName } else { #folder & tar -C "$source" -czf $output *.* } try{ } catch{ throw exit 1 } } Function DecodeToken { Param( [Parameter(Mandatory = $true)][securestring]$Token ) $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token); try { $strToken = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr); } finally { [Runtime.InteropServices.Marshal]::FreeBSTR($bstr); } $strToken } Function New-TFEConfigVersion { [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium', HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Get-TFEConfigVersion')] [OutputType([object])] Param( [Parameter(Mandatory = $true, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL, [Parameter(Mandatory = $true, HelpMessage = "Enter the organization name.")][string]$Org, [Parameter(Mandatory = $true, HelpMessage = "Enter the workspace Name.")][string]$WorkSpaceName, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token ) begin { Write-Verbose "Get workspace" try { $workspace = Get-TFEWorkspace -TFEBaseURL $TFEBaseURL -Org $org -WorkspaceName $WorkSpaceName -Token $Token $WorkSpaceID = $workspace.id Write-verbose "workspace Id for workspace $WorkspaceName is $WorkSpaceID" } catch { throw Exit 1 } Write-verbose "Create a new Terraform Enterprise configuration version" $body = @{ "data" = @{ "type" = "configuration-version" "attributes" = @{ "auto-queue-runs" = $false } } } | ConvertTo-Json $requestParams = @{ Uri = "$TFEBaseURL/api/v2/workspaces/$WorkSpaceID/configuration-versions" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Post' Body = $body ErrorAction = 'stop' UseBasicParsing = $true } } Process { Write-Verbose "requesting new config via URI '$($requestParams.Uri)'" try { if ($PSCmdlet.ShouldProcess($WorkspaceName)) { $ReturnedData = (Invoke-RestMethod @requestParams).data } } catch { throw Exit 1 } } End { $ReturnedData } } Function Get-TFEWorkspace { [CmdletBinding(HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Get-TFEWorkspace')] [OutputType([object])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the organization name.")][string]$Org, [Parameter(Mandatory = $true, HelpMessage = "Enter the workspace name.")][string]$WorkspaceName, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token ) try { $GetRequest = @{ Uri = "$TFEBaseURL/api/v2/organizations/$Org/workspaces/$WorkSpaceName" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Get' ErrorAction = 'stop' UseBasicParsing = $true } $Result = (Invoke-RestMethod @GetRequest).data } catch { throw exit 1 } $Result } Function Set-TFEWorkspace { [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium', HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Set-TFEWorkspace')] [OutputType([object])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the organization name.")][string]$Org, [Parameter(Mandatory = $true, HelpMessage = "Enter the workspace name.")][string]$WorkspaceName, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token, [Parameter(Mandatory = $false, HelpMessage = "Enter the new name of the workspace.")][string]$NewWorkspaceName, [Parameter(Mandatory = $false, HelpMessage = "Enter the description of the workspace.")][string]$WorkspaceDescription, [Parameter(Mandatory = $false, HelpMessage = "Enter the Terraform version of the workspace.")][string]$TerraformVersion, [Parameter(Mandatory = $false, HelpMessage = "Enter the Terraform working Directory of the workspace.")][string]$TerraformWorkingDir, [Parameter(Mandatory = $false, HelpMessage = "Allow automatically apply changes when a Terraform plan is successful.")][boolean]$AutoApply, [Parameter(Mandatory = $false, HelpMessage = "Allow destroy plan.")][boolean]$AllowDestroyPlan, [Parameter(Mandatory = $false, HelpMessage = "Whether to use remote execution mode. When set to false, the workspace will be used for state storage only.")][boolean]$UseRemoteExecution ) begin { #Process input $attributes = @{} If ($PSBoundParameters.ContainsKey('NewWorkspaceName')) { $attributes.add('name', $NewWorkspaceName) } If ($PSBoundParameters.ContainsKey('WorkspaceDescription')) { $attributes.add('description', $WorkspaceDescription) } If ($PSBoundParameters.ContainsKey('TerraformVersion')) { $attributes.add('terraform-version', $TerraformVersion) } If ($PSBoundParameters.ContainsKey('TerraformWorkingDir')) { $attributes.add('working-directory', $TerraformWorkingDir) } If ($PSBoundParameters.ContainsKey('AutoApply')) { $attributes.add('auto-apply', $AutoApply) } If ($PSBoundParameters.ContainsKey('AllowDestroyPlan')) { $attributes.add('allow-destroy-plan', $AllowDestroyPlan) } If ($PSBoundParameters.ContainsKey('UseRemoteExecution')) { $attributes.add('operations', $UseRemoteExecution) } If ($attributes.Keys.count -eq 0) { Throw "No workspace attributes specified. Nothing to set." Exit 1 } $body = @{ "data" = @{ "attributes" = $attributes "type" = "workspaces" } } | ConvertTo-Json -Depth 5 $PatchRequest = @{ Uri = "$TFEBaseURL/api/v2/organizations/$Org/workspaces/$WorkSpaceName" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Patch' ErrorAction = 'stop' Body = $body UseBasicParsing = $true } } Process { try { if ($PSCmdlet.ShouldProcess($WorkspaceName)) { $Result = (Invoke-RestMethod @PatchRequest).data } } catch { throw exit 1 } } End { $Result } } Function New-TFEWorkspace { [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium', HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/New-TFEWorkspace')] [OutputType([object])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the organization name.")][string]$Org, [Parameter(Mandatory = $true, HelpMessage = "Enter the workspace name.")][string]$WorkspaceName, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token, [Parameter(Mandatory = $false, HelpMessage = "Enter the description of the workspace.")][string]$WorkspaceDescription, [Parameter(Mandatory = $false, HelpMessage = "Enter the Terraform version of the workspace.")][string]$TerraformVersion, [Parameter(Mandatory = $false, HelpMessage = "Enter the Terraform working Directory of the workspace.")][string]$TerraformWorkingDir, [Parameter(Mandatory = $false, HelpMessage = "Allow automatically apply changes when a Terraform plan is successful.")][boolean]$AutoApply, [Parameter(Mandatory = $false, HelpMessage = "Allow destroy plan.")][boolean]$AllowDestroyPlan, [Parameter(Mandatory = $false, HelpMessage = "Whether to use remote execution mode. When set to false, the workspace will be used for state storage only.")][boolean]$UseRemoteExecution ) begin { #Process input $attributes = @{} $attributes.add('name', $WorkspaceName) If ($PSBoundParameters.ContainsKey('WorkspaceDescription')) { $attributes.add('description', $WorkspaceDescription) } If ($PSBoundParameters.ContainsKey('TerraformVersion')) { $attributes.add('terraform-version', $TerraformVersion) } If ($PSBoundParameters.ContainsKey('TerraformWorkingDir')) { $attributes.add('working-directory', $TerraformWorkingDir) } If ($PSBoundParameters.ContainsKey('AutoApply')) { $attributes.add('auto-apply', $AutoApply) } If ($PSBoundParameters.ContainsKey('AllowDestroyPlan')) { $attributes.add('allow-destroy-plan', $AllowDestroyPlan) } If ($PSBoundParameters.ContainsKey('UseRemoteExecution')) { $attributes.add('operations', $UseRemoteExecution) } $body = @{ "data" = @{ "attributes" = $attributes "type" = "workspaces" } } | ConvertTo-Json -Depth 5 $PostRequest = @{ Uri = "$TFEBaseURL/api/v2/organizations/$Org/workspaces" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'POST' ErrorAction = 'stop' Body = $body UseBasicParsing = $true } } Process { try { Write-verbose "Creating workspace $Workspace in Organisation $org" if ($PSCmdlet.ShouldProcess($WorkspaceName)) { $Result = (Invoke-RestMethod @PostRequest).data } } catch { throw exit 1 } } End { $Result } } Function Remove-TFEWorkspace { [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High', HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Remove-TFEWorkspace')] [OutputType([boolean])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the organization name.")][string]$Org, [Parameter(Mandatory = $true, HelpMessage = "Enter the workspace name.")][string]$WorkspaceName, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token ) begin { #Process input $DeleteRequest = @{ Uri = "$TFEBaseURL/api/v2/organizations/$Org/workspaces/$WorkspaceName" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'DELETE' ErrorAction = 'stop' UseBasicParsing = $true } } Process { try { Write-verbose "Delete workspace $Workspace from Organisation $org" if ($PSCmdlet.ShouldProcess($WorkspaceName)) { Invoke-RestMethod @DeleteRequest } } catch { throw exit 1 } } End { $true } } Function Add-TFEVariable { [CmdletBinding(HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Add-TFEVariable')] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the organization name.")][string]$Org, [Parameter(Mandatory = $true, HelpMessage = "Enter the workspace name.")][string]$WorkspaceName, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token, [Parameter(Mandatory = $false, HelpMessage = "Non-sensitive Terraform variables in a hashtable")][hashtable]$TFVariables, [Parameter(Mandatory = $false, HelpMessage = "Sensitive Terraform Variables in a hashtable")][hashtable]$TFSecrets, [Parameter(Mandatory = $false, HelpMessage = "Non-sensitive environment variables in a hashtable")][hashtable]$EnvVariables, [Parameter(Mandatory = $false, HelpMessage = "Sensitive Envrionment Variables in a hashtable")][hashtable]$EnvSecrets ) Write-verbose "Getting Existing Variables in Workspace $WorkspaceName" try { $GetVariablesRequest = @{ Uri = "$TFEBaseURL/api/v2/vars?filter%5Borganization%5D%5Bname%5D=$Org&filter%5Bworkspace%5D%5Bname%5D=$WorkSpaceName" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'GET' ErrorAction = 'stop' UseBasicParsing = $true } $VariableResult = (Invoke-RestMethod @GetVariablesRequest).data $ExistingVariables = @() foreach ($item in $variableResult) { $ExistingVariables += New-Object psobject -Property @{"key" = $item.attributes.key; "sensitive" = [bool]$item.attributes.sensitive; "category" = $item.attributes.category} } } Catch { Throw Exit 1 } $SourceVariables = @() If ($PSBoundParameters.containskey('TFVariables')) { Foreach ($key in $TFVariables.keys) { Write-verbose "Processing Terraform variable $key" $SourceVariables += New-Object psobject -Property @{"key" = $key; "value" = $TFVariables.$key; "sensitive" = $false; "category" = "terraform"} } } if ($PSBoundParameters.containskey('TFSecrets')) { Foreach ($key in $TFSecrets.keys) { Write-verbose "Processing Terraform secret $key" $SourceVariables += New-Object psobject -Property @{"key" = $key; "value" = $TFSecrets.$key; "sensitive" = $true; "category" = "terraform"} } } If ($PSBoundParameters.containskey('EnvVariables')) { Foreach ($key in $EnvVariables.keys) { Write-verbose "Processing Environment variable $key" $SourceVariables += New-Object psobject -Property @{"key" = $key; "value" = $EnvVariables.$key; "sensitive" = $false; "category" = "env"} } } if ($PSBoundParameters.containskey('EnvSecrets')) { Foreach ($key in $EnvSecrets.keys) { Write-verbose "Processing Environment secret $key" $SourceVariables += New-Object psobject -Property @{"key" = $key; "value" = $EnvSecrets.$key; "sensitive" = $true; "category" = "env"} } } If ($SourceVariables.count -eq 0) { Throw "No Terraform or Environment variables & secrets have been passed into the function." Exit 1 } else { Write-verbose "The following variables have been passed into the function" foreach ($item in $SourceVariables) { Write-verbose "key = $($item.key); sensitive = '$($item.sensitive)'; category = '$($item.category)'" } } Write-verbose "Comparing Source Variables With Existing Variables" $VariableAction = @() Compare-Object $SourceVariables $ExistingVariables -Property key, sensitive, category -IncludeEqual | Where-Object {$_.key -ne "CONFIRM_DESTROY"} | ForEach-Object { if ($_.sideindicator -eq "==") { $VariableAction += New-Object psobject -Property @{"Variable" = $_; "Action" = "Modify"} } elseif ($_.sideindicator -eq "<=") { $VariableAction += New-Object psobject -Property @{"Variable" = $_; "Action" = "Add"} } elseif ($_.sideindicator -eq "=>") { $VariableAction += New-Object psobject -Property @{"Variable" = $_; "Action" = "Remove"} } } Write-verbose "Updating Variables" if ($VariableAction.action -contains "add") { $Workspace = Get-TFEWorkspace -TFEBaseURL $TFEBaseURL -Org $Org -WorkspaceName $WorkspaceName -Token $Token $WorkspaceID = $Workspace.Id } foreach ($item in $VariableAction) { try { if ($item.action -ieq "add") { write-verbose "Adding variable $($item.variable.key)" try { $body = @{ "data" = @{ "type" = "vars" "attributes" = @{ "key" = $($item.Variable.key) "value" = $($SourceVariables.GetEnumerator() | Where-Object {$_.key -eq $item.variable.key -and $_.category -ieq $item.variable.category -and $_.sensitive -eq $item.variable.sensitive} |Select-Object -ExpandProperty Value) "category" = $($item.variable.category) "hcl" = "false" "sensitive" = $($item.Variable.sensitive) } "relationships" = @{ "workspace" = @{ "data" = @{ "id" = "$WorkSpaceID" "type" = "workspaces" } } } } } | ConvertTo-Json -Depth 5 $AddRequest = @{ Uri = "$TFEBaseURL/api/v2/vars" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Post' Body = $body ErrorAction = 'stop' UseBasicParsing = $true } (Invoke-RestMethod @AddRequest).data | out-null } catch { Throw Exit 1 } } elseif ($item.action -ieq "modify") { write-verbose "Modifying $($item.variable.key)" try { $body = @{ "data" = @{ "type" = "vars" "id" = $($VariableResult | Where-Object {$_.attributes.key -eq $item.variable.key -and $_.attributes.category -ieq $item.Variable.category -and $_.attributes.sensitive -eq $item.variable.sensitive} |Select-Object -exp id) "attributes" = @{ "key" = $($item.variable.key) "value" = $($SourceVariables.GetEnumerator() | Where-Object {$_.key -eq $item.Variable.key -and $_.category -ieq $item.Variable.category -and $_.sensitive -eq $item.variable.sensitive} |Select-Object -ExpandProperty Value) "category" = $($item.variable.category) "hcl" = "false" "sensitive" = $($item.Variable.sensitive) } } } | ConvertTo-Json $PatchRequest = @{ Uri = "$TFEBaseURL/api/v2/vars/$($VariableResult | Where-Object {$_.attributes.key -eq $item.variable.key -and $_.attributes.category -ieq $item.Variable.category -and $_.attributes.sensitive -eq $item.variable.sensitive} |Select-Object -exp id)" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Patch' Body = $body ErrorAction = 'stop' } (Invoke-RestMethod @PatchRequest).data | out-null } catch { throw Exit 1 } } } catch { throw Exit 1 } } Write-verbose "Finished adding variables to TFE workspace." } Function Remove-TFEVariable { [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium', HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Remove-TFEVariable')] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the organization name.")][string]$Org, [Parameter(Mandatory = $true, HelpMessage = "Enter the workspace name.")][string]$WorkspaceName, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token, [Parameter(Mandatory = $false, HelpMessage = "list of names of the non-sensitive Terraform variables to be deleted")][string[]]$TFVariables, [Parameter(Mandatory = $false, HelpMessage = "list of names of the sensitive Terraform variables to be deleted")][string[]]$TFSecrets, [Parameter(Mandatory = $false, HelpMessage = "list of names of the non-sensitive Environment variables to be deleted")][string[]]$EnvVariables, [Parameter(Mandatory = $false, HelpMessage = "list of names of the sensitive Environment variables to be deleted")][string[]]$EnvSecrets ) Begin { Write-verbose "Getting Existing Variables in Workspace $WorkspaceName" try { $GetVariablesRequest = @{ Uri = "$TFEBaseURL/api/v2/vars?filter%5Borganization%5D%5Bname%5D=$Org&filter%5Bworkspace%5D%5Bname%5D=$WorkSpaceName" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'GET' ErrorAction = 'stop' UseBasicParsing = $true } $VariableResult = (Invoke-RestMethod @GetVariablesRequest).data $ExistingVariables = @() foreach ($item in $variableResult) { $ExistingVariables += New-Object psobject -Property @{"key" = $item.attributes.key; "sensitive" = [bool]$item.attributes.sensitive; "category" = $item.attributes.category} } } Catch { Throw Exit 1 } $SourceVariables = @() If ($PSBoundParameters.containskey('TFVariables')) { Foreach ($item in $TFVariables) { Write-verbose "Processing Terraform variable $item" $SourceVariables += New-Object psobject -Property @{"key" = $item; "sensitive" = $false; "category" = "terraform"} } } if ($PSBoundParameters.containskey('TFSecrets')) { Foreach ($item in $TFSecrets) { Write-verbose "Processing Terraform secret $item" $SourceVariables += New-Object psobject -Property @{"key" = $item; "sensitive" = $true; "category" = "terraform"} } } If ($PSBoundParameters.containskey('EnvVariables')) { Foreach ($item in $EnvVariables) { Write-verbose "Processing Environment variable $item" $SourceVariables += New-Object psobject -Property @{"key" = $item; "sensitive" = $false; "category" = "env"} } } if ($PSBoundParameters.containskey('EnvSecrets')) { Foreach ($item in $EnvSecrets) { Write-verbose "Processing Environment secret $item" $SourceVariables += New-Object psobject -Property @{"key" = $item; "sensitive" = $true; "category" = "env"} } } } Process { If ($SourceVariables.count -eq 0) { Throw "No Terraform or Environment variables & secrets have been passed into the function." Exit 1 } else { Write-verbose "The following variables have been passed into the function" foreach ($item in $SourceVariables) { Write-verbose "key = $($item.key); sensitive = '$($item.sensitive)'; category = '$($item.category)'" } } Write-verbose "Comparing Source Variables With Existing Variables" $VariableAction = @() Compare-Object $SourceVariables $ExistingVariables -Property key, sensitive, category -IncludeEqual | Where-Object {$_.key -ne "CONFIRM_DESTROY"} | ForEach-Object { if ($_.sideindicator -eq "==") { $VariableAction += New-Object psobject -Property @{"Variable" = $_; "Action" = "Remove"} } } If ($VariableAction.count -gt 0) { Write-verbose "Removing Variables" } else { Write-Verbose "No variables to remove" } if ($PSCmdlet.ShouldProcess($WorkspaceName)) { foreach ($item in $VariableAction) { write-verbose "Removing $($item.variable.key)" try { $DeleteRequest = @{ Uri = "$TFEBaseURL/api/v2/vars/$($VariableResult | Where-Object {$_.attributes.key -eq $item.variable.key -and $_.attributes.category -ieq $item.Variable.category -and $_.attributes.sensitive -eq $item.variable.sensitive} |Select-Object -exp id)" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Delete' ErrorAction = 'stop' UseBasicParsing = $true } (Invoke-RestMethod @DeleteRequest).data | out-null } catch { throw Exit 1 } } } } End { Write-verbose "Finished removing variables from TFE workspace." } } Function Get-TFEConfigVersion { [CmdletBinding(HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Get-TFEConfigVersion')] [OutputType([object])] Param( [Parameter(Mandatory = $true, HelpMessage = "Enter the base URL for Terraform Enterprise.")][string]$TFEBaseURL, [Parameter(Mandatory = $true, HelpMessage = "Enter the TFE configuration version Id.")][string]$ConfigVersionID, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token ) Write-verbose "Get Terraform Enterprise configuration version $ConfigVersionID" $requestParams = @{ Uri = "$TFEBaseURL/api/v2/configuration-versions/$ConfigVersionID" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Get' ErrorAction = 'stop' UseBasicParsing = $true } Write-Verbose "requesting new config via URI '$($requestParams.Uri)'" try { $ReturnedData = (Invoke-RestMethod @requestParams).data } catch { throw Exit 1 } $ReturnedData } Function Add-TFEContent { [CmdletBinding(HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Add-TFEContent')] [OutputType([bool])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the TFE configuration version Id.")][string]$ConfigVersionID, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token, [Parameter(Mandatory = $true, HelpMessage = "Enter path to the file or folder to be uploaded.")][ValidateScript({Test-Path $_})][System.IO.FileInfo]$ContentPath ) Write-Verbose "Get configuration version $ConfigVersionID" $configVersion = Get-TFEConfigVersion -TFEBaseURL $TFEBaseURL -ConfigVersionID $ConfigVersionID -Token $Token $uploadUrl = $configVersion.attributes.'upload-url' $tarballPath = Join-path $env:temp "$ConfigVersionID.tar.gz" #process content If (Test-Path $ContentPath -PathType Container) { Write-Verbose "Adding directory '$ContentPath' to '$tarballPath'" NewTarballFile -source $ContentPath -output $tarballPath } else { $UploadSource = get-item $ContentPath if ($UploadSource.name -imatch '.\.tar\.gz$') { Write-Verbose "'$ContentPath' is already a tarball. No need to create new tar.gz file" } else { Write-Verbose "Adding file '$ContentPath' to '$tarballPath'" NewTarballFile -source $ContentPath -output $tarballPath } } $requestParams = @{ Uri = $uploadUrl Method = 'Put' InFile = $tarballPath ContentType = "application/octet-stream" ErrorAction = 'Stop' UseBasicParsing = $true } Write-verbose "Adding content to configuration version '$ConfigVersionID'" try { $request = Invoke-WebRequest @requestParams } catch { Throw Exit 1 } #Delete tarball from temp directory #Remove-Item -path $tarballPath -Force | Out-Null if ($request.StatusCode -ge 200 -and $request.statusCode -le 299) { Write-Verbose "Content uploaded successfully" $true } else { $false } } Function Get-TFERunDetails { [CmdletBinding(HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Get-TFERunDetails')] [OutputType([Object])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the TFE Run Id.")][string]$RunID, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token, [Parameter(Mandatory=$false, HelpMessage = "Wait for the TFE Run to complete.")][Switch]$WaitForCompletion, [Parameter(Mandatory=$false, HelpMessage = "When waiting for TFE Run to complete, exit when the status is Planned.")][Switch]$StopAtPlanned ) $GetRequest = @{ Uri = "$TFEBaseURL/api/v2/runs/$RunID" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Get' ErrorAction = 'stop' UseBasicParsing = $true } $StatesToWaitFor = @("applying", 'apply_queued', "canceled", "confirmed", "pending", "planning", "policy_checked", "policy_checking", "policy_override", "plan_queued") If (!$StopAtPlanned) { $StatesToWaitFor += 'planned' } Write-verbose "Getting run details for Id '$RunID'" try { if ($WaitForCompletion) { $bFirstRequest = $true do { if (!$bFirstRequest) { Start-Sleep 10 } $Result = Invoke-RestMethod @GetRequest $bFirstRequest = $false $Status = $Result.data.attributes.status Write-Verbose "Terraform Workspace Run '$RunID' in '$Status' state" } while ($Status -in $StatesToWaitFor) } else { $Result = Invoke-RestMethod @GetRequest $Status = $Result.data.attributes.status } } catch { Throw Exit 1 } $Result.data } Function Get-TFEPlan { [CmdletBinding(HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Get-TFEPlan')] [OutputType([Object])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the TFE Run Id.")][string]$RunID, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token ) $GetRunRequest = @{ Uri = "$TFEBaseURL/api/v2/runs/$RunID" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Get' ErrorAction = 'stop' UseBasicParsing = $true } try { Write-verbose "Getting run status for Id '$RunID'" $RunResponse = Invoke-RestMethod @GetRunRequest $PlanId = $RunResponse.data.relationships.plan.data.id Write-verbose "Plan Id for the run is $PlanId" $GetPlanRequest = @{ Uri = "$TFEBaseURL/api/v2/plans/$PlanId" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Get' ErrorAction = 'stop' UseBasicParsing = $true } $PlanResponse = (Invoke-RestMethod @GetPlanRequest).data } catch { Throw Exit 1 } $PlanResponse } Function Get-TFEPlanLog { [CmdletBinding(HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Get-TFEPlanLog')] [OutputType([string])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the TFE Plan Id.")][string]$PlanId, [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token ) $GetPlanRequest = @{ Uri = "$TFEBaseURL/api/v2/plans/$PlanId" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Get' ErrorAction = 'stop' UseBasicParsing = $true } try { Write-verbose "Getting details ofr Plan Id $PlanId" $PlanResponse = (Invoke-RestMethod @GetPlanRequest).data $LogReadURI = $PlanResponse.attributes.'log-read-url' $LogRequest = @{ Uri = $LogReadURI Method = 'Get' ErrorAction = 'stop' UseBasicParsing = $true } $LogContent = Invoke-RestMethod @LogRequest } catch { Throw Exit 1 } $LogContent } Function New-TFERun { [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low', HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/New-TFERun')] [OutputType([object])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the organization name.")][string]$Org, [Parameter(Mandatory = $true, HelpMessage = "Enter the workspace name.")][string]$WorkspaceName, [Parameter(Mandatory = $true, HelpMessage = "Enter the TFE configuration version Id.")][string]$ConfigVersionID, [Parameter(Mandatory = $false, HelpMessage = "Enter the comment for the queue plan.")][string]$comment = "Run Requested", [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token ) Begin { Write-Verbose "Get TFE workspace" $workspace = Get-TFEWorkspace -TFEBaseURL $TFEBaseURL -Org $Org -WorkspaceName $WorkspaceName -Token $Token $workspaceId = $workspace.Id $body = @{ "data" = @{ "attributes" = @{ "is-destroy" = $false "message" = $Comment "target-addrs" = @() } "type" = "runs" "relationships" = @{ "workspace" = @{ "data" = @{ "type" = "workspaces" "id" = $workspaceId } } "configuration-version" = @{ "data" = @{ "type" = "configuration-versions" "id" = $ConfigVersionID } } } } } | ConvertTo-Json -Depth 5 write-verbose "requesty body $body" $PostRequest = @{ Uri = "$TFEBaseURL/api/v2/runs" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Post' Body = $body ErrorAction = 'stop' UseBasicParsing = $true } } Process { try { Write-verbose "Creating new queue plan for TFE" if ($PSCmdlet.ShouldProcess($WorkspaceName)) { $Result = (Invoke-RestMethod @PostRequest).data } } catch { THrow Exit 1 } } End { $Result } } Function Approve-TFERun { [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High', HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Approve-TFERun')] [OutputType([boolean])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the Run ID.")][string]$RunID, [Parameter(Mandatory = $false, HelpMessage = "Enter the comment for the queue plan.")][string]$comment = "Appy Run via REST API", [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token ) Begin { $body = @{ "comment" = $Comment } | ConvertTo-Json -Depth 5 write-verbose "requesty body $body" $PostRequest = @{ Uri = "$TFEBaseURL/api/v2/runs/$RunID/actions/apply" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Post' Body = $body ErrorAction = 'stop' UseBasicParsing = $true } } Process { try { Write-verbose "Apply Run Id $RunID" if ($PSCmdlet.ShouldProcess($WorkspaceName)) { Invoke-RestMethod @PostRequest } } catch { THrow Exit 1 } } End { $true } } Function Stop-TFERun { [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High', HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/Stop-TFERun')] [OutputType([boolean])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the Run ID.")][string]$RunID, [Parameter(Mandatory = $false, HelpMessage = "Enter the comment for the queue plan.")][string]$comment = "Discard Run via REST API", [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token ) Begin { $body = @{ "comment" = $Comment } | ConvertTo-Json -Depth 5 write-verbose "requesty body $body" $PostRequest = @{ Uri = "$TFEBaseURL/api/v2/runs/$RunID/actions/discard" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Post' Body = $body ErrorAction = 'stop' UseBasicParsing = $true } } Process { try { Write-verbose "Disgard Run Id $RunID" if ($PSCmdlet.ShouldProcess($WorkspaceName)) { Invoke-RestMethod @PostRequest } } catch { THrow Exit 1 } } End { $true } } Function New-TFEDestroyPlan { [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High', HelpUri='https://github.com/tyconsulting/TerraformEnterprise-PS/wiki/New-TFEDestroyPlan')] [OutputType([object])] Param( [Parameter(Mandatory = $false, HelpMessage = "Enter the base URL for Terraform Enterprise. If not specified, the Terraform Cloud URL will be used.")][string]$TFEBaseURL = "https://app.terraform.io", [Parameter(Mandatory = $true, HelpMessage = "Enter the organization name.")][string]$Org, [Parameter(Mandatory = $true, HelpMessage = "Enter the workspace name.")][string]$WorkspaceName, [Parameter(Mandatory = $false, HelpMessage = "Enter the comment for the queue plan.")][string]$comment = "Destroy Plan Requested", [Parameter(Mandatory = $true, HelpMessage = "Enter the API token as a Secure String.")][securestring]$Token ) Begin { Write-Verbose "Get TFE workspace" $workspace = Get-TFEWorkspace -TFEBaseURL $TFEBaseURL -Org $Org -WorkspaceName $WorkspaceName -Token $Token $workspaceId = $workspace.Id $body = @{ "data" = @{ "attributes" = @{ "is-destroy" = $true "message" = $Comment } "type" = "runs" "relationships" = @{ "workspace" = @{ "data" = @{ "type" = "workspaces" "id" = $workspaceId } } } } } | ConvertTo-Json -Depth 5 write-verbose "requesty body $body" $PostRequest = @{ Uri = "$TFEBaseURL/api/v2/runs" Headers = @{"Authorization" = "Bearer $(DecodeToken -Token $Token)" } ContentType = 'application/vnd.api+json' Method = 'Post' Body = $body ErrorAction = 'stop' UseBasicParsing = $true } } Process { try { Write-verbose "Creating new destroy plan for TFE" if ($PSCmdlet.ShouldProcess($WorkspaceName)) { $Result = (Invoke-RestMethod @PostRequest).data } } catch { THrow Exit 1 } } End { $Result } } #Set TLS version [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 #Make sure the tar command is part of the operating system Try { get-command tar -ErrorAction SilentlyContinue -ErrorVariable GetTarError | Out-Null If ($GetTarError) { throw "Unable to locate tar command. Unable to continue." } } catch { throw Exit 1 } |