0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

イベント サブスクリプションを使用して BLOB コンテナーで Azure Functions をトリガーしたメモ

Last updated at Posted at 2024-09-26

概要

マネージドIDでSQLにアクセスマネージドIDでキュートリガーを試したので、Blobトリガーを試してみる。

ソースコード

コード

チュートリアル: イベント サブスクリプションを使用して BLOB コンテナーで Azure Functions をトリガーするを参考に作成。

packages/batch-functions/src/functions/BlobTriggerEventGrid.ts
import { app, InvocationContext, output } from '@azure/functions';
import { isBlobTriggerMetadata } from '../domain/blobTrigger';

const sqlOutput = output.sql({
  commandText: '[dbo].[YakumiCharacter]',
  connectionStringSetting: 'DATABASE_CONNECTION_STRING',
});

export async function BlobTriggerEventGrid(
  blob: unknown, // Buffer
  context: InvocationContext,
): Promise<void> {
  console.log('context'.endsWith('json'), JSON.stringify(context));
  if (!isBlobTriggerMetadata(context.triggerMetadata)) {
    context.warn('context.triggerMetadata is not BlobTriggerMetadata');
    return;
  }
  // zipファイルは処理しない
  if (!context.triggerMetadata.blobTrigger.endsWith('json')) return;
  if (!Buffer.isBuffer(blob)) return;
  const blobContent = blob.toString('utf-8');
  const data = JSON.parse(blobContent);
  const { uid, characterId } = data;
  if (!uid || !characterId) {
    context.log('obj is not Character', data);
    return;
  }
  context.extraOutputs.set(sqlOutput, {
    id: characterId,
    uid,
    data: blobContent,
    updated: new Date(),
  });
}

app.storageBlob('BlobTriggerEventGrid', {
  path: 'web',
  source: 'EventGrid',
  connection: 'TARGET_STORAGE_ACCOUNT',
  handler: BlobTriggerEventGrid,
  extraOutputs: [sqlOutput],
});

インフラ

infrastructure/bin/createBatch.bash
#!/bin/bash

BIN_DIR=$(cd $(dirname $0) && pwd)
BICEP_DIR=$(cd $BIN_DIR/../biceps && pwd)

# 環境変数読み込み
source $BIN_DIR/.env

cd $BICEP_DIR && az deployment group create \
  --name ExampleDeployment \
  --template-file batch.bicep \
  --parameters batch.bicepparam \
  --parameters \
    functionAppName=$BATCH_NAME \
    uploadStroageAccountName=$UPLOAD_STORAGE_ACCOUNT_NAME \
    sqlServerName=$DB_SERVER_NAME \
    sqlServerDbName=$SQLSERVER_DB_NAME \
  -g $RESOURCE_GROUP_NAME
infrastructure/bin/addBatchTrigger.bash
#!/bin/bash

BIN_DIR=$(cd $(dirname $0) && pwd)
BICEP_DIR=$(cd $BIN_DIR/../biceps && pwd)

# 環境変数読み込み
source $BIN_DIR/.env

EXT_KEY=$(az functionapp keys list -g yakumi -n nkymspaiut5dsazfunctionsapp |  jq -r '.systemKeys.blobs_extension'  | tr -d '[:space:]' )

cd $BICEP_DIR && az deployment group create \
  --name ExampleDeployment \
  --template-file eventGrid.bicep \
  --parameters \
    functionAppName=$BATCH_NAME \
    blobExtensionKey=$EXT_KEY \
    uploadStroageAccountName=$UPLOAD_STORAGE_ACCOUNT_NAME \
  -g $RESOURCE_GROUP_NAME

infrastructure/biceps/core/eventGrid/subscription.bicep
param functionAppName string
param blobExtensionKey string

var eventSubName = 'subToStorage'
var systemTopicName = 'mystoragesystemtopic'

resource systemTopic 'Microsoft.EventGrid/systemTopics@2023-12-15-preview' existing = {
  name: systemTopicName
}

var endpoint = 'https://${functionAppName}.azurewebsites.net/runtime/webhooks/blobs?functionName=Host.Functions.BlobTriggerEventGrid&code=${blobExtensionKey}'

resource eventSubscription 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2023-12-15-preview' = {
  parent: systemTopic
  name: eventSubName
  properties: {
    destination: {
      properties: {
        endpointUrl: endpoint
      }
      endpointType: 'WebHook'
    }
    filter: {
      includedEventTypes: [
        'Microsoft.Storage.BlobCreated' // 	BLOB が作成または置換されたときにトリガー.  https://learn.microsoft.com/ja-jp/azure/event-grid/event-schema-blob-storage
      ]
    }
  }
}

マネージドIDでBlobに対するロールの付与が必要。
キューに対するロールはこちらの方法だとblobトリガーと異なり不要となる。

infrastructure/biceps/core/eventGrid/topic.bicep
param location string
param uploadStroageAccountName string

var systemTopicName = 'mystoragesystemtopic'


resource uploadedStorageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = {
  name: uploadStroageAccountName
}

resource systemTopic 'Microsoft.EventGrid/systemTopics@2023-12-15-preview' = {
  name: systemTopicName
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    source: uploadedStorageAccount.id
    topicType: 'Microsoft.Storage.StorageAccounts'
  }
}
infrastructure/biceps/batch.bicep
param location string = resourceGroup().location
param functionsRuntime object
param functionAppName string
param uploadStroageAccountName string
param sqlServerName string
param sqlServerDbName string

var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions'
var applicationInsightsName = '${uniqueString(resourceGroup().id)}applicationinsights'
var logAnalyticsName = '${uniqueString(resourceGroup().id)}logAnalytics'
var environments = [
  {
    name: 'TARGET_STORAGE_ACCOUNT__accountName'
    value: uploadStroageAccountName
  }
  {
    name: 'TARGET_STORAGE_ACCOUNT__blobServiceUri'
    value: 'https://${uploadStroageAccountName}.blob.core.windows.net'
  }
  {
    name: 'TARGET_STORAGE_ACCOUNT__queueServiceUri'
    value: 'https://${uploadStroageAccountName}.queue.core.windows.net'
  }
  {
    name: 'DATABASE_CONNECTION_STRING'
    value: 'Server=tcp:${sqlServerName}.database.windows.net;Authentication=Active Directory Default; Database=${sqlServerDbName};'
  }
]
module myFunctionsApplicationInsights 'core/host/applications.bicep' = {
  name: 'myFunctionsApplicationInsights'
  params: {
    location: location
    applicationInsightsName: applicationInsightsName
    logAnalyticsName: logAnalyticsName
  }
}

module myFunctionsStorage 'core/storage/storage-account.bicep' = {
  name: 'myFunctionsStorage'
  params: {
    location: location
    storageAccountName: storageAccountName
  }
}

module myFunctions 'core/host/functions.bicep' = {
  name: 'myFunctions'
  params: {
    functionAppName: functionAppName
    location: location
    storageAccountName: storageAccountName
    kind: functionsRuntime.kind
    runtime: functionsRuntime.runtime
    linuxFxVersion: functionsRuntime.linuxFxVersion
    extensionVersion: functionsRuntime.extensionVersion
    applicationInsightsInstrumentationKey: myFunctionsApplicationInsights.outputs.applicationInsightsInstrumentationKey
    environments: environments
  }
}

// 組み込みロール: https://learn.microsoft.com/ja-jp/azure/role-based-access-control/built-in-roles
var storageRoleDefinitionId= 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' // ストレージ BLOB データ共同作成者
var principalId = myFunctions.outputs.principalId
module myStorageRole 'core/rbac/role.bicep' = {
  name: 'myStorageRole'
  params: {
    uploadStroageAccountName: uploadStroageAccountName
    principalId: principalId
    roleDefinitionId: storageRoleDefinitionId
  }
}

デプロイ

Functionsをデプロイしてblobs_extensionが作成されたのちにEventGridを設定する流れとする。

./infrastructure/bin/createBatch.bash
cd packages/batch-functions && npm run deploy
./infrastructure/bin/addBatchTrigger.bash

確認

BlobトリガーでデータをDBに保存できた。*

参考: contextからとれる情報

第2引数。第1引数はBlobデータがBuffer型で入っている。

{
  "invocationId": "00000000-0000-0000-0000-000000000000",
  "functionName": "BlobTriggerEventGrid",
  "extraInputs": {},
  "extraOutputs": {},
  "traceContext": {
    "traceParent": "00-00000000000000000000000000000000-0000000000000000-00",
    "traceState": "",
    "attributes": { "OperationName": "BlobTriggerEventGrid" }
  },
  "triggerMetadata": {
    "blobTrigger": "$web/sample-path/character-data/path-to-blob/sample-file.json",
    "uri": "https://example.blob.core.windows.net/$web/sample-path/character-data/path-to-blob/sample-file.json",
    "properties": {
      "lastModified": "2024-09-25T22:53:51+00:00",
      "createdOn": "2024-05-27T13:46:29+00:00",
      "metadata": {},
      "objectReplicationDestinationPolicyId": null,
      "objectReplicationSourceProperties": null,
      "blobType": 0,
      "copyCompletedOn": "0001-01-01T00:00:00+00:00",
      "copyStatusDescription": null,
      "copyId": null,
      "copyProgress": null,
      "copySource": null,
      "copyStatus": 0,
      "blobCopyStatus": null,
      "isIncrementalCopy": false,
      "destinationSnapshot": null,
      "leaseDuration": 0,
      "leaseState": 0,
      "leaseStatus": 1,
      "contentLength": 362814,
      "contentType": "application/octet-stream",
      "eTag": {},
      "contentHash": "00000000000000000000000000000000",
      "contentEncoding": null,
      "contentDisposition": null,
      "contentLanguage": null,
      "cacheControl": null,
      "blobSequenceNumber": 0,
      "acceptRanges": "bytes",
      "blobCommittedBlockCount": 0,
      "isServerEncrypted": true,
      "encryptionKeySha256": null,
      "encryptionScope": null,
      "accessTier": "Hot",
      "accessTierInferred": true,
      "archiveStatus": null,
      "accessTierChangedOn": "0001-01-01T00:00:00+00:00",
      "versionId": null,
      "isLatestVersion": false,
      "tagCount": 0,
      "expiresOn": "0001-01-01T00:00:00+00:00",
      "isSealed": false,
      "rehydratePriority": null,
      "lastAccessed": "0001-01-01T00:00:00+00:00",
      "immutabilityPolicy": { "expiresOn": null, "policyMode": null },
      "hasLegalHold": false
    },
    "metadata": {}
  },
  "options": {
    "trigger": {
      "path": "web",
      "source": "EventGrid",
      "connection": "TARGET_STORAGE_ACCOUNT",
      "type": "blobTrigger",
      "name": "blobTrigger00000000",
      "direction": "in"
    },
    "extraInputs": [],
    "extraOutputs": []
  }
}

検討

マネージド関数ではBlobトリガーを作れないので普通の関数として作成。
apis-functions
image.png

参考

Azure Functions の Azure SQL 出力バインド
SQLバインド接続文字列 ConnectionString マネージド ID 接続の使用

Azure Functions の Azure Blob Storage トリガー
クイック スタート: Bicep を使用して BLOB ストレージ イベントを Web エンドポイントにルーティングする

Blob Event Grid Triggerを動かしてみる
Event Grid経由でのBlob storage triggerでBlobの変更をSlackに自動通知する
EventGridを使用してAzureリソースのデプロイ完了をフックする
Event Grid ソースとしての Azure Blob Storage

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?