Azure Functions で Serilog を実装する
azure-functions-startup-serilog
Serilog を実装した Azure Functions プロジェクトのサンプルです
出力イメージ
コンソール出力
ファイル出力
テンプレートから実装した機能
パッケージの追加
- Serilog.AspNetCore
- Serilog.Exceptions
以下は必要に応じて
- Serilog.Exceptions.EntityFrameworkCore
DI 機能の追加
Startup クラスの追加
[assembly: FunctionsStartup(typeof(Startup))] namespace FunctionApp4; public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { } }
- Serilog の構成
var logger = new LoggerConfiguration() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Worker", LogEventLevel.Warning) .MinimumLevel.Override("Host", LogEventLevel.Warning) .MinimumLevel.Override("System", LogEventLevel.Error) .MinimumLevel.Override("Function", LogEventLevel.Error) .MinimumLevel.Override("Azure.Storage.Blobs", LogEventLevel.Error) .MinimumLevel.Override("Azure.Core", LogEventLevel.Error) .WriteTo.Console() .WriteTo.Debug() .WriteTo.File( $"logs\\{nameof(MyFunctionWithSerilog)}.txt", LogEventLevel.Information, rollingInterval: RollingInterval.Hour) .CreateLogger();
- Serilog のカスタムログプロバイダーを追加
builder.Services.AddLogging(loggingBuilder => { loggingBuilder.AddSerilog(logger, true); });
- ILogger
をコンストラクターに注入
private readonly ILogger<Function1> _logger; public Function1(ILogger<Function1> logger) { _logger = logger; }
GitHub
OAuth 2.0 と OpenID Connect とは
OAuth 2.0 と OpenID Connect について雰囲気で分かってたつもりだったので、調べて備忘録と言語化のために記事を書きます。
認証と認可
前提知識として『認証』と『認可』についてです。
認証 は、クライアントが誰(Who)であるかを特定すること。
英語でいうと Authentication。AuthN と表現されることもあります。認可 は、クライアントがリソースに対して何(What)ができるのかを特定すること。
英語でいうと Authorization。AuthZ や AuthR と表現されることもあります。
OAuth 2.0
OAuth 2.0 は『認可』の仕組みです。
OAuth 2.0 は、アクセストークンの要求とその応答を標準化したものです。
アクセストークン
クライアントが、リソースオーナーのデータを利用することを許可されていることを示すもの。
(データの利用許可書のようなものかな)
OAuth 2.0 の登場人物(ロール)
- リソースオーナー(resource owner)
- データの本来の持ち主
- リソースサーバー (resource server)
- リソースオーナーのデータを預かってる
- クライアント (client)
- Web アプリケーションやクライアントアプリケーションなど、リソースサーバーが持っているデータの要求する者
- 認可サーバー (authorization server)
- アクセストークンの発行者
OAuth 2.0 のプロトコルフローの概要
認可サーバーは、クライアントからの認可要求をリソースオーナーにリダイレクトした後、その許可要求の許諾を受けて、アクセストークンを生成して、クライアントにアクセストークンを渡す。
OpenID Connect
OAuth 2.0 をベースにした『認証』の 仕組みです。
OpenID Connect は、ID トークンの要求とその応答を標準化したものです。
ID トークン
どの OpenID プロバイダーから、
どのユーザーの情報を、
どのアプリケーション(利用者)に対して、
いつまでの有効期限で、
いつ発行されたのか、
という情報を必ず含んだもの。
ID トークンは、ユーザーが認証されたことの証なので、それを利用することで ID 連携が実現できます。
OAuth 2.0 と OpenID Connect の関係性
OpenID Connect が OAuth 2.0 をベースとしているため、プロトコルフローが似ています。
そのため、OpenID Connect で登場した OpenID プロバイダー が OAuth 2.0 の 認可サーバー を兼任することが多くなりました。
(アクセストークン発行と ID トークン発行を同時にやっちゃいましょうという流れですね。)
OpenID Connect のウェブサイトでは、次のように述べられてます。
(Identity, Authentication) + OAuth 2.0 = OpenID Connect
https://openid.net/connect/faq/
参考情報
レガシーコードをリファクタリングしてみた
発端
先日、TTDBC のコミュニティ内でレガシーコードをリファクタリングするライブコーディングに参加させていただきました。 実際に自分でも手を動かしてみようと思い、C# のサンプルでリファクタリングを実践してみました。
サンプルコード
今回リファクタリングのサンプルで使用したリポジトリはこちらです。 様々なプログラミング言語で用意されているので、お好きなものから選べます。
ちなみに、リポジトリ名の Kata
は空手などの 型
という意味だそうです。
実際にリファクタリングしてみたコード
元々用意されていたテストフレームワークが NUnit だったので、別途 xUnit のテストプロジェクトを用意してそちらにテストコードを書きました。 (その際、テストプロジェクトフォルダをルートフォルダに作ってしまった…)
所感
ライブコーディングでは一気にテストを書くのではなく、ある程度テストコードを書いたらリファクタリングを進めて、慎重にリファクタリングをしたい部分があれば、その範囲内を対象としてテストコードを追加で記述するといった感じでした。
流暢にテスト&リファクタリングを進めてて感動しました。
私もそれに倣って簡単なテストコードを書いてグリーンにしてから、まずは IDE のリファクタリング機能を全面に信じて、IDE のリファクタリング機能のみを利用してリファクタリングを進めました。
もうこれ以上は IDE によるリファクタリングができない、つまり本番コードに手を加える必要が出てきた際に、テストコードを書きました。私はここで一気に思いつくテストコードを書きました。
本番コードに手を加えるリファクタリングは、歩幅を広げすぎない程度に少しずつやった方が良いとは思っていたのですが、それがなかなか難しくて一気に本番コードに手を加えてしまいました。反省…。 (Factory パターンを適用する形で一気にコードを書き換えた)
I/F を定義して、それを実装したクラスを作成して、いくつか同じ I/F を実装したクラスが登場してきたら Factory メソッド作りますかとなって…という流れが理想なのかなと。
テストデータを自動生成するライブラリ
データベースに投入するデータや、単体テスト時に利用するデータを作るのはとても面倒です。
今回紹介する Bogus
というライブラリを使うと容易にデータが作れます。
動作環境
- Windows 10 Version 2004
- Visual Studio 2019 Version 16.7.1
- .NET Core 3.1.401
- Bogus 30.0.3 www.nuget.org
サンプルコード
単純なクラス
public class BillingDetails { public string CustomerName { get; set; } public string Email { get; set; } public string Phone { get; set; } public string AddressLine { get; set; } public string City { get; set; } public string PostCode { get; set; } public string Country { get; set; } } private static List<BillingDetails> GenerateRandomBillingDetails(int numberOfRecordsPerBatch) { var faker = new Faker<BillingDetails>() // .StrictMode(true) .RuleFor(x => x.CustomerName, x => x.Person.FullName) .RuleFor(x => x.Email, x => x.Person.Email) .RuleFor(x => x.Phone, x => x.Person.Phone) .RuleFor(x => x.AddressLine, x => x.Address.StreetAddress()) .RuleFor(x => x.City, x => x.Address.City()) .RuleFor(x => x.PostCode, x => x.Address.ZipCode()) .RuleFor(x => x.Country, x => x.Address.Country()); var billingDetails = faker.Generate(numberOfRecordsPerBatch); return billingDetails; }
人に関するプロパティは、Facker.Person クラスのプロパティを紐づけたり、住所に関するプロパティは、Facker.Address クラスのプロパティを紐づけると、イイ感じのデータを自動生成してくれます。
クラスが入れ子になってるクラス
public class Order { public Guid Id { get; set; } public decimal Price { get; set; } public string Currency { get; set; } public BillingDetails BillingDetails { get; set; } } private static List<Order> GenerateRandomOrders(int numberOfRecordsPerBatch) { var billingDetailsFaker = new Faker<BillingDetails>() .RuleFor(x => x.CustomerName, x => x.Person.FullName) .RuleFor(x => x.Email, x => x.Person.Email) .RuleFor(x => x.Phone, x => x.Person.Phone) .RuleFor(x => x.AddressLine, x => x.Address.StreetAddress()) .RuleFor(x => x.City, x => x.Address.City()) .RuleFor(x => x.PostCode, x => x.Address.ZipCode()) .RuleFor(x => x.Country, x => x.Address.Country()); var orderFaker = new Faker<Order>() .RuleFor(x => x.Id, Guid.NewGuid) .RuleFor(x => x.Currency, x => x.Finance.Currency().Code) .RuleFor(x => x.Price, x => x.Finance.Amount(5, 100)) .RuleFor(x => x.BillingDetails, x => billingDetailsFaker); var orders = orderFaker.Generate(numberOfRecordsPerBatch); return orders; }
入れ子になってるクラスの Facker オブジェクトをまずは作っておいてから、親クラスのプロパティに Facker オブジェクトを割り当てる感じに書きます。
実行する度に生成されるデータを変えさせない方法
既定では、実行するたびに自動生成されるデータの中身はランダムに変わりますが、下記のようにあらかじめ Seed 値を設定することで生成されるデータを固定することができます。
Randomizer.Seed = new Random(123456789);
Power BI でセッションリストを作成してみた
Power BI でセッションリストを作成する過程で知り得た知見を、備忘録として残そうと思います。
Power BI でセッションリストを作るキッカケとなったイベントはこちら。
Power BI でセッションリストを作成する際、参考にさせていただいた Power BI MVP 清水優吾さんの YouTube 動画はこちら。
作ったセッションリスト
やったぜ!
— { "しばてぃ" : "Takashi Shibata" } (@shibatea365) August 8, 2020
見た目は置いといて、それっぽいものができたー!#decode夏まつり pic.twitter.com/RmHf4xJa4R
Power Query
Power Query を触ったときの気づき。
- 列の分布、列の品質、数式バーの表示は、[ 表示 ] タブから設定する。
- レコードを展開するとき、「元の列名をプレフィックスとして使用します」にチェックを入れたままだと、次のような列名になる。(冗長的な感じ)
- 上記のチェックを外した場合、展開したときの列名が使われる。
- データが109個に対して、一意データが109個なので SessionID がキーとして使えることがわかる。
- [ 他の列の削除 ]を行うと、選択した列以外の列が削除される。残った列は、Ctrl + 左クリックで選択した順に並ぶ。
- マスターテーブルを作成したい場合、そのテーブルのID列とタイトル列の順に選択して、他の列の削除を行う。その後、ID 列を右クリックして、重複の削除を行う。ID 列を昇順に並び替えたいので、[ ホーム ] タブ > 並び替え > A->Z を選択する。
- 基となった JSON はビジュアルする際に利用しないので、読み込みを無効化する。(右クリック > 読み込みを有効にする を選択して、チェックを外すとそのクエリ名が斜体に変わる)
Power BI Desktop
Power BI Desktop を触ったときの気づき。
データ取得先が URL ならば、Web から取得すること。その際、アクセス方式について問われる。(今回のケースでは「匿名」を選択した) それ以降はキャッシュ?で持ってるせいかアクセス方式についてダイアログが表示されない。(キャッシュクリアしても表示されなかった。やり方が間違ってると思うので、必要になったらまた調べる)
Power Query 適用後、モデルビューでリレーションのラインにマウスオーバーしてリレーションを確認する。想定通りでない場合は編集すること。
- ディメンションテーブルの絞り込みをファクトテーブルに反映させるために、双方向にすること。(ディメンションテーブルからファクトテーブルへの方向に矢印が向けられていること)
ビジュアライズに利用しないテーブル、フィールドは非表示にすること。
Title 列の並び順を id 順にしたい場合(レポートに使われる Title 列の昇順ではなく)、Title 列を選択してから、[ 列ツール ] タブ > 列で並べ替え > id を選択する。
CSOM が .NET Standard に対応したらしいので試してみた
CSOM が .NET Standard に対応したとのことです。
使用方法は次のドキュメントで解説されているので、それに沿って進めていきます。
docs.microsoft.comIt's finally here! .NET Standard 2.0 version of the #SharePoint Online CSOM NuGet package to unblock taking advantage of latest Azure capabilities with your SharePoint Online solutions. See more from https://t.co/eLB7ExlDBY #Microsoft365dev #MSDev pic.twitter.com/JlMkpujhrv
— Microsoft SharePoint (@SharePoint) 2020年6月23日
環境情報
- Windows 10 Version 2004
- Visual Studio 2019 Version 16.6.2
- Microsoft.SharePointOnline.CSOM Version 16.1.20211.12000
Microsoft.SharePointOnline.CSOM をインストール
コンソール アプリケーション(.NET Core)プロジェクトを作成します。
NuGet で Microsoft.SharePointOnline.CSOM Version 16.1.20211.12000 をインストールします。
ソースコードは弄らずにそのままビルドしたところ、警告はありませんでした。
ちなみに、ひとつ前のバージョン(16.1.20211.12000)だと次のような警告が出てました。 最新バージョンではちゃんと .NET Standard に対応してるみたいですね。
認証処理について
今までのようにユーザーIDとパスワードを用いた基本認証は、CSOM .NET Standard 版ではもうできないようです。代わりに、OAuth アクセストークンを用いた認証方式を使用します。
SharePoint Online のアクセストークンを取得するための推奨アプローチは、Azure AD アプリケーションを登録し、SharePoint のアクセス許可を与えること、とのこと。
というわけで、Azure AD アプリケーションの登録方法は次の通りです。
Azure AD アプリケーションの登録
Azure Active Directory > アプリの登録 からアプリケーションを登録します。
作成したアプリケーションのアプリケーションIDは控えておきましょう。
API のアクセス許可から SharePoint を選択します。
委任されたアクセス許可 を選択します。
今回は、AllSites.Manage を選択します。
管理者の同意を与えます をクリックします。
サイドナビゲーションから「認証」をクリックします。
「既定のクライアントの種類」の選択肢を「はい」に変更します。
ソースコード
サンプルコードは Program.cs と AuthenticationManager.cs の 2 ファイルがあります。 Program.cs は一部記載が省略されている部分があったので補足しています。
using System; using System.Security; using System.Threading.Tasks; namespace ConsoleApp2 { class Program { static async Task Main(string[] args) { var site = new Uri("https://contoso.sharepoint.com/sites/hoge"); var user = "shibatea@consoto.onmicrosoft.com"; var password = GetSecureString($"Password for {user} : "); // Note: The PnP Sites Core AuthenticationManager class also supports this using var authenticationManager = new AuthenticationManager(); using var context = authenticationManager.GetContext(site, user, password); context.Load(context.Web, p => p.Title); await context.ExecuteQueryAsync(); Console.WriteLine($"Title: {context.Web.Title}"); } static SecureString GetSecureString(string message) { SecureString sStrPwd = new SecureString(); try { Console.Write(message); for (ConsoleKeyInfo keyInfo = Console.ReadKey(true); keyInfo.Key != ConsoleKey.Enter; keyInfo = Console.ReadKey(true)) { if (keyInfo.Key == ConsoleKey.Backspace) { if (sStrPwd.Length > 0) { sStrPwd.RemoveAt(sStrPwd.Length - 1); Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); Console.Write(" "); Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); } } else if (keyInfo.Key != ConsoleKey.Enter) { Console.Write("*"); sStrPwd.AppendChar(keyInfo.KeyChar); } } Console.WriteLine(""); } catch (Exception e) { sStrPwd = null; Console.WriteLine(e.Message); } return sStrPwd; } } }
AuthenticationManager.cs は、サンプルコードのままコピペすると画像の通り FormDigestHandlingEnabled
プロパティが見つからなくてコンパイルエラーになります。
Microsoft.SharePointOnline.CSOM の一つ前のバージョンとソースコードを逆コンパイルして比較したら、FormDigestHandlingEnabled
プロパティは削除されていました。
( FormDigestHandlingEnabled = false
相当と同じ動きになるように修正されている )
コンパイルエラーが発生している行をコメントアウトすれば動作確認ができました。
( おまけ ) SharePointPnPCoreOnline を試しにインストール
すると、大量に警告が表示されました。SharePointPnPCoreOnline が依存してるライブラリの中に、.NET Framework しか対応してないものが含まれてるようです。
最後に
ひとまず .NET Standard で CSOM が動くことがわかりました。これで SharePoint CSOM を使うために Azure Functions V1 を使っていたものが V3 に移行できそうですね。(PnP を使ってるとダメですが…)
それと以前から疑問に思っていたのが…、Microsoft.SharePointOnline.CSOM のソースコードは公開されていないんでしたっけ?
探し方が下手なのか見つからないので、私は逆コンパイルして中身を確認しています。
Power Automate で指定した日付の予定表のイベント一覧を取得する
コネクタ
今回使うコネクタは イベントの取得 (V4) [ Get events (V4) ] です。
コネクタの説明にもある通り、内部的には Microsoft Graph API が呼ばれています。
コネクタでわからないことがある場合は Microsoft Graph API のドキュメントを見に行くと良いでしょう。
やりたいこと
- まずは予定表のイベント一覧を取得する
- さらに指定した日付に絞り込んでイベント一覧を取得する(例として 6/22 のイベント一覧を取得します)
- 取得したイベント一覧をイベントの開始時刻順に並べる
設定内容
結論から書くと、6/22 のイベント一覧を取得するために設定したパラメーターは以下の通りです。
パラメーター | 値 |
---|---|
予定表ID | 取得したい予定表ID |
フィルター クエリ | start/dateTime ge '2020-06-21T15:00:00' and start/dateTime lt '2020-06-22T15:00:00' |
並べ替え順 | start/dateTime |
上から順に取得 | (なし) |
スキップ数 | (なし) |
6/22 のイベント一覧を取得する場合は、イベントの開始時刻を『6/22 0時0分 以上』、『6/23 0時0分 未満』で絞り込む必要があります。
しかし、Power Automate で日付を扱うときはタイムゾーンを UTC にする必要があります。その為、フィルター クエリに設定する時刻は次のような UTC 時刻を設定する必要があります。
- '2020-06-21T15:00:00' 以上(ge)
- '2020-06-22T15:00:00' 未満(lt)
ODATA フィルタークエリについては、以下のドキュメントが参考になります。
苦労したこと
フィルター クエリに指定するプロパティを探し当てるのに苦労しました。
フィルター クエリなしでイベント一覧を取得したときの出力結果は以下の通りでした。(詳細は省略)
[ { "subject": "夕会", "start": "2020-06-22T08:00:00.0000000", "end": "2020-06-22T08:30:00.0000000", }, { "subject": "朝会", "start": "2020-06-22T00:00:00.0000000", "end": "2020-06-22T00:30:00.0000000", } ]
開始時刻のプロパティは "start" なんだなぁと思って、フィルター クエリに "start" でフィルターしたところエラーになりました。
Microsoft Docs でイベントのプロパティを確認すると、開始時刻は dateTimeTimeZone という型らしい。 Microsoft Graph API の『イベントを取得する』の応答結果の例を確認するとわかりやすいと思います。
docs.microsoft.com docs.microsoft.com
以下のように入れ子になってる場合は、スラッシュを交えて指定する必要があります。今回のケースで言うと "start/dateTime" となります。
まとめ
フィルター クエリの指定方法、特に日付を指定する場合はハマりポイントかなと思い記事を書きました。
Microsoft Graph API に対する理解が深まれば、Power Automate の勘所も備わってくると思います。 何か躓いたら Microsoft Graph API のドキュメントを見ると良いでしょう。