As shown in the previous post, it is easy to deploy a VM with bicep and Microsoft.Compute/virtualMachines. If it is an Azure Linux image, then the root user cannot be used for security reasons and access for SSH via username/password is blocked. Both points require adjustments that can be easily carried out using runCommands.

The following parts from script ubuntuvmwithrootandssh.bicep shows the creation of an Ubuntu machine and the activation of the root user and SSH:

targetScope = 'resourceGroup'
param adminUsername string = 'adminUser'
@secure()
param adminPassword string

...

resource ubuntuVM 'Microsoft.Compute/virtualMachines@2021-03-01' = {
  name: 'ubuntutest'
  location: resourceGroup().location
  properties: {
    storageProfile: {
      imageReference: {
        publisher: 'Canonical'
        offer: 'UbuntuServer'
        sku: '18.04-LTS'
        version: 'latest'
      }
    }
    osProfile: {
      computerName: 'ubuntutest'
      adminUsername: adminUsername
      adminPassword: adminPassword
    }
    ...
  }
}

resource activateRoot 'Microsoft.Compute/virtualMachines/runCommands@2024-07-01' = {
  parent: ubuntuVM
  name: 'ActivateRootWithSSH'
  location: resourceGroup().location
  properties:{
    protectedParameters: [
      {
        name: 'ROOTPW'
        value: adminPassword
      }
    ]
    errorBlobUri: 'https://<STORAGE ACCOUNT>.blob.core.windows.net/<CONTAINER>/error.txt?<SAS TOKEN>'
    outputBlobUri: 'https://<STORAGE ACCOUNT>.blob.core.windows.net/<CONTAINER>/output.txt?<SAS TOKEN>'
    source:{
      script: '''
        sudo passwd -u root && sudo echo "root:$ROOTPW" | chpasswd && echo "root pw changed" \
        sudo echo "PermitRootLogin yes" >> /etc/ssh/sshd_config && sudo service ssh reload && echo "ssh restarted"
      '''
    }
  }
}

The runCommand command can redirect the standard output and the error output. This is useful for debugging and is configured in lines 40-41. The files are constantly regenerated. Instead of a SAS token, you can also work with managed identity.

The Bicep parameter adminPassword (line 4) is made available to the VM as the ROOTPW environment variable in lines 36 and 37. Therefore, in line 44 the root account is unlocked (-u root) and then its password is set to the password of adminPassword using echo "root:$ROOTPW" | chpasswd. The adminUser and root user have the same password now.

In line 45 the text PermitRootLogin yes is added to the SSH configuration file (/etc/ssh/sshd_config). The SSH service was restarted (service ssh reload) afterwards.

⚠️ Attention: On Linux systems, errors in the script can occur easily because each line will be finished with \r\n. I therefore recommend combining logical commands with && and adding an echo log output to each line. The result in the generated ARM template has the following messy content:

"script": "        sudo echo \"root:$ROOTPW\" > /home/adminUser/rootpw.txt && sudo passwd -u root && sudo echo \"root:$ROOTPW\" | chpasswd && echo \"root pw changed\" \\\r\n        sudo echo \"PermitRootLogin yes\" >> /etc/ssh/sshd_config && sudo service ssh reload && echo \"ssh restarted\"\r\n      "

⚠️ Attention: The root user is blocked by default to increase security. The same applies to the SSH configuration. These settings should only be changed with caution.