Reputation: 349
How to run a local ( without storing to a blob storage account ) PowerShell script in terraform azurerm_virtual_machine_extension
Folder having
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
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
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
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
Upvotes: 1
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