Microsoft AzureのBlobストレージを操作するAPEXアプリケーションを作ってみました。Azure Blob Storageにアクセスするために、パッケージDBMS_CLOUDに含まれるプロシージャを呼び出します。
認証に必要なAzureのサービス・プリンシパルの作成は、オラクルの公式ブログに、Product ManagerのCan Tuzlaさんが公開している記事の手順に沿って実施します。
How to Easily Access Azure Resources from Your Autonomous Database作成したアプリケーションは以下のように動作します。実際にはこちらの記事で作成したGoogle Cloud Storageにアクセスするアプリケーションとほとんど同じです。ファイルのダウンロードを行なうボタンを追加してみました。
Oracle APEXよりMicrosoft Azureのストレージにアクセスするにあたって、Azure側で実施した作業を以下より記述します。
最初にAzureのポータルよりMicrosoft Entra IDの概要ページを開き、テナントIDをコピーしておきます。この値は、Autonomous Database側でPrincipal Authenticationを有効にする際の引数として使用します。
Azureのポータルにてリソースグループを開き、作成をクリックします。
サブスクリプションおよびリージョンについては、それぞれの構成に合わせて選択します。
確認および作成をクリックします。タグはスキップします。
作成をクリックします。
リソースグループが作成されます。
Azureのポータルからストレージアカウントを開き、作成をクリックします。
リソースグループには、先ほど作成したリソースグループを選択します。
ストレージアカウント名は、Azure Blob Storageにアクセスする際のURLに含まれる値になります。今回の例ではoracleapex2024としました。
アクセスの確認を行なうだけなので、パフォーマンスはStandard、冗長性は一番低いローカル冗長ストレージ(LRS)を選択します。
その他の詳細設定などはデフォルトの値を採用します。
レビューをクリックします。
作成をクリックします。
作成をクリックするとデプロイが進行します。デプロイが完了すると、ストレージアカウントの作成は完了です。
リソースに移動します。
ストレージアカウントの作成直後には、Blobを保存するためのコンテナーがありません。ナビゲーションメニューのコンテナーまたはプロパティのBlob serviceをクリックして、ストレージアカウントのコンテナー一覧の画面を開きます。
新しいコンテナーの名前を指定します。今回の例ではmy-apex-filesとしました。
作成をクリックします。
新しくコンテナーが作成されました。
Autonomous Database側の作業を行います。
AzureのPrincipal Authenticationを有効にします。
APEXのワークスペース・スキーマWKSP_APEXDEVを対象とします。Azureのポータルより取得したテナントIDをparamsのazure_tenantidに指定します。
ユーザーADMINにて、DBMS_CLOUD_ADMIN.ENABLE_PRINCIPAL_AUTHを実行します。
begin
dbms_cloud_admin.enable_principal_auth(
provider => 'AZURE'
,params => json_object('azure_tenantid' value '********-****-****-****-************')
,username => 'WKSP_APEXDEV'
);
end;
有効になったAzureのプリンシパルに関する情報を確認します。
以下のSELECT文を実行します。
select param_name, param_value from cloud_integrations where param_name like 'azure%'
Azureのポータルを開いているブラウザより、azure_consent_urlとして得られたURLにアクセスします。
アクセス許可を要求する画面が開きます。承諾をクリックすると(なぜかOracle Cloudのサインイン画面に移動しますが)Microsoft Entra IDにazure_app_nameの値がアプリケーション名である、エンタープライズアプリケーションが作成されます。
Microsoft Entra IDの画面からエンタープライズアプリケーションを一覧します。
Autonomous Databaseでazure_app_nameとして得られた値を名前とした、エンタープライズアプリケーションがあることが確認できます。
ストレージアカウントにロールを割り当てます。
作成済みのストレージアカウントを開き、メニューからアクセス制御(IAM)を開きます。
ロールの割り当ての追加をクリックします。
割り当てるロールとしてストレージBLOBデータ所有者を選択します。カテゴリにストレージを選ぶと、見つけやすいでしょう。
次へ進みます。
サービスプリンシパルをメンバーとするので、アクセスの割り当て先はユーザー、グループ、またはサービスプリンシパルです。
メンバーを選択するをクリックします。
メンバーを選択するの画面には、最初はユーザーがリストされています。
選択にazure_app_nameの値(作成されたエンタープライズアプリケーションの名前)を入力すると、そのアプリケーションが選択可能になります。
選択可能になったアプリケーションをクリックし、選択します。
選択したメンバーを確定します。
選択をクリックします。
確認画面が開くので再度、レビューと割り当てをクリックします。
ロール割り当てのタブを開き、ストレージBLOBデータ所有者としてエンタープライズアプリケーションが追加されていることを確認します。
以上でAzure Blob Storageの準備は完了です。
Azure Blob Storageを操作するAPEXアプリケーションについて、Google Cloud Storageのアプリケーションとの差異について説明します。
アプリケーション定義の置換では、置換文字列としてG_STORAGE_ACCOUNT_NAMEとG_CONTAINER_NAMEを定義しています。Azure Blob StorageにアクセスするAPIエンドポイントは、これらの値から構成されます。
select object_name id, object_name, bytes, checksum, created, last_modified
from dbms_cloud.list_objects(
credential_name => 'AZURE$PA'
,location_uri => 'https://' || :G_STORAGE_ACCOUNT_NAME || '.blob.core.windows.net/' || :G_CONTAINER_NAME || '/'
)
フォームのソースのSQL問合せも、同じSELECT文に置き換えています。
プロセスUpload Filesのコードは、以下に置き換えています。Blobファイルのアップロードに必須であるヘッダーx-ms-blob-typeを指定しています。
declare
l_uri varchar2(32767);
begin
for r in (
select * from apex_application_temp_files
where name in (
select column_value from apex_string.split(:P3_FILES,':')
)
)
loop
l_uri := 'https://' || :G_STORAGE_ACCOUNT_NAME || '.blob.core.windows.net/' || :G_CONTAINER_NAME || '/'
|| utl_url.escape(r.filename, false, 'AL32UTF8');
dbms_cloud.send_request(
credential_name => 'AZURE$PA'
,uri => l_uri
,headers => json_object(
'Content-Type' value r.mime_type
,'x-ms-blob-type' value 'BlockBlob'
)
,method => DBMS_CLOUD.METHOD_PUT
,body => r.blob_content
);
end loop;
end;
プロセスDelete Fileのコードは、以下に置き換えています。
declare
l_uri varchar2(32767);
begin
l_uri := 'https://' || :G_STORAGE_ACCOUNT_NAME || '.blob.core.windows.net/' || :G_CONTAINER_NAME || '/'
|| utl_url.escape(:P3_ID, false, 'AL32UTF8');
dbms_cloud.send_request(
credential_name => 'AZURE$PA'
,uri => l_uri
,method => DBMS_CLOUD.METHOD_DELETE
);
end;
ファイル名を保持するページ・アイテムP4_FILE_NAMEと、ヘッダーの前にダウンロード処理を実行するプロセスDownloadを作成しています。
ソースのPL/SQLコードとして以下を記述しています。
declare
l_object_uri varchar2(400);
l_blob blob;
l_download apex_data_export.t_export;
begin
l_object_uri := 'https://' || :G_STORAGE_ACCOUNT_NAME || '.blob.core.windows.net/' || :G_CONTAINER_NAME
|| '/' || utl_url.escape(:P4_FILE_NAME, false, 'AL32UTF8');
l_blob := dbms_cloud.get_object(
credential_name => 'AZURE$PA'
,object_uri => l_object_uri
);
l_download.file_name := :P4_FILE_NAME;
l_download.mime_type := 'application/octet-stream';
l_download.as_clob := FALSE;
l_download.content_blob := l_blob;
apex_data_export.download( p_export => l_download );
apex_application.stop_apex_engine;
end;
ファイルのダウンロードを呼び出すボタンをフォームのページに作成しています。
ボタンDownloadをクリックすると、ダウンロード処理を実装したページにリダイレクトします。
ファイルが選択されているときだけボタンDownloadが表示されるよう、サーバー側の条件のタイプにアイテムはNULLではない、アイテムとしてP3_IDを指定します。
ターゲットのタイプはこのアプリケーションのページ、ページはダウンロード処理が実装されているページ番号4を指定しています。ページ・アイテムP4_FILE_NAMEには&P3_OBJECT_NAME.を渡しています。
以上でAzure Blob StorageにアクセスするAPEXアプリケーションは完成です。
今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-azure-storage-access.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完