Skip to content

Instantly share code, notes, and snippets.

@Geogboe
Created October 27, 2018 19:17
Show Gist options
  • Save Geogboe/8969f7577f5c558014e222933d563950 to your computer and use it in GitHub Desktop.
Save Geogboe/8969f7577f5c558014e222933d563950 to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Installs a given terraform plugin/provider from github
.EXAMPLE
PS C:\> ./Install-TerraformPlugin -GitHubPath Mastercard/terraform-provider-restapi
Install-TerraformPlugin will search github the repo: Mastercard/terraform-provider-restapi
foo and check if the latest version has been added to the local terraform
plugins dir. If not, it will downlaod and install it.
.NOTES
- Only supports Windows at the moment.
- Only tested on Windows 10 1809 so far.
- License: MIT
- Copywrite 2018-Present - George Bowen
#>
[CmdletBinding( PositionalBinding )]
param (
# name of the plugin in as a github path such as Mastercard/terraform-provider-restapi
# also excepts and array for plugins
[Parameter( Mandatory )]
[ValidatePattern( "^\w+\/\.?" )]
[string[]]
$GitHubPath,
# Directory where terraform will store plugins
[Parameter()]
[ValidateScript( { Test-Path $_ })]
[string]
$PluginDir = "$env:APPDATA/terraform.d/plugins/windows_amd64",
# The extenion that the plugin will have. .exe for Windows
[Parameter( DontShow )]
[string]
$PluginExt = ".exe"
)
begin {
Set-StrictMode -Version 2.0
$ErrorActionPreference = "Stop"
DATA LogMessages {
ConvertFrom-StringData @'
#culture="en-US"
FailedCreatePluginDir = Failed to verify or create plugin dir at path: {0}
ResolvingPath = Resolving plugin path for: {0}...
EnsureDirExists = Ensuring plugin directory exists at path: {0}...
DownloadingPlugin = Downloading plugin from source: {0} to dest: {1}...
'@
}
# Create the plugin dir if missing
function Assert-PluginDirExists ( $PluginDir ) {
try {
New-Directory $PluginDir
}
catch {
Write-Warning ( $LogMessages.FailedCreatePluginDir -f $PluginDir )
throw $_
}
}
# Create a dir if missing
function New-Directory ( $Path ) {
if ( Test-DirectoryExists $Path ) {
return $null
}
New-Item $Path -ItemType Directory -Force |
Out-Null
}
# Test if a given directory exiss
function Test-DirectoryExists ( $Path ) {
Test-Path $Path
}
# Gets the path that a web request is attempting to be redirected too
function Get-RedirectedUrl ( $Url ) {
$WebReqArgs = @{
Method = "HEAD"
UseBasicParsing = $true
Uri = $Url
MaximumRedirection = 0
Verbose = $false
ErrorAction = "Stop" # Will throw an error if attempted to be redirected
OutVariable = "Response"
}
try {
Invoke-WebRequest @WebReqArgs | Out-Null
}
catch {
if ( $Response.StatusCode -eq 302 ) {
# return the location that the redirect was attempted
return $Response.Headers.location
}
else {
throw $_
}
}
}
# resolves a plugin path into the github maintainer, repo name,
# url to latest, binaries
function Resolve-PluginPath ( $PluginPath ) {
$Plugin = [PSCustomObject]@{
Maintainer = $null
ProjName = $null
Binaries = @()
}
$Plugin.Maintainer, $Plugin.ProjName = $PluginPath -split "\/"
$Plugin.Binaries = Get-PluginBinaries $Plugin.Maintainer $Plugin.ProjName
return $Plugin
}
# Get the download URL for terraform plugin from github release page
function Get-PluginBinaries ( $Maintainer, $ProjectName ) {
$GithubUrl = "https://github.com"
$SearchUrl = (
$GithubUrl + "/" +
$Maintainer + "/" +
$ProjectName + "/releases/latest"
)
$LatestReleasesUrl = Get-RedirectedUrl $SearchUrl
$Bins = Get-GithubReleaseBins $LatestReleasesUrl | Where-Object {
$_ -match $Maintainer -AND
$_ -match $ProjectName -AND
$_ -match "\/releases\/download\/.*.\/terraform-provider-.*.-windows-amd64"
}
# prefix gituhub domain to urls
$Bins | ForEach-Object {
$GithubUrl + $_
}
}
# return a list of binaries from a github releases page
function Get-GithubReleaseBins ( $Url ) {
$WebReqArgs = @{
Method = "GET"
Uri = $Url
Verbose = $false
TimeoutSec = 5
UseBasicParsing = $true
}
$Response = Invoke-WebRequest @WebReqArgs
# Using the css class fine all links that contain bins
$Releases = $Response.Links | Where-Object {
( $_ | Get-Member -Name class ) -AND
$_.class -match "d-flex flex-items-center"
}
$Releases.href
}
function Invoke-PluginDownload ( $Url, $Dest ) {
if ( -not ( Test-Path $Dest )) {
# Invoke web request is slow, using webclient class
[System.Net.WebClient]::new().DownloadFile( $Url, $Dest)
}
}
}
process {
Write-Verbose ( $LogMessages.EnsureDirExists -f $PluginDir )
Assert-PluginDirExists $PluginDir
foreach ( $Plugin in $GitHubPath ) {
Write-Host ( $LogMessages.ResolvingPath -f $Plugin )
$Plugin = Resolve-PluginPath $Plugin
foreach ( $Bin in $Plugin.Binaries ) {
$BinName = Split-Path $Bin -Leaf
$BinName = $BinName -replace '-windows-amd64'
$BinName = $BinName + $PluginExt
$DestinationPath = Join-Path $PluginDir $BinName
Write-Host ( $LogMessages.DownloadingPlugin -f $Bin, $DestinationPath)
Invoke-PluginDownload $Bin $DestinationPath
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment