Azure devops 当ConnectionString仅在ARM模板部署后才知道时,如何生成EF核心迁移脚本?

Azure devops 当ConnectionString仅在ARM模板部署后才知道时,如何生成EF核心迁移脚本?,azure-devops,entity-framework-core,azure-sql-database,entity-framework-migrations,arm-template,Azure Devops,Entity Framework Core,Azure Sql Database,Entity Framework Migrations,Arm Template,我想在部署Web应用程序之前,将应用程序发布到Azure并将迁移部署到数据库。这听起来相对简单,您可以在构建管道中使用dotnet ef创建migrations.sql脚本,并在发布管道中应用此脚本 但是,我无法在生成管道中创建migrations.sql脚本,因为我正在为DTAP环境使用四个不同的数据库。因此,我需要为每个环境生成migrations.sql脚本,并针对每个数据库分别执行这些脚本。据我所知 在我的发布管道中,我使用增量ARM模板部署资源并设置ConnectionString,它

我想在部署Web应用程序之前,将应用程序发布到Azure并将迁移部署到数据库。这听起来相对简单,您可以在构建管道中使用dotnet ef创建migrations.sql脚本,并在发布管道中应用此脚本

但是,我无法在生成管道中创建migrations.sql脚本,因为我正在为DTAP环境使用四个不同的数据库。因此,我需要为每个环境生成migrations.sql脚本,并针对每个数据库分别执行这些脚本。据我所知

在我的发布管道中,我使用增量ARM模板部署资源并设置ConnectionString,它来自Azure Web App应用程序设置配置中的Azure密钥库

如何/在何处生成migrations.sql脚本?我是否在发布管道中执行此操作?我的推理是否犯了重大错误

编辑:

感谢Madej的回答,这表明环境并不重要。我尝试在管道中创建migrations.sql脚本

ASP.NET核心.NET框架 针对完整的.NET Framework构建和测试ASP.NET核心项目。 添加发布符号、保存生成工件等步骤: https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core 触发: -主人 游泳池: vmImage:“windows最新版本” 变量: 项目:“***.csproj” 构建平台:“任何CPU” buildConfiguration:“发布” 步骤: -任务:DotNetCoreCLI@2 displayName:Install dotnet ef 投入: 命令:“自定义” 自定义:“工具” 参数:“安装-全局dotnet ef” -任务:DotNetCoreCLI@2 显示名称:还原工具 投入: 命令:“自定义” 自定义:“工具” 参数:“还原” -任务:DotNetCoreCLI@2 显示名称:还原 投入: 命令:“还原” 项目:“$projects” feedsToUse:“选择” -任务:DotNetCoreCLI@2 显示名称:构建 投入: 命令:“构建” 项目:“$projects” 参数:'-configuration$BuildConfiguration' -任务:DotNetCoreCLI@2 displayName:Create migrations.sql 投入: 命令:“自定义” 自定义:“ef” 参数:'migrations script-configuration$BuildConfiguration-no build-idempotent-output$build.ArtifactStagingDirectory\migrations.sql' 工作目录:“WebApi.api” -任务:DotNetCoreCLI@2 显示名称:发布 投入: 命令:“发布” publishWebProjects:正确 参数:'-configuration$BuildConfiguration-output$Build.ArtifactStagingDirectory' zipAfterPublish:错误 -任务:PublishBuildArtifacts@1 displayName:发布到Azure管道 投入: PathtoPublish:“$Build.ArtifactStagingDirectory” 工件名称:“drop” publishLocation:“容器” 我的管道不工作,在任务Create migrations.sql中,我遇到以下错误:

An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: DefaultAzureCredential failed to retrieve a token from the included credentials.
- EnvironmentCredential authentication unavailable. Environment variables are not fully configured.
- ManagedIdentityCredential authentication unavailable. No Managed Identity endpoint found.
- Visual Studio Token provider can't be accessed at C:\Users\VssAdministrator\AppData\Local\.IdentityService\AzureServiceAuth\tokenprovider.json
- Stored credentials not found. Need to authenticate user in VSCode Azure Account.
- Please run 'az login' to set up account
这是因为我在my Program.cs中添加了一个密钥库,并使用Azure.Identity DefaultAzureCredential进行身份验证,如下所示:

公共静态IHostBuilder CreateHostBuilderstring[]args=> Host.CreateDefaultBuilderargs .ConfigureWebHostDefaultswebBuilder=> { webBuilder.ConfigureAppConfigurationhostingContext,配置=> { var settings=config.Build; var凭据=新的DefaultAzureCredential 新的DefaultAzureCredentialOptions{ ExcludeSharedTokenCacheCredential=true, VisualStudioTenantId=设置[VisualStudioTenantId], } ; config.AddAzureKeyVaultnew Urisettings[KeyVault:Endpoint],credentials.Build; } .使用启动; };
Azure管道无法从DefaultAzureCredential获取令牌。如何对Azure管道进行身份验证?

您可以在生成管道中执行此操作,因为migration.sql脚本会检查特定的迁移是否已应用

要在确认中使用Azure Key Vault时创建迁移脚本,最简单的方法是从Azure Clit任务运行命令:

-任务:AzureCLI@2 投入: azureSubscription:'rg tcm si' 脚本类型:“pscore” scriptLocation:“inlineScript” inlineScript:'dotnet ef迁移脚本-配置$BuildConfiguration-无构建-幂等元-输出$build.ArtifactStagingDirectory\migrations.sql' 工作目录:“Itan.Database” 在此之前,您需要向连接服务背后的服务主体添加获取和列出权限:

然后,即使您需要将相同的脚本部署到不同的环境/数据库,在它们没有漂移之前,一切都很好。因此,如果您通过ef core进行所有更改,那么就可以使用migration.sql进行一次迁移,并多次应用

在数据库中,您应该具有:

其中包含已应用的mi 感谢。然后在脚本中,您将发现:

如果不存在,请从[\uu EFMigrationsHistory]中选择*,其中[MigrationId]=N'202001011111512\u InitialCreate' 开始 创建表[SomeTable] [Id]唯一标识符不为空, [StorageDate]datetime2不为空, ..... ; 终止 去 因此,在多个数据库上运行它是安全的

然后,您可以使用

步骤: -任务:SqlAzureDacpacDeployment@1 displayName:“Azure SQL SqlTask” 投入: azureSubscription:“您的订阅” 服务器名:“您的服务器名” 数据库名称:“YourDatabaseName” SqlUsername:UserName SqlPassword:“$SqlServerPassword” 部署类型:SqlTask SqlFile:“$System.DefaultWorkingDirectory/staging/drop/migrations.sql”
您可以在生成管道中执行此操作,因为migration.sql脚本会检查特定迁移是否已应用

要在确认中使用Azure Key Vault时创建迁移脚本,最简单的方法是从Azure Clit任务运行命令:

-任务:AzureCLI@2 投入: azureSubscription:'rg tcm si' 脚本类型:“pscore” scriptLocation:“inlineScript” inlineScript:'dotnet ef迁移脚本-配置$BuildConfiguration-无构建-幂等元-输出$build.ArtifactStagingDirectory\migrations.sql' 工作目录:“Itan.Database” 在此之前,您需要向连接服务背后的服务主体添加获取和列出权限:

然后,即使您需要将相同的脚本部署到不同的环境/数据库,在它们没有漂移之前,一切都很好。因此,如果您通过ef core进行所有更改,那么就可以使用migration.sql进行一次迁移,并多次应用

在数据库中,您应该具有:

其中包含已应用的迁移。然后在脚本中,您将发现:

如果不存在,请从[\uu EFMigrationsHistory]中选择*,其中[MigrationId]=N'202001011111512\u InitialCreate' 开始 创建表[SomeTable] [Id]唯一标识符不为空, [StorageDate]datetime2不为空, ..... ; 终止 去 因此,在多个数据库上运行它是安全的

然后,您可以使用

步骤: -任务:SqlAzureDacpacDeployment@1 displayName:“Azure SQL SqlTask” 投入: azureSubscription:“您的订阅” 服务器名:“您的服务器名” 数据库名称:“YourDatabaseName” SqlUsername:UserName SqlPassword:“$SqlServerPassword” 部署类型:SqlTask SqlFile:“$System.DefaultWorkingDirectory/staging/drop/migrations.sql”
我在编辑中找到了这个问题的解决办法

因此,我必须在某个地方定义环境变量。我不想在管道变量中这样做,以避免管理它们,因为它们应该以Azure服务连接的形式从项目中可用

我做了以下工作:

在my pipelines中,添加了AzureCLI任务,以读取服务主体id、密钥和租户id,并将其设置为作业变量,如下所示: -任务:AzureCLI@2 投入: azureSubscription: 脚本类型:“ps” scriptLocation:“inlineScript” inlineScript:| 写入主机“vso[task.setvariable=AZURE\u CLIENT\u ID]”$env:servicePrincipalId 写入主机“vso[task.setvariable=AZURE\u CLIENT\u SECRET]”$env:servicePrincipalKey 写入主机“vso[task.setvariable=AZURE\u TENANT\u ID]”$env:tenantId addSpnToEnvironment:true 在我的Create migrations.sql任务中,将这些变量作为环境变量传递,如下所示: -任务:DotNetCoreCLI@2 displayName:Create migrations.sql 投入: 命令:“自定义” 自定义:“ef” 参数:'migrations script-configuration$BuildConfiguration-no build-idempotent-output$build.ArtifactStagingDirectory\migrations.sql' 工作目录:“WebApi.api” 环境: AZURE\u客户端\u ID:$AZURE\u客户端\u ID AZURE\u客户端\u机密:$AZURE\u客户端\u机密 AZURE\u租户\u ID:$AZURE\u租户\u ID 将服务主体作为密钥保管库机密用户添加到Azure密钥保管库RBAC。我只能用az做到这一点:
这完全解决了我的问题,而不必管理任何更多的机密/变量,因为它们都包含在管道本身中,不会造成任何安全威胁。

我在编辑中找到了问题的解决方案

因此,我必须在某个地方定义环境变量。我不想在管道变量中这样做,以避免管理它们,因为它们应该以Azure服务连接的形式从项目中可用

我做了以下工作:

在my pipelines中,添加了AzureCLI任务,以读取服务主体id、密钥和租户id,并将其设置为作业变量,如下所示: -任务:AzureCLI@2 投入: azureSubscription: 脚本类型:“ps” scriptLocation:“inlineScript” inlineScript:| 写入主机“vso[task.setvariable=AZURE\u CLIENT\u ID]”$env:servicePrincipalId 写入主机的 vso[task.setvariable=AZURE_CLIENT_SECRET]'$env:servicePrincipalKey 写入主机“vso[task.setvariable=AZURE\u TENANT\u ID]”$env:tenantId addSpnToEnvironment:true 在我的Create migrations.sql任务中,将这些变量作为环境变量传递,如下所示: -任务:DotNetCoreCLI@2 displayName:Create migrations.sql 投入: 命令:“自定义” 自定义:“ef” 参数:'migrations script-configuration$BuildConfiguration-no build-idempotent-output$build.ArtifactStagingDirectory\migrations.sql' 工作目录:“WebApi.api” 环境: AZURE\u客户端\u ID:$AZURE\u客户端\u ID AZURE\u客户端\u机密:$AZURE\u客户端\u机密 AZURE\u租户\u ID:$AZURE\u租户\u ID 将服务主体作为密钥保管库机密用户添加到Azure密钥保管库RBAC。我只能用az做到这一点:
这完全解决了我的问题,无需管理更多的机密/变量,因为它们都包含在管道本身中,不会造成任何安全威胁。

这就引出了一个后续问题。由于ConnectionString来自Azure密钥库,因此应用程序将此密钥库添加到Program.cs中,并使用Azure.Identity中的DefaultAzureCredential类对自身进行身份验证,并从密钥库生成配置。在本地和部署到Azure后,这一切正常。但是,在Azure管道中,它无法获取令牌。如何以这种方式对Azure管道进行身份验证?我是否在管道中登录?这似乎是无意的。也许这会对这个问题有所帮助,我不这么认为。在本地和部署后,这都可以正常工作。没有配置环境凭据。我在本地使用VisualStudioTokenProvider,部署后使用ManagedEntityRedential。。密钥库使用RBAC。您在哪个项目中配置了此功能?因此,当您设置上述主题中提到的这些环境变量时,它会起作用。超级酷!这就引出了一个后续问题。由于ConnectionString来自Azure密钥库,因此应用程序将此密钥库添加到Program.cs中,并使用Azure.Identity中的DefaultAzureCredential类对自身进行身份验证,并从密钥库生成配置。在本地和部署到Azure后,这一切正常。但是,在Azure管道中,它无法获取令牌。如何以这种方式对Azure管道进行身份验证?我是否在管道中登录?这似乎是无意的。也许这会对这个问题有所帮助,我不这么认为。在本地和部署后,这都可以正常工作。没有配置环境凭据。我在本地使用VisualStudioTokenProvider,部署后使用ManagedEntityRedential。。密钥库使用RBAC。您在哪个项目中配置了此功能?因此,当您设置上述主题中提到的这些环境变量时,它会起作用。超级酷!这对我来说真的很有效。我唯一的问题是,作业变量会在Env变量前面加一个空格,因此在dotnet应用程序中,我手动检索变量并对其进行修剪。这对我来说非常有效。我唯一的问题是作业变量会在Env变量前面加一个空格,因此在dotnet应用程序中,我手动检索变量并对其进行修剪。
az role assignment create --role 'Key Vault Secrets User (preview)' --scope '/subscriptions/<subscription ID>/resourcegroups/<resource group name>/providers/Microsoft.KeyVault/vaults/<vault name>' --assignee '<service principal object id>'