Last active
July 28, 2021 08:25
-
-
Save brentmcconnell/d1bb14d31ab69578c5d9ef816015ddda to your computer and use it in GitHub Desktop.
Setup RG and SP for a project and optionally create a provider.tf file for use by Terraform
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
#!/bin/bash | |
# This script bootstraps a resource group in Azure with a Service Principal | |
# that has Owner on the resource group. It will also create a Storage Account | |
# and Keyvault in the resource group and load the SP values into the Keyvault. | |
# You can optionally create a provider.tf file that references a tfstate | |
# container for use with Terraform. | |
# This can be used without any parameters and names will be generated or you | |
# can optionally pass in a (-g) resource group, (-r) region. | |
# You can also pass environment variables for configuration. The following are | |
# accepted variables: | |
# RESOURCE_GROUP : Name of resource group to create | |
# REGION : Region resource will be created in | |
# PROFILER_FILE : Name of file to create for Terraform | |
# PREFIX : PREFIX to use with Azure resources for uniqueness | |
# STORAGE_ACCT_NAME : Name of storage account (must be unique across Azure) | |
# CONTAINER_NAME : Name of container in storage acct used for Terraform state | |
# KEYVAULT_NAME : Name of KeyVault in Azure (must be unique across Azure) | |
# SP_NAME : Name of Service Principal to create | |
set -o errexit # exit if any statement returns a non-true return value | |
shopt -s expand_aliases | |
# 3 digit random number | |
if [ -z $PREFIX ]; then | |
RND=$(echo $RANDOM | grep -o ....$) | |
else | |
RND=$PREFIX | |
fi | |
URND=$(echo $RND | tr "[:lower:]" "[:upper:]") | |
LRND=$(echo $RND | tr "[:upper:]" "[:lower:]") | |
echo "Check program requirements..." | |
( | |
set +e | |
programs=(az jq) | |
missing=0 | |
for i in ${programs[@]}; do | |
command -v $i 2&> /dev/null | |
if [ $? -eq 0 ]; then | |
echo " * Found $i" | |
else | |
echo " * ERROR: missing $i" | |
missing=1 | |
fi | |
done | |
if [[ "$missing" -ne 0 ]]; then | |
echo "Missing required commands" | |
exit 1 | |
fi | |
) | |
usage() { | |
echo "`basename $0`" | |
echo " Usage: " | |
echo " [-g <project resource group> project resource group to create" | |
echo " [-r <region>] region to use. EastUs or USGovVirginia are defaults" | |
exit 1 | |
} | |
# Catch any help requests | |
for arg in "$@"; do | |
case "$arg" in | |
--help| -h) | |
usage | |
;; | |
esac | |
done | |
while getopts f:r:g: option | |
do | |
case "${option}" | |
in | |
g) RESOURCE_GROUP=${OPTARG};; | |
r) REGION=${OPTARG};; | |
f) PROVIDER_FILE=${OPTARG};; | |
*) usage;; | |
: ) usage;; | |
esac | |
done | |
shift "$(($OPTIND -1))" | |
# Check if Region is passed in, otherwise eastus will be used | |
if [ -z "$REGION" ]; then | |
# check if in gov or commercial | |
CLOUD=`az account list-locations -o json | jq -r '.[0].name'` | |
if [ ${CLOUD:0:5} = "usgov" ]; then | |
REGION='usgovvirginia' | |
else | |
REGION='eastus' | |
fi | |
fi | |
if [ -z "$RESOURCE_GROUP" ]; then | |
RESOURCE_GROUP=PROJECT-$URND-RG | |
fi | |
if [ -z "$STORAGE_ACCT_NAME" ]; then | |
STORAGE_ACCT_NAME=terraformstate${LRND}sa | |
fi | |
if [ -z "$CONTAINER_NAME" ]; then | |
CONTAINER_NAME=terraformstate | |
fi | |
if [ -z "$KEYVAULT_NAME" ]; then | |
KEYVAULT_NAME=project-secrets-$LRND-kv | |
fi | |
if [ -z "$SP_NAME" ]; then | |
SP_NAME=project-$LRND-sp | |
fi | |
echo "The following resources will be created...\n" | |
echo "RESOURCE_GROUP: $RESOURCE_GROUP" | |
echo "STORAGE_ACCT_NAME: $STORAGE_ACCT_NAME" | |
echo "CONTAINER_NAME: $CONTAINER_NAME" | |
echo "KEYVAULT_NAME: $KEYVAULT_NAME" | |
echo "SP: $SP_NAME" | |
echo "REGION: $REGION" | |
echo "PROVIDER_FILE: $PROVIDER_FILE" | |
echo -e "NOTE: Service Principal with Owner permissions on $RESOURCE_GROUP stored in $KEYVAULT_NAME\n" | |
read -p "Are you sure you want to Proceed [y/N]?" | |
if ! [[ "$REPLY" =~ ^[Yy]$ ]]; then | |
echo "Maybe next time!" | |
exit 1 | |
fi | |
alias echo="echo -e" | |
# Create resource group | |
RG_ID=$(az group create --name $RESOURCE_GROUP --location $REGION --query id -o tsv) | |
# Create storage account | |
az storage account create --resource-group $RESOURCE_GROUP --name $STORAGE_ACCT_NAME --sku Standard_LRS --encryption-services blob 1>/dev/null | |
# Get storage account key | |
ACCOUNT_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP --account-name $STORAGE_ACCT_NAME --query [0].value -o tsv) | |
# Create blob container | |
az storage container create --name $CONTAINER_NAME --account-name $STORAGE_ACCT_NAME --account-key $ACCOUNT_KEY 1>/dev/null | |
echo "storage_account_name: $STORAGE_ACCT_NAME" | |
echo "container_name: $CONTAINER_NAME" | |
# Create Keyvault if it doesn't exist. This keyvault will hold service principal info | |
# for our projects. | |
( | |
set +e | |
KEYVAULT=$(az keyvault show -n $KEYVAULT_NAME -g $RESOURCE_GROUP -o json) | |
if [ $? -ne 0 ]; then | |
KEYVAULT=$(az keyvault create -n $KEYVAULT_NAME -g $RESOURCE_GROUP -o json) | |
fi | |
) | |
############################### | |
## Create Project Resources | |
############################### | |
# Create Service Principal | |
echo "Create or retrieve Service Principal" | |
SP_TOKEN=$(az ad sp create-for-rbac --role Owner -n $SP_NAME --scopes $RG_ID -o json) | |
# Grab info from the Service Principal | |
SP_APPID=$(echo $SP_TOKEN | jq -e -r 'select(.appId != null) | .appId') | |
SP_TENANTID=$(echo $SP_TOKEN | jq -e -r 'select(.tenant != null) | .tenant') | |
SP_PASSWORD=$(echo $SP_TOKEN | jq -e -r 'select(.password != null) | .password') | |
SP_OBJID=$(az ad sp show --id $SP_APPID | jq -e -r 'select(.objectId != null) | .objectId') | |
SUBSCRIPTION_ID=$(az account show --query id -o tsv) | |
# Wait for Progagation via loop | |
echo "Sleeping in loop for 5 seconds to propagate SP" | |
until az ad sp show --id $SP_APPID &> /dev/null ; do echo "Waiting for Azure AD propagation" && sleep 5; done | |
# Add SP info and access key to keyvault | |
az keyvault secret set --vault-name $KEYVAULT_NAME --name "SA-ACCESS-KEY" --value "$ACCOUNT_KEY" | |
az keyvault secret set --vault-name $KEYVAULT_NAME --name "SP-CLIENTID" --value "$SP_APPID" | |
az keyvault secret set --vault-name $KEYVAULT_NAME --name "SP-SUBSCRIPTIONID" --value "$SUBSCRIPTION_ID" | |
az keyvault secret set --vault-name $KEYVAULT_NAME --name "SP-TENANTID" --value "$SP_TENANTID" | |
az keyvault secret set --vault-name $KEYVAULT_NAME --name "SP-PASSWORD" --value "$SP_PASSWORD" | |
# Allow SP to manage Keyvault | |
az keyvault set-policy --name $KEYVAULT_NAME --object-id $SP_OBJID \ | |
--certificate-permissions backup create delete deleteissuers get getissuers import list listissuers managecontacts manageissuers purge recover restore setissuers update \ | |
--key-permissions backup create decrypt delete encrypt get import list purge recover restore sign unwrapKey update verify wrapKey \ | |
--secret-permissions backup delete get list purge recover restore set \ | |
--storage-permissions backup delete deletesas get getsas list listsas purge recover regeneratekey restore set setsas update | |
if ! [ -z $PROVIDER_FILE ]; then | |
nl='\n'; tab='\t' | |
# Get the latest release of azurerm provider | |
AZURE_RM_LATEST=$(curl --silent "https://api.github.com/repos/terraform-providers/terraform-provider-azurerm/releases/latest" | | |
grep '"tag_name":' | | |
sed -E 's/.*"([^"]+)".*/\1/' | | |
sed -E 's/^v/~>/' | |
) | |
if [ -z AZURE_RM_LATEST ]; then | |
AZURE_RM_LATEST="2.50.0" | |
fi | |
OUTPUTS=$(cat <<-EOF | |
terraform { $nl | |
${tab}required_providers { $nl | |
${tab}${tab}azurerm = { $nl | |
${tab}${tab}${tab}source = "hashicorp/azurerm" $nl | |
${tab}${tab}${tab}version = "$AZURE_RM_LATEST" $nl | |
${tab}${tab}} $nl | |
${tab}} $nl | |
${tab}backend "azurerm" { $nl | |
${tab}${tab}storage_account_name = "$STORAGE_ACCT_NAME" $nl | |
${tab}${tab}container_name = "$CONTAINER_NAME" $nl | |
${tab}${tab}key = "terraform.state.main" $nl | |
${tab}} $nl | |
} $nl | |
$nl | |
provider "azurerm" { $nl | |
${tab}skip_provider_registration = true $nl | |
${tab}features {} $nl | |
} $nl | |
EOF | |
) | |
#Output file to location | |
echo $OUTPUTS > $PROVIDER_FILE | |
fi | |
echo "THE END" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment