Skip to content
4 changes: 4 additions & 0 deletions src/command_modules/azure-cli-vm/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
Release History
===============

2.2.9
++++++
* `vm/vmss create`: `--authentication-type` now accepts "all" to create a VM with password and ssh authentication.

2.2.8
++++++
* `vm/vmss create --storage-sku`: can now specify the storage account sku for managed os and data disks separately.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,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. If "all" is specified, a password and SSH public key must be provided.', 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 != 'all',
'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 != 'all',
'ssh': {
'publicKeys': [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -837,16 +837,11 @@ def _validate_vm_vmss_create_auth(namespace):
# 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,
Expand All @@ -855,14 +850,36 @@ def _validate_vm_vmss_create_auth(namespace):
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)

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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: make 2 parameters in the same line

namespace.os_type)

validate_ssh_key(namespace)
if not namespace.ssh_dest_key_path:
namespace.ssh_dest_key_path = \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless it is 120 characters plus, make it same line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I copied and pasted code from the other blocks without updating. Will make the changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch, @yugangw-msft

'/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):
import re
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