Hyper-V 上の Windows 11 で WSL2 を有効化する
いつの間にか WSL のインストール方法が簡単になっていた。
PowerShell を管理者として起動して以下のコマンドを実行する。既定では Ubuntu がインストールされる。インストールが完了したら再起動。
wsl --install
再起動後、Ubuntu が自動的に起動してユーザー名とパスワードの設定が求められるので、設定する。
起動したらアップデートしておきましょう。
sudo apt update sudo apt upgrade
めちゃくちゃ簡単ですね。素晴らしい。
ちなみに初期化も簡単にできます。
Hyper-V 上の Windows 11 で Hyper-V を有効化する
VM を停止した状態で、Hyper-V ホスト側で PowerShell を管理者として起動して以下のコマンドを実行する。
$vmName = "Win11" # 仮想マシンの名前 Set-VMProcessor -VMName $vmName -ExposeVirtualizationExtensions $true
VM を起動して、VM 上で PowerShell を管理者として起動して以下のコマンドを実行する。
実行後、再起動を促されるので再起動する。
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
ここによると何かしらネットワーク設定が必要なように見えるけど Windows 11 を普通にインストールしたら使えた(Default Switch を指定)。 docs.microsoft.com
Bot Framework SDK で開発したボットを Teams で利用するために必要な構成
ボットを Teams で利用するために登録する方法は2つある。
- Azure Bot を利用して登録する
- Bot Framework Portal を利用して登録する
Azure Bot は Azure のリソースとして登録する。作成すると Azure AD にアプリケーションとして登録される。
Bot Framework Portal の GUI からボットを登録しようとすると Azure Portal に誘導される。Bot Framework Portal でボットを作成するためには作成用の URL に直接アクセスする必要がある。
Bot Framework Portal から作成した場合も Azure AD にアプリケーションとして登録される。
Teams App Studio や Teams Developer Portal からボットを作成した場合は Bot Framework Portal にボットとして登録される。
Azure Bot も無料で利用できるので Azure サブスクリプションが使えるのであれば Azure Bot を使った方が良さそう、というか Microsoft としては使ってほしそう。
ただ Azure Bot のためだけに Azure サブスクリプションを用意するのは…という場合は Bot Framework Portal にボットを登録すればよい。
...という感じだろうか。最近のドキュメントは Azure Bot 前提で記載してあるのがほとんどで Bot Framework Portal についての記述がほとんどなくて混乱した。
こちらも参照。
Azure Bot が最近?マネージド ID に対応したので Azure 使っている場合は Azure App Service でボットをホストしてマネージド ID を使うのがベストプラクティスっぽい。
[随時更新] Windows 11 にインストールするアプリケーション一覧
管理者としてターミナルを実行してインストールを実施する。(Windows キー + x)
# 既定でインストールされているターミナルを最新化 winget upgrade --id Microsoft.WindowsTerminal # スコープはすべてシステム。--interactive を外すと完全自動インストールになるが設定を既定値以外にしたいので対話型インストールにしている。 winget install --scope machine --id Microsoft.PowerShell --interactive winget install --scope machine --id Microsoft.VisualStudioCode --interactive winget install --scope machine --id Git.Git --interactive winget install --scope machine --id Google.Chrome --interactive winget install --scope machine --id Mozilla.Firefox --interactive winget install --scope machine --id 7zip.7zip --interactive winget install --scope machine --id NickeManarin.ScreenToGif --interactive winget install --scope machine --id Adobe.Acrobat.Reader.64-bit --interactive winget install --scope machine --id Microsoft.PowerToys --interactive # --scope machine を入れると失敗する。入れなくても Program Files 配下にインストールされる winget install --id Microsoft.VisualStudio.2022.Community --interactive
Bot Framework SDK で開発したボットを Teams で動かすところまで
プロジェクトの作成
Bot Framework Emulator で動作確認
Visual Studio でデバッグ実行しておく。
Bot Framework Emulator を起動して [File] -[Open Bot] を開く。
[Bot URL] に http://localhost:3978/api/messages
と入力して [Connect] をクリックする。
動作確認。
Bot を Azure にデプロイする
Visual Studio から発行する。
プロファイルを作成したら発行する。
Bot Framework Emulator で動作確認
Bot Framework Emulator で ngrok のパスを指定する。
Bot URL に http://{Azure Web App の URL}/api/messages
と入力して [Connect] をクリックする。
動作確認。
Azure Bot の作成
Key Vault にクライアントシークレットが格納されているので見に行く(本当は Web App からマネージド ID で見に行くのがいいのだろうな)
Web App の Application 設定に MicrosoftAppId / MicrosoftAppPassword / MicrosoftAppTenantId を設定する。
Azure Bot のメッセージングエンドポイントに http://{Azure Web App の URL}/api/messages
を設定する。
「Web チャットでテスト」で動作確認。
Teams チャネル追加
「Open in Teams」をクリック。
アプリパッケージ作成
manifest.json
id
と botId
にアプリケーション ID を入力。
{ "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.11/MicrosoftTeams.schema.json", "manifestVersion": "1.11", "version": "1.0.0", "id": "Micrsoft_APP_ID", "packageName": "com.example.mysamplebot", "developer": { "name": "The Developer", "websiteUrl": "https://example.com/", "privacyUrl": "https://example.com/privacy", "termsOfUseUrl": "https://example.com/app-tos" }, "name": { "short": "EchoBot", "full": "Echo-Bot" }, "description": { "short": "Echo Bot.", "full": "This is the Echo Bot." }, "icons": { "outline": "icon32x32.png", "color": "icon192x192.png" }, "accentColor": "#ff0000", "bots": [ { "botId": "Microsft_APP_ID", "needsChannelSelector": false, "isNotificationOnly": false, "scopes": [ "personal", "team", "groupchat" ], "supportsFiles": false, "commandLists": [] } ] }
アイコン
192 x 192 と 32 x 32 の PNG ファイルを用意する。manifest.json でファイル名を指定する。
圧縮
マニフェストファイルとアイコン2つを ZIP で固める。
アプリアップロード(サイドローディング)
Teams 管理センター の「Teams のアプリ」-「セットアップポリシー」から「カスタムアプリをアップロード」をオンにする。
Teams クライアントを開いて「アプリ」-「アプリを管理」-「カスタムアプリをアップロード」をクリック。アプリパッケージの ZIP ファイルを選択して追加する。
アプリアップロード(組織のアプリカタログ)
Teams クライアントを開いて「アプリ」-「アプリを管理」-「組織のアプリカタログにアプリをアップロードします」をクリック。アプリパッケージの ZIP ファイルを選択して追加する。
「アプリ」-「組織向けに開発」にアプリが表示される。
"Build Bot Framework bots with Microsoft Graph" を試した
おそらく仕様が変わっていて、何か所か悩むところがあったので忘れないように残す。
Create a Bot Channels registration
タイトルが Bot Channels Registration になっているけど手順は Azure Bot。
Azure Bot 作成時にシングルテナント、マルチテナント、マネージド ID の選択肢が出てくる。 手順通りに進めるならマルチテナント。シングルテナントとマネージド ID は前は選べなかったっぽい。
手順 13 以降が Bot Channel Registration の設定になっているけど、この手順は不要。
Add Microsoft identity platform authentication
appsettings.json が若干違った。
{ "MicrosoftAppType": "", "MicrosoftAppId": "YOUR_BOT_APP_ID_HERE", "MicrosoftAppPassword": "YOUR_BOT_CLIENT_SECRET_HERE", "MicrosoftAppTenantId": "YOUR_TENANT_ID_HERE", "ConnectionName": "GraphBotAuth" }
コードをそのまま使うとログアウトに失敗する。(BotFrameworkAdapter にキャストできない。)
以下 2 行を
var botAdapter = (BotFrameworkAdapter)innerDc.Context.Adapter; await botAdapter.SignOutUserAsync(innerDc.Context, ConnectionName, null, cancellationToken);
以下のように修正する。
var userTokenClient = innerDc.Context.TurnState.Get<UserTokenClient>(); await userTokenClient.SignOutUserAsync(innerDc.Context.Activity.From.Id, ConnectionName, innerDc.Context.Activity.ChannelId, cancellationToken).ConfigureAwait(false);
Test authentication
ここで指定する Microsoft App ID は Graph Calendar Bot Auth ではなく Azure Bot 作成時に作られたアプリの方の Application ID。
Endpoint URL が https になっているが正しくは http。
謎のエラーが出たが Bot Framework Emulator と ngrok を最新化すると解消。
認証用のコードを入力する画面は表示されない。
Get the logged on user
ユーザーに写真が設定されていないとユーザー取得に失敗する。
「ASP.NET Core Blazor WebAssembly でホストされるアプリを Azure Active Directory でセキュリティ保護する」のアプリ登録をコマンドで。
サーバー API アプリの登録。
# Create Application Object $app = az ad app create --display-name "Blazor Server AAD" # Get Application ID $appId = ($app | ConvertFrom-Json).appId # Create Service Principal az ad sp create --id $appId # 既定のスコープを削除 $oauth2Permissions = ($app | ConvertFrom-Json).oauth2Permissions $oauth2Permissions[0].isEnabled = $false $oauth2Permissions = ConvertTo-Json -InputObject @($oauth2Permissions) $oauth2Permissions | Out-File -FilePath .\oauth2Permissions.json az ad app update --id $appId --set oauth2Permissions=`@oauth2Permissions.json az ad app update --id $appId --set oauth2Permissions="[]" Remove-Item -Path .\oauth2Permissions.json # アプリケーションIDのURIを追加 az ad app update --id $appId --identifier-uris "api://$appId" # スコープを追加 $oauth2Permissions = @{ adminConsentDescription = "Allows the app to access server app API" adminConsentDisplayName = "Access API" id = (New-Guid).Guid isEnabled = $true type = "Admin" userConsentDescription = "" userConsentDisplayName = "" value = "API.Access" } $oauth2Permissions = ConvertTo-Json -InputObject @($oauth2Permissions) $oauth2Permissions | Out-File -FilePath .\oauth2Permissions.json az ad app update --id $appId --set oauth2Permissions=`@oauth2Permissions.json Remove-Item -Path .\oauth2Permissions.json
クライアントアプリの登録。
# Create Application Object $app = az ad app create --display-name "Blazor Client AAD" # Get Application ID & Object ID $appId = ($app | ConvertFrom-Json).appId $objectId = ($app | ConvertFrom-Json).objectId # Create Service Principal az ad sp create --id $appId # 既定のスコープを削除 $oauth2Permissions = ($app | ConvertFrom-Json).oauth2Permissions $oauth2Permissions[0].isEnabled = $false $oauth2Permissions = ConvertTo-Json -InputObject @($oauth2Permissions) $oauth2Permissions | Out-File -FilePath .\oauth2Permissions.json az ad app update --id $appId --set oauth2Permissions=`@oauth2Permissions.json az ad app update --id $appId --set oauth2Permissions="[]" Remove-Item -Path .\oauth2Permissions.json # アクセストークンを取得する $response = az account get-access-token --resource-type ms-graph | ConvertFrom-Json $accessToken = $response.accesstoken $headers = @{"Authorization" = "Bearer $accessToken"; "Content-Type" = "application/json" } # SPA のリダイレクトURLを設定する $body = @{ spa = @{ redirectUris = @( "https://localhost:5001/authentication/login-callback" ) } } | ConvertTo-Json Invoke-RestMethod -Method Patch -Uri https://graph.microsoft.com/v1.0/applications/$objectId -Headers $headers -Body $body # 暗黙的な許可を無効にする $body = @{ web = @{ implicitGrantSettings = @{ enableAccessTokenIssuance = $false enableIdTokenIssuance = $false } } } | ConvertTo-Json Invoke-RestMethod -Method Patch -Uri https://graph.microsoft.com/v1.0/applications/$objectId -Headers $headers -Body $body # アクセス許可を付与する $apiName = "Microsoft Graph" $permissionType = "Scope" $permissionName = "User.Read" $apiServicePrincipal = az ad sp list --filter "displayname eq '$apiName'" | ConvertFrom-Json $apiPermission = $apiServicePrincipal.oauth2Permissions | Where-Object { $_.value -eq $permissionName } az ad app permission add --id $appId --api $apiServicePrincipal.appId --api-permissions "$($apiPermission.id)=$permissionType" # アクセス許可を付与する $apiName = "Blazor Server AAD" $permissionType = "Scope" $permissionName = "API.Access" $apiServicePrincipal = az ad sp list --filter "displayname eq '$apiName'" | ConvertFrom-Json $apiPermission = $apiServicePrincipal.oauth2Permissions | Where-Object { $_.value -eq $permissionName } az ad app permission add --id $appId --api $apiServicePrincipal.appId --api-permissions "$($apiPermission.id)=$permissionType" # 管理者の同意を与える az ad app permission grant --id $appId --api $apiServicePrincipal.appId --scope $permissionName