gsgill76
gsgill76

Reputation: 349

Terraform azurerm_virtual_machine_extension, run local PowerShell Script using CustomScriptExtension

How to run a local ( without storing to a blob storage account ) PowerShell script in terraform azurerm_virtual_machine_extension

Folder having

  1. main.tf
  2. install.ps1

    resource "azurerm_virtual_machine_extension" "software" { name = "install-software" resource_group_name = azurerm_resource_group.azrg.name virtual_machine_id = azurerm_virtual_machine.vm.id publisher = "Microsoft.Compute" type = "CustomScriptExtension" type_handler_version = "1.9"

      settings = <<SETTINGS
        { 
          "commandToExecute": "powershell -ExecutionPolicy Unrestricted -File \"install.ps1\""
        } 
        SETTINGS
    } 
    

    but failed

    [
            {
                "code": "ComponentStatus/StdOut/succeeded",
                "level": "Info",
                "displayStatus": "Provisioning succeeded",
                "message": "Windows PowerShell \r\nCopyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n"
            },
            {
                "code": "ComponentStatus/StdErr/succeeded",
                "level": "Info",
                "displayStatus": "Provisioning succeeded",
                "message": "The argument 'install.ps1' to the -File parameter does not exist. Provide the path to an existing '.ps1' file as an argument to the -File parameter.\r\n"
            }
        ]
    

any lead.

Thanks

Upvotes: 3

Views: 16596

Answers (4)

TomWhi
TomWhi

Reputation: 31

I managed to get parameters to be passed from the data "template_file" into the PowerShell command line to execute on the server, if this helps anyone.

The credentials aren't actually needed in my case but I wanted to pass them anyway. The creds are being taken from the VM setup in Azure in my case.

resource "azurerm_virtual_machine_extension" "software" {
  name                 = "install-software"
#  resource_group_name  = azurerm_resource_group.main.name
  virtual_machine_id   = azurerm_windows_virtual_machine.ADVM1.id
  publisher            = "Microsoft.Compute"
  type                 = "CustomScriptExtension"
  type_handler_version = "1.9"

  protected_settings = <<SETTINGS
  {    
    "commandToExecute": "powershell -command \"[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${base64encode(data.template_file.DomainControllerSetup.rendered)}')) | Out-File -filepath DomainControllerSetup.ps1\" && powershell -ExecutionPolicy Unrestricted -File DomainControllerSetup.ps1 -DomainName ${data.template_file.DomainControllerSetup.vars.DomainName} -AdmincredsUserName ${data.template_file.DomainControllerSetup.vars.AdmincredsUserName} -AdmincredsPassword ${data.template_file.DomainControllerSetup.vars.AdmincredsPassword}" 
  }
  
  SETTINGS
}

data "template_file" "DomainControllerSetup" {
    template = "${file("DomainControllerSetup.ps1")}"
    vars = {
        DomainName              = "azlab.local"
        AdmincredsUserName      = "${azurerm_windows_virtual_machine.ADVM1.admin_username}"
        AdmincredsPassword      = "${azurerm_windows_virtual_machine.ADVM1.admin_password}"
  }
} 

This is my "create new forest" script - again if it helps. The creds aren't being used in this example, only the DomainName is used. However i wanted to keep it in there in case I wanted to promote a member server into an existing domain.


[CmdletBinding()]

param 
( 
    [Parameter(ValuefromPipeline=$true,Mandatory=$true)] [string]$DomainName,
    [Parameter(ValuefromPipeline=$true,Mandatory=$true)] [string]$AdmincredsUserName,
    [Parameter(ValuefromPipeline=$true,Mandatory=$true)] [string]$AdmincredsPassword
)

$username = $AdmincredsUserName
$password = ConvertTo-SecureString -AsPlainText $AdmincredsPassword -Force
$Cred = New-Object System.Management.Automation.PSCredential ($username, $password)

install-windowsfeature AD-Domain-Services -IncludeManagementTools

Install-ADDSForest `
-DomainName $DomainName `
-SafeModeAdministratorPassword $password `
-CreateDnsDelegation:$false `
-DatabasePath "C:\Windows\NTDS" `
-InstallDns:$true `
-LogPath "C:\Windows\NTDS" `
-NoRebootOnCompletion:$false `
-SysvolPath "C:\Windows\SYSVOL" `
-Force:$true

Upvotes: 3

Oleksandr
Oleksandr

Reputation: 21

Here's a little bit prettier solution, based on gsgill76's answer. Note, that we should use textencodebase64 (Terraform >= v0.14), because it allows to specify Unicode encoding ,which is expected by
powershell -encodedCommand.

resource "azurerm_virtual_machine_extension" "software" {
  name                 = "install-software"
  resource_group_name  = azurerm_resource_group.azrg.name
  virtual_machine_id   = azurerm_virtual_machine.vm.id
  publisher            = "Microsoft.Compute"
  type                 = "CustomScriptExtension"
  type_handler_version = "1.9"

  protected_settings = <<SETTINGS
  {
     "commandToExecute": "powershell -encodedCommand ${textencodebase64(file("install.ps1"), "UTF-16LE")}"
  }
  SETTINGS
}

Upvotes: 2

Get-Tek
Get-Tek

Reputation: 81

Thanks to gsgill76 for the working command!

Here's how to use functions in favor of data sources

locals {
  scriptName     = "install.ps1"
  scriptRendered = filebase64("${path.module}/${local.scriptName}")
  # use templatefile() to parse script parameters
  ifTemplateFile = base64encode(templatefile("${path.module}/${local.scriptName}", {}))
  commandToExecute = jsonencode({
    commandToExecute = "powershell -command \"[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${local.scriptRendered}')) | Out-File -filepath ${local.scriptName}\" && powershell -ExecutionPolicy Unrestricted -File ${local.scriptName}"
  })
}

  # settings block will look like
  protected_settings = local.commandToExecute

path.module assumes the script is located in the same directory as your terraform code

filebase64

templatefile

jsonencode

Upvotes: 1

gsgill76
gsgill76

Reputation: 349

this worked for me.

resource "azurerm_virtual_machine_extension" "software" {
  name                 = "install-software"
  resource_group_name  = azurerm_resource_group.azrg.name
  virtual_machine_id   = azurerm_virtual_machine.vm.id
  publisher            = "Microsoft.Compute"
  type                 = "CustomScriptExtension"
  type_handler_version = "1.9"

  protected_settings = <<SETTINGS
  {
    "commandToExecute": "powershell -command \"[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${base64encode(data.template_file.tf.rendered)}')) | Out-File -filepath install.ps1\" && powershell -ExecutionPolicy Unrestricted -File install.ps1"
  }
  SETTINGS
}

data "template_file" "tf" {
    template = "${file("install.ps1")}"
} 

Upvotes: 9

Related Questions