normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

University of Washington の Certificate コースを受けてみた感想(日本からでも OK

最近 University of Washington の講義を取っているのですが、何人かと雑談した際に話題にしたら「それ良いですね」「どうやって受けるのか教えてください」等々の話があったので、こちらもせっかくなのでまとめてみたいと思います。保証はしかねますが以前にあるコースを受講している際に日本やフィリピン(うろ覚え)からも参加している方がいたのも記憶しているので、今回のポストで興味があれば自分で受講することはできると思います。さすがに保証はしかねるので不安な場合は University of Washington 直に確認ください。

あんたが受講してるのは何なの?

私が受けているのは以下の CERTIFICATE IN CYBERSECURITY というコースです。日本人的に CERTIFICATE はあんまり聞きなれない方も多いと思いますが(私が最初分からんかったので他の人もそうだろうと想定)、いわゆる修士号や博士号の様な学位をとるコースをは違っており、単に「このコース受講してちゃんと課題も一通り水準以上にこなせましたよ」という様なものです。
www.pce.uw.edu
CERTIFICATE にした理由について、私の場合は「CERTIFICATE か修士か博士かなんて分かった人じゃないと踏み込んでこないし、日本国外の大学で専門知識の受講実績を持っていると箔がつく」と思ったのが主な動機です(中身が分かる方には普通に実態としての技術的な話をすればいいので)。CERTIFICATE は1年未満で修了可能なことに加え、身近にアメリカ側の修士に通っている日本人の知り合いがいますが課題量が半端じゃないので業務と並行は難しいと思って断念しました。
色々と専門分野をどれにしようかと迷いましたが、セキュリティはなんだかんだで需要は尽きないし、コンプライアンスと実務の折り合いをつけながら話せる人材は中々見かけないので、深堀分野としては悪くないかなと思った次第です。

日本からでも受けられるの?&お高いんじゃないの?

こちらのコース、アメリカ時間で夕方の午後6時~午後9時に実施されるので、日本時間としては金曜の午前11時~午後2時となります(時期によってサマータイムでやや変わるはずですが、1時間程度の差なので目安として)。平日の午前中なので調整が難しい方もいるかと思いますが、時間的には不可能な参加時間ではありません。

最新情報はリンク先を確認して欲しいですが、2023年12月23日現在での価格は8カ月コースで $4,197 となり、約60万円弱となります。国立大学の年間授業料が50万円を超えるくらいなので、安いと言える金額ではないですが物価差等々を考えるとそこまで高い値段でもないと思います。
www.chibakogyo-bank.co.jp

また、CERTIFICATE のコースは以下を見てもらえればと思いますが様々なコースがあります。こちらからコースを選択して apply すれば受講申請は可能です。
www.pce.uw.edu

技術分野でいうなら以下のコースが $3,786 とやや安めです。
www.pce.uw.edu

英語は大丈夫なの?

私自身は普段ソリューションアーキテクトとしてアメリカ側で業務しており英語でのコミュニケーション頻度が高いので、講義をちゃんと理解する&グループディスカッション等でもそこまでは困っていません。ただ、渡米当初 or 渡米直後だとかなりの負荷になったであろうことは想像に難くない程度には英語を使います。私の受講しているコースでは英語が SCORES FOR CREDIT PROGRAMS REQUIRING INTERMEDIATE ENGLISH SKILLS レベルの水準を要求されており、以下で確認すると TOEFL iBT で 92点 が目安となります。
www.pce.uw.edu

こういう換算がどこまで当てになるかは分かりませんが、TOEIC 換算だと以下によると 820 点程度らしいです。目安としてはこの程度となりますが、特に審査があるわけではないので「俺は前のめりに英語を学んでやるぜ」という人間にはお勧めです。
www.conversation.jp

実際に受けてみてどう?

セキュリティ関連について、私自身が過去に実プロジェクトで閉域網やら WAF やら PCI-DSS やら FICS やらと雑多なプロジェクトに多々放り込まれてきたので、純技術的に新しいトピックが多いかと言われると決して多くはないです。とは言っても Security Development Lifecycle や Thread Modeling 等は SI 現場だとあまり話題にならないので、その点では勉強になっています。
グループワークに限らず講師の方が話題にする内容は当然ながら「アメリカでの常識」となるので「公共案件と言ったら軍隊(日本だったら官公庁を指すことが多い)」や「なんでアメリカには GDPR みたいな統一ルールがないのか(州ごとの権限が強すぎて統一ルールが作りにくい)」等の日本に住んでるとあんまり話題にならないことが話題になるのが参考になったりします。
また、当然ながら自分の専門分野での英会話になるので、英会話の実践という意味でも良いなと思っています(自分の興味分野なら続きますし)。以下とかを試したのは今回の講義で出た課題の一部だったりします。
normalian.hatenablog.com


色々と書きましたが、英会話がある程度できる方が興味のある技術領域等々(分野は色々とあります)で受けてみるのは悪くないと思います。

Azure KeyVault に格納した SSH Key を使って、Azure Bastion 経由で Linux VM にアクセスする

掲題の内容、SSH Key をローカルのファイルとして管理することなく Key Vault で一元管理したいというのは良くあるニーズだと思いますが、意外とまとまった記事が無かったので備忘録も兼ねて書いてみました。試してみてアレコレ詰まったところはありますが、実態としては簡単な内容です。
以下に簡単なアーキテクチャを記載しましたが、VM 作成時にポータル上で作成した SSH Key を Key Vault 側に登録し、同 SSH Key を利用して Azure Bastion 経由で VM にアクセスします。

作業手順としては手順になります。仮想ネットワーク、Key Vault、Azure Bastion はリソース作成済なことを前提とします。

  • SSH Key をポータル上で作成する( 今回は VM 作成時の物を利用
  • Key Vault に Secrets として SSH Key の private key を登録する
  • Azure Bastion 経由で VM にアクセスする

SSH Key をポータル上で作成する( 今回は VM 作成時の物を利用

今回は VM 作成時に SSH Key を新規に作成します。以下の様にポータル上で SSH Key を作成可能です。パラメータを入力して VM 作成を行った際、SSH Key の private key 側をダウンロードできるので手元に保存しておきましょう。

既存の SSH Key を利用する場合は Reference のリンク一つ目に参考情報がありますが、仮想マシンのポータル側のメニューから Reset password を選び、Add SSH public key を利用して当該仮想マシンに利用する SSH Key の情報を別途設定する必要がある点に注意してください。

Key Vault に Secrets として SSH Key の private key を登録する

次に Key Vault の Secrets に作成した SSH Key の private key を登録します( Keys や Certificates 側は Azure Bastion だと選択できません)。ここで一つ罠があり、private key は複数行のファイルになりますが Key Vault のポータル操作は複数行の文字列登録に対応してないのでコマンドで実行する必要があります。以下にコマンド実行例と実行結果の例を記載します。

az keyvault secret set --name "your-secret-name" --vault-name "your-key-name" --file "my-ssh-private-key.pem"

コマンドが無事に成功していれば以下の様に Key Vault 上に登録された private key が確認できるはずです。

Azure Bastion 経由で VM にアクセスする

ここまでくれば後は Azure Bastion 経由で Linux VM にアクセスするのみです。接続したい Linux VM を選択し、Authentication Type から SSH Private Key from Azure Key Vault をえらんだ後、先ほど登録した SSH Key の private key を利用することで接続できます。

Microsoft Azure で pfSense アプライアンスを仮想マシンとインターネットの間に配置して疎通を取る

皆様は pfSense というネットワークアプライアンス製品はご存じでしょうか?日本の社畜業界で生きていた身からすると F5 さんの BIG-IP が有名かなという気もしますが、アメリカに限らず各国で幅広く利用されています。今回はこちらを Azure に配置する簡単な手順を紹介します。
www.pfsense.org
実は仮想マシンを作成後、裏側に配置した別の仮想マシンからのネットワークトラフィックが通らずどこが悪いのか、しばくら悶々としながら調査しておりました。自分の備忘も兼ねて何をしたかを簡単にまとめておきたいと思います。

日本の方が試す場合の注意点

注意点として、pfSense は Azure Marketplace 上に公開されていますが、実は日本向けには公開されていません。日本リージョン向けに作成したアカウントで pfSense の Azure Marketplace イメージを利用した VM を作成しようとすると以下のエラーがでます。

また、上記を解消する為にアカウントの国情報を変えることができず、私は今回の検証をするために新規にアメリカ側でのアカウントを作成しました。公式ドキュメントとしては以下を参照ください。
learn.microsoft.com

今回作る構成のアーキテクチャ

今回は以下の様に pfSense VM には Public IP 経由で接続し、SSH は Azure Bastion 経由で pfSense VM と裏にある Kali Linux に接続します。Route Table を利用して Kali Linux 側のネットワークを pfSense VM 経由する様にしている点に留意してください。

具体的に作成するリソース一覧は以下です。

  • Virtual Network x 1
  • Azure Bastion
  • Kali Linux x 1
  • pfSense network appliance x 1
  • Route Table x 1

本番環境を考える場合、冗長化等は必須になります。今回はあくまで検証用であるという点には留意してください。

実際に作る手順

まずは仮想ネットワークを作ります。今回は West US3 リージョンを選びました。

次の画面で "Enable Azure Bastion" にチェックを入れて Azure Bastion を作成するようにしておきましょう。

次に以下の画像を参考にして front-subnet、back-subnet、AzureBastionSubnet の三つのサブネットが含まれる仮想ネットワークを作成します。サブネットの大きさは自由に設定しても構いませんが、AzureBastionSubnet については公式ドキュメントの About Azure Bastion configuration settings | Microsoft Learn を参照してください。

上記の作成は Azure Bastion インスタンスの作成に時間がかかるので、数分程度待つ必要があります。

次にバックエンド相当になる Kali Linux を作成します。ここではお安めに抑えたいので B シリーズインスタンスを選びます。B-series burstable - Azure Virtual Machines | Microsoft Learn

ここで SSH キーを作成し、後で Azure Bastion で接続する際に利用します。

以下の様に backend-subnet に配置します。

VM 作成時に以下の様に private key をダウンロードする画面が表示されるので、ダウンロードしてください。こちらは後に利用します。

同様のステップで pfSense の仮想マシンも作成します。pfSense Plus Firewall/VPN/Router TAC Lite という仮想マシンイメージがあるので、こちらを選びます。front-subnet をデプロイ先のサブネットとして選び、SSH キーは Kali Linux を作成した際のものをそのまま利用しましょう。

pfSense の VM 作成後、同仮想マシンのネットワークインターフェースを開き、IP configuration から Enable IP forwarding にチェックを入れてください。こちらに合わせて IP アドレスも 10.0.0.4 に固定するように変更をしておきましょう。
Hybrid connection with two-tier application | Microsoft Learn

最後に Route Table を作成し、Kali Linux が配置された back-subnet にアタッチします。まずは新規に Route Table を以下の様に作成します。

次に back-subnet 側の Next hop を全て pfSense 側にトラフィックを流すように以下の様にルートを設定します。

最後に back-subnet 側に本 RouteTable は関連付けて Azure リソースの作成は完了です。

pfSense ファイヤウォールの設定

まずは管理画面にアクセスします。pfSense の Public IP をブラウザに開くと以下の画面が表示されるので、デフォルトのユーザ名・パスワードである admin/pfsense を利用して管理画面にログインします。

ログインすると以下の画面が表示されるので Change the password in the User Manager リンクからパスワードを変更しておきましょう。

次に Firewall / NAT / Outbound をメニューから開き、新規に outbound を許可する以下のルールを作成します。

更に以下の Hybrid Outbound NAT rule generation.(Automatic Outbound NAT + rules below) をチェックします。これにより Kali Linux から WAN インターフェース経由での outbound 接続自体がルーティングされます。最後に Apply Changes を押下するのを忘れない様にしてください。

pfSense デフォルトの設定では Kali Linux 側から HTTP/HTTPS の outbound 接続を許可していません。Firewall / Rules / WAN メニューを開き、以下のルールを追加します。こちらは back-subnet(10.2.0.0./24)からの HTTPS による outbound を許可する設定です。こちら加えて HTTP のルールも加えた後、Apply Changes を押下してルールを適用します。

最終的に Firewall / Rules / WAN の画面は以下の様になります。

Kali Linux から HTTPS で outbound 接続をしてみる

さっそく Azure Bastion 経由で Kali Linux に接続してみましょう。この際に保存済の SSH キーを利用します。

ログイン後 wget コマンドを使って任意の URL にアクセスし、レスポンスが帰ってくれば成功です。

Kali Linux から HTTPS で outbound 接続を許可しない場合

次に以下の様に HTTPS の outbound 接続を不許可にしてみましょう。

その後に Kali Linux 側で wget を実行すると以下の様にレスポンスが返ってきません。

pfSense 側の Diagnostics / States / States を確認すると FIN_WAIT_2: FIN_WAIT_2 でステップが止まっているのを確認できます。

Bicep ファイル で Azure Firewall のルール上の IP Group を更新する

ちょっと人から聞かれて軽く書いておいたので供養致します。特に「IP Group が複数の場合にどうするの?」と聞かれたので、以下が bicep ファイルの内容です。

resource firewallPolicy 'Microsoft.Network/firewallPolicies@2023-04-01' existing = {
  name: 'StandardPolicy'
}

resource ipgroup1 'Microsoft.Network/ipGroups@2023-04-01' existing = {
    name: 'IPG-Test01-WestUS2'
}

resource ipgroup2 'Microsoft.Network/ipGroups@2023-04-01' existing = {
    name: 'IPG-Test02-WestUS2'
}

resource networkRuleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2022-01-01' = {
  parent: firewallPolicy
  name: 'DefaultNetworkRuleCollectionGroup'
  properties: {
    priority: 2000
    ruleCollections: [
      {
        ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
        action: {
          type: 'Allow'
        }
        name: 'Net-RuleCollection-01'
        priority: 1250
        rules: [
          {
            ruleType: 'NetworkRule'
            name: 'time-windows'
            ipProtocols: [
              'ANY'
            ]
            destinationAddresses: [
              'AzureCloud'
            ]
            sourceIpGroups: [
                ipgroup1.id
                ipgroup2.id
            ]
            destinationPorts: [
              '80'
            ]
          }
        ]
      }
    ]
  }
}

それを以下のコマンドでデプロイしました。

az deployment group create --resource-group "your resource group" --template-file "your bicep file"--mode incremental

ここでのポイントは既存のリソースに対する update なので bicep 側で existing キーワードを忘れない様にすることです。IP Group 辺りでうっかり忘れると以下の様になるのでご注意を。

az deployment group create --resource-group RG-Firewall-Test-WestUS2 --template-file .\hello01.bicep --mode incremental
az : WARNING: C:\Users\daisami\OneDrive - Microsoft\Desktop\hello01.bicep(1,7) : Warning no-unused-params: Parameter "location" is declared but never used. 
[https://aka.ms/bicep/linter/no-unused-params]
At line:1 char:1
+ az deployment group create --resource-group RG-Firewall-Test-WestUS2  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (WARNING: C:\Use...-unused-params]:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
 
ERROR: {"status":"Failed","error":{"code":"DeploymentFailed","target":"/subscriptions/423a72cd-b110-476f-8b8b-0569fe1773bd/resourceGroups/RG-Firewall-Test-West
US2/providers/Microsoft.Resources/deployments/hello01","message":"At least one resource deployment operation failed. Please list deployment operations for 
details. Please see https://aka.ms/arm-deployment-operations for usage details.","details":[{"code":"BadRequest","target":"/subscriptions/423a72cd-b110-476f-8b
8b-0569fe1773bd/resourceGroups/RG-Firewall-Test-WestUS2/providers/Microsoft.Resources/deployments/hello01","message":"{\r\n  \"Message\": \"The request is 
invalid.\",\r\n  \"ModelState\": {\r\n    \"resource\": [\r\n      
\"{\\\"Error\\\":{\\\"Code\\\":\\\"FirewallPolicyRuleCollectionGroupInvalidPriorityValue\\\",\\\"Message\\\":\\\"Invalid Rule Collection Group . Invalid 
priority value 0, must be between 100 and 65000\\\",\\\"Target\\\":null,\\\"Details\\\":null},\\\"Status\\\":\\\"Failed\\\"}\"\r\n    ]\r\n  }\r\n}"}]}}

Microsoft Entra アプリケーション プロキシで閉域なオンプレミスサーバにアクセスする+α な TIPS

皆様は Microsoft Entra アプリケーション プロキシ という機能をご存じでしょうか?Entra ID の認証・認可機能を利用して、オンプレミスのサービスに対してセキュアな接続を提供する社畜垂涎のサービスとなります。細かくは色々とありますが、以下の様に EntraID が認証した情報を用い、Microsoft Entra application proxy connectors をインストールした Windows Server を経由してセキュアかつ閉域的な Web サーバにアクセスすることが可能です。

learn.microsoft.com

Microsoft Entra application proxy connectors(実態はエージェントで Windows サービスとして稼働。以下はコネクタと呼びます)をインストールした端末が必要&当該サーバが Windows Server である必要がありますが、接続先のサーバは Windows, Linux 含めて OS は問わないのでかなり汎用性の高いサービスだと思っています。

昨今では Microsoft Entra Private Access というサービスが’発表されており、今後 Entra ID 的にはこちらのサービスを利用することが推奨されると思いますが、現在 Microsoft Entra Private Access はパブリックプレビューなので、まだまだ使いどころはあるサービスだと思っています。
www.microsoft.com

セットアップはどうやってやるの?

以下ふたつのセットアップが必要になります。ここでは既に HTTPS アクセスが可能な Web サーバ(今回は IIS を利用していますが、Linux でも構いません)がセットアップ済としています。Web サーバには認証局から発行された証明書を利用することを強く推奨します。オレオレ証明書を利用する場合、後述するようなエラーが発生します

  • Windows Server にコネクタのセットアップ
  • Entra ID に Enterprise Application を作成する

まず、アーキテクチャ図における「Windows Server ※要コネクタ」のセットアップを行います。当該サーバが Entra ID を介したユーザリクエストを中継処理するサーバとなります。上記で記載した Microsoft Entra Private Access だと本サーバのセットアップは不要となりますが、逆にネットワーク分離的にはこちらのコネクタ入りの Windows Server が中継する様にネットワーク接続を制限するうま味もあるので残るのではないかという気もしております。
セットアップ自体は簡単です、以下のスクリーンショットに従い Entra ID の Application Proxy メニューに移動します。そこに Download connector service があるので、こちらでコネクタのセットアップ用のファイル(単なる exe 形式です)をダウンロードします。

コネクタのインストール先 Windows Server で以下の様にセットアップファイルを実行し、ウィザードに従ってセットアップを実行してください( Uninstall になってるのは既にセットアップ済のサーバで実行したからなので、ここが Install になってるはずです)。

セットアップが完了すると以下の様に Windows サービスに Microsoft AAD Application Proxy Connector/Microsoft AAD Application Proxy Connector Updater の二つが表示されます。

最後に以下のスクリーンショットに従い Entra ID の Application Proxy メニューに移動します。こちらで当該 Windows Server の Status が Active になっていればセットアップ完了です。

オンプレミスのファイヤーウォールNSG 等でアクセスが制限されている場合、Windows Server のセットアップが上手くいっていても Active にならない場合があります。その場合は以下のネットワーク接続要件を確認してください。
learn.microsoft.com


次にEntra ID 上に Enterprise Application のリソースを作ります。Azure ポータルから Entra ID の画面に遷移して以下の Enterprise applications を選択してください。ここでMicrosoft Entra ID P1 ライセンスが確認できますので、もしライセンスが割り当てられてない場合はライセンスの購入・割り当てを検討ください。

以下の New application からアプリケーションプロキシを利用するリソースを作成します。

New application を選択後、以下の画面となりますがこの中から Add an on-premise application を選択していきます。

次に各種パラメータを入力します。以下に図示しましたが、この中で特に重要なのが Internal Url と External Url です。

  • Name:ここに入力した名前で Entra ID 上に表示されます
  • External Url:ユーザが直接アクセスする URL になります。オンプレミスのリソースにコネクタ経由でアクセスする際、ユーザはこちらをブラウザ等に入力することになります
  • Internal Url:コネクタがリクエストを送るオンプレミス内の URL となります(今回の例では Azure 内ですが)。こちらの名前解決はコネクタをインストールしたサーバに準拠するので、極論当該サーバの hosts ファイルを編集しても対応可能です。こちらで既にセットアップ済 HTTPS アクセス対象の Web サーバの URL を指定してください
  • Connector Group:こちらは後述しますが、ここでコネクタをセットアップした Windows Server を選択します。

リソース作成後、本 Enterprise Application のリソースを利用するユーザを割り当てます。

最後にブラウザに External Url を入力してアクセスを試します。この際 Enterprise Application を作成した Entra ID テナントでの認証を求められるので、認証完了後に以下の様にオンプレミス(今回は相当)へのリソースへアクセスが可能となります。

オレオレ証明書を利用する場合

デフォルト設定でコネクタが認証済の証明書以外を利用する場合、以下の SSL に関するエラーを表示します。

開発環境に限定した場合ですが、Enterprise Application リソースの Application proxy メニューから、以下の Advanced メニューにおける Validate Backend SSL certificate メニューのチェックを外します。こちらの有効化には体感で数分~10分程度かかったので、設定直後にエラーが続いても焦らなくて OK です。

こちらでオレオレ証明書でもアクセス可能ですが、あくまで開発環境用だという点に留意ください。

ネット―ワークの接続要件は?

皆様の一番関心のあるところ&アプリケーションプロキシの良い点だと考えています。許可する必要があるトラフィックはコネクタが利用する一部のアウトバウンド接続であり、インバウンドは原則開放する必要がありません。以下に接続要件が記載されています。
learn.microsoft.com
インバウンドはセキュア通信であれば 443 をコネクタがインストールされた Windows Server 向けにだけ許可すれば良く、それ以外のサーバやポートに対しては特に公開する必要が無いのは大きなメリットだと考えています。

ライセンスは必要?

本サービスの利用には Microsoft Entra ID P1 or P2 ライセンスが必要になる点に注意が必要です。以下を参照ください。
Microsoft Entra アプリケーション プロキシに関してよく寄せられる質問 | Microsoft Learn

接続先の Internal URL に IP アドレスは指定できるの?

以下の様に IP アドレスを直接指定する場合、ポータルでエラーが発生して指定できません。認証局が発行した証明書の利用を前提としているので、IP アドレスの利用は非推奨どころか不可となります。

冗長化はどう考えたらいいの?

今回は特に触れませんでしたが、コネクタグループを利用して冗長化を行います。詳細は以下の記事を参照して頂ければと思いますが、基本的には用途ごとにコネクタグループを作成し、各コネクタグループにコネクタをセットアップしたサーバを複数登録することで冗長化します。
learn.microsoft.com

Azure Batch を試す際の TIPS を簡単にまとめてみた

ちょっと人に聞かれた際に久々に触ったので、振り返りを含めてちょっとまとめてみました。チュートリアル的な hello world は既存の公式ドキュメントを読めば良いと思うので、簡単な概要部分だけ紹介した後は簡単な TIPS 系を羅列していきたいと思っています。

まず、Azure Batch の概要となります。Azure Batch は task という形式で様々な粒度の処理を実行可能であり、スケジューリング機能等々を持っています。以下に Azure Batch のアーキテクチャ図を記載しています。

最初に Azure Batch アカウントを作成しますが、簡単な利用をする場合に重要となる用語は Pool, Job, Application となります。

  1. Pool :最初に作成する必要があり、様々な処理を実行するためのコンピュートリソースです。こちらは Azure になじみのある方なら VMSS の様なものと言えばわかりやすいかもしれません。図でも記載がある通り、既存の template image から OS を選択することもできますが、自身で作成した custom image の利用も可能です。
  2. Job :処理行うまとまりの単位になります。こちらには Job/Job Schedule という二種類があり、on-demand 実行をする job と定期実行をするための job の二種類が存在します。
  3. Application :自身が作成したカスタムアプリケーションを指します。zip 形式でアーカイブする必要があり、Azure Storage アカウント上に配置されることを想定しています。事前に Azure Batch アカウントに Azure Storage を紐づけるのを忘れないようにしましょう。

こちらのアーキテクチャについては以下の動画開始 100 秒位のところで図示されているので、そちらも参照すると良いと思います。

最初に Quotas タブを見よう

上記に記載した通り、最初に Pool を作成する必要がありますが、こちらは VM の SKU(Dv3 Series 等)を選択する必要があります。以下の画像でも表示されていますが、必要となる VM SKU の Quota が 0 の場合、Pool を作成してもリソースが割り当てられずに処理が実行されません。

Azure Batch アカウントの作成直後、こちらの Quota を確認し、必要な場合は Quota increase request をしてください。多少の時間がかかるので、これを最初にやるのがお勧めです。

Pool を作成する際の tips

様々な処理を実行するために必須な Pool を作成します。こちらも作成時に幾つかポイントがありますが、OS イメージの選択に幾つか方法があります。まずは以下の様に Marketplace から標準で提供されているものを選ぶパターン。

次に、以下の様に作成したカスタム VM イメージを利用することも可能です。今回は利用しませんでしたが、Docker イメージの利用も可能なようです。

次に、VM SKU の指定、ノード数等を指定するので、ここで指定する VM SKU は事前に Quota increase request を作成してください。

当然閉域網で処理を行いたいニーズも出てくると思いますが、コンピュートリソース単位(プール単位)で VNET 等の指定をすることが分かります。

Job を作成する際の TIPS

Job は実際にコマンドやプログラムを実行する単位である Task をグループ化したものになります。Job の作成時は以下の様に利用する Pool を指定することに加え、必要な場合は環境変数を指定することも可能です。

Job の配下に Task を作成する画面は以下の様になります。今回は以下の様に hello world と出力するだけの Task となります。管理者権限で実行可能なことに加え、冒頭で説明した Application Package も利用可能なことが分かります。

こちらを実際に実行すると以下の様にすぐ実行が完了し、特にエラー等が発生しなければ Completed になります。

実行が完了した task01 をクリックする詳細画面となり、標準出力・標準エラー出力がそれぞれファイルとして格納されていることが分かります。stdout.txt を開くと hello world が出力されていることが確認できます。

Application を利用する際の TIPS

せっかく Azure Batch を利用するので、OS 標準コマンドだけでなく作成したアプリケーションを利用したいこともあるでしょう。その場合、zip 形式にパッケージングしたファイルを Azure Storage にアップロードします。しかし、その前に zip ファイルを上げる先の Azure Storage を Azure Batch アカウントとリンクさせましょう。以下の画面の様になっていれば設定完了です。

今回の場合、Azure Storage にアップロードする zip ファイルは以下のようにパッケージしました。自作の python ファイルを含んでいます。

test01.zip
        readme.md
        test01.py

ポータルのウィザードに従って正常にアップロード出来れば以下の様になります。

Job のセクションで紹介した通り、Job 作成時にアップロードした Application を指定します。加えて以下の様にスクリプトを指定します。

bash -c 'python3 $AZ_BATCH_APP_PACKAGE_test01_1_0_0/test01.py'

指定した Application の zip ファイルは特定の作業ディレクトリに展開されています。ディレクトリ名を含む環境変数は以下の記事が参考になります。
learn.microsoft.com

Azure Monitor を利用した場合

Diagnostic Setting を有効化し、Log Analytics ワークスペースにすべての情報を転送する設定をしました。しばらく操作をした後、試しに実行すると以下の様に AzureDiagnostics, AzureMetrics, Usage の三つのテーブルが作成されていました。試しに AzureMetrics にクエリを発行したところ、ノード数がどの程度使われているかを見ている様です。

次に Usage にクエリを投げてみましょう。データは出ていますが、deprecated になっており、別のテーブルを利用することが推奨されています。

最後に AzureDiagnostics へのクエリを実行すると ServiceLog と AuditLog が出力されています。AuditLog については以下の様にポータル操作のログが出力されています。ServiceLog 側はノードの実行数等が出力されていました。

Semantic Kernel/C# で Azure Cognitive Search にベクトル化データを保存する(成功編

今回は掲題のテーマを試してみたいと思います。実は以下の記事にて試した結果、上手く動きませんでした。
normalian.hatenablog.com

以前試した際は Semantic Kernel 側が Azure Cognitive Search のコネクタ対し未実装だったので結果が返ってこないという悲しい自体でした。しかし、以下の記事の様に無事実装がされたようなのでさっそく試してみました。
devblogs.microsoft.com

事前準備

以下のリソースを事前に作成してください。

  • Azure OpenAI リソース、text-davinci-003、text-embedding-ada-002 モデルのデプロイメント
  • Azure Cognitive Search(今回は Japan East を利用

次に Visual StudioC# のプロジェクトを作成作成します。作成したプロジェクトに NuGet でのライブラリインストールします。Visual Studio で NuGet の画面を開き、以下の Microsoft.SemanticKernel と Microsoft.SemanticKernel.Connectors.Memory.AzureCognitiveSearch の二つをインストールします。この際に "Include prerelease" にチェックを入れるのを忘れないようにしてください。

ソースコードの作成

C#ソースコードを記載します。私が今回利用したものは以下となります。

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.Memory.AzureCognitiveSearch;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Text;
using System.Reflection;
using System.Security.Cryptography;

namespace MyOpenAITest
{
    class Program
    {
        public static async Task Main(string[] args)
        {
            // Azure OpenAI parameters
            const string deploymentName_chat = "text-davinci-003";
            const string endpoint = "https://your-azureopenai-endpoint.openai.azure.com/";
            const string openAiKey = "your-azureopenai-key";
            const string deploymentName_embedding = "text-embedding-ada-002";

            const string azuresearchEndpoint = "https://your-azuresearch-endpoint.search.windows.net";
            const string azuresearchKey = "your-azuresearch-key";
            const string azuresearchIndexname = "SKGitHub";

            Console.WriteLine("== Start Applicaiton ==");

            var builder = new KernelBuilder();
            builder.WithAzureTextCompletionService(
                     deploymentName_chat,
                     endpoint,
                     openAiKey);
            builder.WithAzureTextEmbeddingGenerationService(
                deploymentName_embedding,
                endpoint,
                openAiKey);
            builder.WithMemoryStorage(new AzureCognitiveSearchMemoryStore(azuresearchEndpoint, azuresearchKey));

            var githubFiles = new Dictionary<string, string>()
            {
                ["https://github.com/microsoft/semantic-kernel/blob/main/README.md"]
                    = "README: Installation, getting started, and how to contribute",
                ["https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/02-running-prompts-from-file.ipynb"]
                    = "Jupyter notebook describing how to pass prompts from a file to a semantic skill or function",
                ["https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/00-getting-started.ipynb"]
                    = "Jupyter notebook describing how to get started with the Semantic Kernel",
                ["https://github.com/microsoft/semantic-kernel/tree/main/samples/skills/ChatSkill/ChatGPT"]
                    = "Sample demonstrating how to create a chat skill interfacing with ChatGPT",
                ["https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel/Memory/Volatile/VolatileMemoryStore.cs"]
                    = "C# class that defines a volatile embedding store",
                ["https://github.com/microsoft/semantic-kernel/tree/main/samples/dotnet/KernelHttpServer/README.md"]
                    = "README: How to set up a Semantic Kernel Service API using Azure Function Runtime v4",
                ["https://github.com/microsoft/semantic-kernel/tree/main/samples/apps/chat-summary-webapp-react/README.md"]
                    = "README: README associated with a sample starter react-based chat summary webapp",
            };

            var kernel = builder.Build();
            Console.WriteLine("Adding some GitHub file URLs and their descriptions to a volatile Semantic Memory.");
            var i = 0;
            foreach (var entry in githubFiles)
            {
                await kernel.Memory.SaveReferenceAsync(
                    collection: azuresearchIndexname,
                    description: entry.Value,
                    text: entry.Value,
                    externalId: entry.Key,
                    externalSourceName: "GitHub"
                );
                Console.WriteLine($"  URL {++i} saved");
            }

            string ask = "I love Jupyter notebooks, how should I get started?";
            Console.WriteLine("===========================\n" +
                                "Query: " + ask + "\n");

            //From here, search with vector data store
            var memories = kernel.Memory.SearchAsync(azuresearchIndexname, ask, limit: 5, minRelevanceScore: 0.77);

            i = 0;
            await foreach (MemoryQueryResult memory in memories)
            {
                Console.WriteLine($"Result {++i}:");
                Console.WriteLine("  URL:     : " + memory.Metadata.Id);
                Console.WriteLine("  Title    : " + memory.Metadata.Description);
                Console.WriteLine("  Relevance: " + memory.Relevance);
                Console.WriteLine();
            }
        }
    }
}

アプリケーションの実行

実行すると以下のメッセージが返ってきました。 "Result 1:" が返ってきて、ちゃんと結果も記載されています。

Azure ポータル側でインデックスを確認すると、以下の様にドキュメントデータが保存されていることが分かります。

どの様なフィールドが作成されるかについては、以下のソースコードを参照すると理解が早い認識です。
semantic-kernel/dotnet/src/Connectors/Connectors.Memory.AzureCognitiveSearch/AzureCognitiveSearchMemoryRecord.cs at main · microsoft/semantic-kernel · GitHub

何とか無事に検索できることは分かりましたが、以下のソースコードを眺めている限り Azure Cognitive Search で利用可能なはずの Semantic Search を有効化するポイントがなく、どうやら実装を見送ったことが分かります。2023年7月時点でようやく Semantic Kernel で Azure Cognitive Search ベクトルデータストアとして活用することができるようになりましたが、急激に発展を遂げるこの分野、まだまだ目が離せないようです。
github.com