Azure / M365の更新をパワポにしてくれるアプリを使ってみた件(前編)

運用フェイズに入っていくと割とどこのお客様でもAzureのアップデート情報\([Azure Update](https://azure.microsoft.com/ja-jp/updates)\)をサマライズして定期的に出して欲しい、というご要望があるという話があるようで、こんなツールがあるよと云う話を社内で聞いたので実際に動かしてみました。
アップデートを取ってくるAPI(非公開だそう)ができたそうで、それが使われているようです。

本記事では、仮想ネットワークの設定から一通り書いているので、ベーススキルをお持ちの方は見出し単位で飛ばしながら見ていってくださると良いと思います。

# ツールのありか
[GitHub - koudaiii/AzureUpdatePPTX: Azure Updates Summary](https://github.com/koudaiii/AzureUpdatePPTX)
神ツールです。

# 作った環境
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330959/903cf077-2061-43b2-82f6-77929568ebe2.png)


* 仮想マシン・・・ツールの実行基盤。以降は、アプリサーバーと称する。
* Azure OpenAI・・・アップデート内容を3行に要約するAI機能
* プライベートエンドポイント・・・Azure OpenAI Serviceを閉域で接続するエンドポイント
* Application Gateway・・・外部公開するにあたりサーバーを守るためのNWリソースおよびWeb Application Firewall機能
* Azure Bastion・・・仮想マシンを設定するためのジャンプサーバー

:::note info
プライベートエンドポイント、Application Gateway、Azure Bastionは省略可能です。その場合は一部の設定を変える必要があります。
:::

# 機能と各リソースの役割
このツールをアプリサーバー(仮想マシン)で動作させると、Webブラウザ経由でアクセスして使えるようになります。Bationはツールの導入のため仮想マシンを操作するために使います。
ツールのREADMEにある通り、内部的にAzure OpenAIのGPT-4oモデルを使うとのことで構成していますが、閉域で安全に利用するためにプライベートエンドポイント経由での構成にしています。
今回は敢えてこのツールをインターネット経由でも利用できるようにするため、Application Gateway(WAF)を置いています。プライベートのみでツールを使ってみる場合にはApplication Gatewayは不要で、ExpressRouteやVPN経由での接続、あるいはクライアント仮想マシンをこの環境にもう一つ置いてそこからアクセスすることができます。

# 事前の作業①　仮想ネットワークの準備
最初にこの環境のための仮想ネットワークを準備します。
1\. Azureポータルで、[仮想ネットワーク] を検索して選択します。
2\. [仮想ネットワーク] ページで、[+ 作成] を選択します。
3\. [仮想ネットワークの作成] の [基本] タブで、以下の情報を入力するか選択します。

|設定|値|
|:-|:-|
|**プロジェクトの詳細**||
|サブスクリプション|使用しているサブスクリプション|
|リソースグループ|[新規作成] を選択し、名前に「rg-getAzureUpdate」と入力。|
|**インスタンスの詳細**||
|名前|vnet-1|
|リージョン|(Asia Pacific) Japan East|

4\. [次へ] を選択して、[セキュリティ] タブに進みます。
5\. [Azure Bastion] セクションで、[Azure Bastion の有効化] を選択します。
6\. [Azure Bastion] で、次の情報を入力するか選びます。

|設定|値|
|:-|:-|
|Azure Bastion ホスト名|vnet-1-bastion|
|Azure Bastion のパブリック IP アドレス|[パブリック IP アドレスを作成] を選択し、名前に「pip-bastion」と入力。|

7\. [次へ] を選択して、[IP アドレス] タブに進みます。
8\. [サブネット] のアドレス空間ボックスで、既定のサブネットを選択します。
9\. [サブネットの編集] で次の情報を入力または選択します。

|設定|値|
|:-|:-|
|サブネットの目的|Default (既定値)|
|名前|subnet-1|
|**IPv4**||
|IPv4 アドレス範囲|10.0.0.0/16(既定値)|
|開始アドレス|10.0.0.0 (既定値)|
|サイズ|/24 (256 アドレス) (既定値)|
|**プライベート サブネット**||
|プライベート サブネットを有効にする|有効|
|**セキュリティ**||
|NAT ゲートウェイ|[新規作成] を選択し、名前に「ngw-subnet-1」と入力。パブリック IP アドレスで「(新規) subnet-1」を選択。|
|ネットワーク セキュリティ グループ|[新規作成] を選択し、名前に「nsg-subnet-1」と入力。|

10\. [保存] を選択します。
11\. ウィンドウの下部にある [レビューと作成] を選びます。検証に合格した場合は、[作成] を選択します。

2025年9月30日以降に仮想マシンからの既定のアウトバウンド接続が廃止されるので、それ以降でこの記事を見て頂けることを見越してNATゲートウェイを配置しておきました。(2025年9月30日までに作成された仮想マシンには影響ありません)

https://learn.microsoft.com/ja-jp/azure/virtual-network/ip-services/default-outbound-access#how-can-i-transition-to-an-explicit-method-of-public-connectivity-and-disable-default-outbound-access

# 事前の作業②　Azure OpenAIのデプロイ
続いてツールが内部的に使っているAzure OpenAIのGPT-4oモデルをデプロイします。AI FoundryからプロジェクトやAIハブも含めて作成することも可能ですが、本環境ではそこまで必要ないので、AzureポータルでAzure OpenAI単体のデプロイをしていきます。
1\. Azureポータルで [Azure OpenAI] を検索して選択します。
2\. [Azure OpenAI] ページで、[+ 作成]を選択します。
3\. [Azure OpenAIの作成] の [基本] タブで、以下の情報を入力するか選択します。

|設定|値|
|:-|:-|
|**プロジェクトの詳細**||
|サブスクリプション|使用しているサブスクリプション|
|リソースグループ|rg-getAzureUpdate|
|**インスタンスの詳細**||
|リージョン|Japan East|
|名前|任意|
|価格レベル|Standard S0 (固定)|

4\. [次へ] を選択して、[ネットワーク] タブに進みます。
5\. [Azure OpenAIの作成] の [ネットワーク] タブで、以下の情報を選択します。

|設定|値|
|:-|:-|
|種類|無効になっています。どのネットワークもこのリソースにアクセスできません。このリソースにアクセスする唯一の方法となるプライベート エンドポイント接続を構成できます。|

6\. [プライベート エンドポイント] で [+ Add Private Endpoint] を選択します。
7\. [プライベート エンドポイントの作成] で、次の情報を入力するか選びます。

|設定|値|
|:-|:-|
|場所|Azure OpenAIと同じリージョン|
|名前|pe-aoai|
|Target sub-resources|account (固定)|
|**ネットワーク**||
|仮想ネットワーク|vnet-1|
|サブネット|subnet-1|
|**プライベート DNS 統合**||
|プライベート DNS ゾーンと統合する|はい|
|プライベート DNS ゾーン|azureopenai|

8\. [OK] を選択します。
9\. [次へ] を選択して、[Tags] タブに進みます。
10\. [次へ] を選択して、[レビューおよび送信] タブに進みます。
11\. 最終検査が実行され合格した後でウィンドウの下部にある [作成] を選択します。

# 事前の作業③　Azure OpenAIの設定
Azure OpenAIのデプロイが完了したら、Azure AI Foundryポータルでモデルを作っていきます。
1\. 作成したAzure OpenAIの概要画面で、[Go to Azure AI Foundry portal] を選択します。
2\. 左ペインの[モデル カタログ]を選択します。
3\. 検索ウインドウに [gpt-4o] と入力し、[gpt-4o] を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330959/1e051460-daeb-442a-a795-253445545c01.png)
3\. [このモデルを使用する] を選択し、[デプロイ] を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330959/157d5fdf-6efa-4198-83aa-02f2d3a63368.png)
4\. デプロイが終わると[デプロイ]画面が表示されるので、後々ツールで設定していくので [エンドポイント] の情報を控えておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330959/ce3bdcac-162b-4ccc-a7fb-37641b1a542e.png)

# 環境構築①　仮想マシンのデプロイ
事前作業が終わったので、実際にツールを実行するサーバーとして仮想マシンを作っていきます。今回のOSはAlmaLinuxを使いました。なぜAlmaLinuxなのかと云うと、これまで使ったことがなかったのでいい機会だったから、です。他のディストリビューションに慣れているようでしたら読み替えなどをしてください。

1\. ポータルの検索ボックスに、「仮想マシン」と入力します。検索結果から [仮想マシン] を選択します。
2\. [仮想マシンの作成] の [基本] タブで、次の情報を入力または選択します。

|設定|値|
|:-|:-|
|**プロジェクトの詳細**||
|サブスクリプション|使用しているサブスクリプション|
|リソースグループ|rg-getAzureUpdate|
|**インスタンスの詳細**||
|仮想マシン名|vm-1|
|リージョン|(Asia Pacific) Japan East (既定値)|
|可用性のオプション|インフラストラクチャ冗長は必要ありません|
|セキュリティの種類|トラステッド起動の仮想マシン (既定値)|
|イメージ|AlmaLinux OS 9 - x64 Gen2|
|Azure Spot 割引で実行する|無効 (既定値)|
|サイズ|Standard_DS1_v2 - 1 vcpu、3.5Gib のメモリ (既定値)|
|**管理者アカウント**||
|認証の種類パスワード|
|ユーザー名|azureuser|
|パスワード/パスワードの確認|任意のパスワード|
|**受信ポートの規則**||
|パブリック受信ポート|なし|

3\. [次へ: ディスク] を選びます。
4\. [ディスク] タブで、次の情報を入力または選択します。
|設定|値|
|:-|:-|
|**OS ディスク**||
|OS ディスク サイズ|イメージの既定値 (30 GiB) (既定値)|
|OS ディスクの種類|Standard SSD (ローカル冗長ストレージ)|
|VM と共に削除|有効 (既定値)|
|キーの管理|プラットフォーム マネージド キー (既定値)|

5\. [次へ: ネットワーク] を選びます。
6\. [ネットワーク] タブで、次の情報を入力または選択します。

|設定|値|
|:-|:-|
|**ネットワーク インターフェイス**||
|仮想ネットワーク|vnet-1|
|サブネット|subnet-1 (10.0.0.0/24)|
|パブリック IP|なし|
|NIC ネットワーク セキュリティ グループ|なし|
|パブリック受信ポート|なし|
|VM が削除されたときにパブリック IP と NIC を削除する|有効|
|高速ネットワークを有効にする|有効 (既定値)|

7\. 残りの設定は既定値のままにして、[確認と作成] を選びます。
8\. ［作成］ を選択します

# 環境構築②　実行環境の設定
仮想マシンにBastion経由で接続し、ツールを導入していきます。
1\. 仮想マシンの [概要] より [接続] を選び、[Bastion を介した接続] を選択します。
2\. [ユーザー名] と [VM パスワード] を入力し、[接続] を選択します。
3\. 次のコマンドでAlmaLinuxのアップデートを行います。
```
[azureuser@vm-1 ~]$ sudo dnf update
　・・・
Complete!
[azureuser@vm-1 ~]$ sudo dnf upgrade
　・・・
Complete!
```
4\. このツールは、Ptyhon 3.12とPythonパッケージ管理ツール(Pip Installs Packages(pip))が必要であるためインストールします。
```
[azureuser@vm-1 ~]$ sudo dnf install python312 -y
　・・・
Installed:
  libnsl2-2.0.0-1.el9.x86_64
  mpdecimal-2.5.1-3.el9.x86_64  
  python3.12-3.12.9-1.el9.x86_64  
  python3.12-libs-3.12.9-1.el9.x86_64
  python3.12-pip-wheel-23.2.1-4.el9.noarch
Complete!

[azureuser@vm-1 ~]$ sudo dnf install python3.12-pip -y
　・・・
Installed:
  python3.12-pip-23.2.1-4.el9.noarch
  python3.12-setuptools-68.2.2-4.el9.noarch                                           
Complete!
```
5\. またリポジトリからツールを取得してくるためにGitもインストールします。
```
[azureuser@vm-1 ~]$ sudo dnf install git -y
Installed:
  emacs-filesystem-1:27.2-13.el9_6.noarch             git-2.47.1-2.el9_6.x86_64                            git-core-2.47.1-2.el9_6.x86_64                          
  git-core-doc-2.47.1-2.el9_6.noarch                  perl-AutoLoader-5.74-481.el9.noarch                  perl-B-1.80-481.el9.x86_64                              
  perl-Carp-1.50-460.el9.noarch                       perl-Class-Struct-0.66-481.el9.noarch                perl-Data-Dumper-2.174-462.el9.x86_64                   
  perl-Digest-1.19-4.el9.noarch                       perl-Digest-MD5-2.58-4.el9.x86_64                    perl-DynaLoader-1.47-481.el9.x86_64                     
  perl-Encode-4:3.08-462.el9.x86_64                   perl-Errno-1.30-481.el9.x86_64                       perl-Error-1:0.17029-7.el9.noarch                       
  perl-Exporter-5.74-461.el9.noarch                   perl-Fcntl-1.13-481.el9.x86_64                       perl-File-Basename-2.85-481.el9.noarch                  
  perl-File-Find-1.37-481.el9.noarch                  perl-File-Path-2.18-4.el9.noarch                     perl-File-Temp-1:0.231.100-4.el9.noarch                 
  perl-File-stat-1.09-481.el9.noarch                  perl-FileHandle-2.03-481.el9.noarch                  perl-Getopt-Long-1:2.52-4.el9.noarch                    
  perl-Getopt-Std-1.12-481.el9.noarch                 perl-Git-2.47.1-2.el9_6.noarch                       perl-HTTP-Tiny-0.076-462.el9.noarch                     
  perl-IO-1.43-481.el9.x86_64                         perl-IO-Socket-IP-0.41-5.el9.noarch                  perl-IO-Socket-SSL-2.073-2.el9.noarch                   
  perl-IPC-Open3-1.21-481.el9.noarch                  perl-MIME-Base64-3.16-4.el9.x86_64                   perl-Mozilla-CA-20200520-6.el9.noarch                   
  perl-NDBM_File-1.15-481.el9.x86_64                  perl-Net-SSLeay-1.94-1.el9.x86_64                    perl-POSIX-1.94-481.el9.x86_64                          
  perl-PathTools-3.78-461.el9.x86_64                  perl-Pod-Escapes-1:1.07-460.el9.noarch               perl-Pod-Perldoc-3.28.01-461.el9.noarch                 
  perl-Pod-Simple-1:3.42-4.el9.noarch                 perl-Pod-Usage-4:2.01-4.el9.noarch                   perl-Scalar-List-Utils-4:1.56-462.el9.x86_64            
  perl-SelectSaver-1.02-481.el9.noarch                perl-Socket-4:2.031-4.el9.x86_64                     perl-Storable-1:3.21-460.el9.x86_64                     
  perl-Symbol-1.08-481.el9.noarch                     perl-Term-ANSIColor-5.01-461.el9.noarch              perl-Term-Cap-1.17-460.el9.noarch                       
  perl-TermReadKey-2.38-11.el9.x86_64                 perl-Text-ParseWords-3.30-460.el9.noarch             perl-Text-Tabs+Wrap-2013.0523-460.el9.noarch            
  perl-Time-Local-2:1.300-7.el9.noarch                perl-URI-5.09-3.el9.noarch                           perl-base-2.27-481.el9.noarch                           
  perl-constant-1.33-461.el9.noarch                   perl-if-0.60.800-481.el9.noarch                      perl-interpreter-4:5.32.1-481.el9.x86_64                
  perl-lib-0.65-481.el9.x86_64                        perl-libnet-3.13-4.el9.noarch                        perl-libs-4:5.32.1-481.el9.x86_64                       
  perl-mro-1.23-481.el9.x86_64                        perl-overload-1.31-481.el9.noarch                    perl-overloading-0.02-481.el9.noarch                    
  perl-parent-1:0.238-460.el9.noarch                  perl-podlators-1:4.14-460.el9.noarch                 perl-subs-1.03-481.el9.noarch                           
  perl-vars-1.05-481.el9.noarch                      
Complete!
```
6\. ツールでは、/usr/bin/pythonおよび/usr/bin/pipコマンドが利用できる必要があるため設定します。将来的にPythonの複数バージョンが混在できるようにしておきます。
```
[azureuser@vm-1 ~]$ sudo alternatives --install /usr/bin/python python /usr/bin/python3.12 30
[azureuser@vm-1 ~]$ python -V
Python 3.12.9
[azureuser@vm-1 ~]$ sudo alternatives --install /usr/bin/pip pip /usr/bin/pip3.12 30
[azureuser@vm-1 ~]$ pip -V
pip 23.2.1 from /usr/lib/python3.12/site-packages/pip (python 3.12)
```

# 環境構築③　神ツールの取得と実行
1\. リポジトリからツールを取得します。
```
[azureuser@vm-1 ~]$ git clone https://github.com/koudaiii/AzureUpdatePPTX.git
Cloning into 'AzureUpdatePPTX'...
　・・・
Receiving objects: 100% (862/862), 15.06 MiB | 21.88 MiB/s, done.
Resolving deltas: 100% (464/464), done.
```
2\. READMEにある通り諸設定を行います。
```
[azureuser@vm-1 ~]$ cd AzureUpdatePPTX/
[azureuser@vm-1 AzureUpdatePPTX]$  cp .env.sample .env
[azureuser@vm-1 AzureUpdatePPTX]$  vi .env
```
```.env
# Azure OpenAI の API Key, API Endpoint の接続文字列を設定
API_KEY=       ← [ 事前の作業③　Azure OpenAIの設定 ] 項番4 の "キー" を入力
API_ENDPOINT=  ← [ 事前の作業③　Azure OpenAIの設定 ] 項番4 の "ターゲット URL" を入力
```
3\. READMEの通りにコマンドを実行しツールを起動します。
```
[azureuser@vm-1 AzureUpdatePPTX]$ script/bootstrap
Successfully built ・・・
Installing collected packages: ・・・
Successfully installed ・・・
[azureuser@vm-1 AzureUpdatePPTX]$  script/server
Collecting usage statistics. To deactivate, set browser.gatherUsageStats to false.

  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8000
  Network URL: http://10.0.0.4:8000
  External URL: http://xxx.xxx.xxx.xxx:8000
```

この時点ではApplication Gatewayなどの設定を行っていないのでパブリック接続はできず、仮想ネットワーク内のプライベートでしかアクセスできないですが、同一仮想ネットワークにクライアント仮想マシンを配置するなどして、ツールが出力したNetwork URLにWebブラウザ経由でアクセスすると次のような画面が表示されれば、ここまでの設定は問題ありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330959/e0f64e9b-9e01-4196-a927-21df1ab18cd0.png)

取得期間を設定して、[データの取得]ボタンを押すと、要約を生成しながら、パワーポイントファイルが作成されダウンロードできるようになります。

プライベートで利用する分には、ここまでで充足していると思いますが、後半ではパブリックでセキュアに利用するまで触れていきたいと思います。

ここまでお読みいただきありがとうございました。