Search
K

Azure DevOps pipeline - Create resource in Azure Cloud with Terraform

This Self-Serv Action uses Azure DevOps pipeline to deploys a demo storage in Azure using terraform cloud and push the deployment info back to configure8 via API.

Create configure8 API key

We will use the configure8 public API to create a Deployment in the Catalog once the resource is created.
If you don't have a configure8 API Key, follow this guide to create a new one.

Action setup

1. Details

Go to your configure8 app into the Self Service menu. Select "+ Add Action" button.
Fill in your action Name, Type, Logo, Status and Description.

2. Context

Set your action Owners, Visibility Control, Viewers, Allowed People and Applies to fields.

3. User Input

Activate the user input toggle so that you can insert the JSON for user input.
Paste the JSON below to visualize the inputs we are asking to the user in this sample.
[
{
"required": true,
"type": "text",
"title": "Azure Storage Name",
"payloadName": "azureStorageName",
"defaultValue": "terraformc8demo001"
},
{
"required": true,
"type": "dropdown",
"title": "Azure Storage location",
"payloadName": "azureStorageLocation",
"defaultValue": "eastus",
"options": [
{
"label": "eastus",
"value": "eastus"
},
{
"label": "eastus2",
"value": "eastus2"
},
{
"label": "centralus",
"value": "centralus"
},
{
"label": "westus",
"value": "westus"
},
{
"label": "westus2",
"value": "westus2"
},
{
"label": "brazilsouth",
"value": "brazilsouth"
},
{
"label": "brazilsoutheast",
"value": "brazilsoutheast"
},
{
"label": "northeurope",
"value": "northeurope"
},
{
"label": "westeurope",
"value": "westeurope"
},
{
"label": "francecentral",
"value": "francecentral"
}
]
}
]

4. Method

Set the following values:

Webhook URL:

https://dev.azure.com/ORG/REPO/_apis/build/builds
Change the ORG and REPO above to reflect your Azure DevOps account.

Headers:

{
"Authorization": "Basic Your_ADO_Token",
"Content-type": "application/json"
}
Here you can find instructions on how to create such a token. Build (Read & execute) permissions should be enough.

Payload:

{
"templateParameters": {
"c8ReportToken": "{{ c8ReportToken }}",
"c8ReportUrl": "https://app.configure8.io/self-service/api/v1/reports/webhook",
"tfVarOverrides": "-var storage_account_name={{ azureStorageName }} -var location={{ azureStorageLocation }}"
},
"definition": {
"id": "2"
}
}
The definition and id above can vary according to your pipeline info.

Backend setup

Create a repository or use a existing one

Create or use a repository that matches the REPO name passed on the webhook URL.

Create a pipeline to execute the action

trigger: none
pool:
vmImage: 'ubuntu-latest'
parameters:
- name: c8ReportToken
type: string
- name: c8ReportUrl
type: string
default: "https://app.configure8.io/self-service/api/v1/reports/webhook"
- name: tfVarOverrides
type: string
default: " "
variables:
serviceConnectionName: 'azure-demo-account'
resourceGroupName: 'demo'
stateStorageAccountName: 'terraformc8demotfstate'
stateContainerName: 'terraformc8demotfstate'
stateStorageKey: 'terraform-demo-c8.tfstate'
steps:
- checkout: self
displayName: 'Checkout repository'
- bash: |
startedAt=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)
echo "##vso[task.setvariable variable=startedAt]$startedAt"
- task: TerraformTaskV4@4
displayName: 'Terraform Init'
inputs:
command: 'init'
workingDirectory: '$(Build.SourcesDirectory)/terraform'
backendType: 'azurerm'
ensureBackend: true
backendServiceArm: $(serviceConnectionName)
backendAzureRmStorageAccountName: $(stateStorageAccountName)
backendAzureRmResourceGroupName: $(resourceGroupName)
backendAzureRmContainerName: $(stateContainerName)
backendAzureRmKey: $(stateStorageKey)
- task: TerraformTaskV3@3
displayName: 'Terraform Validate'
inputs:
command: 'validate'
workingDirectory: '$(Build.SourcesDirectory)/terraform'
- task: TerraformTaskV3@3
displayName: 'Terraform Plan'
inputs:
command: 'plan'
workingDirectory: '$(Build.SourcesDirectory)/terraform'
environmentServiceNameAzureRM: $(serviceConnectionName)
commandOptions: "-out=tfplan ${{ parameters.tfVarOverrides }}"
- task: TerraformTaskV3@3
displayName: 'Terraform Apply'
inputs:
command: 'apply'
workingDirectory: '$(Build.SourcesDirectory)/terraform'
environmentServiceNameAzureRM: $(serviceConnectionName)
commandOptions: '-auto-approve -input=false tfplan'
- bash: |
curl -s -f -H "Content-type: application/json" \
-H "Api-Key: $(C8-API-KEY)" \
-X POST "https://app.configure8.io/public/v1/deployments" \
-d '{"name":"$(Build.DefinitionName)-$(Build.BuildId)","repositoryId":"66f15f65-adb9-44c7-b29d-d743cff49954","environment":"production","status":"SUCCESS", "sha":"$(Build.SourceVersion)","ref":"$(Build.SourceBranchName)","url":"$(System.TeamFoundationCollectionUri)Demo%20Azure%20APP/_build/results?buildId=$(Build.BuildId)","startedAt":"'$(startedAt)'"}'
displayName: Link to C8 Deployment
- bash: |
completedAt=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)
cd terraform
terraform_output=$(terraform output endpoint_url | sed 's/"//g')
echo ${terraform_output}
curl -s -X POST -H "c8-report-token: ${{ parameters.c8ReportToken }}" \
-H "Content-Type: application/json" \
-d '{
"status": "complete",
"startedAt": "'$(startedAt)'",
"completedAt": "'${completedAt}'",
"steps": [
{
"title":"Terraform output",
"status": "complete",
"startedAt": "'$(startedAt)'",
"completedAt": "'${completedAt}'",
"logs": ["endpoint_url = '${terraform_output}'"]
},
{
"title":"Azure DevOps Pipeline URL",
"status": "complete",
"startedAt": "'$(startedAt)'",
"completedAt": "'${completedAt}'",
"logs": ["$(System.TeamFoundationCollectionUri)Demo%20Azure%20APP/_build/results?buildId=$(Build.BuildId)"]
}
]
}' \
"${{ parameters.c8ReportUrl }}"
displayName: Report SSA Status
```
In the above section, the following info inside variables should have been pre-created:
Commit the workflow.
This sample is using terraform files to create the resource in Azure. This is the directory structure and the content of the files (with secrets obfuscated, you need to replace by your own):
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>C8 Azure Demo Site</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
header {
background-color: #333;
color: #fff;
text-align: center;
padding: 10px;
}
nav {
background-color: #444;
padding: 10px;
}
nav ul {
list-style-type: none;
padding: 0;
margin: 0;
}
nav li {
display: inline;
margin-right: 20px;
}
.container {
max-width: 800px;
margin: 20px auto;
padding: 20px;
}
footer {
background-color: #333;
color: #fff;
text-align: center;
padding: 10px;
}
</style>
</head>
<body>
<header>
<h1>Welcome to C8 Azure Storage Demo Site</h1>
</header>
<div class="container">
<h2>About Us</h2>
<p>Help your developers move faster and build better software <br />
with self-serve access to the knowledge and functionality they need.
</p>
<a href="https://www.configure8.io/" target="_blank">configure8</a>
</div>
</body>
</html>
main.tf:
resource "azurerm_storage_account" "storage_account" {
name = var.storage_account_name
resource_group_name = var.resource_group_name
location = var.location
account_tier = "Standard"
account_replication_type = "LRS"
account_kind = "StorageV2"
static_website {
index_document = "index.html"
}
}
resource "azurerm_storage_blob" "this" {
name = "index.html"
content_type = "text/html"
storage_account_name = azurerm_storage_account.storage_account.name
storage_container_name = "$web"
type = "Block"
source = "index.html"
}
outputs.tf:
output "endpoint_url" {
value = azurerm_storage_account.storage_account.primary_web_endpoint
}
variables.tf:
variable "subscription_id" {
type = string
default = "XXXXXX-1111-2222-3333-YYYYYYYYYY"
description = "Subscription ID in Azure"
}
variable "tenant_id" {
type = string
default = "XXXXXXX-1111-2222-3333-YYYYYYYYY"
description = "Tenant ID in Azure"
}
variable "resource_group_name" {
type = string
default = "demo"
description = "RG name in Azure"
}
variable "location" {
type = string
default = "eastus"
description = "RG location in Azure"
}
variable "storage_account_name" {
type = string
default = "terraformc8demo"
description = "Storage Account name in Azure"
}
variables.tf:
provider "azurerm" {
features {}
subscription_id = var.subscription_id
tenant_id = var.tenant_id
}
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.83.0"
}
}
backend "azurerm" {
subscription_id = "XXXXXX-1111-2222-3333-YYYYYYYYYY"
resource_group_name = "demo"
storage_account_name = "terraformc8demotfstate"
container_name = "terraformc8demotfstate"
key = "terraform-demo-c8.tfstate"
}
}
```

Run Action and view results

Onwers and allowed users will be able to execute it and view the results:
Copyright © 2023 configure8, Inc. All rights reserved.