Bug Report #1: Az CLI always creating a Public IP when non-interactive
In today’s bug report, we have an issue with the Az CLI extension vm-repair
. Affected Repo: https://github.com/Azure/azure-cli-extensions
Table of Contents
The Issue
When creating a repair VM via the Az CLI command az vm repair create
both options to bypass the Public-IP address prompt always create a public IP. No option to avoid creating a public IP.
As documented here, this command has 2 parameters related to Public IPs.
--yes
Will create a public IP address for the VM.--associate-public-ip
Will also create a public IP address for the VM.
However there is no way to create a repair VM non-interactively without also creating a public IP. When these parameters are used, they create Public IPs that are not within the common naming convention for VM resources.
Reproduction steps
- Open Az CLI in a shell session of some kind.
- Add the
vm-repair
extension to the Az CLI.az extension add --name vm-repair
- Then create a repair VM using one of the following commands.
# uses --yes and creates a public IP address named 'False'
az vm repair create -g cloudj -n cloudj --yes --repair-username *********** --repair-password '***********'
# uses --associate-public-ip set to '$False' and creates a public IP address named 'True'
az vm repair create -g cloudj -n cloudj --associate-public-ip $false --repair-username *********** --repair-password '***********'
# uses --associate-public-ip set to '$True' and creates a public IP address named 'True'
az vm repair create -g cloudj -n cloudj --associate-public-ip $True --repair-username *********** --repair-password '***********'
Notes
- Using the interactive methods does deliver the expected results.
y
will create a public IP namedrepair-cloudj_PublicIP
. Using ‘n’ will not create an IP. - Even if IP is being created as expected, i.e.
--yes
or--associate-public-ip
, it should still be in the proper naming conventionrepair-<VM_Name>_<Resource>
.
The fix
Affected Repo: https://github.com/Azure/azure-cli-extensions
In line 96 of src/vm-repair/azext_vm_repair/custom.py
we can see the first call of az vm create
.
create_repair_vm_command = 'az vm create -g {g} -n {n} --tag {tag} --image {image} --admin-username {username} --admin-password {password} --public-ip-address {option} --custom-data {cloud_init_script}' \
.format(g=repair_group_name, n=repair_vm_name, tag=resource_tag, image=os_image_urn, username=repair_username, password=repair_password, option=associate_public_ip, cloud_init_script=_get_cloud_init_script())
According to the documentation for az vm create
, the input for --public-ip-address
will be the name of the new IP address. If the input is '""'
then no public IP address is created.
Based on how the variable associate_public_ip
is a boolean, whatever boolean value provided will be the new name of the Public IP.
The fix should simple. Check the variable associate_public_ip
and then create a new variable containing a compliant naming scheme. This fixes the issue with the Public IP naming, but shows a second problem. There is no argument to non-interactively create the repair VM without a Public IP.
Still I fixed the naming issue and created the pull request.
public_ip_name = '""'
if associate_public_ip or yes:
public_ip_name = ('repair-' + vm_name + '_PublicIP')
# Set up base create vm command
if is_linux:
create_repair_vm_command = 'az vm create -g {g} -n {n} --tag {tag} --image {image} --admin-username {username} --admin-password {password} --public-ip-address {option} --custom-data {cloud_init_script}' \
.format(g=repair_group_name, n=repair_vm_name, tag=resource_tag, image=os_image_urn, username=repair_username, password=repair_password, option=public_ip_name, cloud_init_script=_get_cloud_init_script())
The second issue
To fix the second issue, we need to create a new input parameter for the command. --no
seems to make the most sense here. So that is what I went with.
To make a new parameter, we needed to edit the _params.py
file. Specifically fn load_arguments()
. File path: src/vm-repair/azext_vm_repair/_params.py
with self.argument_context('vm repair create') as c:
c.argument('repair_username', help='Admin username for repair VM.')
c.argument('repair_password', help='Admin password for the repair VM.')
c.argument('repair_vm_name', help='Name of repair VM.')
c.argument('copy_disk_name', help='Name of OS disk copy.')
c.argument('repair_group_name', help='Name for new or existing resource group that will contain repair VM.')
c.argument('unlock_encrypted_vm', help='Option to auto-unlock encrypted VMs using current subscription auth.')
c.argument('enable_nested', help='enable nested hyperv.')
c.argument('associate_public_ip', help='Option to create repair vm with public ip')
c.argument('distro', help='Option to create repair vm from a specific linux distro (rhel7|rhel8|suse12|ubuntu20|centos7|oracle7)')
c.argument('yes', help='Option to skip prompt for associating public ip and confirm yes to it in no Tty mode')
c.argument('no', help='Option to skip prompt for associating public ip and confirm no to it in no Tty mode')
Then we needed to ensure we would avoid the prompt for a Public Ip when we provided the new parameter. For this, we would have to edit fn validate_create()
in the file. _validators.py
# Prompt input for public ip usage
if (not namespace.associate_public_ip) and (not namespace.yes) and (not namespace.no):
_prompt_public_ip(namespace)
Only change needed was ` and (not namespace.no)`.
Conclusion
Once this fix was pushed. Public IPs in non-interactive mode were being created in the proper naming scheme. The best part; when we don’t want a Public IP, we are no longer required to have one.
# Creates no Public IP in non-interactive mode (no-tty)
az vm repair create -g cloudj -n cloudj --repair-username *********** --repair-password '***********' --no
# Fixed Public IP naming in non-interactive mode
az vm repair create -g cloudj -n cloudj --repair-username *********** --repair-password '***********'
#> creates public ip called 'repair-cloudj-PublicIP'
Link to branch: https://github.com/jimurrito/azure-cli-extensions/tree/noPip-in-notty-mode
Written by James Immer