秘密作为vstest中的环境变量

Secrets as environmental variables in vstest

在我的测试中,我使用了环境变量。出于安全原因,在 azure devops 管道中设置它时,我需要将其标记为机密。是否可以在 vstest 任务中将其作为参数传递?

我经历过这个。无法在命令行上将变量传递给 VSTest,这意味着您必须跳过几个环节。

您有几个选择:

  • 使用带有 TestRunParameters 部分的 runsettings 文件,然后在测试本身中通过 TestContext.Properties["variableName"] 访问它。您可以使用标准标记替换模式来转换 XML 文件。

  • 使用 app.config 或 appsettings.json(取决于您的平台)。这与上面的工作方式几乎相同,当然,除了您使用标准配置 类 来检索值。

  • 向您的管道添加一个步骤以设置适当的环境变量。出于安全目的,秘密不会自动映射到环境变量,但没有什么可以阻止您自己做。

  • 将秘密值移动到密钥库或某种其他类型的外部秘密存储中,并配置测试以在运行时提取秘密。

显然缺乏足够的声誉来评论@daniel-mann 的回答,我需要自己跟进一个回答。

关于运行设置文件的使用;

是的,我相信您可以这样做,但还有一种更简单的方法。在任务定义中,您可以选择覆盖测试 运行 参数。

对于非 YAML 管道,您可以在“覆盖测试 运行 参数”选项中执行此操作(工具提示显示“覆盖 [=101= 的 TestRunParameters 部分中定义的参数]settings 文件或 testsettings 文件的 Properties 部分。例如:-key1 value1 -key2 value2."),那么像这样:

-SomeSecret $(SomeSecret)

对于 YAML 管道,您可以这样做:

overrideTestrunParameters: '-SomeSecret $(SomeSecret)'

好的是,您要覆盖的测试 运行 参数不需要存在于您的 运行 设置文件中。事实上,根本不需要 运行 设置文件!

总的来说,这种方法的缺点是您必须通过“TestContext.Properties”集合(对于 NUnit)访问您的秘密,如果您想要一个同样有效的透明解决方案,这真的很糟糕对于本地开发(使用“用户机密”)和 Azure 管道都很好。

另一个潜在的坏处是这些“覆盖”测试 运行 参数只是 - 仅用于该测试 运行 的参数。如果您碰巧混合使用 .NET Framework 和 .NET Core 测试,您可能希望在两个不同的测试 运行 中执行这些测试(为了它能正常工作......),然后你会需要复制这些覆盖(假设需要一些相同的秘密)。

关于在管道中添加附加任务以设置适当的环境变量;

当然可以。使用“批处理脚本”任务非常适合此操作,您只需将基于秘密的变量作为参数传递给脚本,然后在脚本文件中获取它们。

不过,要使其按预期工作,您需要允许任务修改环境。

对于非 YAML 管道,这是通过勾选“修改环境”复选框来完成的。

对于 YAML 管道,您可以这样做:

- task: BatchScript@1
  displayName: 'Export key vault vars as env. vars (for tests)'
  inputs:
    filename: 'ExportKeyVaultEnvironmentVariables.cmd'
    arguments: '$(SomeSecret)'
    modifyEnvironment: true

在“ExportKeyVaultEnvironmentVariables.cmd”脚本中,您只需执行以下操作:

set SomeSecret=%1

注意:如果你的秘密偶然有一些有趣的字符,尤其是尾随的“=”字符,你可能会遇到在脚本中收集参数时得到的结果不是您发送的内容。

您可以通过将参数括在双引号中来避免此问题,如下所示:

arguments: '"$(SomeSecret)"'

然后通过使用“~”参数修饰符删除周围的引号来收集参数,如下所示:

set SomeSecret=%~1

这种方法的一个很好的额外效果是,您闪亮的全新成熟环境变量会在管道的其余部分持续存在。回到我的“坏事”,关于必须可能重复测试 运行 参数覆盖,这里不需要。

关于OP提到的附加选项;

当然,在这种情况下,您不需要“批处理脚本”任务(需要调用脚本文件),而只需要“命令行”任务。

但是,请注意可能存在的问题!是的,如果您通过“Environment.GetEnvironmentVariable”访问它,这将为您的测试代码创建一个环境变量。

就我而言,我正在构建一个“IConfigurationRoot”实例,如下所示:

/// <summary>
/// Gets a configuration instance, based on user secrets (for local test execution) and environment variables (for Azure pipeline execution).
/// </summary>
/// <typeparam name="T">The type of the class that represents the "runtime" (and thus is able to get hold of any configuration).</typeparam>
/// <returns>An <see cref="IConfigurationRoot"/> instance.</returns>
public static IConfigurationRoot GetConfigurationRoot<T>()
    where T : class =>
    new ConfigurationBuilder()
        //// Note: The "AddUserSecrets" method requires the "Microsoft.Extensions.Configuration.UserSecrets" package
        .AddUserSecrets<T>()
        //// Note: The "AddEnvironmentVariables" method requires the "Microsoft.Extensions.Configuration.EnvironmentVariables" package
        .AddEnvironmentVariables()
        .Build();

这通过添加各种“配置提供程序”来实现,最终允许您通过“configuration["SomeSecret"]”无缝访问它们。

不过,如果我没记错的话,我发现“SomeSecret”在添加的“EnvironmentVariablesConfigurationProvider”中仍然不可用,尽管我可以直接使用上面提到的方法。算了吧(但我可能弄错了……)。

可能的替代方法(但仅适用于 YAML 管道?);

似乎对于 YAML 管道,您可以显式地为任务设置环境变量,如下所示:

- task: VSTest@2
  env:
    SomeSecret: $(SomeSecret)
[...]

我自己没有测试过,但看到一位同事这样做了(我自己,我目前没有 YAML 管道)。至少这个变量可以用“Environment.GetEnvironmentVariable”获取,但我不知道是否可以通过“IConfigurationRoot”实例获取。

我还没有看到在非 YAML 管道中实现相同目的的任何选项。

但同样,这也可能遇到同样的“必须在多个测试 运行 中复制环境变量”的问题。

另请参阅我在 Azure DevOps guys

上对自己问题的解决方案