Autonomous Database：Azureの仮想マシンを起動/停止するPL/SQLプロシージャを作ってみた

# はじめに
AzureのJapan EastリージョンでAutonomous Database@Azureが利用できるようになりました。
それを記念して、SQLコマンドを使用してAutonomous DatabaseからAzureの仮想マシンを起動/停止ができたら便利かと思い、PL/SQLプロシージャを作成してみました。

例えば、PL/SQLを使用したバッチ処理を実行する際、開始時にユーザアクセスを遮断するために仮想マシンを停止し、終了時に仮想マシンを再度起動するといったワークフローが実現可能になります。

※こちらの記事の内容はあくまで個人の実験メモ的な内容のため、こちらの内容を利用した場合のトラブルには一切責任を負いません。
また、こちらの記事の内容を元にしたOracleサポートへの問い合わせはご遠慮ください。

# 1. 事前準備

あらかじめ、Azureポータルで以下の情報を確認し、メモしておきます。
・AzureのテナントID
・AzureのサブスクリプションID
・起動/停止の対象となる仮想マシンのリソース・グループ
・起動/停止の対象となる仮想マシンの名前

# 2. Azureサービス・プリンシパル認証の有効化
adminユーザとして、Autonomous Databaseに接続します。
```
[oracle@oracle23ai ~]$ sqlplus admin/Demo#1Demo#1@atp23ai

SQL*Plus: Release 23.0.0.0.0 - Production on Fri Feb 7 07:54:39 2025
Version 23.4.0.24.05

Copyright (c) 1982, 2024, Oracle.  All rights reserved.

Last Successful login time: Thu Feb 06 2025 23:05:33 +09:00

Connected to:
Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - Production
Version 23.7.0.25.02

SQL>
```
DBMS_CLOUD_ADMIN.ENABLE_PRINCIPAL_AUTHプロシージャを使用して、Azureサービス・プリンシパル認証を有効化します。
providerとして「AZURE」、paramsの「azure_tenantid」のvalueにはAzureのテナントIDを指定します。
```
SQL> BEGIN
  2      DBMS_CLOUD_ADMIN.ENABLE_PRINCIPAL_AUTH(
  3          provider => 'AZURE',
  4          username => 'admin',
  5          params   => JSON_OBJECT('azure_tenantid' value 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')
  6      );
  7  END;
  8  /

PL/SQL procedure successfully completed.

SQL>
```
cloud_integrationsビューから設定に必要な情報を入手します。
```
SQL> col param_name for a20
SQL> SELECT param_name, param_value FROM cloud_integrations
  2  WHERE param_name LIKE 'azure%';


PARAM_NAME
--------------------
PARAM_VALUE
------------------------------------------------------------------------------------------------------------------------
azure_app_name
ADBS_APP_OCID1.AUTONOMOUSDATABASE.OC1.AP-TOKYO-1.ANXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXLMQ

azure_consent_url
https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/oauth2/v2.0/authorize?client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&response_type=code&scope=User.read

azure_tenantid
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx


SQL>
```
azure_app_nameとazure_consent_urlの値をメモしておきます。
azure_app_nameは、Autonomous DatabaseがAzureでエンタープライズ・アプリケーションとして登録される際のアプリケーション名です。
azure_consent_urlは、Azure側のアプリケーション同意のためのURLです。

# 3. Azure側のアプリケーション同意

ブラウザでAzureポータルにサインインした状態で、2.で取得したアプリケーション同意のためのURLにアクセスし、「承諾」をクリックします。
![スクリーンショット 2025-02-07 8.04.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/793fac65-53b9-0b9d-7dfc-c8fb34ce8b39.png)

Azureポータルでエンタープライズ・アプリケーションを確認すると、Autonomous Databaseが登録されていることが確認できます。

![スクリーンショット 2025-02-07 8.15.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/41688ca2-4ce1-7191-8a92-492279864ca7.png)

# 4. 仮想マシンを操作するためのロールの割り当て
Azureポータルで、操作の対象となる仮想マシンの「アクセス制御（IAM）」のページにアクセスします。
![スクリーンショット 2025-02-07 8.11.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/8fa15274-d31a-1288-bf7d-130e3345e186.png)

「＋追加」をクリックし、「ロールの追加」をクリックします。
![スクリーンショット 2025-02-07 8.11.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/135e6208-d2a2-6c9d-5e23-d7b03f1d6a32.png)

割り当てるロールとして、適切なロールを選択し、「次へ」をクリックします。
ここでは「仮想マシン共同作成者」を選択しています。
![スクリーンショット 2025-02-07 8.12.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/7d9213ac-4984-2b4e-a35b-7e0e84a818c3.png)

「＋メンバーを選択する」をクリックします。
![スクリーンショット 2025-02-07 8.12.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/9b847d58-6d30-ef14-0d93-8c44e12023bb.png)

ADBと入力して検索し、エンタープライズ・アプリケーションに追加されたAutonomous Databaseを選択し、「選択」をクリックします。
![スクリーンショット 2025-02-07 8.13.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/7a5d9d37-6ecc-8b29-be85-b0e067d8bd54.png)

「レビューと割り当て」をクリックします。
![スクリーンショット 2025-02-07 8.13.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/2bd404c3-2908-f997-ad17-f344694e2b19.png)

「レビューと割り当て」をクリックして、ロールを割り当てます。
![スクリーンショット 2025-02-07 8.14.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/154462e2-de6e-ce63-f43a-cee66e2e9fb4.png)

Autonomous Databaseに対して、ロール「仮想マシン共同作成者」が割り当てられました。
![スクリーンショット 2025-02-07 8.14.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/5a10bccb-9f57-89cb-8d37-00d0a97ea0b1.png)



# 5. 仮想マシンを起動するPL/SQLプロシージャの作成
パラメータとしてAzureのサブスクリプションID、仮想マシンのリソース・グループ名、仮想マシン名を渡すと、対象の仮想マシンを起動するPL/SQLプロシージャstart_azure_vmを作成します。

このプロシージャでは、DBMS_CLOUD.SEND_REQUESTファンクションを使用して、Azure仮想マシンを起動するためのREST APIをコールしています。
```sql:start_azure_vmプロシージャ
CREATE OR REPLACE PROCEDURE start_azure_vm (subscription_id IN VARCHAR2, res_grp_name IN VARCHAR2, vm_name IN VARCHAR2)
IS
    resp    DBMS_CLOUD_TYPES.resp;
    api_url VARCHAR2(4000);
BEGIN
    -- APIをコールするためのURLを生成
    api_url := 'https://management.azure.com/subscriptions/'||subscription_id||
               '/resourceGroups/'||res_grp_name||
               '/providers/Microsoft.Compute/virtualMachines/'||vm_name||
               '/start?api-version=2024-07-01';
    -- 仮想マシンを起動するAPIをコール
    resp := DBMS_CLOUD.SEND_REQUEST(
                credential_name => 'AZURE$PA',
                uri             => api_url,
                method          => DBMS_CLOUD.METHOD_POST,
                headers         => '{"Content-Length":0}',
                timeout         => 600
            );
    -- レスポンスのステータスコードを表示
    DBMS_OUTPUT.PUT_LINE('Status Code: ' || CHR(10) || '------------' || CHR(10) ||
    DBMS_CLOUD.GET_RESPONSE_STATUS_CODE(resp));
END;
```

上記のSQLを実行して、プロシージャを作成します。
```
SQL> CREATE OR REPLACE PROCEDURE start_azure_vm (subscription_id IN VARCHAR2, res_grp_name IN VARCHAR2, vm_name IN VARCHAR2)
  2  IS
  3  	 resp	 DBMS_CLOUD_TYPES.resp;
  4  	 api_url VARCHAR2(4000);
  5  BEGIN
  6  	 api_url := 'https://management.azure.com/subscriptions/'||subscription_id||
  7  		    '/resourceGroups/'||res_grp_name||
  8  		    '/providers/Microsoft.Compute/virtualMachines/'||vm_name||
  9  		    '/start?api-version=2024-07-01';
 10  
 11  	     resp := DBMS_CLOUD.SEND_REQUEST(
 12  		     credential_name => 'AZURE$PA',
 13  		     uri	     => api_url,
 14  		     method	     => DBMS_CLOUD.METHOD_POST,
 15  		     headers	     => '{"Content-Length":0}',
 16  		     timeout	     => 600
 17  		 );
 18  
 19  	     DBMS_OUTPUT.PUT_LINE('Status Code: ' || CHR(10) || '------------' || CHR(10) ||
 20  	 DBMS_CLOUD.GET_RESPONSE_STATUS_CODE(resp));
 21  END;
 22  /

Procedure created.

SQL>
```

# 6. 仮想マシンを停止するPL/SQLプロシージャの作成
パラメータとしてAzureのサブスクリプションID、仮想マシンのリソース・グループ名、仮想マシン名を渡すと、対象の仮想マシンを停止するPL/SQLプロシージャstop_azure_vmを作成します。

このプロシージャでは、DBMS_CLOUD.SEND_REQUESTファンクションを使用して、Azure仮想マシンを停止するためのREST APIをコールしています。
```sql:stop_azure_vmプロシージャ
CREATE OR REPLACE PROCEDURE stop_azure_vm (subscription_id IN VARCHAR2, res_grp_name IN VARCHAR2, vm_name IN VARCHAR2)
IS
    resp    DBMS_CLOUD_TYPES.resp;
    api_url VARCHAR2(4000);
BEGIN
    -- APIをコールするためのURLを生成
    api_url := 'https://management.azure.com/subscriptions/'||subscription_id||
               '/resourceGroups/'||res_grp_name||
               '/providers/Microsoft.Compute/virtualMachines/'||vm_name||
               '/deallocate?api-version=2024-07-01';
    -- 仮想マシンを停止するAPIをコール
    resp := DBMS_CLOUD.SEND_REQUEST(
                credential_name => 'AZURE$PA',
                uri             => api_url,
                method          => DBMS_CLOUD.METHOD_POST,
                headers         => '{"Content-Length":0}',
                timeout         => 600
            );
    -- レスポンスのステータスコードを表示
    DBMS_OUTPUT.PUT_LINE('Status Code: ' || CHR(10) || '------------' || CHR(10) ||
    DBMS_CLOUD.GET_RESPONSE_STATUS_CODE(resp));
END;
```

上記のSQLを実行して、プロシージャを作成します。
```
SQL> CREATE OR REPLACE PROCEDURE stop_azure_vm (subscription_id IN VARCHAR2, res_grp_name IN VARCHAR2, vm_name IN VARCHAR2)
  2  IS
  3  	 resp	 DBMS_CLOUD_TYPES.resp;
  4  	 api_url VARCHAR2(4000);
  5  BEGIN
  6  	 api_url := 'https://management.azure.com/subscriptions/'||subscription_id||
  7  		    '/resourceGroups/'||res_grp_name||
  8  		    '/providers/Microsoft.Compute/virtualMachines/'||vm_name||
  9  		    '/deallocate?api-version=2024-07-01';
 10  
 11  	     resp := DBMS_CLOUD.SEND_REQUEST(
 12  		     credential_name => 'AZURE$PA',
 13  		     uri	     => api_url,
 14  		     method	     => DBMS_CLOUD.METHOD_POST,
 15  		     headers	     => '{"Content-Length":0}',
 16  		     timeout	     => 600
 17  		 );
 18  
 19  	     DBMS_OUTPUT.PUT_LINE('Status Code: ' || CHR(10) || '------------' || CHR(10) ||
 20  	 DBMS_CLOUD.GET_RESPONSE_STATUS_CODE(resp));
 21  END;
 22  /

Procedure created.

SQL>
```


# 6. 動作確認
Azureポータルで、対象となる仮想マシンの状態を確認します。
現在は「停止済み（割り上げ解除）」になっています。
![スクリーンショット 2025-02-07 8.17.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/188bc2a9-3bd5-7951-9c06-7d5439694222.png)

サブスクリプションID、仮想マシンのリソース・グループ名、仮想マシン名を指定して、PL/SQLプロシージャstart_azure_vmを実行します。
```
SQL> set serveroutput on
SQL> EXEC start_azure_vm('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'MyResourceGroup', 'TestAzureVM')
Status Code: 
------------
202

PL/SQL procedure successfully completed.

SQL> 
```

PL/SQLプロシージャstart_azure_vmの実行が成功し、レスポンスのステイタス・コードとして202が返ってきました。
ステイタス・コード202は「Accepted」を意味しますので、リクエストが受け入れられたことがわかります。

https://learn.microsoft.com/ja-jp/rest/api/compute/virtual-machines/start?view=rest-compute-2024-11-04&tabs=HTTP#response

少し待ってから「最新の情報に更新」をクリックすると、状態が「実行中」になりました。
![スクリーンショット 2025-02-07 8.35.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/6bc6571b-a289-b300-b9c9-2134e06a31d2.png)

アクティビティログを確認します。
![スクリーンショット 2025-02-07 8.31.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/0df03572-3466-7757-6e56-b5d594aa2f32.png)
「Start Virtual Machine」の操作がAutonomous Databaseによって開始されたことが確認できました。

次に、サブスクリプションID、仮想マシンのリソース・グループ名、仮想マシン名を指定して、PL/SQLプロシージャstop_azure_vmを実行します。
```
SQL> set serveroutput on
SQL> EXEC stop_azure_vm('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'MyResourceGroup', 'TestAzureVM')
Status Code: 
------------
202

PL/SQL procedure successfully completed.

SQL>
```

PL/SQLプロシージャstop_azure_vmの実行が成功し、レスポンスのステイタス・コードとして202が返ってきました。
ステイタス・コード202は「Accepted」を意味しますので、リクエストが受け入れられたことがわかります。

https://learn.microsoft.com/ja-jp/rest/api/compute/virtual-machines/deallocate?view=rest-compute-2024-11-04&tabs=HTTP#response

少し待ってから「最新の情報に更新」をクリックすると、状態が「停止済み（割り当て解除）」になりました。
![スクリーンショット 2025-02-07 8.41.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/2221bb74-e20a-bf7a-596a-4faa79b736d0.png)

アクティビティログを確認します。
![スクリーンショット 2025-02-07 8.31.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/0df03572-3466-7757-6e56-b5d594aa2f32.png)

「Deallocate Virtual Machine」の操作がAutonomous Databaseによって開始されたことが確認できました。
![スクリーンショット 2025-02-07 8.27.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/d38c3eee-238d-89ee-0319-8420a567b81f.png)


# 参考情報
・[DBMS_CLOUD_ADMIN.ENABLE_PRINCIPAL_AUTHプロシージャ](https://docs.oracle.com/cd/E83857_01/paas/autonomous-database/serverless/adbsb/dbms-cloud-admin.html#GUID-DB61623B-E4AF-4F82-9AFE-DF58601D4852)
・[DBMS_CLOUD.SEND_REQUESTファンクションおよびプロシージャ](https://docs.oracle.com/cd/E83857_01/paas/autonomous-database/serverless/adbsb/dbms-cloud-subprograms.html#GUID-B063870D-6C1F-4F33-B354-885B73C81D37)
・[REST API：Virtual Machines - Start](https://learn.microsoft.com/ja-jp/rest/api/compute/virtual-machines/start?view=rest-compute-2024-11-04&tabs=HTTP)
・[REST API：Virtual Machines - Deallocate](https://learn.microsoft.com/ja-jp/rest/api/compute/virtual-machines/deallocate?view=rest-compute-2024-11-04&tabs=HTTP)
・[Autonomous DatabaseからAzureリソースへの簡単なアクセス](https://blogs.oracle.com/oracle4engineer/post/how-to-easily-access-azure-resources-from-your-autonomous-database-ja)