Automating Storage Account and Service Principal Creation with Terraform
Tags: terraform, azure
Published: 2024-01-17 08:01:00

 ![Automating Storage Account and Service Principal Creation with Terraform](https://www.techielass.com/content/images/2023/12/Automating-Storage-Account-and-Service-Principal-Creation-with-Terraform.png)

When deploying Terraform using [GitHub Actions](https://www.techielass.com/tag/github-actions/) there are certain perquisites you need to set up. You need to set up somewhere for your [Terraform](https://www.techielass.com/tag/terraform/) state file to be stored. And you need to have credentials that allow resources to be deployed to your cloud environment. 

When working with Azure those things are typically an [Azure Service Principal](https://www.techielass.com/azure-service-principals-and-azure-managed-identities/) and an Azure Storage account.

It can be tiresome and unreliable to create those things manually.

In this tutorial, I am going to show you how you can use Terraform to create those items and set up your [GitHub Actions secrets](https://www.techielass.com/github-actions-secrets/). 

💡

_Please note the code I am using was originally created by_ [_Ned Bellavance_](https://github.com/ned1313/ado-labs-github-actions?ref=techielass.com) _in 2021, I have taken it and updated it so that it works with the changes to Terraform and Azure that have happened since then. _

## Prerequisites

- Azure subscription
- GitHub account
- Basic understanding of Terraform
- Code editor such as [VS Code](https://code.visualstudio.com/?ref=techielass.com)
- A GitHub repository
- [Terraform installed on your local machine](https://www.techielass.com/introduction-to-terraform/)
- [Azure CLI installed on your local machine](https://learn.microsoft.com/cli/azure/install-azure-cli-windows?tabs=azure-cli&ref=techielass.com)

## Terraform

We’re going to create three Terraform files to help us create the necessary components within Azure and GitHub.  Those three files will be:

- **Terraform.tf** - where we will define our Terraform configuration and providers required.
- **Main.tf** - this is the body of our configuration and it will define all the parts we need. 
- **Variables.tf** - this stores some of the configurable information we need to define for our resources. 

Let’s start creating these files.  We’ll start with **variables.tf** , open up your favourite code editor, I will be using VS Code. 

Create a new file, and save it in an appropriate folder and call it **variables.tf**.  Copy the following code into it: 

```
##
# Variables
##

variable "location" {
  type = string
  default = "northeurope"
}

variable "naming_prefix" {
  type = string
  default = "techielass"
}

variable "github_repository" {
  type = string
  default = "github-actions-terraform"
}

variable "tag_usage" {
  type = string
  default = "terraform-state"
}

variable "tag_owner" {
  type = string
  default = "sarah"
}

```

This file is defining some variables that we need: 

- **Location** : the Azure region we want to use for the resources we will be creating. 
- **Naming Prefix** : this will go at the start of each of our resources to be created. 
- **GitHub repository** : this is where we want our new secrets to be stored. This is the repository you will be using for a Terraform deployment of your architecture. 
- **Tag usage** : this is what our Azure resources will be tagged with for easy identification. 
- **Tag owner:** this is what our Azure resources will be tagged with for easy identification. 

💡

_You will need to modify the values on each of these variables to suit your own requirements. _

The next file we will create is the **terraform.tf** file: 

```
##
# Terraform Configuration
##
terraform {

  required_version = ">=1.6.0"
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = "~> 3.85.0"
    }
    azuread = {
      source = "hashicorp/azuread"
      version = "~> 2.47.0"
    }
    github = {
      source = "integrations/github"
      version = "~> 5.0"
    }
    random = {
      source = "hashicorp/random"
      version = "~> 3.6.0"
    }
  }
}

##
# Provider configuration
##

provider "azurerm" {
  features {}
}

provider "random" {
  # Configuration options available
}

provider "azuread" {
  # Configuration options
}

provider "github" {
  # Configuration options
}

```

This file defines what the Terraform providers that we need, in this case we are using: 

- [<u>Azure AD</u>](https://registry.terraform.io/providers/hashicorp/azuread/latest?ref=techielass.com)
- [<u>Azure RM</u>](https://registry.terraform.io/providers/hashicorp/azurerm/latest?ref=techielass.com)
- [<u>GitHub</u>](https://registry.terraform.io/providers/integrations/github/latest?ref=techielass.com)

💡

_At time of writing, all versions listed are the latest. _

The next file we need to create is the **main.tf** file, which contains the bulk of our configuration:

```
##
# Locals
##
locals {
  resource_group_name = "${var.naming_prefix}-${random_integer.sa_num.result}"
  storage_account_name = "${lower(var.naming_prefix)}${random_integer.sa_num.result}"
  service_principal_name = "${var.naming_prefix}-${random_integer.sa_num.result}"
}

##
# Resources
##

## Create Azure Entra Service Principal ##

data "azurerm_subscription" "current" {}

data "azuread_client_config" "current" {}

resource "azuread_application" "gh_actions" {
  display_name = local.service_principal_name
  owners = [data.azuread_client_config.current.object_id]
}

resource "azuread_service_principal" "gh_actions" {
  client_id = azuread_application.gh_actions.client_id
  owners = [data.azuread_client_config.current.object_id]
}

resource "azuread_service_principal_password" "gh_actions" {
  service_principal_id = azuread_service_principal.gh_actions.object_id
}

## This assigns contributor role to the service principal ##

resource "azurerm_role_assignment" "gh_actions" {
  scope = data.azurerm_subscription.current.id
  role_definition_name = "Contributor"
  principal_id = azuread_service_principal.gh_actions.id
}

## Create an Azure resource group an an Azure Storage Account to store Terraform State ##

resource "random_integer" "sa_num" {
  min = 10000
  max = 99999
}

resource "azurerm_resource_group" "setup" {
  name = local.resource_group_name
  location = var.location
    tags = {
    usage = var.tag_usage
    owner = var.tag_owner
  }
}

resource "azurerm_storage_account" "sa" {
  name = local.storage_account_name
  resource_group_name = azurerm_resource_group.setup.name
  location = var.location
  account_tier = "Standard"
  account_replication_type = "LRS"
  tags = {
    usage = var.tag_usage
    owner = var.tag_owner
  }
}

resource "azurerm_storage_container" "ct" {
  name = "terraform-state"
  storage_account_name = azurerm_storage_account.sa.name

}

## Store the Service Principal ID, Password, plus information about the storage account and Azure subscription in GitHub Secrets ##

resource "github_actions_secret" "actions_secret" {
  for_each = {
    STORAGE_ACCOUNT = azurerm_storage_account.sa.name
    RESOURCE_GROUP = azurerm_storage_account.sa.resource_group_name
    CONTAINER_NAME = azurerm_storage_container.ct.name
    ARM_CLIENT_ID = azuread_service_principal.gh_actions.client_id
    ARM_CLIENT_SECRET = azuread_service_principal_password.gh_actions.value
    ARM_SUBSCRIPTION_ID = data.azurerm_subscription.current.subscription_id
    ARM_TENANT_ID = data.azuread_client_config.current.tenant_id
  }

  repository = var.github_repository
  secret_name = each.key
  plaintext_value = each.value
}

```

There is a lot in this file, so let’s try and break it down and explain it. 

### Variable definition

First we are defining some local variables to help us with the naming of our resources, they are combining the naming prefix variable we defined earlier and then generating a random number to add to the name of the resources. 

### Gather Azure subscription information

We then have two data blocks gathering information from your Azure subscription so it can use the information to create the resources. 

The next section is creating the necessary Entra Application and Service Principal. 

Contributor access is then assigned to that Service Principal. 

### Azure resource creation

And next we create an Azure resource group, Azure storage account and then a storage container inside that storage account.   It is creating a Standard LRS storage account. This section pulls in information from our variable file to define the Azure region used as the resources location and also the tags that will be attached to it. 

### Create secrets in GitHub

The last section is taking the information from our generated resources and storing them inside our GitHub repository secret section.  Seven secrets will be created containing the following information: 

- The storage account name
- The resource group name where the storage account resides
- The storage container name
- The Service Principal ID
- The Service Principal secret
- The Azure subscription ID
- The Azure Tenant ID

💡

_If you would like a copy of these files you can find them stored within GitHub_ [_here_](https://github.com/weeyin83/Azure-SP-SA-Automation-w-Terraform?ref=techielass.com)_. _

## Deploy Terraform

Now we have the Terraform created we want to be able to deploy that to create our storage account and Service Principal. 

### Create GitHub Personal Access Token

To do that we first need to create a GitHub personal access token (PAT) that Terraform can use for authentication to your GitHub account when it tries to create the secrets inside your repository.  

To generate a GitHub PAT token head over to [<u>https://www.github.com</u>](https://www.github.com/?ref=techielass.com)<u>.</u>

Click on your **profile icon** in the right-hand corner and select **Settings.**

![Automating Storage Account and Service Principal Creation with Terraform](https://www.techielass.com/content/images/2023/12/github-settings.PNG)
_GitHub Settings_

Scroll down to **Developer settings.**

![Automating Storage Account and Service Principal Creation with Terraform](https://www.techielass.com/content/images/2023/12/github-developer.PNG)

Click on **Personal access tokens** then **fine-grained tokens.**

![Automating Storage Account and Service Principal Creation with Terraform](https://www.techielass.com/content/images/2023/12/github-pat.PNG)
_GitHub Personal Access Tokens_

Then click on **Generate new token.**

You will be prompted with a bunch of questions, you need to give your token a name and set how long it will be active for. And then you need to start to define what repositories and permissions the token has. 

I like to scope it to the minimum required, by selecting the relevant repository and then only giving it Read/Write access to **Secrets** and Read access to **Metadata**. 

![Automating Storage Account and Service Principal Creation with Terraform](https://www.techielass.com/content/images/2023/12/github-permissions.PNG)
_GitHub Personal Access Token permissions_

Once you’ve provided all the required answers click on the **Generate token** button. 

The next screen will display the token information, take a note of this as you’ll need it in the next step and it is only displayed once. 

Now that we have the PAT token, we need to head over to our browser and open [<u>https://shell.azure.com</u>](https://shell.azure.com/?ref=techielass.com).  We are going to use that to deploy our Terraform creation files. 

When the Azure Cloud Shell launches, ensure you are using the Bash environment. 

Click on the **Upload/Download icon** and select **Manage file share.**

![Automating Storage Account and Service Principal Creation with Terraform](https://www.techielass.com/content/images/2023/12/shell-upload.PNG)
_Azure Cloud Shell_

Click on **Add Directory.**

![Automating Storage Account and Service Principal Creation with Terraform](https://www.techielass.com/content/images/2023/12/shell-directory.PNG)
_Azure Cloud Shell_

Give the directory a name, such as **Terraform-Backend-configuration.**

Once it has been created, click into it and then click on the **Upload** button **. ** Upload the three terraform files that you created earlier. 

![Automating Storage Account and Service Principal Creation with Terraform](https://www.techielass.com/content/images/2023/12/shell-upload3.png)
_Azure Cloud Shell_

With the files uploaded, switch back to your terminal window. 

Let's enter our GitHub PAT token key.

```
export GITHUB_TOKEN=PAT_TOKEN_VALUE

```

The next step we need to carry out is move our terminal to work within the directory where our Terraform is stored. 

```
cd clouddrive/Terraform-Backend-configuration/

```

We are now ready to initialise and apply the code.

```
terraform init
terraform apply -auto-approve

```

After a few minutes you will have an Azure Service Principal, Azure Storage Account, storage container deployed.  Along with all the required secrets created within your GitHub repository. 

![Automating Storage Account and Service Principal Creation with Terraform](https://www.techielass.com/content/images/2023/12/github-secrets.PNG)
_GitHub Secrets_

You can now start to build your Terraform deployment files and GitHub Actions workflow using these secrets and Terraform backend infrastructure.

## Conclusion

You can reuse this Terraform deployment as many times as you need, it’s a great way to quickly spin up the required bits to enable you to set up an Azure deployment within GitHub Actions quickly.  Great if you have a lot of repositories for different functions or are building a lot of demo environments.