如何使用 Pulumi 为 Web App 添加身份验证

How to add Authentiction to WebApp with Pulumi

我正在尝试向 Pulumi WebApp 添加身份验证提供程序,但我完全不清楚如何实现。包 @pulumi/azure-native/web 中的 class WebApp 仅提供 属性 identity 但没有 属性 分配,例如微软广告。谁能提供有关如何设置的提示?

有一些 Pulumi Azure pre-requisites 并在您的租户和 Azure 订阅中拥有适当的权限。

按照以下步骤使用 pulumi 将身份验证添加到应用程序服务 webapp 并部署:

正在创建项目:

首先创建应用程序,然后添加我们创建 Azure AD application registration.

所需的 AzureAD 包
pulumi new azure-csharp `
  --name easyauth-webapp `
  --description "azure ad secured app" `
  --stack dev `
  --config azure-native:location=eastus

dotnet add package Pulumi.AzureAD

接下来我们需要更新 pulumi.dev.yaml 文件的内容以包含一些额外的配置项。将以下内容粘贴到文件中:

config:
  azure-native:location: eastus
  azure-native:subscriptionId: UPDATE_ME
  azure-native:tenantId: UPDATE_ME
  easyauth-webapp:tenantId: UPDATE_ME
  easyauth-webapp:ownerId: UPDATE_ME
  easyauth-webapp:siteName: UPDATE_ME
  easyauth-webapp:appRegistrationName: UPDATE_ME

您可以将 siteNameappRegistrationName 设置为您想要的任何值。

subscriptionIdtenantId 应分别设置为您的 Azure 应用服务和 Azure AD 应用程序注册的适当目标。

以下命令可能有助于检索这些值:

# Get your user's id
az ad signed-in-user show --query objectId

# List all subscriptions (and their tenant) that you have access to
az account list

部署网站(无安全):

接下来我们将创建我们要部署的网站。我们将使用 run from ZIP package 功能来部署 wwwroot 文件夹的内容。 创建该文件夹并向 index.htm 文件添加一些内容:

例如:

<!-- wwwroot/index.htm -->
<html>
  <head>
    <title>A very secure app</title>
  </head>
  <body>
    Hello EasyAuth with Pulumi!
  </body>
</html>

现在我们可以使用 Pulumi 将此文件部署到 Azure。 修改 MyStack.cs 文件以包含以下代码,该代码改编自 Pulumi Function Stack example:

// MyStack.cs
using System;
using Pulumi;
using Pulumi.AzureAD;
using Pulumi.AzureAD.Inputs;
using Pulumi.AzureNative.Resources;
using Pulumi.AzureNative.Storage;
using Pulumi.AzureNative.Storage.Inputs;
using Pulumi.AzureNative.Web;
using Pulumi.AzureNative.Web.Inputs;

class MyStack : Stack
{
  public MyStack()
  {
    var config = new Pulumi.Config();
    var tenantId = config.Require("tenantId");
    var ownerId = config.Require("ownerId");
    var siteName = config.Require("siteName");
    var appRegistrationName = config.Require("appRegistrationName");

    var rg = new ResourceGroup($"RG-{siteName}");

    var storageAccount = new StorageAccount("storageaccount", new StorageAccountArgs
    {
      ResourceGroupName = rg.Name,
      Kind = "StorageV2",
      Sku = new SkuArgs
      {
        Name = SkuName.Standard_LRS,
      },
    });

    var appServicePlan = new AppServicePlan("appserviceplan", new AppServicePlanArgs
    {
      ResourceGroupName = rg.Name,
      Kind = "App",
      Sku = new SkuDescriptionArgs
      {
        Tier = "Basic",
        Name = "B1",
      },
    });

    var container = new BlobContainer("zips", new BlobContainerArgs
    {
      AccountName = storageAccount.Name,
      PublicAccess = PublicAccess.None,
      ResourceGroupName = rg.Name,
    });

    var blob = new Blob("appservice-blob", new BlobArgs
    {
      ResourceGroupName = rg.Name,
      AccountName = storageAccount.Name,
      ContainerName = container.Name,
      Type = BlobType.Block,
      Source = new FileArchive("wwwroot"),
    });

    var codeBlobUrl = SignedBlobReadUrl(blob, container, storageAccount, rg);

    var app = new WebApp("app", new WebAppArgs
    {
      Name = siteName,
      ResourceGroupName = rg.Name,
      ServerFarmId = appServicePlan.Id,
      SiteConfig = new SiteConfigArgs
      {
        AppSettings = {
          new NameValuePairArgs{
              Name = "WEBSITE_RUN_FROM_PACKAGE",
              Value = codeBlobUrl,
          }
        },
      }
    });

    this.Endpoint = app.DefaultHostName;
  }

  // From https://github.com/pulumi/examples/blob/master/azure-cs-functions/FunctionsStack.cs
  private static Output<string> SignedBlobReadUrl(Blob blob, BlobContainer container, StorageAccount account, ResourceGroup resourceGroup)
  {
    return Output.Tuple<string, string, string, string>(
        blob.Name, container.Name, account.Name, resourceGroup.Name).Apply(t =>
    {
      (string blobName, string containerName, string accountName, string resourceGroupName) = t;

      var blobSAS = ListStorageAccountServiceSAS.InvokeAsync(new ListStorageAccountServiceSASArgs
      {
        AccountName = accountName,
        Protocols = HttpProtocol.Https,
        SharedAccessStartTime = "2021-01-01",
        SharedAccessExpiryTime = "2030-01-01",
        Resource = SignedResource.C,
        ResourceGroupName = resourceGroupName,
        Permissions = Permissions.R,
        CanonicalizedResource = "/blob/" + accountName + "/" + containerName,
        ContentType = "application/json",
        CacheControl = "max-age=5",
        ContentDisposition = "inline",
        ContentEncoding = "deflate",
      });
      return Output.Format($"https://{accountName}.blob.core.windows.net/{containerName}/{blobName}?{blobSAS.Result.ServiceSasToken}");
    });
  }

  [Output] public Output<string> Endpoint { get; set; }
}

我们现在可以部署站点并验证它是否按预期工作:

pulumi up --stack dev

curl (pulumi stack --stack dev output Endpoint)

[

保护站点:

要配置 Easy Auth,我们首先创建一个 Azure AD 应用程序注册。 在此示例中,我指定 AzureADMyOrg 来限制对部署应用程序注册的租户的访问。我还添加了一个 RedirectUri 指向已部署站点的 Easy Auth 中间件。需要密码才能用作客户端密码(在这种情况下,Web 应用程序是客户端)。

创建应用程序注册后,我们可以将 WebAppAuthSettings 添加到我们的站点。该示例指定没有匿名访问(使用 RedirectToLoginPage),并使用 ClientIdClientSecret(密码)将站点连接到应用程序注册。

将下面的代码粘贴到上面 MyStack.cs 中的 this.Endpoint... 代码之后:

// MyStack.cs
// After this.Endpoint = app.DefaultHostName;

var adApp = new Application("ADAppRegistration", new ApplicationArgs
{
  DisplayName = appRegistrationName,
  SignInAudience = "AzureADMyOrg",
  Owners = new[] { ownerId },
  Web = new ApplicationWebArgs
  {
    ImplicitGrant = new ApplicationWebImplicitGrantArgs
    {
      IdTokenIssuanceEnabled = true
    },
    RedirectUris = new System.Collections.Generic.List<string> { $"https://{siteName}.azurewebsites.net/.auth/login/aad/callback" }
  }
}
);

var applicationPassword = new ApplicationPassword("appPassword", new ApplicationPasswordArgs
{
  ApplicationObjectId = adApp.Id,
  DisplayName = "Client secret for web app"
});

var allowedAudience = adApp.ApplicationId.Apply(id => $"api://{id}");

var authSettings = new WebAppAuthSettings("authSettings", new WebAppAuthSettingsArgs
{
  ResourceGroupName = rg.Name,
  Name = app.Name,
  Enabled = true,
  UnauthenticatedClientAction = UnauthenticatedClientAction.RedirectToLoginPage,
  DefaultProvider = BuiltInAuthenticationProvider.AzureActiveDirectory,
  ClientId = adApp.ApplicationId,
  ClientSecret = applicationPassword.Value,
  Issuer = $"https://sts.windows.net/{tenantId}/v2.0",
  AllowedAudiences = new[] { allowedAudience },
});

我们现在可以更新站点,从命令行我们不能比这更进一步。 但在浏览器中,我们将被重定向以完成登录流程并访问该站点。

pulumi up --stack dev

# Redirect to HTTPS
curl (pulumi stack --stack dev output Endpoint)

# Access denied
curl "https://$(pulumi stack --stack dev output Endpoint)"

有关 pulumi 示例,请参考此 Github link。