Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion src/azure-cli/azure/cli/command_modules/vm/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -1136,11 +1136,12 @@
- name: --image
type: string
short-summary: >
The name of the operating system image as a URN alias, URN, custom image name or ID, custom image version ID, or VHD blob URI.
The name of the operating system image as a URN alias, URN, custom image name or ID, custom image version ID, or VHD blob URI. In addition, it also supports shared gallery image.
This parameter is required unless using `--attach-os-disk.` Valid URN format: "Publisher:Offer:Sku:Version". For more information, see https://docs.microsoft.com/azure/virtual-machines/linux/cli-ps-findimage
populator-commands:
- az vm image list
- az vm image show
- az sig image-version show-shared
- name: --size
populator-commands:
- az vm list-sizes
Expand Down Expand Up @@ -1209,6 +1210,9 @@
- name: Create multiple VMs. In this example, 3 VMs are created. They are MyVm0, MyVm1, MyVm2.
text: >
az vm create -n MyVm -g MyResourceGroup --image centos --count 3
- name: Create a VM from shared gallery image.
text: >
az vm create -n MyVm -g MyResourceGroup --image /SharedGalleries/{gallery_unique_name}/Images/{image}/Versions/{version}
"""

helps['vm deallocate'] = """
Expand Down
18 changes: 18 additions & 0 deletions src/azure-cli/azure/cli/command_modules/vm/_template_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class StorageProfile(Enum):
ManagedPirImage = 4 # this would be the main scenarios
ManagedCustomImage = 5
ManagedSpecializedOSDisk = 6
SharedGalleryImage = 7


def build_deployment_resource(name, template, dependencies=None):
Expand Down Expand Up @@ -448,6 +449,19 @@ def _build_storage_profile():
'id': attach_os_disk
}
}
},
'SharedGalleryImage': {
"osDisk": {
"caching": os_caching,
"managedDisk": {
"storageAccountType": disk_info['os'].get('storageAccountType'),
},
"name": os_disk_name,
"createOption": "fromImage"
},
"imageReference": {
'sharedGalleryImageId': image_reference
}
}
}
if os_disk_encryption_set is not None:
Expand All @@ -457,6 +471,10 @@ def _build_storage_profile():
storage_profiles['ManagedCustomImage']['osDisk']['managedDisk']['diskEncryptionSet'] = {
'id': os_disk_encryption_set,
}
storage_profiles['SharedGalleryImage']['osDisk']['managedDisk']['diskEncryptionSet'] = {
'id': os_disk_encryption_set,
}

profile = storage_profiles[storage_profile.name]
if os_disk_size_gb:
profile['osDisk']['diskSizeGb'] = os_disk_size_gb
Expand Down
17 changes: 15 additions & 2 deletions src/azure-cli/azure/cli/command_modules/vm/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ def _parse_image_argument(cmd, namespace):
if is_valid_resource_id(namespace.image):
return 'image_id'

from ._vm_utils import is_shared_gallery_image_id
if is_shared_gallery_image_id(namespace.image):
return 'shared_gallery_image_id'

# 2 - attempt to match an URN pattern
urn_match = re.match('([^:]*):([^:]*):([^:]*):([^:]*)', namespace.image)
if urn_match:
Expand Down Expand Up @@ -324,7 +328,7 @@ def _get_image_plan_info_if_exists(cmd, namespace):
"will be skipped", namespace.image, ex.message)


# pylint: disable=inconsistent-return-statements
# pylint: disable=inconsistent-return-statements, too-many-return-statements
def _get_storage_profile_description(profile):
if profile == StorageProfile.SACustomImage:
return 'create unmanaged OS disk created from generalized VHD'
Expand All @@ -338,6 +342,8 @@ def _get_storage_profile_description(profile):
return 'create managed OS disk from Azure Marketplace image'
if profile == StorageProfile.ManagedSpecializedOSDisk:
return 'attach existing managed OS disk'
if profile == StorageProfile.SharedGalleryImage:
return 'create OS disk from shared gallery image'


def _validate_location(cmd, namespace, zone_info, size_info):
Expand Down Expand Up @@ -379,6 +385,8 @@ def _validate_vm_create_storage_profile(cmd, namespace, for_scale_set=False):
elif image_type == 'image_id':
# STORAGE PROFILE #5
namespace.storage_profile = StorageProfile.ManagedCustomImage
elif image_type == 'shared_gallery_image_id':
namespace.storage_profile = StorageProfile.SharedGalleryImage
elif image_type == 'urn':
if namespace.use_unmanaged_disk:
# STORAGE PROFILE #1
Expand Down Expand Up @@ -411,6 +419,11 @@ def _validate_vm_create_storage_profile(cmd, namespace, for_scale_set=False):
if for_scale_set:
forbidden.append('os_disk_name')
Copy link
Member

Choose a reason for hiding this comment

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

Is os_disk_name also forbidden by shared gallery image?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

'SharedGalleryImage':{
    "osDisk": {
         ......
        "name": os_disk_name,
         ......
    },
    ......
}

No, it supports setting the os_disk_name for shared gallery image


elif namespace.storage_profile == StorageProfile.SharedGalleryImage:
required = ['image']
forbidden = ['os_type', 'attach_os_disk', 'storage_account',
'storage_container_name', 'use_unmanaged_disk']

elif namespace.storage_profile == StorageProfile.ManagedSpecializedOSDisk:
required = ['os_type', 'attach_os_disk']
forbidden = ['os_disk_name', 'os_caching', 'storage_account', 'ephemeral_os_disk',
Expand Down Expand Up @@ -505,7 +518,7 @@ def _validate_vm_create_storage_profile(cmd, namespace, for_scale_set=False):
namespace.attach_data_disks = [_get_resource_id(cmd.cli_ctx, d, namespace.resource_group_name, 'disks',
'Microsoft.Compute') for d in namespace.attach_data_disks]

if not namespace.os_type:
if not namespace.os_type and namespace.storage_profile != StorageProfile.SharedGalleryImage:
Copy link
Member

Choose a reason for hiding this comment

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

Is shared gallery image excluded in this logic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, because the shared gallery image does not require os_type.

namespace.os_type = 'windows' if 'windows' in namespace.os_offer.lower() else 'linux'

from ._vm_utils import normalize_disk_info
Expand Down
11 changes: 11 additions & 0 deletions src/azure-cli/azure/cli/command_modules/vm/_vm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,17 @@ def _update(info, lun, value):
_update(info_dict, lun, value)


def is_shared_gallery_image_id(image_reference):
if not image_reference:
return False

shared_gallery_id_pattern = re.compile(r'^/SharedGalleries/[^/]*/Images/[^/]*/Versions/.*$', re.IGNORECASE)
Copy link
Member

Choose a reason for hiding this comment

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

Ensure this regex covers all scenario for shared gallery image id?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I confirmed this regex expression with @kangsun-ctrl

if shared_gallery_id_pattern.match(image_reference):
return True

return False


class ArmTemplateBuilder20190401(ArmTemplateBuilder):

def __init__(self):
Expand Down
Loading