From 3214f96afc9744b19144735edc0f9aa22c89afc3 Mon Sep 17 00:00:00 2001 From: Zhiyi Huang <17182306+calvinhzy@users.noreply.github.com> Date: Thu, 26 Feb 2026 11:50:52 +0800 Subject: [PATCH 1/5] use latest azcopy version 10.32.1 on github https://github.com/Azure/azure-storage-azcopy/releases --- .../azure/cli/command_modules/storage/azcopy/util.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py b/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py index 6f6a8c24400..5ce97bad17c 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py +++ b/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py @@ -22,7 +22,7 @@ STORAGE_RESOURCE_ENDPOINT = "https://storage.azure.com" SERVICES = {'blob', 'file'} -AZCOPY_VERSION = '10.13.0' +AZCOPY_VERSION = '10.32.1' class AzCopy: @@ -56,22 +56,22 @@ def install_azcopy(self, install_location): install_dir = os.path.dirname(install_location) if not os.path.exists(install_dir): os.makedirs(install_dir) - base_url = 'https://azcopyvnext-awgzd8g7aagqhzhe.b02.azurefd.net/release20211027/azcopy_{}_{}_{}.{}' + base_url = 'https://github.com/Azure/azure-storage-azcopy/releases/download/v{}/azcopy_{}_{}_{}.{}' if self.system == 'Windows': if platform.machine().endswith('64'): - file_url = base_url.format('windows', 'amd64', AZCOPY_VERSION, 'zip') + file_url = base_url.format(AZCOPY_VERSION, 'windows', 'amd64', AZCOPY_VERSION, 'zip') if _verify_url(file_url) is None: file_url = _verify_url('https://aka.ms/InstallAzCopyForCLIWindowsX64') else: - file_url = base_url.format('windows', '386', AZCOPY_VERSION, 'zip') + file_url = base_url.format(AZCOPY_VERSION, 'windows', '386', AZCOPY_VERSION, 'zip') if _verify_url(file_url) is None: file_url = _verify_url('https://aka.ms/InstallAzCopyForCLIWindows') elif self.system == 'Linux': - file_url = base_url.format('linux', 'amd64', AZCOPY_VERSION, 'tar.gz') + file_url = base_url.format(AZCOPY_VERSION, 'linux', 'amd64', AZCOPY_VERSION, 'tar.gz') if _verify_url(file_url) is None: file_url = _verify_url('https://aka.ms/InstallAzCopyForCLILinux') elif self.system == 'Darwin': - file_url = base_url.format('darwin', 'amd64', AZCOPY_VERSION, 'zip') + file_url = base_url.format(AZCOPY_VERSION, 'darwin', 'amd64', AZCOPY_VERSION, 'zip') if _verify_url(file_url) is None: file_url = _verify_url('https://aka.ms/InstallAzCopyForCLIDarwin') else: From fcc51bb6b9c6a294bea4a0852a6e0b6e5791e45a Mon Sep 17 00:00:00 2001 From: Zhiyi Huang <17182306+calvinhzy@users.noreply.github.com> Date: Fri, 27 Feb 2026 16:39:05 +0800 Subject: [PATCH 2/5] update static download urls to links on azcopy doc https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10 --- .../command_modules/storage/azcopy/util.py | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py b/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py index 5ce97bad17c..be21221db71 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py +++ b/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py @@ -56,36 +56,42 @@ def install_azcopy(self, install_location): install_dir = os.path.dirname(install_location) if not os.path.exists(install_dir): os.makedirs(install_dir) - base_url = 'https://github.com/Azure/azure-storage-azcopy/releases/download/v{}/azcopy_{}_{}_{}.{}' + file_extension = 'zip' if self.system == 'Windows': if platform.machine().endswith('64'): - file_url = base_url.format(AZCOPY_VERSION, 'windows', 'amd64', AZCOPY_VERSION, 'zip') + file_url = 'https://aka.ms/downloadazcopy-v10-windows' if _verify_url(file_url) is None: file_url = _verify_url('https://aka.ms/InstallAzCopyForCLIWindowsX64') else: - file_url = base_url.format(AZCOPY_VERSION, 'windows', '386', AZCOPY_VERSION, 'zip') + file_url = 'https://aka.ms/downloadazcopy-v10-windows-32bit' if _verify_url(file_url) is None: file_url = _verify_url('https://aka.ms/InstallAzCopyForCLIWindows') elif self.system == 'Linux': - file_url = base_url.format(AZCOPY_VERSION, 'linux', 'amd64', AZCOPY_VERSION, 'tar.gz') + file_extension = 'tar.gz' + file_url = 'https://aka.ms/downloadazcopy-v10-linux' if _verify_url(file_url) is None: file_url = _verify_url('https://aka.ms/InstallAzCopyForCLILinux') elif self.system == 'Darwin': - file_url = base_url.format(AZCOPY_VERSION, 'darwin', 'amd64', AZCOPY_VERSION, 'zip') + file_url = 'https://aka.ms/downloadazcopy-v10-mac' if _verify_url(file_url) is None: file_url = _verify_url('https://aka.ms/InstallAzCopyForCLIDarwin') else: - raise CLIError('Azcopy ({}) does not exist.'.format(self.system)) + raise CLIError('Azcopy executable does not exist for {}.'.format(self.system)) + azcopy_install_guide = 'https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10' + if not file_url: + raise CLIError('Error while attempting to download azcopy. You could manually install the azcopy ' + 'executable to {} by following the guide here: {}'.format(install_dir, + azcopy_install_guide)) + from zipfile import BadZipFile try: os.chmod(install_dir, os.stat(install_dir).st_mode | stat.S_IWUSR) - _urlretrieve(file_url, install_location) + _urlretrieve(file_url, install_location, file_extension) os.chmod(install_location, os.stat(install_location).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) - except OSError as err: - azcopy_install_guide = 'https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10' - raise CLIError('Connection error while attempting to download azcopy {}. You could also install the ' - 'specified azcopy version to {} manually following the guide here: {} ' - '({})'.format(AZCOPY_VERSION, install_dir, azcopy_install_guide, err)) + except (OSError, CLIError, BadZipFile) as err: + raise CLIError('Error while attempting to download azcopy from {}. You could manually install the azcopy ' + 'executable to {} by following the guide here: {} ' + '({})'.format(file_url, install_dir, azcopy_install_guide, err)) def check_version(self): try: @@ -226,26 +232,26 @@ def _get_default_install_location(): return install_location -def _urlretrieve(url, install_location): +def _urlretrieve(url, install_location, file_extension): import io logger.warning('Downloading AzCopy from %s', url) - req = urlopen(url) - compressedFile = io.BytesIO(req.read()) - if url.endswith('zip'): + res = urlopen(url) + if res.status != 200: + raise CLIError('Invalid downloading url {}'.format(url)) + compressedFile = io.BytesIO(res.read()) + if file_extension == 'zip': zip_file = zipfile.ZipFile(compressedFile) for fileName in zip_file.namelist(): if fileName.endswith('azcopy') or fileName.endswith('azcopy.exe'): with open(install_location, 'wb') as f: f.write(zip_file.read(fileName)) - elif url.endswith('gz'): + else: import tarfile with tarfile.open(fileobj=compressedFile, mode="r:gz") as tar: for tarinfo in tar: if tarinfo.isfile() and tarinfo.name.endswith('azcopy'): with open(install_location, 'wb') as f: f.write(tar.extractfile(tarinfo).read()) - else: - raise CLIError('Invalid downloading url {}'.format(url)) def _verify_url(url): @@ -253,7 +259,7 @@ def _verify_url(url): try: response = urlopen(url) if response.code == 200: - return response.url + return url return None except (HTTPError, URLError): logger.warning('There is an error downloading from the url: %s', url) From 8140c76826c1ad945a89fe61a99bada75e561889 Mon Sep 17 00:00:00 2001 From: Zhiyi Huang <17182306+calvinhzy@users.noreply.github.com> Date: Fri, 27 Feb 2026 17:01:31 +0800 Subject: [PATCH 3/5] catch boarder exceptions when trying to uncompress zip/tar.gz --- .../command_modules/storage/azcopy/util.py | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py b/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py index be21221db71..82e54f050c3 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py +++ b/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py @@ -7,7 +7,6 @@ import platform import subprocess import datetime -import zipfile import stat from urllib.parse import urlparse from urllib.request import urlopen @@ -82,13 +81,12 @@ def install_azcopy(self, install_location): raise CLIError('Error while attempting to download azcopy. You could manually install the azcopy ' 'executable to {} by following the guide here: {}'.format(install_dir, azcopy_install_guide)) - from zipfile import BadZipFile try: os.chmod(install_dir, os.stat(install_dir).st_mode | stat.S_IWUSR) _urlretrieve(file_url, install_location, file_extension) os.chmod(install_location, os.stat(install_location).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) - except (OSError, CLIError, BadZipFile) as err: + except (OSError, CLIError) as err: raise CLIError('Error while attempting to download azcopy from {}. You could manually install the azcopy ' 'executable to {} by following the guide here: {} ' '({})'.format(file_url, install_dir, azcopy_install_guide, err)) @@ -240,18 +238,25 @@ def _urlretrieve(url, install_location, file_extension): raise CLIError('Invalid downloading url {}'.format(url)) compressedFile = io.BytesIO(res.read()) if file_extension == 'zip': - zip_file = zipfile.ZipFile(compressedFile) - for fileName in zip_file.namelist(): - if fileName.endswith('azcopy') or fileName.endswith('azcopy.exe'): - with open(install_location, 'wb') as f: - f.write(zip_file.read(fileName)) - else: - import tarfile - with tarfile.open(fileobj=compressedFile, mode="r:gz") as tar: - for tarinfo in tar: - if tarinfo.isfile() and tarinfo.name.endswith('azcopy'): + try: + import zipfile + zip_file = zipfile.ZipFile(compressedFile) + for fileName in zip_file.namelist(): + if fileName.endswith('azcopy') or fileName.endswith('azcopy.exe'): with open(install_location, 'wb') as f: - f.write(tar.extractfile(tarinfo).read()) + f.write(zip_file.read(fileName)) + except Exception as ex: # pylint: disable=broad-except + raise CLIError(ex) + else: + try: + import tarfile + with tarfile.open(fileobj=compressedFile, mode="r:gz") as tar: + for tarinfo in tar: + if tarinfo.isfile() and tarinfo.name.endswith('azcopy'): + with open(install_location, 'wb') as f: + f.write(tar.extractfile(tarinfo).read()) + except Exception as ex: # pylint: disable=broad-except + raise CLIError(ex) def _verify_url(url): From 32800e6c25af8816f230a9102ac420992b29bb16 Mon Sep 17 00:00:00 2001 From: Zhiyi Huang <17182306+calvinhzy@users.noreply.github.com> Date: Fri, 27 Feb 2026 17:03:58 +0800 Subject: [PATCH 4/5] keep previous version so users with previous versions are not forced to update --- src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py b/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py index 82e54f050c3..9b01664e466 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py +++ b/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py @@ -21,7 +21,7 @@ STORAGE_RESOURCE_ENDPOINT = "https://storage.azure.com" SERVICES = {'blob', 'file'} -AZCOPY_VERSION = '10.32.1' +AZCOPY_VERSION = '10.13.0' class AzCopy: From 7da212f33109e6bc7892a111700ecd3a5a236efa Mon Sep 17 00:00:00 2001 From: Zhiyi Huang <17182306+calvinhzy@users.noreply.github.com> Date: Tue, 3 Mar 2026 14:18:06 +0800 Subject: [PATCH 5/5] style --- .../azure/cli/command_modules/storage/azcopy/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py b/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py index 9b01664e466..1ab2c2b167c 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py +++ b/src/azure-cli/azure/cli/command_modules/storage/azcopy/util.py @@ -245,7 +245,7 @@ def _urlretrieve(url, install_location, file_extension): if fileName.endswith('azcopy') or fileName.endswith('azcopy.exe'): with open(install_location, 'wb') as f: f.write(zip_file.read(fileName)) - except Exception as ex: # pylint: disable=broad-except + except Exception as ex: # pylint: disable=broad-except raise CLIError(ex) else: try: @@ -255,7 +255,7 @@ def _urlretrieve(url, install_location, file_extension): if tarinfo.isfile() and tarinfo.name.endswith('azcopy'): with open(install_location, 'wb') as f: f.write(tar.extractfile(tarinfo).read()) - except Exception as ex: # pylint: disable=broad-except + except Exception as ex: # pylint: disable=broad-except raise CLIError(ex)