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.
Copy [
{
"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:
Copy https://dev.azure.com/ORG/REPO/_apis/build/builds
Change the ORG and REPO above to reflect your Azure DevOps account.
Copy {
"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:
Copy {
"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
Copy 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:
Copy <! 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 : 10 px ;
}
nav {
background-color : #444 ;
padding : 10 px ;
}
nav ul {
list-style-type : none ;
padding : 0 ;
margin : 0 ;
}
nav li {
display : inline ;
margin-right : 20 px ;
}
.container {
max-width : 800 px ;
margin : 20 px auto ;
padding : 20 px ;
}
footer {
background-color : #333 ;
color : #fff ;
text-align : center ;
padding : 10 px ;
}
</ 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:
Copy 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:
Copy output "endpoint_url" {
value = azurerm_storage_account.storage_account.primary_web_endpoint
}
variables.tf:
Copy 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:
Copy 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: