Created
February 17, 2019 17:41
-
-
Save adamrushuk/878b783a6fbe56b558683857bef9ad5b to your computer and use it in GitHub Desktop.
Configures Azure for secure Terraform access. Loads Azure Key Vault secrets into Terraform environment variables for the current PowerShell session.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Configures Azure for secure Terraform access. | |
.DESCRIPTION | |
Configures Azure for secure Terraform access using Azure Key Vault. | |
The following steps are automated: | |
- Creates an Azure Service Principle for Terraform. | |
- Creates a new Resource Group. | |
- Creates a new Storage Account. | |
- Creates a new Storage Container. | |
- Creates a new Key Vault. | |
- Configures Key Vault Access Policies. | |
- Creates Key Vault Secrets for these sensitive Terraform login details: | |
- ARM_SUBSCRIPTION_ID | |
- ARM_CLIENT_ID | |
- ARM_CLIENT_SECRET | |
- ARM_TENANT_ID | |
- ARM_ACCESS_KEY | |
.NOTES | |
Assumptions: | |
- Azure PowerShell module is installed: https://docs.microsoft.com/en-us/powershell/azure/install-az-ps | |
- Azure CLI is installed: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-windows | |
- You are already logged into Azure before running this script (eg. Connect-AzAccount) | |
Author: Adam Rush | |
Blog: https://adamrushuk.github.io | |
GitHub: https://github.com/adamrushuk | |
Twitter: @adamrushuk | |
#> | |
[CmdletBinding()] | |
param ( | |
# This is used to assign yourself access to KeyVault | |
$adminUserDisplayName = '<Joe Bloggs>', | |
$servicePrincipleName = 'terraform', | |
$servicePrinciplePassword = 'MyStrongPassw0rd!', | |
$resourceGroupName = 'terraform-mgmt-rg', | |
$location = 'eastus', | |
$storageAccountSku = 'Standard_LRS', | |
$storageContainerName = 'terraform-state', | |
# Prepend random prefix with A character, as some resources cannot start with a number | |
$randomPrefix = ("a" + -join ((48..57) + (97..122) | Get-Random -Count 8 | ForEach-Object {[char]$_})), | |
$vaultName = "$randomPrefix-terraform-kv", | |
$storageAccountName = "$($randomPrefix)terraform" | |
) | |
#region Helper function for padded messages | |
function Write-HostPadded { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory = $true)] | |
[String] | |
$Message, | |
[Parameter(Mandatory = $false)] | |
[String] | |
$ForegroundColor, | |
[Parameter(Mandatory = $false)] | |
[Int] | |
$PadLength = 80, | |
[Parameter(Mandatory = $false)] | |
[Switch] | |
$NoNewline | |
) | |
$writeHostParams = @{ | |
Object = $Message.PadRight($PadLength, '.') | |
} | |
if ($ForegroundColor) { | |
$writeHostParams.Add('ForegroundColor', $ForegroundColor) | |
} | |
if ($NoNewline.IsPresent) { | |
$writeHostParams.Add('NoNewline', $true) | |
} | |
Write-Host @writeHostParams | |
} | |
#endregion Helper function for padded messages | |
#region Check Azure login | |
Write-HostPadded -Message "Checking for an active Azure login..." -NoNewline | |
# Get current context | |
$azContext = Get-AzContext | |
if (-not $azContext) { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw "There is no active login for Azure. Please login first (eg 'Connect-AzAccount' and 'az login'" | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion Check Azure login | |
#region New Terraform SP (Service Principal) | |
Write-HostPadded -Message "Using Azure CLI to Create a Terraform Service Principle: [$servicePrincipleName] ..." -NoNewline | |
try { | |
az ad sp create-for-rbac --name $servicePrincipleName --password $servicePrinciplePassword | Out-String | Write-Verbose | |
$terraformSP = Get-AzADServicePrincipal -DisplayName $servicePrincipleName -ErrorAction 'Stop' | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion New Terraform SP (Service Principal) | |
#region Get Subscription | |
$taskMessage = "Finding Subscription and Tenant details" | |
Write-HostPadded -Message "`n$taskMessage..." -NoNewline | |
try { | |
$subscription = Get-AzSubscription -ErrorAction 'Stop' | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion Get Subscription | |
#region New Resource Group | |
$taskMessage = "Creating Terraform Management Resource Group: [$resourceGroupName]" | |
Write-HostPadded -Message "`n$taskMessage..." -NoNewline | |
try { | |
$azResourceGroupParams = @{ | |
Name = $resourceGroupName | |
Location = $location | |
ErrorAction = 'Stop' | |
Verbose = $VerbosePreference | |
} | |
New-AzResourceGroup @azResourceGroupParams | Out-String | Write-Verbose | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion New Resource Group | |
#region New Storage Account | |
$taskMessage = "Creating Terraform backend Storage Account: [$storageAccountName]" | |
Write-HostPadded -Message "`n$taskMessage..." -NoNewline | |
try { | |
$azStorageAccountParams = @{ | |
ResourceGroupName = $resourceGroupName | |
Location = $location | |
Name = $storageAccountName | |
SkuName = $storageAccountSku | |
Kind = 'StorageV2' | |
ErrorAction = 'Stop' | |
Verbose = $VerbosePreference | |
} | |
New-AzStorageAccount @azStorageAccountParams | Out-String | Write-Verbose | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion New Storage Account | |
#region Select Storage Container | |
$taskMessage = "Selecting Default Storage Account" | |
Write-HostPadded -Message "`n$taskMessage..." -NoNewline | |
try { | |
$azCurrentStorageAccountParams = @{ | |
ResourceGroupName = $resourceGroupName | |
AccountName = $storageAccountName | |
ErrorAction = 'Stop' | |
Verbose = $VerbosePreference | |
} | |
Set-AzCurrentStorageAccount @azCurrentStorageAccountParams | Out-String | Write-Verbose | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion Select Storage Account | |
#region New Storage Container | |
$taskMessage = "Creating Terraform State Storage Container: [$storageContainerName]" | |
Write-HostPadded -Message "`n$taskMessage..." -NoNewline | |
try { | |
$azStorageContainerParams = @{ | |
Name = $storageContainerName | |
Permission = 'Off' | |
ErrorAction = 'Stop' | |
Verbose = $VerbosePreference | |
} | |
New-AzStorageContainer @azStorageContainerParams | Out-String | Write-Verbose | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion New Storage Container | |
#region New KeyVault | |
$taskMessage = "Creating Terraform KeyVault: [$vaultName]" | |
Write-HostPadded -Message "`n$taskMessage..." -NoNewline | |
try { | |
$azKeyVaultParams = @{ | |
VaultName = $vaultName | |
ResourceGroupName = $resourceGroupName | |
Location = $location | |
ErrorAction = 'Stop' | |
Verbose = $VerbosePreference | |
} | |
New-AzKeyVault @azKeyVaultParams | Out-String | Write-Verbose | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion New KeyVault | |
#region Set KeyVault Access Policy | |
$taskMessage = "Setting KeyVault Access Policy for Admin User: [$adminUserDisplayName]" | |
Write-HostPadded -Message "`n$taskMessage..." -NoNewline | |
$adminADUser = Get-AzADUser -DisplayName $adminUserDisplayName | |
try { | |
$azKeyVaultAccessPolicyParams = @{ | |
VaultName = $vaultName | |
ResourceGroupName = $resourceGroupName | |
ObjectId = $adminADUser.Id | |
PermissionsToKeys = @('Get', 'List') | |
PermissionsToSecrets = @('Get', 'List', 'Set') | |
PermissionsToCertificates = @('Get', 'List') | |
ErrorAction = 'Stop' | |
Verbose = $VerbosePreference | |
} | |
Set-AzKeyVaultAccessPolicy @azKeyVaultAccessPolicyParams | Out-String | Write-Verbose | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
$taskMessage = "Setting KeyVault Access Policy for Terraform SP: [$servicePrincipleName]" | |
Write-HostPadded -Message "`n$taskMessage..." -NoNewline | |
try { | |
$azKeyVaultAccessPolicyParams = @{ | |
VaultName = $vaultName | |
ResourceGroupName = $resourceGroupName | |
ObjectId = $terraformSP.Id | |
PermissionsToKeys = @('Get', 'List') | |
PermissionsToSecrets = @('Get', 'List', 'Set') | |
PermissionsToCertificates = @('Get', 'List') | |
ErrorAction = 'Stop' | |
Verbose = $VerbosePreference | |
} | |
Set-AzKeyVaultAccessPolicy @azKeyVaultAccessPolicyParams | Out-String | Write-Verbose | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion Set KeyVault Access Policy | |
#region Terraform login variables | |
# Get Storage Access Key | |
$storageAccessKeys = Get-AzStorageAccountKey -ResourceGroupName $resourceGroupName -Name $storageAccountName | |
$storageAccessKey = $storageAccessKeys[0].Value # only need one of the keys | |
$terraformLoginVars = @{ | |
'ARM-SUBSCRIPTION-ID' = $subscription.Id | |
'ARM-CLIENT-ID' = $terraformSP.ApplicationId | |
'ARM-CLIENT-SECRET' = $servicePrinciplePassword | |
'ARM-TENANT-ID' = $subscription.TenantId | |
'ARM-ACCESS-KEY' = $storageAccessKey | |
} | |
Write-Host "`nTerraform login details:" | |
$terraformLoginVars | Out-String | Write-Verbose | |
#endregion Terraform login variables | |
#region Create KeyVault Secrets | |
$taskMessage = "Creating KeyVault Secrets for Terraform" | |
Write-HostPadded -Message "`n$taskMessage..." -NoNewline | |
try { | |
foreach ($terraformLoginVar in $terraformLoginVars.GetEnumerator()) { | |
$AzKeyVaultSecretParams = @{ | |
VaultName = $vaultName | |
Name = $terraformLoginVar.Key | |
SecretValue = (ConvertTo-SecureString -String $terraformLoginVar.Value -AsPlainText -Force) | |
ErrorAction = 'Stop' | |
Verbose = $VerbosePreference | |
} | |
Set-AzKeyVaultSecret @AzKeyVaultSecretParams | Out-String | Write-Verbose | |
} | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion Create KeyVault Secrets |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Loads Azure Key Vault secrets into Terraform environment variables for the current PowerShell session. | |
.DESCRIPTION | |
Loads Azure Key Vault secrets into Terraform environment variables for the current PowerShell session. | |
The following steps are automated: | |
- Identifies the Azure Key Vault matching a search string (default: 'terraform-kv'). | |
- Retrieves the Terraform secrets from Azure Key Vault. | |
- Loads the Terraform secrets into these environment variables for the current PowerShell session: | |
- ARM_SUBSCRIPTION_ID | |
- ARM_CLIENT_ID | |
- ARM_CLIENT_SECRET | |
- ARM_TENANT_ID | |
- ARM_ACCESS_KEY | |
.NOTES | |
Assumptions: | |
- Azure PowerShell module is installed: https://docs.microsoft.com/en-us/powershell/azure/install-az-ps | |
- You are already logged into Azure before running this script (eg. Connect-AzAccount) | |
Author: Adam Rush | |
Blog: https://adamrushuk.github.io | |
GitHub: https://github.com/adamrushuk | |
Twitter: @adamrushuk | |
#> | |
[CmdletBinding()] | |
param ( | |
# Find the Azure Key Vault that includes this string in it's name | |
$keyVaultSearchString = 'terraform-kv' | |
) | |
#region Helper function for padded messages | |
function Write-HostPadded { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory = $true)] | |
[String] | |
$Message, | |
[Parameter(Mandatory = $false)] | |
[String] | |
$ForegroundColor, | |
[Parameter(Mandatory = $false)] | |
[Int] | |
$PadLength = 60, | |
[Parameter(Mandatory = $false)] | |
[Switch] | |
$NoNewline | |
) | |
$writeHostParams = @{ | |
Object = $Message.PadRight($PadLength, '.') | |
} | |
if ($ForegroundColor) { | |
$writeHostParams.Add('ForegroundColor', $ForegroundColor) | |
} | |
if ($NoNewline.IsPresent) { | |
$writeHostParams.Add('NoNewline', $true) | |
} | |
Write-Host @writeHostParams | |
} | |
#endregion Helper function for padded messages | |
#region Check Azure login | |
Write-HostPadded -Message "Checking for an active Azure login..." -NoNewline | |
# Get current context | |
$azContext = Get-AzContext | |
if (-not $azContext) { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw "There is no active login for Azure. Please login first (eg 'Connect-AzAccount' and 'az login'" | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion Check Azure login | |
#region Identify Azure Key Vault | |
$loadMessage = "loading Terraform environment variables just for this PowerShell session" | |
Write-Host "`nSTARTED: $loadMessage" -ForegroundColor 'Green' | |
# Get Azure objects before Key Vault lookup | |
Write-HostPadded -Message "Searching for Terraform KeyVault..." -NoNewline | |
$tfKeyVault = Get-AzKeyVault | Where-Object VaultName -match $keyVaultSearchString | |
if (-not $tfKeyVault) { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw "Could not find Azure Key Vault with name including search string: [$keyVaultSearchString]" | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion Identify Azure Key Vault | |
#region Get Azure KeyVault Secrets | |
Write-HostPadded -Message "Retrieving Terraform secrets from Azure Key Vault..." -NoNewline | |
$secretNames = @( | |
'ARM_SUBSCRIPTION_ID' | |
'ARM_CLIENT_ID' | |
'ARM_CLIENT_SECRET' | |
'ARM_TENANT_ID' | |
'ARM_ACCESS_KEY' | |
) | |
$terraformEnvVars = @{} | |
# Compile Get Azure KeyVault Secrets | |
foreach ($secretName in $secretNames) { | |
try { | |
# Retrieve secret | |
$azKeyVaultSecretParams = @{ | |
Name = $secretName -replace '_', '-' | |
VaultName = $tfKeyVault.VaultName | |
ErrorAction = 'Stop' | |
} | |
$tfSecret = Get-AzKeyVaultSecret @azKeyVaultSecretParams | |
# Add secret to hashtable | |
$terraformEnvVars.$secretName = $tfSecret.SecretValueText | |
} catch { | |
Write-Error -Message "ERROR: $taskMessage." -ErrorAction 'Continue' | |
throw $_ | |
} | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
#endregion Get Azure KeyVault Secrets | |
#region Load Terraform environment variables | |
$sessionMessage = "Setting session environment variables for Azure / Terraform" | |
Write-Host "`nSTARTED: $sessionMessage" -ForegroundColor 'Green' | |
foreach ($terraformEnvVar in $terraformEnvVars.GetEnumerator()) { | |
Write-HostPadded -Message "Setting [$($terraformEnvVar.Key)]..." -NoNewline | |
try { | |
$setItemParams = @{ | |
Path = "env:$($terraformEnvVar.Key)" | |
Value = $terraformEnvVar.Value | |
ErrorAction = 'Stop' | |
} | |
Set-Item @setItemParams | |
} catch { | |
Write-Host "ERROR!" -ForegroundColor 'Red' | |
throw $_ | |
} | |
Write-Host "SUCCESS!" -ForegroundColor 'Green' | |
} | |
Write-Host "FINISHED: $sessionMessage" -ForegroundColor 'Green' | |
Write-Host "`nFINISHED: $loadMessage" -ForegroundColor 'Green' | |
#endregion Load Terraform environment variables |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment