让 terraform null_resource local-exec 运行 powershell 安装模块

Chr*_*our 2 powershell terraform

这可能是邪恶的做法,但为了在我们的 azure 数据库中设置托管身份,我们使用 null_resource,如下所示:

\n\n
# https://www.terraform.io/docs/providers/null/resource.html\n# This technique was stolen from /sf/answers/3816637401/\nresource "null_resource" "create-sql-user" {\n\n  triggers = {\n    db = azurerm_sql_database.x.id\n  }\n\n  # https://www.terraform.io/docs/provisioners/local-exec.html\n  provisioner "local-exec" {\n\n    # https://learn.microsoft.com/en-us/powershell/module/sqlserver/Invoke-Sqlcmd?view=sqlserver-ps\n    # Adding the Managed Identity to the database as a user and assign it the roles of db_datareader and db_datawriter\n    # NOTE: This is using the executing users credentials to connect to the db, this may not work if this is executed from a service principal within a devops pipeline\n    # NOTE: this requires powershell to have the SqlServer module installed.  We tried a bunch of things to make it so it\'d auto install the module but couldn\'t get it to work\n    command = <<EOF\n     Invoke-Sqlcmd `\n       -Query "CREATE USER [${azurerm_app_service.x.name}] FROM EXTERNAL PROVIDER; ALTER\xc2\xa0ROLE\xc2\xa0db_datareader\xc2\xa0ADD\xc2\xa0MEMBER\xc2\xa0[${azurerm_app_service.x.name}]; ALTER\xc2\xa0ROLE\xc2\xa0db_datawriter\xc2\xa0ADD\xc2\xa0MEMBER\xc2\xa0[${azurerm_app_service.x.name}];" `\n       -ConnectionString "Server=tcp:${azurerm_sql_server.x.fully_qualified_domain_name},1433;Initial Catalog=${azurerm_sql_database.x.name};Persist Security Info=False;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=Active Directory Integrated;" `\n    EOF\n\n    interpreter = ["PowerShell", "-Command"]\n  }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

问题是需要 Invoke-Sqlcmd 可用,但这只能通过带外安装模块 SqlServer w/ terraform 来实现。我在命令中尝试了一些不同的事情来实现这一点。喜欢:

\n\n
  # https://www.terraform.io/docs/provisioners/local-exec.html\n  provisioner "local-exec" {\n\n    # https://learn.microsoft.com/en-us/powershell/module/sqlserver/Invoke-Sqlcmd?view=sqlserver-ps\n    # Adding the Managed Identity to the database as a user and assign it the roles of db_datareader and db_datawriter\n    command = "Install-Module -Name SqlServer -AcceptLicense -SkipPublisherCheck -Force -AllowClobber -Scope CurrentUser;"\n\n    interpreter = ["PowerShell", "-ExecutionPolicy", "Bypass", "-Command"]\n  }\n
Run Code Online (Sandbox Code Playgroud)\n\n

错误:运行命令 \'Install-Module -Name SqlServer -AcceptLicense -SkipPublisherCheck -Force -AllowClobber -Scope CurrentUser;\' 时出错:退出状态 1。输出:Install-Module:在以下位置找到 \'Install-Module\' 命令模块“PowerShellGet”,但无法加载该模块。有关详细信息,请运行“Import-Module PowerShellGet”。

\n\n

所以将命令切换为

\n\n
command = "Import-Module PowerShellGet; Install-Module -Name SqlServer -AcceptLicense -SkipPublisherCheck -Force -AllowClobber -Scope CurrentUser;"\n
Run Code Online (Sandbox Code Playgroud)\n\n

但这会导致这个输出

\n\n
Error: Error running command \'Import-Module PowerShellGet; Install-Module -Name SqlServer -AcceptLicense -SkipPublisherCheck -Force -AllowClobber -Scope CurrentUser;\': exit status 1. Output: Import-Module : The specified module \'C:\\program\nfiles\\powershell\\6\\Modules\\PackageManagement\\fullclr\\Microsoft.PackageManagement.dll\' was not loaded because no valid\nmodule file was found in any module directory.\nAt line:1 char:1\n+ Import-Module PowerShellGet; Install-Module -Name SqlServer -AcceptLi ...\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n    + CategoryInfo          : ResourceUnavailable: (C:\\program file...eManagement.dll:String) [Import-Module], FileNot\n   FoundException\n    + FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand\n\nPackageManagement\\Get-PackageProvider : The term \'PackageManagement\\Get-PackageProvider\' is not recognized as the name\nof a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included,\nverify that the path is correct and try again.\nAt C:\\program files\\powershell\\6\\Modules\\PowerShellGet\\PSModule.psm1:2926 char:26\n+ ...        $nugetProvider = PackageManagement\\Get-PackageProvider -ErrorA ...\n+                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n    + CategoryInfo          : ObjectNotFound: (PackageManagement\\Get-PackageProvider:String) [], CommandNotFoundExcept\n   ion\n    + FullyQualifiedErrorId : CommandNotFoundException\n\nPackageManagement\\Get-PackageProvider : The term \'PackageManagement\\Get-PackageProvider\' is not recognized as the name\nof a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included,\nverify that the path is correct and try again.\nAt C:\\program files\\powershell\\6\\Modules\\PowerShellGet\\PSModule.psm1:2940 char:40\n+ ... ailableNugetProviders = PackageManagement\\Get-PackageProvider -Name $ ...\n+                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n    + CategoryInfo          : ObjectNotFound: (PackageManagement\\Get-PackageProvider:String) [], CommandNotFoundExcept\n   ion\n    + FullyQualifiedErrorId : CommandNotFoundException\n\nException calling "ShouldContinue" with "2" argument(s): "Object reference not set to an instance of an object."\nAt C:\\program files\\powershell\\6\\Modules\\PowerShellGet\\PSModule.psm1:3115 char:8\n+     if($Force -or $psCmdlet.ShouldContinue($shouldContinueQueryMessag ...\n+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException\n    + FullyQualifiedErrorId : NullReferenceException\n\nInstall-Module : NuGet provider is required to interact with NuGet-based repositories. Please ensure that \'2.8.5.201\'\nor newer version of NuGet provider is installed.\nAt line:1 char:30\n+ ... erShellGet; Install-Module -Name SqlServer -AcceptLicense -SkipPublis ...\n+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n    + CategoryInfo          : InvalidOperation: (:) [Install-Module], InvalidOperationException\n    + FullyQualifiedErrorId : CouldNotInstallNuGetProvider,Install-Module\n
Run Code Online (Sandbox Code Playgroud)\n\n

我想知道的一件事是 powershell 版本 6 与 5 是否以某种方式妨碍了这里......

\n

pij*_*olu 10

编辑:我相信您使用了错误的解释器,请尝试将 Powershell 切换到 pwsh 以使用 powershell 6 作为解释器。

  provisioner "local-exec" {
    ...
    interpreter = ["pwsh", "-Command"]
    ...
  }
Run Code Online (Sandbox Code Playgroud)

我不确定需要 powershell 运行的底层基础设施。看来您正在使用 powershell 6。

我还使用空提供者资源,调用脚本,传入参数,然后创建用户。这样做的一个优点是我知道我正在运行哪个 powershell 版本(核心),因为触发命令是pwsh

我将向您展示如何创建空资源和脚本片段,希望对您有所帮助。

用于调用负责创建用户的脚本的空资源

resource "null_resource" "create_sql_user" {
  provisioner "local-exec" {
    command     = ".'${path.module}\\scripts\\create-sql-user.ps1' -password \"${random_password.sql_password.result}\" -username \"${var.sql_username}\" -sqlSaConnectionString \"${var.sql_server_connectionstring}\" -databaseName \"${azurerm_sql_database.db.name}\" "
    interpreter = ["pwsh", "-Command"]
  }
  depends_on = [azurerm_sql_database.db]
}
Run Code Online (Sandbox Code Playgroud)

创建-sql-用户.ps1

[CmdletBinding()]
param (
    [Parameter(Mandatory = $true)]
    [string]
    $password,
    [Parameter(Mandatory = $true)]
    [string]
    $username,
    [Parameter(Mandatory = $true)]
    [string]
    $sqlSaConnectionString
)

Install-Module -Name SqlServer -Force

$sqlCmd = "CREATE LOGIN $username WITH PASSWORD = '$password'; ALTER LOGIN $username enable"
Invoke-Sqlcmd -ConnectionString $sqlSaConnectionString -Query $sqlCmd

...
Run Code Online (Sandbox Code Playgroud)

附加功能:

在本例中,我使用随机资源生成 sql 密码。可以对用户名使用类似的方法:

resource "random_password" "sql_password" {
  length           = 54
  special          = true
  override_special = "$%@&*()"
}
Run Code Online (Sandbox Code Playgroud)