Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
24a2db9
Create extension
Oct 22, 2025
f5b4378
Update src/migrate/azext_migrate/__init__.py
saifaldin14 Oct 22, 2025
67bb359
Fix import issues
Oct 22, 2025
70c525b
Merge branch 'main' of https://github.com/saifaldin14/azure-cli-exten…
Oct 22, 2025
c3021fb
Update src/migrate/setup.py
saifaldin14 Oct 22, 2025
f6e2dd8
Small
Oct 22, 2025
0174a38
Merge branch 'main' of https://github.com/saifaldin14/azure-cli-exten…
Oct 22, 2025
ae26651
Small lint
Oct 22, 2025
09ff801
Small
Oct 22, 2025
749bd43
disable lint for this check
Oct 22, 2025
8002b06
Add json
Oct 22, 2025
be276db
Fix licesnse issue
Oct 22, 2025
8da3466
fix small
Oct 22, 2025
d9fa098
Small
Oct 22, 2025
68f0d46
Get rid of unused variables
Oct 22, 2025
f916208
Add service name and code owner
Oct 23, 2025
9ad08a3
Merge branch 'main' of https://github.com/saifaldin14/azure-cli-exten…
Oct 23, 2025
8ae69d2
New version
Oct 23, 2025
532cbb3
Style
Oct 23, 2025
f216aa3
Small
Oct 23, 2025
77d8eb0
Update
Oct 23, 2025
f7558d6
Follow standard
Oct 23, 2025
ea8d636
Add suggestions
Oct 23, 2025
7117986
Small
Oct 23, 2025
143028f
Not preview
Oct 23, 2025
242fb99
Add flag to become experimental
Oct 23, 2025
4dae020
Merge branch 'Azure:main' into main
saifaldin14 Oct 23, 2025
6a1f184
Update history
Oct 23, 2025
4178016
Merge branch 'main' of https://github.com/saifaldin14/azure-cli-exten…
Oct 23, 2025
8874d5e
Fix
Oct 23, 2025
38b0de2
small
Oct 23, 2025
cf0b180
Adding "Migrate" folder to the project
mettursathish Oct 23, 2025
0f7acb7
Create get job and remove replication commands
Oct 24, 2025
5d4d83b
Add better error handling for jobs command
Nov 3, 2025
2f8b6d8
Add better messages to remove protected item
Nov 3, 2025
9ffb8e6
Return job id in remove command
Nov 3, 2025
d3120e3
Move helpers
Nov 3, 2025
ff6f498
Rename get discovered server helper
Nov 3, 2025
0f4af5b
Refactor _initialize_replication_infrastructure_helpers
Nov 3, 2025
cb8370b
Refactor new replication
Nov 3, 2025
f4514e8
Refactor jobs
Nov 3, 2025
5b482a7
Refactor delete protected item
Nov 3, 2025
15ab074
Fix lint issues
Nov 12, 2025
314d750
Merge branch 'main' into user/saif/newCommands
saifaldin14 Nov 12, 2025
f0dc844
Change release version
Nov 12, 2025
743370b
Merge branch 'user/saif/newCommands' of https://github.com/saifaldin1…
Nov 12, 2025
c373e29
Fix lint issues
Nov 12, 2025
93ff490
Add fix
Nov 12, 2025
86547d4
Updae correct version
Nov 18, 2025
c71edfa
Put beta version before
Nov 18, 2025
5c85e2b
Fix
Nov 19, 2025
096fcc0
Update
Nov 19, 2025
05a82bd
Fix bifurcation tool issue
Nov 19, 2025
4ff904e
Update amh correctly if not proper
Nov 19, 2025
d562112
Use current subscription id if not passed in
Nov 19, 2025
c38da1d
Fix correct location and target resource group creation
Nov 19, 2025
9b6e4c0
Create list protected items command
Nov 19, 2025
7921583
Update versions
Dec 22, 2025
8488ad7
Add __init__.py to all folders
Dec 23, 2025
087bf1a
Create unit tests for get replication
Dec 23, 2025
3613a0e
Update readme with accurate description of commands
Dec 23, 2025
3a44467
Update readme
Dec 23, 2025
9dac4d4
Create tests for jobs and remove commands
Dec 23, 2025
93adc8f
Add more tests to increase coverage to 74%
Dec 23, 2025
3b19466
Merge branch 'main' of https://github.com/mettursathish/azure-cli-ext…
mettursathish Dec 23, 2025
ffc8eac
Added migrate command
mettursathish Dec 23, 2025
9206119
Add start migration command
Dec 23, 2025
e194aac
Address fixes
Dec 26, 2025
709efcf
Merge branch 'main' into user/saif/newCommands
saifaldin14 Dec 26, 2025
3516cb0
Remove doc strings from custom.py
Dec 26, 2025
ef3d351
Merge branch 'user/saif/newCommands' of https://github.com/saifaldin1…
Dec 26, 2025
6f645fa
Add init
Dec 26, 2025
9c628fd
Remove old file locations
Dec 26, 2025
bc56712
Adding preview tag for commands, moving migration to a seperate folde…
mettursathish Dec 26, 2025
54be25e
Addressing style issues
mettursathish Dec 26, 2025
fc9b6d6
addressing the styles and removing the duplicate key
mettursathish Dec 26, 2025
189fbcf
addressing style issues
mettursathish Dec 26, 2025
12f8085
addesssing more style issues
mettursathish Dec 26, 2025
bac3d75
addressing inline disable at the specific line
mettursathish Dec 26, 2025
85ef1ed
Added R1702 to the pylint disable comment on the function definition.
mettursathish Dec 26, 2025
52efe2a
Addressing to disable too many nested blocks R1702
mettursathish Dec 26, 2025
e1995cb
Merge branch 'main' into user/saif/newCommands
saifaldin14 Dec 29, 2025
f4f58ec
fix failing tests
Dec 29, 2025
6bfea6b
Fixing test cases and updating version changes
mettursathish Dec 29, 2025
2e469fa
Fix secret test
Dec 30, 2025
384a0bf
Fix test
Dec 30, 2025
3317afe
Fix secret detection
Dec 30, 2025
faa2716
Fix duplicates
Dec 30, 2025
59fd97f
Merge branch 'main' into user/saif/newCommands
saifaldin14 Dec 30, 2025
a898c56
Fix get-discovered-server parameter errors
Jan 7, 2026
3a46f57
Merge branch 'Azure:main' into user/saif/newCommands
saifaldin14 Jan 8, 2026
3e23b99
Small
Jan 12, 2026
b0cd11d
Merge branch 'Azure:main' into user/saif/newCommands
saifaldin14 Jan 13, 2026
620d94d
Update version
Jan 13, 2026
06892a8
Merge branch 'user/saif/newCommands' of https://github.com/saifaldin1…
Jan 13, 2026
b3bc481
Fix style issues
Jan 13, 2026
370eb7d
Fix linter help
Jan 13, 2026
2215b78
Disable linter
Jan 13, 2026
3f55d7a
Fix help examples
Jan 13, 2026
90ab86a
Exclusions
Jan 13, 2026
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
4 changes: 4 additions & 0 deletions src/migrate/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
Release History
===============

3.0.0b3
+++++++++++++++
* Fix edge case bugs with az migrate get-discovered-server.

3.0.0b2
+++++++++++++++
* Added replication list, get and start migration commands.
Expand Down
1 change: 1 addition & 0 deletions src/migrate/azext_migrate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# --------------------------------------------------------------------------------------------

from azure.cli.core import AzCommandsLoader
from azext_migrate._help import helps # pylint: disable=unused-import


class MigrateCommandsLoader(AzCommandsLoader):
Expand Down
28 changes: 14 additions & 14 deletions src/migrate/azext_migrate/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
environments.
"""

helps['migrate local get-discovered-server'] = """
helps['migrate get-discovered-server'] = """
type: command
short-summary: Retrieve discovered servers from an Azure Migrate project.
long-summary: |
Expand Down Expand Up @@ -69,31 +69,31 @@
examples:
- name: List all discovered servers in a project
text: |
az migrate local get-discovered-server \\
az migrate get-discovered-server \\
--project-name myMigrateProject \\
--resource-group myRG
- name: Get a specific discovered server by name
text: |
az migrate local get-discovered-server \\
az migrate get-discovered-server \\
--project-name myMigrateProject \\
--resource-group myRG \\
--name machine-12345
- name: Filter discovered servers by display name
text: |
az migrate local get-discovered-server \\
az migrate get-discovered-server \\
--project-name myMigrateProject \\
--resource-group myRG \\
--display-name "web-server"
- name: List VMware servers discovered by a specific appliance
text: |
az migrate local get-discovered-server \\
az migrate get-discovered-server \\
--project-name myMigrateProject \\
--resource-group myRG \\
--appliance-name myVMwareAppliance \\
--source-machine-type VMware
- name: Get a specific server from a specific appliance
text: |
az migrate local get-discovered-server \\
az migrate get-discovered-server \\
--project-name myMigrateProject \\
--resource-group myRG \\
--appliance-name myAppliance \\
Expand Down Expand Up @@ -226,7 +226,7 @@
Space-separated list of NIC IDs
to replicate from the source server.
Use this for power user mode.
- name: --vm-name
- name: --target-vm-name
short-summary: Name of the VM to be created.
long-summary: >
The name for the virtual machine
Expand Down Expand Up @@ -377,13 +377,13 @@
Note: This command uses a preview API version
and may experience breaking changes in future releases.
parameters:
- name: --protected-item-id --id
- name: --protected-item-id
short-summary: Full ARM resource ID of the protected item.
long-summary: >
The complete ARM resource ID of the protected item.
If provided, --resource-group and --project-name are not required.
This ID can be obtained from the 'list' or 'new' commands.
- name: --protected-item-name --name
- name: --protected-item-name
short-summary: Name of the protected item (replicating server).
long-summary: >
The name of the protected item to retrieve.
Expand Down Expand Up @@ -439,14 +439,14 @@
Note: This command uses a preview API version
and may experience breaking changes in future releases.
parameters:
- name: --target-object-id --id
- name: --target-object-id
short-summary: Replicating server ARM ID to disable replication.
long-summary: >
Specifies the ARM resource ID of the replicating server
for which replication needs to be disabled.
The ID should be retrieved using a get or list command
for replication items.
- name: --force-remove --force
- name: --force-remove
short-summary: Force remove the replication.
long-summary: >
Specifies whether the replication needs to be
Expand Down Expand Up @@ -486,7 +486,7 @@
Note: This command uses a preview API version
and may experience breaking changes in future releases.
parameters:
- name: --job-id --id
- name: --job-id
short-summary: Job ARM ID for which details need to be retrieved.
long-summary: >
Specifies the full ARM resource ID of the job.
Expand All @@ -502,7 +502,7 @@
long-summary: >
The name of the Azure Migrate project.
Required when using --resource-group.
- name: --job-name --name
- name: --job-name
short-summary: Job identifier/name.
long-summary: >
The name of the specific job to retrieve.
Expand Down Expand Up @@ -558,7 +558,7 @@
Note: This command uses a preview API version
and may experience breaking changes in future releases.
parameters:
- name: --protected-item-id --id
- name: --protected-item-id
short-summary: Full ARM resource ID of the protected item to migrate.
long-summary: >
The complete ARM resource ID of the replicating server.
Expand Down
2 changes: 1 addition & 1 deletion src/migrate/azext_migrate/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def load_arguments(self, _):
required=True)
c.argument(
'project_name',
project_name_type,
options_list=['--project-name'],
help='The name of the migrate project.',
required=True)
c.argument('subscription_id', subscription_id_type)
Expand Down
27 changes: 13 additions & 14 deletions src/migrate/azext_migrate/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
logger = get_logger(__name__)


# pylint: disable=too-many-locals
def get_discovered_server(cmd,
project_name,
resource_group,
Expand All @@ -23,6 +24,7 @@ def get_discovered_server(cmd,
from azext_migrate.helpers._utils import APIVersion
from azext_migrate.helpers._server import (
validate_get_discovered_server_params,
extract_machine_name_from_id,
build_base_uri,
fetch_all_servers,
filter_servers_by_display_name,
Expand All @@ -33,6 +35,10 @@ def get_discovered_server(cmd,
validate_get_discovered_server_params(
project_name, resource_group, source_machine_type)

# Extract machine name if a full resource ID was provided for --name
if name:
name = extract_machine_name_from_id(name)

# Use current subscription if not provided
if not subscription_id:
from azure.cli.core.commands.client_factory import \
Expand All @@ -44,34 +50,27 @@ def get_discovered_server(cmd,
subscription_id, resource_group, project_name,
appliance_name, name, source_machine_type)

# Use the correct API version
# Construct the full URI with appropriate API version
# Note: Azure Migrate API does not support OData $filter for machines endpoint
# We'll apply client-side filtering after fetching all results
api_version = (APIVersion.Microsoft_OffAzure.value if appliance_name
else APIVersion.Microsoft_Migrate.value)

# Prepare query parameters
query_params = [f"api-version={api_version}"]
if not appliance_name and display_name:
query_params.append(f"$filter=displayName eq '{display_name}'")

# Construct the full URI
request_uri = (
f"{cmd.cli_ctx.cloud.endpoints.resource_manager}{base_uri}?"
f"{'&'.join(query_params)}"
f"api-version={api_version}"
)

try:
# Fetch all servers
values = fetch_all_servers(cmd, request_uri, send_get_request)

# Apply client-side filtering for display_name when using site
# endpoints
if appliance_name and display_name:
# Apply client-side filtering for display_name
if display_name:
values = filter_servers_by_display_name(values, display_name)

# Format and display the discovered servers information
for index, server in enumerate(values, 1):
server_info = extract_server_info(server, index)
print_server_info(server_info)
print_server_info(extract_server_info(server, index))

except Exception as e:
logger.error("Error retrieving discovered servers: %s", str(e))
Expand Down
90 changes: 80 additions & 10 deletions src/migrate/azext_migrate/helpers/_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@ def validate_get_discovered_server_params(project_name,
raise CLIError("source_machine_type is not 'VMware' or 'HyperV'.")


def extract_machine_name_from_id(name_or_id):
"""
Extract the machine name from a full resource ID or return the name as-is.

Handles formats like:
- Full ID: /subscriptions/.../Machines/5939691e-5505-4016-b4cd-4fa2d862a975
- Simple name: 5939691e-5505-4016-b4cd-4fa2d862a975
"""
if not name_or_id:
return None

# If it looks like a full resource ID, extract the machine name
if name_or_id.startswith('/subscriptions/') or name_or_id.startswith('/Subscriptions/'):
parts = name_or_id.rstrip('/').split('/')
# The machine name should be the last part after "machines" or "Machines"
for i, part in enumerate(parts):
if part.lower() == 'machines' and i + 1 < len(parts):
return parts[i + 1]
# If we can't find machines, return the last segment
return parts[-1] if parts else name_or_id

return name_or_id


def build_base_uri(subscription_id, resource_group_name, project_name,
appliance_name, name, source_machine_type):
"""Build the base URI for the API request."""
Expand Down Expand Up @@ -66,43 +90,60 @@ def fetch_all_servers(cmd, request_uri, send_get_request):
"""Fetch all servers including paginated results."""
response = send_get_request(cmd, request_uri)
data = response.json()
values = data.get('value', [])

while data.get('nextLink'):
response = send_get_request(cmd, data.get('nextLink'))
data = response.json()
values += data.get('value', [])

return values
# Handle single item response (when fetching by name/ID)
# Single items have 'id' and 'properties' at root level, not 'value'
if 'value' in data:
values = data.get('value', [])
while data.get('nextLink'):
response = send_get_request(cmd, data.get('nextLink'))
data = response.json()
values += data.get('value', [])
return values
if 'id' in data and 'properties' in data:
# Single machine response - wrap in list
return [data]
return []


def filter_servers_by_display_name(servers, display_name):
"""Filter servers by display name."""
"""Filter servers by display name or machine name."""
filtered = []
for server in servers:
properties = server.get('properties', {})

# Check properties.displayName first
if properties.get('displayName', '') == display_name:
filtered.append(server)
continue

# Also check discoveryData[0].machineName
discovery_data = properties.get('discoveryData', [])
if discovery_data:
machine_name = discovery_data[0].get('machineName', '')
if machine_name == display_name:
filtered.append(server)
return filtered


# pylint: disable=too-many-locals
def extract_server_info(server, index):
"""Extract server information from discovery data."""
properties = server.get('properties', {})
discovery_data = properties.get('discoveryData', [])

# Default values
machine_name = "N/A"
machine_id = "N/A"
machine_id = server.get('id', 'N/A')
ip_addresses_str = 'N/A'
os_name = "N/A"
boot_type = "N/A"
os_disk_id = "N/A"

if discovery_data:
# Format from Microsoft.Migrate/migrateprojects/machines
latest_discovery = discovery_data[0]
machine_name = latest_discovery.get('machineName', 'N/A')
machine_id = server.get('id', 'N/A')
ip_addresses = latest_discovery.get('ipAddresses', [])
ip_addresses_str = ', '.join(ip_addresses) if ip_addresses else 'N/A'
os_name = latest_discovery.get('osName', 'N/A')
Expand All @@ -114,6 +155,35 @@ def extract_server_info(server, index):
disk_details = json.loads(disk_details_json)
if disk_details:
os_disk_id = disk_details[0].get("InstanceId", "N/A")
else:
# Format from Microsoft.OffAzure/VMwareSites/machines or HyperVSites/machines
machine_name = properties.get('displayName', 'N/A')

# Try to get IP addresses from different locations
network_adapters = properties.get('networkAdapters', [])
if network_adapters:
all_ips = []
for adapter in network_adapters:
ips = adapter.get('ipAddressList', [])
all_ips.extend(ips)
ip_addresses_str = ', '.join(all_ips) if all_ips else 'N/A'

# Get OS info from guestOSDetails or operatingSystemDetails
guest_os = properties.get('guestOSDetails', {})
if guest_os:
os_name = guest_os.get('osName', 'N/A')
else:
os_details = properties.get('operatingSystemDetails', {})
os_name = os_details.get('osName', os_details.get('osType', 'N/A'))

# Get firmware/boot type
firmware = properties.get('firmware', 'N/A')
boot_type = firmware.lower() if firmware and firmware != 'N/A' else 'N/A'

# Get disk info
disks = properties.get('disks', [])
if disks:
os_disk_id = disks[0].get('uuid', disks[0].get('diskId', 'N/A'))

return {
'index': index,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,13 @@ def _format_protected_item(item):
# Add custom properties if available
if custom_properties:
formatted_item['instanceType'] = custom_properties.get('instanceType', 'N/A')
formatted_item['sourceMachineName'] = custom_properties.get('sourceMachineName', 'N/A')
formatted_item['targetVmName'] = custom_properties.get('targetVmName', 'N/A')
formatted_item['targetResourceGroupId'] = custom_properties.get('targetResourceGroupId', 'N/A')
formatted_item['customLocationRegion'] = custom_properties.get('customLocationRegion', 'N/A')

# Use sourceVmName from API response (the actual VM display name)
formatted_item['sourceMachineName'] = custom_properties.get('sourceVmName', 'N/A')

return formatted_item


Expand Down
Loading
Loading