I have a virtual machine module that I want to add the option to run a ps1 on startup, just as I would use a bash script as a cloud-init script. I have seen several different ways to do this, but they all have different requirements and do not work for our needs:
- Use unattend xml configs to run
- Run in-line code within azurerm_virtual_machine_extension
- Run ps1 from public repo within azurerm_virtual_machine_extension
- Pass in script to azurerm_virtual_machine_extension with filebase64 or base64encode
Now here are my problems with the above solutions:
- I want the users of my vm module to only have to provide a valid ps1 file - not 2 additional xml configs which also require username / password for autologon.
- I want to pass in a script file, not a line of code. The first script we’re being asked to be able to use is about 50 lines long.
- The script(s) to run are not available in public repos, and we do not want to add functionality for the VM to authenticate to the private repo for this purpose.
- I can pass in the file to the machine using
custom_data = base64encode("${file("./${var.cloudinit_file}")}")
, however this results in malformed base64 file.
Here’s the error:
Exception calling "FromBase64String" with "1" argument(s): "The input is not a valid Base- 64 string as it contains a
non-base 64 character, more than two padding characters, or an illegal character among the padding characters. "
At line:1 char:1
Here is the VM portion of my module (not including all the networking and other backend parts as they are not the problem:
resource "azurerm_windows_virtual_machine" "vm" {
count = var.vm_offer == "WindowsServer" ? 1 : 0
name = "${var.sz_application}-${var.sz_environment}-${local.region-code}"
resource_group_name = data.azurerm_resource_group.iac-rg.name
location = var.location
size = var.vm_size
admin_username = azurerm_key_vault_secret.username.value
admin_password = azurerm_key_vault_secret.password.value
provision_vm_agent = "true"
enable_automatic_updates = "true"
network_interface_ids = [
azurerm_network_interface.vm.id,
]
os_disk {
caching = "ReadWrite"
storage_account_type = var.vm_storage_tier
}
custom_data = base64encode("${file("./${var.cloudinit_file}")}")
source_image_reference {
publisher = var.vm_publisher
offer = var.vm_offer
sku = var.vm_sku
version = var.vm_image_version
}
tags = local.tags
}
Other things I have tried:
- using
data.template_file.cloudinit.rendered
- using
data.template_cloudinit_config.cloudinit.rendered
- using
azurerm_virtual_machine_extension
with this monstrousity:"commandToExecute": "powershell -command -Command [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${base64encode(data.template_file.cloudinit.rendered)}'))\""
edit: using custom_data = filebase64("./${var.cloudinit_file}")
seems to yield a better result … now deploying VM and I’ll check if the c:\AzureData\CustomData.bin
is a useable file or not
edit 2: It is still un-decodable… at least using this:
PS C:\AzureData> $data = get-content .\CustomData.bin
PS C:\AzureData> [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($data)) > out.ps1
Exception calling "FromBase64String" with "1" argument(s): "The input is not a valid Base-64 string as it contains a
non-base 64 character, more than two padding characters, or an illegal character among the padding characters. "
At line:1 char:1
+ [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase6 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : FormatException
So… still stuck
edit 3: Success! But still have a question…
This worked:
in my azurerm_windows_virtual_machine
resource, I pass in my custom_data
using custom_data = filebase64("./${var.cloudinit_file}")
This copies the file onto the machine as the file C:\AzureData\CustomData.bin
Then, I use the following azurerm_virtual_machine_extension
extension:
resource "azurerm_virtual_machine_extension" "cloudinit" {
name = "cloudinit"
virtual_machine_id = azurerm_windows_virtual_machine.vm[0].id
publisher = "Microsoft.Compute"
type = "CustomScriptExtension"
type_handler_version = "1.10"
settings = <<SETTINGS
{
"commandToExecute": "powershell -ExecutionPolicy unrestricted -NoProfile -NonInteractive -command \"cp c:/azuredata/customdata.bin c:/azuredata/install.ps1; c:/azuredata/install.ps1\""
}
SETTINGS
}
My last question is: Is there a better way for the commandToExecute
to directly call the CustomData.bin
as a .ps1
script without the need to copy the .bin
as a .ps1
file?