ごった煮

色々な事を書いてます

AAD のゲストユーザでも SQL Database の AAD 認証を使う話

SQL Database は、 AAD ユーザを使ったログインが出来ますが、外部の AAD からのゲストユーザの場合、直接権限付与が出来ないので、外部ユーザでもログイン出来るようにする方法をまとめます。

仕組み

これを実現するための仕組みは、以下のような形です。

  1. AAD にグループを作成する
  2. 作成したグループにゲストユーザを追加する
  3. グループを DB ユーザとして登録する

この流れで実現が可能です。

グループを作成して、ゲストユーザを追加までは、今回の本質ではないので省略です。

権限付与は、以下の SQL を流すだけで OK です。

GO

CREATE USER [グループ名] FROM EXTERNAL PROVIDER;

ALTER ROLE db_owner ADD MEMBER [グループ名];

GO

これを実行するとユーザ作成完了です。

CREATE USER [グループ名] に FROM EXTERNAL PROVIDER を付けると、AAD のユーザを DB のユーザとして追加するという構文になります。

グループ名に、作成したグループの名前を入れて実行すると、そのグループを DB のユーザとして登録できます。

ゲストユーザ以外なら、ユーザ名にメールアドレスを入れてやると AAD のユーザを DB ユーザに追加できます。

ログインする際、master 以外の DB にユーザを作成した場合は、SSMS などで接続する際に DB 名を指定してやる必要があるので気を付けましょう。

まとめ

SQL Database に繋ぎに行く場合、AAD を使ってセキュアに繋ぐことが自分たちのデータを保護する上でとても重要です。

ゲストユーザ以外を追加する場合でも、グループ単位でまとめてユーザ作成をしておけば、権限の付与やはく奪という作業の手間を省くことが出来るので、ぜひ使ってみてください。

Azure CLI で Azure Monitor に登録されているアラートの情報を取得する

Azure を使っていると監視で Azure Monitor を使うと思いますが、毎回似たようなことを手動でやるのもしんどいので CLI でいい感じにした方が色々楽ですよね

ということで今回は、Azure CLI でAzure Monitor を触るための第一歩をまとめます。

Azure CLI でメトリックアラートの情報を取得する

とりあえず az コマンドを使えるように Azure CLI を入れるか Windows Terminal で Cloud Shell に繋ぎましょう

以下のコマンドで Azure Monitor に登録されているメトリックアラートの情報が取得できます。

az monitor metrics alert list

このコマンドで、現在選択しているサブスクリプションのすべてのアラートが表示されます。(デフォルトだとJson形式)

[
  {
    "actions": [
      {
        "actionGroupId": "アクショングループid",
        "webhookProperties": null
      }
    ],
    "autoMitigate": null,
    "criteria": {
      "additionalProperties": null,
      "allOf": [
        {
          "criterionType": "StaticThresholdCriterion",
          "dimensions": [],
          "metricName": "Http5xx",
          "metricNamespace": "microsoft.web/sites",
          "name": "cond0",
          "operator": "GreaterThan",
          "threshold": 0.0,
          "timeAggregation": "Count"
        }
      ],
      "odatatype": "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria"
    },
    "description": "High CPU",
    "enabled": true,
    "evaluationFrequency": "0:01:00",
    "id": "リソースid",
    "lastUpdatedTime": null,
    "location": "global",
    "name": "CLI_Test",
    "resourceGroup": "Staging",
    "scopes": [
      "監視ターゲットid"
    ],
    "severity": 2,
    "tags": null,
    "type": "Microsoft.Insights/metricAlerts",
    "windowSize": "0:05:00"
  }
]

結果は、上のような感じです。

az monitor alert list

というコマンドもありますが、将来的に廃止されるようなので

az monitor metrics alert list

を使用しましょう。

結果をフィルタする

単純に json でデータをドカンと落としても、ほとんどの情報は、必要のないデータなので必要なデータだけ出すようにフィルタを掛けましょう。 Azure CLI では、結果を jmespath 方式でフィルタ出来ます。

az monitor alert list --query "jmespathのクエリ"

という形でフィルタが出来ます。

アラート自体のリソースid と名前だけを表示するようにするクエリは、以下のような感じです。

az monitor alert list --query "[*].[id,name]"

これを実行すると以下のような結果が返されます。

[
  [
    "リソースid",
    "リソース名"
  ]
]

まとめ

Azure CLI で簡単にAzure Monitor の情報を取得できました。

CLI からアラートの作成なども行えるので、よくある VM のリソース監視や DB のリソース監視などのように大体みんな同じようなアラートを設定するような場合に、一つテンプレートを作っておけば簡単に同じようなアラートを展開できるためミスも減らせて非常に有意義です。

積極的に使っていきましょう。

Windows10がスリープした直後に復帰を繰り返す件に対応した話

タイトルの通り、ここ最近自宅のWindows10マシンがスリープした直後にすぐ復帰を繰り返しスリープ機能が実質死んでしまったので、その時に行った対処法をまとめます。

発生している事象

  • Windowsのスリープ機能でスリープモードに入る → 5秒程ですぐ復帰する

いつぐらいから

  • Windows 10を1903に更新した直後から

原因の調査

  • Event Viewerを見てみましょう

Event Viewerでの調査

Event Viewer > Windows Logs > System に電源関連などシステム周りのログが残っているのでそれを開きます。 SourceがPower-Troubleshooter を見るとそれっぽいログが残っていました。

f:id:papemk2:20190831112733p:plain

イベントのログを見ると、Wake Source: Device - Realtek PCIe GbE Family Controller とありました。

f:id:papemk2:20190831112841p:plain

マザーボードのネットワークコントローラが悪さをしているようです。

とりあえずこのデバイスがスリープ復帰出来ないように設定を変えます。

バイスの設定を変える

Device Managerから設定を変更します。

今回の場合は、ネットワークコントローラなのでNetwork adaptersの下にいました

f:id:papemk2:20190831113411p:plain

このプロパティを変更します。

Power Managementタブにこの設定があります。

f:id:papemk2:20190831113552p:plain

Allow this device to wake the computer にチェックが入っているとそのデバイスからスリープ復帰が出来るようになるのでチェックを外します。 これで解決です。

まとめ

Windowsで何か困ったらEvent Viewerを見てみましょう。

OData用のPocoを生成する(C#)

ODataを使用する際、OData用ライブラリを使う場合などでPocoがあると何かと便利なので、Pocoを生成する簡単な方法についてメモを残します。

やってみる

ツールを入れる

odata2pocoという便利なツールがOSSで公開されているのでこちらを入れます。 dotnetコマンドが入っている前提なので、先にdotnetコマンドを入れておきましょう。(dotnetコマンドを使わない方法も一応あります。)

dotnetコマンド

dotnet tool install --global OData2Poco.dotnet.o2pgen  

何もエラーが出なければインストール完了です。

使ってみる

基本的なコマンドは、

dotnet o2pgen

です。

認証があるURLに対してのコマンドは、以下のような感じ

dotnet o2pgen -r "Url" -u "ユーザID" -p "パスワード" -o "認証方式"

認証方式は、none、basic、token、oauth2の4種類、デフォルトはnoneなので、basic認証が掛かっている対象に対してユーザIDとパスワードを設定しただけで 認証方式を指定しないと認証エラーが返ってくるので注意が必要です。

これを実行するとpoco.csというPocoが生成されます。 ファイル名を指定する場合などは、以下のような感じです。

dotnet o2pgen -r "Url" -f "ファイル名" -x "メタデータファイル名" -u "ユーザ名" -p "パスワード" -o "認証方式"

まとめ

生のODataもいいけどPocoがあると便利ですよね

ASP.NET Core Identityをシンプルに使うための実装方法

ASP.NET Core Identiyは、アプリケーションにメンバーシップ機能を実装する仕組みとしては、かなり優秀です。 テンプレートでそのまま普通のメンバーシップ機能を使用可能です。

ですが、ロール機能など場合によって使わない機能もデフォルトで付いてくるため、それらを削除して使えるようにしてみます。

実装方法

今回は、認証付きのASP.NET Core テンプレートを使用します。

実装に必要な全コードは、以下のものです。 startup.csのConfigureServicesに以下を実装します。

            services.AddIdentityCore<IdentityUser>()
                .AddDefaultUI(UIFramework.Bootstrap4)
                .AddEntityFrameworkStores<ApplicationDbContext>();

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
                options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
                options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
            })
            .AddCookie(IdentityConstants.ApplicationScheme, o =>
            {
                o.LoginPath = new PathString("/Account/Login");
                o.Events = new CookieAuthenticationEvents()
                {
                    OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
                };
            })
            .AddCookie(IdentityConstants.ExternalScheme, o => 
            {
                o.Cookie.Name = IdentityConstants.ExternalScheme;
                o.ExpireTimeSpan = TimeSpan.FromMinutes(5.0);
            })
            .AddCookie(IdentityConstants.TwoFactorRememberMeScheme, o =>
            {
                o.Cookie.Name = IdentityConstants.TwoFactorRememberMeScheme;
            })
            .AddCookie(IdentityConstants.TwoFactorUserIdScheme, o =>
            {
                o.Cookie.Name = IdentityConstants.TwoFactorUserIdScheme;
                o.ExpireTimeSpan = TimeSpan.FromMinutes(5.0);
            });
            services.AddScoped<ISecurityStampValidator, SecurityStampValidator<IdentityUser>>();

AddIdentityCoreは、ユーザテーブルだけを指定可能なので、ユーザテーブルのみの実装でIdentityを使用可能です。

まとめ

無駄なコードがあると見通しが悪くなりがちなので、いらない機能は、削除してしまいましょう。

ASP.NET Core MVCでAzure App Configurationを使う

App Configurationは、ASP.NET Coreでも使えるようにクライアントライブラリが提供されているので、簡単な実装を試してみます。

実装する

前提

  • App Configurationに設定を追加してある
  • ASP.NET Core MVCのテンプレートを作っておく

パッケージを入れる

以下のパッケージをNuGetから入れます。

Microsoft.Extensions.Configuration.AzureAppConfiguration

一番シンプルな読み込み

Program.csのCreateWebHostBuilderの設定を以下のようにします。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        var settings = config.Build();
        config.AddAzureAppConfiguration("接続文字列");
    })
        .UseStartup<Startup>();
}

これでApp Configurationの設定は、完了です。 あとは、IConfigurationから通常のアプリケーション設定と同じように設定を読み出せます。 この設定の場合、ラベルをセットしていない設定のみ取得可能です。

ラベルを指定して読み込む

ラベルを指定して読み出す場合は、Function Appと同じようにUseメソッドを使用します。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        var settings = config.Build();
        config.AddAzureAppConfiguration(_ => 
            _.Connect("接続文字列")
            .Use("キー名", "ラベル名"));
    })
    .UseStartup<Startup>();

更新を検知する

更新を検知する場合もFunction Appと同様にWatchメソッドを使用して変更の監視を設定します。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        var settings = config.Build();
        config.AddAzureAppConfiguration(_ => 
            _.Connect("接続文字列")
            .Watch("キー名", "ラベル名", TimeSpan)
            .Use("キー名", "ラベル名"));
    })
    .UseStartup<Startup>();

これで指定したTimeSpanの度に設定の変更をポーリングして、変更が入ったら自動的に差し変わります。

以下のようなViewを用意して画面をリロードしてやると、変化が分かりやすいと思います。

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<div class="text-center">
    <h1 class="display-4">@(Configuration["Message"])</h1>
</div>

特定の設定が変更されたタイミングで全部を再読み込みしたい場合もFunction Appと同じくWatchAndReloadAllメソッドを使用します。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        var settings = config.Build();
        config.AddAzureAppConfiguration(_ => 
            _.Connect("接続文字列")
            .Use("キー名", "ラベル名")
            .WatchAndReloadAll("キー名2", "ラベル名2", TimeSpan)
            .Use("キー名2", "ラベル名2")
            );
    })
    .UseStartup<Startup>();

まとめ

基本的にFunction AppもASP.NET Coreも同じ.NET Coreなのでほとんど違い無く使用できます。

特に難しいこともなくASP.NET Coreに組み込めるのは、かなり便利かなと思います。

Azure FunctionsでAzure App Configurationを使う

Azure App Configurationは、既に.NET Core、.NET Frameworkに対応したクラスライブラリが提供されているのでFunction Appでもすぐに使用できます。

簡単に使用できるので覚えておくと便利かと思います。

実装する

前提条件

  • Azure App Configurationのリソースを作成済み
  • 最新のFunctions toolsをインストール済み

パッケージのインストール

以下のパッケージをNuGetからインストールします。

Microsoft.Extensions.Configuration.AzureAppConfiguration

App Configurationで設定を作る

ポータル > アプリ構成 > キー値のエクスプローラー で設定します。 f:id:papemk2:20190228221326p:plain

ラベルは、何も設定しないと「ラベルなし」になります。 特にラベル作成用の画面がないので、最初戸惑うかもしれませんが、ラベルのテキストボックスにテキストを入れて、設定を作成するとラベルも一緒に作成されます。

一度ラベルが作成されると、次回からラベルのテキストエリアで作成済みのラベルが選択できます。 また、ラベルが選択できる状態でも同じようにテキストエリアにテキストを入力するとラベルが追加されます。 f:id:papemk2:20190228221632p:plain

通常設定を追加すると以下のように表示されます。 f:id:papemk2:20190228221733p:plain

同じ名前のキーに異なるラベルを付けて作成すると、同一キーでまとめて表示してくれます。 キーの横にある三角マークで展開できます。 f:id:papemk2:20190228221955p:plain

アプリを実装する

Function Appは、v2.xを使用します。

一番シンプルな実装

[FunctionName("Function1")]
public static void Run([TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, ILogger log)
{            
    var builder = new ConfigurationBuilder();
    builder.AddAzureAppConfiguration("接続文字列");

    var config = builder.Build();
    log.LogInformation(config["キー名"]);
}
builder.AddAzureAppConfiguration("接続文字列")

を呼び出すと、これだけで設定を読み込む準備が完了します。 あとは、通常のIConfigurationを使うのと同じ感覚で設定の読み込みができます。

この実装では、ラベルを指定していません。 そのためこれで取得できるのは、ラベルを設定していない設定に限ります。

ラベル付きの設定を取得する

ラベルを指定して設定を読み込む場合は、AddAzureAppConfigurationで読み込む設定を指定します。 以下のような感じです。

builder.AddAzureAppConfiguration(_ =>
    _.Connect("接続文字列")
    .Use("キー名", "ラベル名")
);

var config = builder.Build();

log.LogInformation(config["キー名"]);

これで指定した設定を読み出せます。

設定の変更を検知する

App Configurationは、アプリケーションの再起動なしに設定の変更を反映する仕組みを備えています。

以下のようにWatchメソッドに登録すると、設定したTimeSpanの時間でポーリングをして変更を監視してくれます。

builder.AddAzureAppConfiguration(_ =>
    _.Connect("接続文字列")
    .Watch("Config:Test", "Staging", TimeSpan.FromSeconds(1))
    .Use("Config:Test", "Staging")
);

var config = builder.Build();

while (true)
{
    var value = config["Config:Test"];

    log.LogInformation(value);
    Thread.Sleep(10000);
}

ループ中に設定を変更すると、変更されることが確認できます。

特定の設定を変更したらすべて再読み込みさせる

設定変更の監視に近い機能で、特定の設定が変更されたらすべての変更を再読み込みする設定も可能です。

builder.AddAzureAppConfiguration(_ =>
    _.Connect("接続文字列")
    .WatchAndReloadAll("Config:Test", "Staging", TimeSpan.FromSeconds(1))
    .Use("Config:Test", "Staging")
    .Use("Config:Test2", "Staging")
);

var config = builder.Build();

while (true)
{
    var value = config["Config:Test"];
    var value2 = config["Config:Test2"];

    log.LogInformation(value);
    log.LogInformation(value2);
    Thread.Sleep(10000);
}

上記のソースコードで、WatchAndReloadAllに設定していない値を変更しただけだと値は何も変更されないが、そのあとにWatchAndReloadAllに設定した値を変更すると、 変更した値がすべて変更される動きが確認できます。

まとめ

特に難しい実装をせずにApp Configurationから設定を読み出しができました。

アプリの再起動なしで設定を変更する機能は、結構使えるシナリオがあるかなと思います。 ですが、例えばRedisなどでコネクションを張っている状態で接続文字列を差し替えたりした場合など不用意に差し替えをすると危なそうなシナリオも考えられるので慎重に扱う方が良さそうです。

また短い時間での大量な設定に対するポーリングや、全件差し替えを行うと結構コストが高そうなので、その点も考慮が必要かもしれません。