0% found this document useful (0 votes)
22 views9 pages

Azure Servicebus Functions Terraform

Uploaded by

leela 08
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views9 pages

Azure Servicebus Functions Terraform

Uploaded by

leela 08
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Azure Service Bus + HTTP-triggered Azure

Functions (TypeScript) + Terraform + GitHub


Actions
This workspace contains:
 terraform/ — Terraform files to provision Azure resources (Resource
Group, Storage Account, App Service Plan, Function App, Service Bus
Namespace + Queue, Application Insights).
 functions/ — Example TypeScript Azure Functions (HTTP trigger and
Service Bus trigger) with package.json and build config.
 .github/workflows/ci-cd.yml — GitHub Actions to: run terraform (plan
& apply) and build & deploy the function app.

File tree
azure-servicebus-functions-terraform/
├─ terraform/
│ ├─ provider.tf
│ ├─ main.tf
│ ├─ variables.tf
│ ├─ outputs.tf
│ └─ backend.tf
├─ functions/
│ ├─ host.json
│ ├─ local.settings.json (example, do NOT commit secrets)
│ ├─ package.json
│ ├─ tsconfig.json
│ ├─ src/
│ │ ├─ HttpTrigger/index.ts
│ │ └─ ServiceBusTrigger/index.ts
│ └─ function.json (for each function, alongside index.ts)
└─ .github/workflows/ci-cd.yml

Notes before you start


 This is a template. Replace placeholder values (like
<YOUR_SUBSCRIPTION_ID>, <RG_NAME>, <LOCATION>, etc) or wire them to
Terraform variables / GitHub secrets.
 Keep secrets in GitHub repository secrets (ARM_CLIENT_ID,
ARM_CLIENT_SECRET, ARM_SUBSCRIPTION_ID, ARM_TENANT_ID) for
Terraform and AZURE_CREDENTIALS or publish profile for deployment.
 The example uses a Linux Function App (Node 18) and a Zip deploy via
azure/webapps-deploy action. You can switch to azure/functions-
action if you prefer.

Terraform: terraform/provider.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">=3.0.0"
}
}
required_version = ">= 1.1.0"
}

provider "azurerm" {
features = {}
}

Terraform: terraform/backend.tf
# optional: configure remote state
# Uncomment and configure to use an Azure Storage backend
# terraform {
# backend "azurerm" {
# resource_group_name = "<state-rg>"
# storage_account_name = "<stateacct>"
# container_name = "tfstate"
# key = "infra.tfstate"
# }
# }

Terraform: terraform/variables.tf
variable "resource_group_name" {
type = string
default = "rg-func-sb-demo"
}

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

variable "function_app_name" {
type = string
default = "func-sb-demo"
}
variable "storage_account_name" {
type = string
default = "funcsbstoreacct"
}

variable "servicebus_namespace" {
type = string
default = "sbn-demo-namespace"
}

variable "servicebus_queue_name" {
type = string
default = "myqueue"
}

Terraform: terraform/main.tf
resource "azurerm_resource_group" "rg" {
name = var.resource_group_name
location = var.location
}

resource "azurerm_storage_account" "sa" {


name = var.storage_account_name
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_app_service_plan" "plan" {


name = "plan-${var.function_app_name}"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name

kind = "FunctionApp"

sku {
tier = "Dynamic"
size = "Y1"
}
}

resource "azurerm_application_insights" "ai" {


name = "ai-${var.function_app_name}"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
application_type = "web"
}
resource "azurerm_function_app" "function" {
name = var.function_app_name
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
app_service_plan_id = azurerm_app_service_plan.plan.id
storage_account_name = azurerm_storage_account.sa.name
storage_account_access_key =
azurerm_storage_account.sa.primary_access_key
version = "~4"
os_type = "linux"

site_config {
linux_fx_version = "NODE|18"
always_on = false
}

app_settings = {
FUNCTIONS_WORKER_RUNTIME = "node"
WEBSITE_RUN_FROM_PACKAGE = "1"
APPINSIGHTS_INSTRUMENTATIONKEY =
azurerm_application_insights.ai.instrumentation_key
SERVICEBUS_CONNECTION =
azurerm_servicebus_namespace.sbn.default_primary_connection_string
}
}

resource "azurerm_servicebus_namespace" "sbn" {


name = var.servicebus_namespace
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
sku = "Standard"
}

resource "azurerm_servicebus_queue" "queue" {


name = var.servicebus_queue_name
namespace_name = azurerm_servicebus_namespace.sbn.name
resource_group_name = azurerm_resource_group.rg.name
}

# Create a namespace authorization rule to get connection string


resource "azurerm_servicebus_namespace_authorization_rule" "auth" {
name = "RootManageSharedAccessKey"
namespace_name = azurerm_servicebus_namespace.sbn.name
resource_group_name = azurerm_resource_group.rg.name
listen = true
send = true
manage = true
}
Terraform: terraform/outputs.tf
output "function_app_default_hostname" {
value = azurerm_function_app.function.default_hostname
}

output "servicebus_connection_string" {
value =
azurerm_servicebus_namespace_authorization_rule.auth.primary_connectio
n_string
sensitive = true
}

Azure Functions (TypeScript)


functions/package.json
{
"name": "func-sb-demo",
"version": "1.0.0",
"scripts": {
"build": "tsc",
"prestart": "npm run build",
"start": "func start --verbose"
},
"dependencies": {
"@azure/service-bus": "^7.10.0",
"@azure/functions": "^3.0.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"@types/node": "^18.0.0"
}
}

functions/tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es2020",
"outDir": "dist",
"rootDir": "src",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
functions/host.json
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true
}
}
}
}

functions/src/HttpTrigger/index.ts
import { AzureFunction, Context, HttpRequest } from
"@azure/functions";

const httpTrigger: AzureFunction = async function (context: Context,


req: HttpRequest): Promise<void> {
context.log('HTTP trigger processed a request.');

const name = req.query.name || (req.body && req.body.name) ||


'world';
context.res = {
status: 200,
body: { message: `Hello, ${name}` }
};
};

export default httpTrigger;

functions/src/HttpTrigger/function.json
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [ "get", "post" ]
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
functions/src/ServiceBusTrigger/index.ts
import { AzureFunction, Context } from "@azure/functions";

const serviceBusQueueTrigger: AzureFunction = async function (context:


Context, myQueueItem: any): Promise<void> {
context.log('ServiceBus queue trigger function processed message:',
myQueueItem);
// do processing here
};

export default serviceBusQueueTrigger;

functions/src/ServiceBusTrigger/function.json
{
"bindings": [
{
"name": "myQueueItem",
"type": "serviceBusTrigger",
"direction": "in",
"queueName": "myqueue",
"connection": "SERVICEBUS_CONNECTION"
}
]
}

Note: connection references the app setting name that contains the
Service Bus connection string. In Terraform we set
SERVICEBUS_CONNECTION.

GitHub Actions: .github/workflows/ci-cd.yml


name: Infra + Build + Deploy

on:
push:
branches:
- main

jobs:
terraform:
name: Terraform Plan & Apply
runs-on: ubuntu-latest
env:
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }}
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.5.0
- name: Terraform Init
working-directory: terraform
run: terraform init
- name: Terraform Plan
working-directory: terraform
run: terraform plan -out=tfplan
- name: Terraform Apply
working-directory: terraform
# Be careful: apply will create resources. For safety you may
require manual approval.
run: terraform apply -auto-approve tfplan
- name: Save TF outputs
id: outputs
working-directory: terraform
run: |
echo "FUNCTION_HOSTNAME=$(terraform output -raw
function_app_default_hostname)" >> $GITHUB_ENV
echo "SERVICEBUS_CONN=$(terraform output -raw
servicebus_connection_string)" >> $GITHUB_ENV

build_and_deploy:
name: Build & Deploy Function
runs-on: ubuntu-latest
needs: terraform
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
working-directory: functions
run: npm ci
- name: Build
working-directory: functions
run: npm run build
- name: Archive function app
run: |
cd functions
zip -r ../functionapp.zip . -x "node_modules/**/.cache/**"
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy to Function App via Zip
uses: azure/webapps-deploy@v2
with:
app-name: ${{ secrets.FUNCTION_APP_NAME }}
package: functionapp.zip

Important GitHub secrets


 ARM_CLIENT_ID, ARM_CLIENT_SECRET, ARM_TENANT_ID,
ARM_SUBSCRIPTION_ID — used by Terraform azurerm provider when
running in GH Actions.
 AZURE_CREDENTIALS — JSON credentials for azure/login step (service
principal). See Azure docs for formatting.
 FUNCTION_APP_NAME — name of your function app (or you can get it from
TF outputs and pass dynamically).

How to use
1. Customize terraform/variables.tf values or pass them via
terraform.tfvars or environment variables.
2. Push repository to GitHub, configure secrets listed above.
3. On push to main, GitHub Actions will run Terraform and deploy the
function.

If you’d like, I can:


 Generate the repository as a zip you can download.
 Convert the Terraform to use Managed Identities for the Function App
instead of connection strings.
 Add a safer workflow step that requires manual approval before
terraform apply.

Tell me which option you want next.

You might also like