【手順解説】GitHub Actions×TerraformでAzure Functionsを自動構築する方法
はじめに
こんにちは、株式会社メンバーズ 生成AI基盤チームの谷森です。
最近、Azureを利用したRAG(Retrieval-Augmented Generation)システムの構築に携わったのですが、気が付けば手作業でのリソース作成や設定が多くなっていました。
そこで今回は、Azureのリソース構築・設定の自動化に挑戦した内容についてご紹介します。
同じような課題を感じている方や、これからインフラの自動化に取り組みたいと考えている若手エンジニアの皆さんの参考になれば幸いです。
自動化に至った背景
今回構築したRAGシステムは、データベースに格納した特定の知識を参照して質問応答を行うというものです。Azure Functionsを中心に様々なAzureサービスを組み合わせて実現しました。しかし、開発を進めるにつれて以下のような課題が顕在化してきました。
- 環境構築の手間:検証環境・開発環境・本番環境など複数の環境を構築するたびに同じ作業を繰り返す必要がありました。
- 設定のばらつき:作業者や構築環境が異なると手作業での設定では、環境間で微妙なずれが生じてしまう可能性がありました。
- 変更への対応:システムの拡張や設定変更が発生した場合、複数のリソースに対して手作業で修正を行うのは時間もかかり、ミスも起こりやすくなる可能性がありました。
これらの課題を解決するためにインフラの自動化が不可欠だと感じました。
自動化対象となるAzureリソース一覧
今回は、RAGシステムを構成するAzureリソースの中でも特にAzure Functionsを動かすために必要な以下のリソースを自動化の対象としました。
- Azure Functions:サーバーレスコンピューティングのプラットフォームで、イベントドリブン型のアプリケーションを構築するためのサービス(今回はRAGシステムの処理を行うために使用)
- Azure API Management:バックエンドサービスを統合して一元的に管理するためのサービス(今回はAzure Functionsを管理するために使用)
- Azure App Service Plan:Azure上でWebアプリケーションやAPIをホストするためのサービス(今回はAzure Functionsをホストするために使用)
- Azure Storage Account:Azureのストレージサービスを利用するためのアカウント(今回はAzure Functionsの実行ログなどを保存するために使用)
- Azure Log Analytics Workspace:Azure Monitorの一部で、ログデータを収集・分析・可視化するためのサービス(今回はAzure Functionsのログを確認するために使用)
- Azure Application Insights:アプリケーションのパフォーマンスや使用状況をリアルタイムで監視するためのサービス(今回はAzure Functionsのログを確認するために使用)
これらのリソース作成を自動化することで、環境構築の効率化・設定の一貫性維持・変更への迅速な対応を目指します。
自動化のための環境準備
インフラの自動化ツールはいくつかありますが、今回はGitHub ActionsとTerraformを選択しました。
1. ローカル環境の準備
①Azure CLIのインストール
下記コマンドでAzure CLIがインストールされているか確認します。
az --version
なければご自身の環境に合わせて下記リンクを参考にインストールします。
②Terraformのインストール
下記コマンドでTerraformがインストールされているか確認します。
terraform -v
なければご自身の環境に合わせて下記リンクを参考にインストールします。
2. Azureサービスプリンシパルの作成
①Azureログイン
下記コマンドでAzure環境にログインできるか確認します。
az login --use-device-code
上記コマンドを実行すると下記のように表示されるので、実行結果に記載されているリンクとコードでAzure環境にログインします。
$ az login--use-device-code
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code XXXXX to authenticate.
Retrieving tenants and subscriptionsforthe selection...
[Tenant and subscription selection]
No Subscription name Subscription ID Tenant
----- ----------------------- ---------------------- -----------
[1] * XXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXX
〜〜〜〜〜以下略〜〜〜〜〜
②Azureリソースグループ確認
Azure環境にログインできることを確認したら、下記コマンドでご自身が閲覧可能なAzureリソースグループを確認します。
az group list --query "[].{Name:name, Location:location}" --output table
上記コマンドを実行すると下記のように表示されます。
$ az group list --query "[].{Name:name, Location:location}" --output table
Name Location
---------- ----------
XXXXXXXXXX japaneast
③Azureサービスプリンシパル作成
閲覧可能なAzureリソースを確認したら、下記コマンドで閲覧可能なリソースグループ直下にサービスプリンシパルを作成します。
az ad sp create-for-rbac --role="Contributor" --display-name="<任意のサービスプリンシパル名>" --scopes="/subscriptions/<サブスクリプションID>/resourceGroups/<リソースグループ名>"
上記コマンドを実行すると下記のように表示されます。
$ az ad sp create-for-rbac --role="Contributor" --display-name="XXXXX" --scopes="/subscriptions/XXXXX/resourceGroups/XXXXX"
Creating 'Contributor' role assignment under scope '/subscriptions/XXXXX/resourceGroups/XXXXX'
The output includes credentials that you must protect. Be sure that youdonot include these credentialsinyour code or check the credentials into your source control. For more information, see <https://aka.ms/azadsp-cli>
{
"appId": "XXXXX",
"displayName": "XXXXX",
"password": "XXXXX",
"tenant": "XXXXX"
}
上記コマンド結果で表示されたクライアントシークレットを必ず控えておきます。
3. GitHub Secretsの設定
①GitHubリポジトリの作成
GitHub ActionsとTerraform用のコードなどを保存するためにGitHubリポジトリを任意の名前で作成します。
②GitHub Actionsの環境変数設定
GitHubリポジトリのSettingsからサイドバーのSecurity>Secrets and variables>Actionsを選択します。

Repository secretsに任意の環境変数名とクライアントシークレットを登録します。今回はTerraformの設定で取得した以下の4種類のクライアントシークレットを登録します。
任意の環境変数名 | クライアントシークレット |
AZURE_CLIENT_ID | サービスプリンシパルのService Principal ID |
AZURE_SUBSCRIPTION_ID | サービスプリンシパルのSubscription ID |
AZURE_TENANT_ID | サービスプリンシパルのTenant ID |
CLIENT_SECRET | サービスプリンシパルのService Principal Secret |
GitHubリポジトリのディレクトリ構成
GitHubリポジトリを作成したら、下記のようなディレクトリ構成にします。
terraform-rag(任意のGitHubリポジトリ名)
├── .github
│ ├── workflows
│ │ └── terraform-apply.yml(任意のGitHub Actionsコードファイル名)
├── terraform(任意のTerraformコードディレクトリ名)
│ ├── .terraform.lock.hcl
│ ├── main.tf
│ ├── rg.tf
│ └── variables.tf
├── .gitignore
└── README.md
GitHub Actions用のterraform-apply.ymlはGitHubリポジトリのActionsからサイドバーにあるNew workflowをクリックして作成します。

terraformディレクトリとその中にあるmain.tf・rg.tf・variables.tfは手動で作成します。
terraform.lock.hclファイルはterraform initコマンドを実行する度に自動生成・自動更新されるためご自身で作成する必要はありません。
Terraformコード
main.tf
ここでAzureリソースの自動作成コードを記載します。
以下の項目に関してはご自身のお好みで変更して下さい。
- 識別子
- リソース名
- バージョン
var.client・var.environmentに関してはvariables.tfファイルで記載しています。
terraform {
# Terraformプロバイダとそのバージョン指定(今回はAzureRM4.24.0以上を使用)
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.24.0"
}
}
# Terraform自体のバージョン指定(今回は1.10.1以上を使用)
required_version = ">= 1.10.1"
}
provider "azurerm" {
skip_provider_registration = true
features {}
subscription_id = var.subscription_id
tenant_id = var.tenant_id
client_id = var.client_id
client_secret = var.client_secret
}
resource "azurerm_api_management" "common" {
name = "rag-apim-${var.client}-${var.environment}"
location = data.azurerm_resource_group.rg.location
resource_group_name = data.azurerm_resource_group.rg.name
publisher_name = "<APIの管理者名>"
publisher_email = "<ご自身のメールアドレス>"
sku_name = "Consumption_0"
}
resource "azurerm_app_service_plan" "insertdb-function" {
name = "rag-appservice-${var.client}-${var.environment}"
location = data.azurerm_resource_group.rg.location
resource_group_name = data.azurerm_resource_group.rg.name
kind = "Linux"
reserved = true
sku {
tier = "Standard"
size = "S1"
}
}
resource "azurerm_storage_account" "insertdb-function" {
name = "raginsertdb${var.client}${var.environment}"
resource_group_name = data.azurerm_resource_group.rg.name
location = data.azurerm_resource_group.rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_function_app" "insertdb-function" {
name = "rag-insertdb-${var.client}-${var.environment}"
location = data.azurerm_resource_group.rg.location
resource_group_name = data.azurerm_resource_group.rg.name
app_service_plan_id = azurerm_app_service_plan.insertdb-function.id
storage_account_name = azurerm_storage_account.insertdb-function.name
storage_account_access_key = azurerm_storage_account.insertdb-function.primary_access_key
version = "~3"
site_config {
linux_fx_version = "PYTHON|3.11"
}
}
resource "azurerm_log_analytics_workspace" "insertdb-function" {
name = "rag-workspace-${var.client}-${var.environment}"
location = data.azurerm_resource_group.rg.location
resource_group_name = data.azurerm_resource_group.rg.name
sku = "PerGB2018"
retention_in_days = 30
}
resource "azurerm_application_insights" "insertdb-function" {
name = "rag-insights-${var.client}-${var.environment}"
location = data.azurerm_resource_group.rg.location
resource_group_name = data.azurerm_resource_group.rg.name
workspace_id = azurerm_log_analytics_workspace.insertdb-function.id
# アプリケーションタイプを指定(今回はHTTPベースのアプリケーションを監視するためwebを使用)
application_type = "web"
}
各リソースにタグを付けたい場合は下記のようにコードを追加します。
resource "azurerm_api_management" "common" {
name = "rag-apim-${var.client}-${var.environment}"
location = data.azurerm_resource_group.rg.location
resource_group_name = data.azurerm_resource_group.rg.name
publisher_name = "<ご自身の会社名>"
publisher_email = "<ご自身のメールアドレス>"
sku_name = "Consumption_0"
tags = {
AppName = "rag-common-${var.client}-${var.environment}"
Author = "terraform"
Client = var.client
Env = var.environment
System = "rag"
}
}
rg.tf
ここで既存リソースグループの情報を取得するコードを記載します。
識別子に関してはご自身のお好みで変更して下さい。
# 既存のリソースグループの情報を取得
data "azurerm_resource_group" "rg" {
name = var.resource_group_name
}
variables.tf
ここで環境変数を取得するコードを記載します。
以下の変数に関しては私が入力値として使用したいものを設定していますが、設定しなくてもAzureリソース作成自動化はできます。
- resource_group_name
- environment
- client
これらを設定しない場合はmain.tfファイルで記載した各リソース名やタグ内容は任意の文字列に変更します。
variable "subscription_id" {}
variable "client_id" {}
variable "client_secret" {}
variable "tenant_id" {}
# 任意のリージョンをデフォルトで設定可
variable "location" {
default = "japaneast"
}
# GitHub Actions開始時の入力値を設定可
variable "resource_group_name" {
default = "<リソースグループ名>"
}
variable "environment" {
default = "<環境名>"
}
variable "client" {
default = "<顧客名>"
}
GitHub Actionsコード
ここでmain.tfファイルを実行するコードを記載します。
name: '<フロー名>'
on:
# 手動トリガーを設定
workflow_dispatch:
inputs:
resource_group_name:
description: 'リソースグループ名'
required: true
default: '<リソースグループ名>'
environment:
description: '環境名'
required: true
default: '<環境名>'
client:
description: '顧客名'
required: true
default: '<顧客名>'
permissions:
contents: read
id-token: write
jobs:
terraform:
name: '<ジョブ名>'
runs-on: ubuntu-latest
environment: production
steps:
# リポジトリをGitHub Actionsランナーにチェックアウト
- name: Checkout
uses: actions/checkout@v4
# Azureにログイン
- name: 'Az CLI login'
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# Terraformの指定バージョンをインストール
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: 1.10.1
# 新しいまたは既存のTerraform作業ディレクトリの初期ファイルを作成
- name: Terraform Init
run: terraform init
working-directory: terraform
# Terraformの実行プランを生成
- name: Terraform Plan
run: |
terraform plan \\
-var "environment=${{ github.event.inputs.environment }}" \\
-input=false
working-directory: terraform
# Terraform設定ファイルに従ってインフラを構築または変更
- name: Terraform Apply
env:
CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
run: |
terraform apply -auto-approve \\
-var "resource_group_name=${{ github.event.inputs.resource_group_name }}" \\
-var "environment=${{ github.event.inputs.environment }}" \\
-var "client=${{ github.event.inputs.client }}"
working-directory: terraform
TerraformのstateファイルをAzure内で管理したい場合は、手動でAzure Blob Storageを作成して上記コードの最後に下記コードを追加します。
# ステートファイルを指定のストレージにアップロード
- name: Upload State File to Azure Storage
run: |
az storage blob upload \\
--account-name "<ストレージアカウント名>" \\
--container-name "<コンテナ名>" \\
--file terraform/terraform.tfstate \\
--name "${{ github.event.inputs.resource_group_name }}.tfstate"
私が遭遇したエラー
【terraform initコマンド実行時に出たエラー】
- main.tfファイルのresource以降の項目のダブルクォーテーションを付け忘れている場合に形式エラー発生
resource "azurerm_api_management" "common" {
name =
"rag-apim-${var.client}-${var.environment}"
〜〜〜〜〜以下略〜〜〜〜〜
【terraform applyコマンド実行時に出たエラー】
- LinuxOSのAzure Functionsを立ち上げたい場合、App Service Planのreserved項目がfalseになっていると設定の不整合エラー発生
- Linux OSのAzure Functionsを立ち上げたい場合、App Service Planのreservedパラメータをtrueに設定する必要があります。これがfalseのままだとOSタイプとの整合性が取れず、デプロイ時に設定不整合のエラーが発生します。
resource "azurerm_app_service_plan" "insertdb-function" {
name = "rag-appservice-${var.client}-${var.environment}"
location = data.azurerm_resource_group.rg.location
resource_group_name = data.azurerm_resource_group.rg.name
kind = "Linux"
reserved = true
sku {
tier = "Standard"
size = "S1"
}
}
- Azure Application Insightsでoutput項目を記載している場合に重要情報出力エラー発生
output "instrumentation_key" {
value = azurerm_application_insights.example.instrumentation_key
}
動作確認
GitHubリポジトリのActionsからサイドバーにある作成したActionsを選択してRun workflowをクリックします。必要事項を入力して緑のRun workflowをクリックするとActionsが動き出します。

workflow runsから任意の動作結果を選択するとログを確認できます。

まとめ
今回はGitHub ActionsとTerraformを用いたAzure Functionsの作成自動化をご紹介しました。
何度も同じシステムを構築しないといけない場合はかなり業務効率化になると思います。
次回はAzure Functionsのコードデプロイもご紹介できればと考えているのでそちらも参考にしていただけると幸いです。
この記事を書いた人

Advent Calendar!
Advent Calendar 2024
What is BEMA!?
Be Engineer, More Agile