3

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

0

4 Answers 4

9

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")}"
} 
Sign up to request clarification or add additional context in comments.

Comments

3

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
}

Comments

3

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

Comments

1

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.