Skip to content
1 change: 1 addition & 0 deletions src/command_modules/azure-cli-vm/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Release History
++++++
* `vm extension show / wait`: deprecated --expand parameter.
* `vm restart`: Added `--force` which redeploys unresponsive VMs.
* `vm/vmss create`: `--authentication-type` now accepts/infers "all" to create a VM with both password and ssh authentication.

2.2.8
++++++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ def load_arguments(self, _):
c.argument('admin_password', help="Password for the VM if authentication type is 'Password'.")
c.argument('ssh_key_value', help='SSH public key or public key file path.', completer=FilesCompleter(), type=file_type)
c.argument('ssh_dest_key_path', help='Destination file path on the VM for the SSH key.')
c.argument('authentication_type', help='Type of authentication to use with the VM. Defaults to password for Windows and SSH public key for Linux.', arg_type=get_enum_type(['ssh', 'password']))
c.argument('authentication_type', help='Type of authentication to use with the VM. Defaults to password for Windows and SSH public key for Linux. "all" enables both ssh and password authentication. ', arg_type=get_enum_type(['ssh', 'password', 'all']))

with self.argument_context(scope, arg_group='Storage') as c:
if StorageAccountTypes:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def build_msi_role_assignment(vm_vmss_name, vm_vmss_resource_id, role_definition
def build_vm_resource( # pylint: disable=too-many-locals
cmd, name, location, tags, size, storage_profile, nics, admin_username,
availability_set_id=None, admin_password=None, ssh_key_value=None, ssh_key_path=None,
image_reference=None, os_disk_name=None, custom_image_os_type=None,
image_reference=None, os_disk_name=None, custom_image_os_type=None, authentication_type=None,
os_publisher=None, os_offer=None, os_sku=None, os_version=None, os_vhd_uri=None,
attach_os_disk=None, os_disk_size_gb=None, custom_data=None, secrets=None, license_type=None, zone=None,
disk_info=None, boot_diagnostics_storage_uri=None, ultra_ssd_enabled=None):
Expand All @@ -266,7 +266,7 @@ def _build_os_profile():

if ssh_key_value and ssh_key_path:
os_profile['linuxConfiguration'] = {
'disablePasswordAuthentication': True,
'disablePasswordAuthentication': authentication_type == 'ssh',
'ssh': {
'publicKeys': [
{
Expand Down Expand Up @@ -366,12 +366,8 @@ def _build_storage_profile():

return profile

vm_properties = {
'hardwareProfile': {'vmSize': size},
'networkProfile': {'networkInterfaces': nics}
}

vm_properties['storageProfile'] = _build_storage_profile()
vm_properties = {'hardwareProfile': {'vmSize': size}, 'networkProfile': {'networkInterfaces': nics},
'storageProfile': _build_storage_profile()}

if availability_set_id:
vm_properties['availabilitySet'] = {'id': availability_set_id}
Expand Down Expand Up @@ -707,11 +703,13 @@ def build_vmss_resource(cmd, name, naming_prefix, location, tags, overprovision,
'computerNamePrefix': naming_prefix,
'adminUsername': admin_username
}
if authentication_type == 'password' and admin_password:

if admin_password:
os_profile['adminPassword'] = "[parameters('adminPassword')]"
else:

if ssh_key_value and ssh_key_path:
os_profile['linuxConfiguration'] = {
'disablePasswordAuthentication': True,
'disablePasswordAuthentication': authentication_type == 'ssh',
'ssh': {
'publicKeys': [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -827,41 +827,58 @@ def _validate_vm_vmss_create_auth(namespace):
raise CLIError("Unable to resolve OS type. Specify '--os-type' argument.")

if not namespace.authentication_type:
# apply default auth type (password for Windows, ssh for Linux) by examining the OS type
namespace.authentication_type = 'password' \
if (namespace.os_type.lower() == 'windows' or namespace.admin_password) else 'ssh'
# if both ssh key and password, infer that authentication_type is all.
if namespace.ssh_key_value and namespace.admin_password:
namespace.authentication_type = 'all'
else:
# apply default auth type (password for Windows, ssh for Linux) by examining the OS type
namespace.authentication_type = 'password' \
if (namespace.os_type.lower() == 'windows' or namespace.admin_password) else 'ssh'

if namespace.os_type.lower() == 'windows' and namespace.authentication_type == 'ssh':
raise CLIError('SSH not supported for Windows VMs.')

# validate proper arguments supplied based on the authentication type
if namespace.authentication_type == 'password':
if namespace.ssh_key_value or namespace.ssh_dest_key_path:
raise ValueError(
"incorrect usage for authentication-type 'password': "
"[--admin-username USERNAME] --admin-password PASSWORD")
raise CLIError('SSH key cannot be used with password authentication type.')

from knack.prompting import prompt_pass, NoTTYException
try:
if not namespace.admin_password:
namespace.admin_password = prompt_pass('Admin Password: ', confirm=True)
except NoTTYException:
raise CLIError('Please specify password in non-interactive mode.')
# if password not given, attempt to prompt user for password.
if not namespace.admin_password:
_prompt_for_password(namespace)

# validate password
_validate_admin_password(namespace.admin_password,
namespace.os_type)
_validate_admin_password(namespace.admin_password, namespace.os_type)

elif namespace.authentication_type == 'ssh':

if namespace.admin_password:
raise ValueError('Admin password cannot be used with SSH authentication type')
raise CLIError('Admin password cannot be used with SSH authentication type.')

validate_ssh_key(namespace)

if not namespace.ssh_dest_key_path:
namespace.ssh_dest_key_path = \
'/home/{}/.ssh/authorized_keys'.format(namespace.admin_username)
namespace.ssh_dest_key_path = '/home/{}/.ssh/authorized_keys'.format(namespace.admin_username)

elif namespace.authentication_type == 'all':
if namespace.os_type.lower() == 'windows':
raise CLIError('SSH not supported for Windows VMs. Use password authentication.')

if not namespace.admin_password:
_prompt_for_password(namespace)
_validate_admin_password(namespace.admin_password, namespace.os_type)

validate_ssh_key(namespace)
if not namespace.ssh_dest_key_path:
namespace.ssh_dest_key_path = '/home/{}/.ssh/authorized_keys'.format(namespace.admin_username)


def _prompt_for_password(namespace):
from knack.prompting import prompt_pass, NoTTYException
try:
namespace.admin_password = prompt_pass('Admin Password: ', confirm=True)
except NoTTYException:
raise CLIError('Please specify password in non-interactive mode.')


def _validate_admin_username(username, os_type):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ def create_vm(cmd, vm_name, resource_group_name, image=None, size='Standard_DS1_
cmd=cmd, name=vm_name, location=location, tags=tags, size=size, storage_profile=storage_profile, nics=nics,
admin_username=admin_username, availability_set_id=availability_set, admin_password=admin_password,
ssh_key_value=ssh_key_value, ssh_key_path=ssh_dest_key_path, image_reference=image,
os_disk_name=os_disk_name, custom_image_os_type=os_type,
os_disk_name=os_disk_name, custom_image_os_type=os_type, authentication_type=authentication_type,
os_publisher=os_publisher, os_offer=os_offer, os_sku=os_sku, os_version=os_version, os_vhd_uri=os_vhd_uri,
attach_os_disk=attach_os_disk, os_disk_size_gb=os_disk_size_gb, custom_data=custom_data, secrets=secrets,
license_type=license_type, zone=zone, disk_info=disk_info,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,9 @@ def test_linux_with_password(self):

# throw when conflict with ssh key value
ns.ssh_key_value = 'junk but does not matter'
with self.assertRaises(ValueError) as context:
with self.assertRaises(CLIError) as context:
_validate_vm_vmss_create_auth(ns)
self.assertTrue("incorrect usage for authentication-type 'password':" in str(context.exception))
self.assertTrue("SSH key cannot be used with password authentication type." in str(context.exception))


class TestVMImageDefaults(unittest.TestCase):
Expand Down
Loading