ROMANCE DAWN for the new world

Microsoft Azure を中心とした技術情報を書いています。

Managed Service Identity を使って Azure Key Vault から接続文字列を取得する

Azure Key Vault は、アプリケーションが利用するシークレットを安全に保管してくれるサービスです。シークレットには、DBの接続文字列、API のアクセスキー、証明書などの情報があります。
アプリケーションで Azure Key Vault から接続文字列を取得するには、Azure Active Directory のサービスプリンシパルが必要となり、ソースコードに決め打ちしない構成やサービスプリンシパルの有効期間の管理など、少し手間がかかります。
Managed Service Identity(MSI)を使うことで、このあたりの煩わしさを解消できるようになっていたので、まとめておきます。

azure.microsoft.com

Azure Key Vault を作成する

Azure Portal を開いて、Azure Key Vault を作成します。特に説明は必要ないと思います。

f:id:TonyTonyKun:20190410202248p:plain

続いて、シークレットを作成します。今回は、SQL の接続文字列を想定したシークレット(SqlConnection)を追加しました。

f:id:TonyTonyKun:20190410202408p:plain

Azure Key Vault 側の準備は、これで完了です。

ローカル開発環境で接続文字列を取得する

Managed Service Identity(MSI)を利用すると、認証資格情報をアプリケーションに含めずに、Azure の各サービスへ認証を行うことができます。
今回は、ASP.NET Core で作成した Web アプリケーションで試してみます。Visual Studio のテンプレートから作成したシンプルな MVC アプリケーションです。
MSI を使って Azure Key Vault に接続するための2つの NuGet ライブラリをインストールします。

Install-Package Microsoft.Azure.KeyVault -Version 3.0.3
Install-Package Microsoft.Azure.Services.AppAuthentication -Version 1.0.3

HomeController クラスで、MSI を使って Azure Key Vault から接続文字列を取得するコードを書きます。
vaultBaseUrl は、先ほど作成した Azure Key Vault のURL です。AzureServiceTokenProvider を使って KeyVaultClient をインスタンス化している部分がポイントです。

public class HomeController : Controller
{
    public async Task<IActionResult> About()
    {
        var vaultBaseUrl = "https://xxx.vault.azure.net/";
        var provider = new AzureServiceTokenProvider();
        using (var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(provider.KeyVaultTokenCallback)))
        {
            var secret = await client.GetSecretAsync(vaultBaseUrl, "SqlConnection");
            ViewData["Message"] = secret.Value;
        }

        return View();
    }
}

ローカル開発環境で Visual Studio からデバッグ実行してみます。Azure Key Vault に作成したシークレット(SqlConnection)の値が取得できたことがわかります。

f:id:TonyTonyKun:20190410202424p:plain

ここで疑問に思うのが、ローカル開発環境からシークレットを取得できた理由です。
最近の Visual Studio では、Azure サービス認証に登録されたアカウントのトークンを使って AzureServiceTokenProvider が生成されるようになっているので、Visual Studio から Azure Web Apps にデプロイなどの機能を使っていれば、特に意識せずに Azure Key Vault に作成したシークレットを取得することができます。

f:id:TonyTonyKun:20190420121207p:plain

環境によってこの方法が利用できない場合には、環境変数「AzureServicesAuthConnectionString」を使いましょう。設定する値については、こちらを参照してください。

Azure Web Apps で接続文字列を取得する

先ほど作成した ASP.NET Core の Web アプリケーションを Azure Web Apps にデプロイしてみます。
Azure Web Apps の MSI を有効にしておきます。このタイミングで、サービスプリンシパルが追加されます。

f:id:TonyTonyKun:20190410203001p:plain

続いて、Azure Key Vault の Access policies で、Azure Web Apps のサービスプリンシパルを許可します。必要最低限のパーミッションとして、シークレットの Get だけを許可しました。忘れずに Save ボタンで保存ましょう。

f:id:TonyTonyKun:20190410202454p:plain

ASP.NET Core の Web アプリケーションを Azure Web Apps にデプロイし、結果を確認します。

f:id:TonyTonyKun:20190410202505p:plain

Azure Key Vault に作成したシークレット(SqlConnection)の値が取得できたことがわかります。

まとめ

アプリケーションを開発する際には、DBの接続文字列、API のアクセスキー、証明書などのシークレットは手間をかけずに安全に管理したいところです。MSI と Azure Key Vault を組み合わせて使うことで、このあたりの煩わしさを解消できます。パブリックなリポジトリでシークレットを公開してしまう事故を防ぐためにも、しっかりとアーキテクチャで考慮しておきたい事項です。

Visual Studio 2019 for MAC を試してみた

先日 GA したばかりの Visual Studio 2019 for MAC を試してみました。
初めて MAC 向けの Visual Studio(Preview)が発表された際と同様に、ASP.NET Core で作った Web API を Xamarin Forms のアプリから呼び出してみます。

gooner.hateblo.jp

インストール

Visual Studio 2019 for MAC は、Community Edition であれば、こちらから無料でダウンロードすることができます。

visualstudio.microsoft.com

Xcode が必須なので最初にインストールしましたが、それ以外はウィザード通りに進めれば、問題なくインストールできました。

f:id:TonyTonyKun:20190411004439p:plain

ASP.NET Core で Web API を作る

Visual Studio を起動して、新しいプロジェクトを作成します。テンプレートは、ASP.NET Core MVC のアプリケーションを選択します。

f:id:TonyTonyKun:20190411004524p:plain

まず、モデルとして、Person クラスを追加します。

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

API コントローラーとして、PersonController クラスを追加します。サンプルデータは前回と同じにしました。「君の名は。」が流行っていた頃だったようです。

[Route("api/[controller]")]
[ApiController]
public class PersonController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        var result = new List<Person> {
            new Person{ Id = 1, Name = "立花 瀧"},
            new Person{ Id = 2, Name = "宮水 三葉"},
        };
        return Ok(result);
    }
}

iOS と Android のエミュレータからは、localhost に HTTPS で接続できないので、Azure Web Apps にデプロイしておきます。Windows 版と同様に、Visual Studio からダイレクトに Azure Web Apps へアプリケーションを発行できるようになりました。

f:id:TonyTonyKun:20190411011405p:plain

Xamarin Forms から Web API を呼び出す

前回はターミナルからコマンドで起動する必要がありましたが、Dock から複数の Visual Studio を起動できるようになりました。

f:id:TonyTonyKun:20190411011851p:plain

Xamarin Forms 用にもう1つの Visual Studio が起動したら、新しいプロジェクトを作成します。テンプレートは、Xamarin.Forms の空白フォームのアプリを選択します。

f:id:TonyTonyKun:20190411005226p:plain

Web API から返された JSON をデシリアライズするための JSON.NET の NuGet ライブラリをインストールします。

f:id:TonyTonyKun:20190411005502p:plain

Xamarin Forms プロジェクトにボタンとリストビューを追加して、コードビハインドから Web API を呼び出すコードを追加します。

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:xamarinapp2" x:Class="xamarinapp2.MainPage">
    <StackLayout>
        <ListView x:Name="personList" >
          <ListView.ItemTemplate>
            <DataTemplate>
              <TextCell Text="{Binding Name}" />
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
        <Button Text="your name" Clicked="getPerson" />
    </StackLayout>
</ContentPage>
[DesignTimeVisible(true)]
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    public async void getPerson(object sender, System.EventArgs e)
    {
        using (var client = new HttpClient())
        {
            var response = await client.GetAsync("https://xxx.azurewebsites.net/api/person");
            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                this.personList.ItemsSource = JsonConvert.DeserializeObject<List<Person>>(json);
            }
        }
    }
}

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

結果確認(iOS)

Xamarin Forms プロジェクトで iOS のプロジェクトをスタートアップに設定してデバッグ実行すると、エミュレータが起動して Web API を呼び出して結果を表示することができます。

f:id:TonyTonyKun:20190411010008p:plain

結果確認(Android)

同様に Android のプロジェクトをスタートアップに設定してデバッグ実行すると、エミュレータが起動して Web API を呼び出して結果を表示することができます。

f:id:TonyTonyKun:20190411010052p:plain

まとめ

Visual Studio for MAC が最初にリリースされてから2年半が経ち、実用に耐え得るようになってきたかなという印象です。
Visual Studio Code もありますし、開発者が好きなデバイスでモバイルやクラウドのアプリケーションを作れるのはいいことだと思います。

Azure DevOps を使って Azure Functions v2 の CI/CD パイプラインを構築する

昨年の9月に Azure Functions v2 が GA しましたので、これから .NET で Azure Functions を構築するなら、.NET Core 2.1 ベースの v2 を選択することになります。
今回は、Azure DevOps を使って Azure Functions v2 の CI/CD パイプラインを構築する手順をまとめておきます。

ローカル開発環境

Azure Functions v2 は、Azure にデプロイせずとも、ローカル開発環境で Visual Studio を使って開発とデバッグができます。基本的には、下記の2つのツールがインストールされていれば大丈夫です。

  • Visual Studio の拡張機能「Azure Functions と Web ジョブ ツール」
  • Azure Storage Emulator

Visual Studio のプロジェクトテンプレートを選択すれば、すぐにローカル開発環境で Azure Functions を実行できますし、Visual Studio の発行機能を使えば、Azure Functions にデプロイすることもできます。
詳細な手順は、公式ドキュメントが分かりやすいです。先日 GA したばかりの Visual Studio 2019 でも、同様の手順で実行できます。
docs.microsoft.com

サンドボックスな Azure 環境であればこの方法でもいいですが、自動でビルドやデプロイを行いたい場合は、Azure DevOps で CI/CD パイプラインを構築するのがお勧めです。

ソースコードのリポジトリ

Azure DevOps のリポジトリを使ってもよかったのですが、今回は GitHub のリポジトリを使いました。上記の手順でプロジェクトを作成し、そのままプッシュしたシンプルなリポジトリです。
Azure DevOps と GitHub の関連付けは、Pipelines メニューから行います。

f:id:TonyTonyKun:20190404182141p:plain

Build パイプライン

Azure DevOps の Pipelines - Builds メニューより、「ASP.NET Core」のテンプレートを選択します。

f:id:TonyTonyKun:20190404182220p:plain

Azure Functions v2 向けに、デフォルトの設定から1つだけ変更する必要があります。通常の Web アプリケーションではないので、「Publish Web Projects」のチェックをオフにします。

f:id:TonyTonyKun:20190404182232p:plain

Save & Queue ボタンを押し、ビルドを実行して結果を確認します。

Releases パイプライン

事前に、Azure Portal から Azure Functions を作成しておきます。今回は、Consumption で .NET 向けの Azure Functions を作成しました。
Azure DevOps の Pipelines - Releases メニューより、「Azure App Service deployment」のテンプレートを選択します。

f:id:TonyTonyKun:20190404182246p:plain

Azure Functions v2 向けに、デフォルトの設定から2つだけ変更する必要があります。

  • App Service type は「Function App on Windows」を選択する
  • Additional Deployment Options で「Select deployment method」のチェックをオンにし、「Run From Package」を選択する

f:id:TonyTonyKun:20190404182302p:plain

Build と Releases パイプラインの関連付け

まず、Releases パイプラインにおいて、Build パイプラインで生成した Artifact を関連付けます。

f:id:TonyTonyKun:20190404182318p:plain

次に、Build パイプラインの Continuous deployment trigger を有効にします。

f:id:TonyTonyKun:20190404182331p:plain

最後に、Releases パイプラインにおいて、新しいリリースを作成します。

f:id:TonyTonyKun:20190404182344p:plain

以上で、Azure Functions v2 向けの CI/CD パイプラインが完成しました。

CI/CD パイプラインの実行

GitHub のリポジトリに変更をプッシュし、CI/CD パイプラインを実行します。トリガーが発火すると、Build パイプラインの Artifact を使って、Azure Functions にデプロイが実行されます。

f:id:TonyTonyKun:20190404182415p:plain

Azure Portal を開くと、Azure Functions にデプロイされていることが確認できます。

f:id:TonyTonyKun:20190404182427p:plain

Kudu で「data/SitePackages」ディレクトリを確認すると、Run From Package の zip ファイルが配置されていることが分かります。

f:id:TonyTonyKun:20190404182441p:plain

まとめ

サーバーレスの Azure Functions ですが、v2 になってローカル開発から CI/CD パイプラインの構築まで、通常の Web アプリケーションと同じように開発できるようになりました。
コールドスタートやVNET統合などに対応した Premium Plan(Public Preview)も登場してきましたし、Durable Functions もあるので、サーバーレスを適用できるシナリオは増えてくると思います。