diff --git a/scripts/release/macos/build_binary_tar_gz.py b/scripts/release/macos/build_binary_tar_gz.py new file mode 100644 index 00000000000..b1dc303eeda --- /dev/null +++ b/scripts/release/macos/build_binary_tar_gz.py @@ -0,0 +1,514 @@ +#!/usr/bin/env python3 +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +"""Build Azure CLI tar.gz using Homebrew Python (no bundled Python). + +This build produces a lightweight tar.gz with: +1. Azure CLI packages and dependencies (site-packages) +2. Pre-built native extensions (.so files) - Will be signed & notarized separately. +3. No bundled Python runtime embedded - relies on Homebrew Python or AZ_PYTHON environment variable. +4. An entry script that supports Homebrew Python or AZ_PYTHON + + +Output Structure: +``` +dist/binary_tar_gz/ + azure-cli-{VERSION}-macos-arm64-nopython.tar.gz + azure-cli-{VERSION}-macos-arm64-nopython.tar.gz.sha256 +``` + +Archive Contents: +``` +├── bin/ +│ └── az → ../libexec/bin/az +├── completions/ +│ ├── zsh/ +│ │ └── _az +│ ├── bash/ +│ │ └── az +│ └── fish/ +│ └── az.fish +└── libexec/ + ├── bin/ + │ └── az (entry script - Homebrew or AZ_PYTHON) + ├── lib/ + │ └── python3.13 + │ └── site-packages/ + │ ├── azure/ + │ ├── msal/ + │ └── ... (all CLI packages) + └── README.txt +``` + +Usage: + python build_binary_tar_gz.py --help + python build_binary_tar_gz.py --platform-tag macos-arm64 + python build_binary_tar_gz.py --platform-tag macos-arm64 --output-dir ./dist/custom + python build_binary_tar_gz.py --platform-tag macos-arm64 --keep-temp + +Requirements: + - Homebrew python@x.yz installed: brew install python@x.yz + - Packages are installed into a temporary venv during the build +""" + +from __future__ import annotations + +import argparse +import hashlib +import os +import shutil +import subprocess +import sys +import tarfile +import tempfile +from pathlib import Path + +# Azure CLI project structure +PROJECT_ROOT = Path(__file__).resolve().parents[3] +SRC_DIR = PROJECT_ROOT / "src" +AZURE_CLI_CORE_DIR = SRC_DIR / "azure-cli-core" +REQUIREMENTS_FILE = SRC_DIR / "azure-cli" / "requirements.py3.MacOS.txt" + +# Package configuration +APP_NAME = "azure-cli" +CLI_EXECUTABLE_NAME = "az" +TARBALL_NAME_TEMPLATE_DEFAULT = "{APP_NAME}-{VERSION}-{PLATFORM_TAG}-nopython.tar.gz" + +# Python version we're building for (must match Homebrew python@X.Y) +# Can be overridden via --python-version CLI arg or PYTHON_MAJOR_MINOR env var +PYTHON_MAJOR_MINOR = os.environ.get("PYTHON_MAJOR_MINOR", "3.13") +PYTHON_BIN = "python3" +TEMPLATE_DIR = Path(__file__).resolve().parent / "templates" +LAUNCHER_TEMPLATE_PATH = TEMPLATE_DIR / "az_launcher.sh.in" +README_TEMPLATE_PATH = TEMPLATE_DIR / "README.txt.in" + + +class BuildError(RuntimeError): + """Raised when the packaging pipeline fails.""" + + +def get_cli_version() -> str: + """Get Azure CLI version from source.""" + version_file = AZURE_CLI_CORE_DIR / "azure" / "cli" / "core" / "__init__.py" + if not version_file.exists(): + raise BuildError(f"Version file not found: {version_file}") + + content = version_file.read_text(encoding="utf-8") + for line in content.splitlines(): + if line.startswith("__version__"): + version = line.split("=")[1].strip().strip("'\"") + return version + + raise BuildError(f"Could not find __version__ in {version_file}") + + +def _is_python_version(python_path: Path, major_minor: str) -> bool: + """Return True when python_path matches the requested major.minor.""" + try: + result = subprocess.run( + [str(python_path), "-c", "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"], + capture_output=True, + text=True, + check=True, + ) + return result.stdout.strip() == major_minor + except subprocess.CalledProcessError: + return False + + +def _render_template(template: str, values: dict[str, str]) -> str: + """Replace template placeholders using the provided values.""" + rendered = template + for key, value in values.items(): + rendered = rendered.replace(f"{{{key}}}", value) + return rendered + + +def _load_template(path: Path) -> str: + """Load a template file from disk.""" + if not path.exists(): + raise BuildError(f"Template not found: {path}") + return path.read_text(encoding="utf-8") + + +def find_homebrew_python() -> Path: + """Find necessary Homebrew Python x.yz installation.""" + candidates = [ + Path(f"/opt/homebrew/opt/python@{PYTHON_MAJOR_MINOR}/libexec/bin/python3"), + Path(f"/usr/local/opt/python@{PYTHON_MAJOR_MINOR}/libexec/bin/python3"), + Path(f"/opt/homebrew/bin/python{PYTHON_MAJOR_MINOR}"), + Path(f"/usr/local/bin/python{PYTHON_MAJOR_MINOR}"), + ] + + for python_path in candidates: + if python_path.exists() and _is_python_version(python_path, PYTHON_MAJOR_MINOR): + print(f"Found Homebrew Python: {python_path}") + return python_path + + try: + result = subprocess.run( + ["brew", "--prefix", f"python@{PYTHON_MAJOR_MINOR}"], + capture_output=True, + text=True, + check=True, + ) + prefix = Path(result.stdout.strip()) + python_path = prefix / "libexec" / "bin" / "python3" + if python_path.exists() and _is_python_version(python_path, PYTHON_MAJOR_MINOR): + print(f"Found Homebrew Python via brew --prefix: {python_path}") + return python_path + except (subprocess.CalledProcessError, FileNotFoundError): + pass + + raise BuildError(f"Homebrew Python {PYTHON_MAJOR_MINOR} not found. Install it with: brew install python@{PYTHON_MAJOR_MINOR}") + + +def create_venv(python_path: Path, venv_dir: Path) -> Path: + """Create a virtual environment using Homebrew Python.""" + print("\n=== Creating virtual environment ===") + print(f"Python: {python_path}") + print(f"Venv: {venv_dir}") + + subprocess.run([str(python_path), "-m", "venv", str(venv_dir)], check=True) + + venv_python = venv_dir / "bin" / "python3" + + subprocess.run([str(venv_python), "-m", "pip", "install", "--upgrade", "pip"], check=True) + + return venv_python + + +def install_azure_cli(venv_python: Path) -> None: + """Install Azure CLI components from source, then install pinned dependencies. + + Mirrors the run.sh approach: + 1. Install all src packages with --no-deps (local source code takes precedence) + 2. Install pinned dependencies from requirements.py3.Darwin.txt + """ + # Step 1: install every package found under SRC_DIR from source, without pulling + # transitive deps from PyPI (--no-deps). This ensures the locally-built wheels + # are used for the CLI components themselves. + print("\n=== Step 1: Installing Azure CLI components from source (--no-deps) ===") + + # Install from local source first so in-tree patches are picked up + components = [ + SRC_DIR / "azure-cli-telemetry", + SRC_DIR / "azure-cli-core", + SRC_DIR / "azure-cli", + ] + + for component in components: + print(f" Installing {component.name} (--no-deps)...") + subprocess.run( + [str(venv_python), "-m", "pip", "install", "--no-deps", str(component)], + check=True, + ) + + # Step 2: install all pinned transitive dependencies so every package + # resolves to the exact version recorded in the requirements file. + print("\n=== Step 2: Installing pinned dependencies from requirements file ===") + + if not REQUIREMENTS_FILE.exists(): + raise BuildError(f"Requirements file not found: {REQUIREMENTS_FILE}") + + print(f" Using: {REQUIREMENTS_FILE}") + subprocess.run( + [str(venv_python), "-m", "pip", "install", "-r", str(REQUIREMENTS_FILE)], + check=True, + ) + + print("\nVerifying Azure CLI installation...") + subprocess.run([str(venv_python), "-m", "azure.cli", "--version"], check=True) + + +def create_install_structure(venv_dir: Path, install_dir: Path, version: str, platform_tag: str) -> None: + """Create the final installation directory structure.""" + print("\n=== Creating installation structure ===") + + libexec_dir = install_dir / "libexec" + bin_dir = install_dir / "bin" + libexec_bin = libexec_dir / "bin" + libexec_lib = libexec_dir / "lib" / f"python{PYTHON_MAJOR_MINOR}" + site_packages = libexec_lib / "site-packages" + + for d in [bin_dir, libexec_bin, site_packages]: + d.mkdir(parents=True, exist_ok=True) + + venv_site_packages = venv_dir / "lib" / f"python{PYTHON_MAJOR_MINOR}" / "site-packages" + print(f"Copying site-packages from: {venv_site_packages}") + print(f" to: {site_packages}") + + shutil.copytree(venv_site_packages, site_packages, dirs_exist_ok=True) + + _create_launcher_script(bin_dir=libexec_bin, python_version=PYTHON_MAJOR_MINOR) + + az_symlink = bin_dir / CLI_EXECUTABLE_NAME + az_target = Path("..") / "libexec" / "bin" / CLI_EXECUTABLE_NAME + az_symlink.symlink_to(az_target) + print(f"Created symlink: {az_symlink} -> {az_target}") + + _create_readme(install_dir=libexec_dir, version=version, platform_tag=platform_tag) + + _generate_shell_completions(venv_dir=venv_dir, install_dir=install_dir) + + _cleanup_bytecode(root=site_packages) + + _report_sizes(install_dir=install_dir) + + +def _create_launcher_script(bin_dir: Path, python_version: str) -> None: + """Create az launcher script that supports Homebrew and offline installs.""" + template = _load_template(path=LAUNCHER_TEMPLATE_PATH) + launcher_script = _render_template( + template=template, + values={ + "PYTHON_MAJOR_MINOR": python_version, + "PYTHON_BIN": PYTHON_BIN, + }, + ) + + az_path = bin_dir / CLI_EXECUTABLE_NAME + az_path.write_text(launcher_script, encoding="utf-8") + az_path.chmod(0o755) + print(f"Created launcher script: {az_path}") + + +def _create_readme(install_dir: Path, version: str, platform_tag: str) -> None: + """Create README.txt.""" + template = _load_template(path=README_TEMPLATE_PATH) + readme_content = _render_template( + template=template, + values={ + "AZURE_CLI_VERSION": version, + "PLATFORM_TAG": platform_tag, + "PYTHON_MAJOR_MINOR": PYTHON_MAJOR_MINOR, + }, + ) + + readme_path = install_dir / "README.txt" + readme_path.write_text(readme_content, encoding="utf-8") + print(f"Created README: {readme_path}") + + +def _generate_shell_completions(venv_dir: Path, install_dir: Path) -> None: + """Generate shell completion files using argcomplete from the build venv.""" + print("\n=== Generating shell completions ===") + + generator = venv_dir / "bin" / "register-python-argcomplete" + if not generator.exists(): + raise BuildError(f"Completion generator not found: {generator}") + + completion_specs = [ + ("zsh", install_dir / "completions" / "zsh" / "_az"), + ("bash", install_dir / "completions" / "bash" / "az"), + ("fish", install_dir / "completions" / "fish" / "az.fish"), + ] + + for shell, output_path in completion_specs: + output_path.parent.mkdir(parents=True, exist_ok=True) + result = subprocess.run( + [str(generator), CLI_EXECUTABLE_NAME, "--shell", shell], + capture_output=True, + text=True, + check=True, + ) + output_path.write_text(result.stdout, encoding="utf-8") + print(f"Generated {shell} completion: {output_path}") + + +def _cleanup_bytecode(root: Path) -> None: + """Remove __pycache__ directories and .pyc files.""" + print("\n=== Cleaning bytecode cache ===") + + removed_count = 0 + for path in sorted(root.rglob("*.pyc"), reverse=True): + path.unlink() + removed_count += 1 + + for path in sorted(root.rglob("__pycache__"), reverse=True): + if path.is_dir(): + try: + shutil.rmtree(path) + removed_count += 1 + except OSError: + pass + + print(f"Removed {removed_count} bytecode files/directories") + + +def _report_sizes(install_dir: Path) -> None: + """Report sizes of components.""" + print("\n=== Size Report ===") + + def get_size(path: Path) -> int: + if path.is_file(): + return path.stat().st_size + total = 0 + for p in path.rglob("*"): + if p.is_file(): + total += p.stat().st_size + return total + + def fmt_size(size: int) -> str: + if size >= 1024 * 1024: + return f"{size / (1024 * 1024):.1f} MB" + if size >= 1024: + return f"{size / 1024:.1f} KB" + return f"{size} B" + + print(f" Total: {fmt_size(get_size(install_dir))}") + + for subdir in [ + "bin", + "libexec/bin", + "libexec/lib", + "completions/zsh", + "completions/bash", + "completions/fish", + ]: + path = install_dir / subdir + if path.exists(): + print(f" {subdir}: {fmt_size(get_size(path))}") + + so_files = list((install_dir / "libexec" / "lib").rglob("*.so")) + print(f"\n Native extensions (.so): {len(so_files)} files") + for so_file in sorted(so_files)[:10]: + rel_path = so_file.relative_to(install_dir) + print(f" - {rel_path.name}: {fmt_size(get_size(so_file))}") + if len(so_files) > 10: + print(f" ... and {len(so_files) - 10} more") + + +def create_tarball( + install_dir: Path, + output_dir: Path, + version: str, + platform_tag: str, + tarball_name_template: str, +) -> Path: + """Create the final tar.gz archive.""" + print("\n=== Creating tarball ===") + + output_dir.mkdir(parents=True, exist_ok=True) + + tarball_name = tarball_name_template.format( + APP_NAME=APP_NAME, + VERSION=version, + PLATFORM_TAG=platform_tag, + ) + tarball_path = output_dir / tarball_name + + with tarfile.open(tarball_path, "w:gz") as tar: + for item in install_dir.iterdir(): + arcname = item.name + tar.add(item, arcname=arcname) + + print(f"Created: {tarball_path}") + print(f"Size: {tarball_path.stat().st_size / (1024 * 1024):.1f} MB") + + sha256_path = Path(str(tarball_path) + ".sha256") + sha256_hash = hashlib.sha256() + with open(tarball_path, "rb") as f: + for chunk in iter(lambda: f.read(8192), b""): + sha256_hash.update(chunk) + + checksum = sha256_hash.hexdigest() + sha256_path.write_text(f"{checksum} {tarball_name}\n") + print(f"SHA256: {checksum}") + + return tarball_path + + +def main() -> int: + """Main entry point.""" + parser = argparse.ArgumentParser( + description="Build Azure CLI tar.gz using Homebrew Python (no bundled Python)" + ) + parser.add_argument( + "--platform-tag", required=True, choices=["macos-arm64", "macos-x86_64"], help="Platform tag for the build" + ) + parser.add_argument( + "--output-dir", + type=Path, + default=PROJECT_ROOT / "dist" / "binary_tar_gz", + help="Output directory for the tarball", + ) + parser.add_argument( + "--tarball-name-template", + default=TARBALL_NAME_TEMPLATE_DEFAULT, + help="Tarball filename template with {APP_NAME}, {VERSION} (from azure-cli-core), {PLATFORM_TAG}", + ) + parser.add_argument("--keep-temp", action="store_true", help="Keep temporary build directory for debugging") + + args = parser.parse_args() + + print("=" * 70) + print("Azure CLI Tarball Builder (Homebrew Python)") + print("=" * 70) + print(f"Platform: {args.platform_tag}") + print(f"Output: {args.output_dir}") + print() + + try: + version = get_cli_version() + print(f"Azure CLI version: {version}") + + python_path = find_homebrew_python() + + with tempfile.TemporaryDirectory(prefix="azure-cli-build-") as temp_dir: + temp_path = Path(temp_dir) + venv_dir = temp_path / "venv" + install_dir = temp_path / "install" + + venv_python = create_venv(python_path=python_path, venv_dir=venv_dir) + install_azure_cli(venv_python=venv_python) + + create_install_structure( + venv_dir=venv_dir, + install_dir=install_dir, + version=version, + platform_tag=args.platform_tag, + ) + + tarball_path = create_tarball( + install_dir=install_dir, + output_dir=args.output_dir, + version=version, + platform_tag=args.platform_tag, + tarball_name_template=args.tarball_name_template, + ) + + if args.keep_temp: + print(f"\nTemp directory preserved: {temp_path}") + preserved_dir = args.output_dir / "build-temp" + if preserved_dir.exists(): + shutil.rmtree(preserved_dir) + shutil.copytree(temp_path, preserved_dir) + print(f"Copied to: {preserved_dir}") + + print("\n" + "=" * 70) + print("BUILD SUCCESSFUL") + print("=" * 70) + print(f"Tarball: {tarball_path}") + print("\nTo test locally:") + print(f" tar xzf {tarball_path}") + print(" ./libexec/bin/az --version") + print() + + return 0 + + except BuildError as e: + print(f"\nBUILD FAILED: {e}", file=sys.stderr) + return 1 + except Exception as e: + print(f"\nUNEXPECTED ERROR: {e}", file=sys.stderr) + import traceback + + traceback.print_exc() + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/azure-cli-core/azure/cli/core/_profile.py b/src/azure-cli-core/azure/cli/core/_profile.py index c1950d26c0e..325046135a4 100644 --- a/src/azure-cli-core/azure/cli/core/_profile.py +++ b/src/azure-cli-core/azure/cli/core/_profile.py @@ -908,8 +908,10 @@ def _create_identity_instance(cli_ctx, authority, tenant_id=None, client_id=None # On Windows, use core.enable_broker_on_windows=false to disable broker (WAM) for authentication. enable_broker_on_windows = cli_ctx.config.getboolean('core', 'enable_broker_on_windows', fallback=True) + # On macOS, use core.enable_broker_on_mac=false to disable broker for authentication. + enable_broker_on_mac = cli_ctx.config.getboolean('core', 'enable_broker_on_mac', fallback=True) from .telemetry import set_broker_info - set_broker_info(enable_broker_on_windows) + set_broker_info(enable_broker_on_windows, enable_broker_on_mac) # PREVIEW: In Azure Stack environment, use core.instance_discovery=false to disable MSAL's instance discovery. instance_discovery = cli_ctx.config.getboolean('core', 'instance_discovery', True) @@ -918,4 +920,5 @@ def _create_identity_instance(cli_ctx, authority, tenant_id=None, client_id=None encrypt=encrypt, use_msal_http_cache=use_msal_http_cache, enable_broker_on_windows=enable_broker_on_windows, + enable_broker_on_mac=enable_broker_on_mac, instance_discovery=instance_discovery) diff --git a/src/azure-cli-core/azure/cli/core/auth/identity.py b/src/azure-cli-core/azure/cli/core/auth/identity.py index 91629e89441..229731fd0d3 100644 --- a/src/azure-cli-core/azure/cli/core/auth/identity.py +++ b/src/azure-cli-core/azure/cli/core/auth/identity.py @@ -58,7 +58,7 @@ class Identity: # pylint: disable=too-many-instance-attributes _service_principal_store_instance = None def __init__(self, authority, tenant_id=None, client_id=None, encrypt=False, use_msal_http_cache=True, - enable_broker_on_windows=None, instance_discovery=None): + enable_broker_on_windows=None, enable_broker_on_mac=None, instance_discovery=None): """ :param authority: Authentication authority endpoint. For example, - AAD: https://login.microsoftonline.com @@ -74,6 +74,7 @@ def __init__(self, authority, tenant_id=None, client_id=None, encrypt=False, use self._encrypt = encrypt self._use_msal_http_cache = use_msal_http_cache self._enable_broker_on_windows = enable_broker_on_windows + self._enable_broker_on_mac = enable_broker_on_mac self._instance_discovery = instance_discovery # Build the authority in MSAL style @@ -111,9 +112,10 @@ def _msal_app_kwargs(self): @property def _msal_public_app_kwargs(self): """kwargs for creating PublicClientApplication.""" - # enable_broker_on_windows can only be used on PublicClientApplication. + # enable_broker_on_windows and enable_broker_on_mac can only be used on PublicClientApplication. return {**self._msal_app_kwargs, "enable_broker_on_windows": self._enable_broker_on_windows, + "enable_broker_on_mac": self._enable_broker_on_mac, "enable_pii_log": True} @property diff --git a/src/azure-cli-core/azure/cli/core/telemetry.py b/src/azure-cli-core/azure/cli/core/telemetry.py index 714bd751263..2bddd70cd86 100644 --- a/src/azure-cli-core/azure/cli/core/telemetry.py +++ b/src/azure-cli-core/azure/cli/core/telemetry.py @@ -78,6 +78,7 @@ def __init__(self, correlation_id=None, application=None): self.user_agent = None # authentication-related self.enable_broker_on_windows = None + self.enable_broker_on_mac = None self.msal_telemetry = None self.login_experience_v2 = None @@ -237,6 +238,7 @@ def _get_azure_cli_properties(self): set_custom_properties(result, 'SecretNames', ','.join(self.secret_names or [])) # authentication-related set_custom_properties(result, 'EnableBrokerOnWindows', str(self.enable_broker_on_windows)) + set_custom_properties(result, 'EnableBrokerOnMac', str(self.enable_broker_on_mac)) set_custom_properties(result, 'MsalTelemetry', self.msal_telemetry) set_custom_properties(result, 'LoginExperienceV2', str(self.login_experience_v2)) @@ -484,9 +486,10 @@ def set_region_identified(region_input, region_identified): # region authentication-related @decorators.suppress_all_exceptions() -def set_broker_info(enable_broker_on_windows): - # Log the value of `enable_broker_on_windows` +def set_broker_info(enable_broker_on_windows, enable_broker_on_mac=None): + # Log the value of `enable_broker_on_windows` and `enable_broker_on_mac` _session.enable_broker_on_windows = enable_broker_on_windows + _session.enable_broker_on_mac = enable_broker_on_mac @decorators.suppress_all_exceptions() diff --git a/src/azure-cli-core/setup.py b/src/azure-cli-core/setup.py index 90bec1c5f19..b2da9152aa4 100644 --- a/src/azure-cli-core/setup.py +++ b/src/azure-cli-core/setup.py @@ -55,8 +55,8 @@ 'knack~=0.11.0', 'microsoft-security-utilities-secret-masker~=1.0.0b4', 'msal-extensions==1.2.0', - 'msal[broker]==1.35.0b1; sys_platform == "win32"', - 'msal==1.35.0b1; sys_platform != "win32"', + 'msal[broker]==1.35.0b1; sys_platform == "win32" or sys_platform == "darwin"', + 'msal==1.35.0b1; sys_platform != "win32" and sys_platform != "darwin"', 'packaging>=20.9', 'pkginfo>=1.5.0.1', # psutil can't install on cygwin: https://github.com/Azure/azure-cli/issues/9399 diff --git a/src/azure-cli/requirements.py3.Darwin.txt b/src/azure-cli/requirements.py3.Darwin.txt index 417ba55963d..c04bdda063e 100644 --- a/src/azure-cli/requirements.py3.Darwin.txt +++ b/src/azure-cli/requirements.py3.Darwin.txt @@ -109,6 +109,8 @@ jmespath==0.9.5 jsondiff==2.0.0 knack==0.11.0 msal-extensions==1.2.0 +# macOS/Darwin: standard msal without broker support. (this is for homebrew-core parallel run for next few months). +# For broker support, use requirements.py3.MacOS.txt msal==1.35.0b1 msrest==0.7.1 oauthlib==3.2.2 diff --git a/src/azure-cli/requirements.py3.MacOS.txt b/src/azure-cli/requirements.py3.MacOS.txt new file mode 100644 index 00000000000..e24d6f47987 --- /dev/null +++ b/src/azure-cli/requirements.py3.MacOS.txt @@ -0,0 +1,138 @@ +antlr4-python3-runtime==4.13.1 +applicationinsights==0.11.9 +argcomplete==3.5.2 +asn1crypto==0.24.0 +azure-appconfiguration==1.7.2 +azure-batch==15.0.0b1 +azure-cli-core==2.84.0 +azure-cli-telemetry==1.1.0 +azure-cli==2.84.0 +azure-common==1.1.22 +azure-core==1.38.0 +azure-cosmos==3.2.0 +azure-data-tables==12.4.0 +azure-datalake-store==1.0.1 +azure-keyvault-administration==4.4.0 +azure-keyvault-certificates==4.7.0 +azure-keyvault-keys==4.11.0 +azure-keyvault-secrets==4.7.0 +azure-keyvault-securitydomain==1.0.0b1 +azure-mgmt-advisor==9.0.0 +azure-mgmt-apimanagement==4.0.0 +azure-mgmt-appconfiguration==6.0.0b1 +azure-mgmt-appcontainers==2.0.0 +azure-mgmt-applicationinsights==1.0.0 +azure-mgmt-authorization==5.0.0b1 +azure-mgmt-batch==17.3.0 +azure-mgmt-batchai==7.0.0b1 +azure-mgmt-billing==6.0.0 +azure-mgmt-botservice==2.0.0b3 +azure-mgmt-cdn==12.0.0 +azure-mgmt-cognitiveservices==14.1.0 +azure-mgmt-compute==34.1.0 +azure-mgmt-containerinstance==10.2.0b1 +azure-mgmt-containerregistry==14.1.0b1 +azure-mgmt-containerservice==40.2.0 +azure-mgmt-core==1.6.0 +azure-mgmt-cosmosdb==9.9.0 +azure-mgmt-datalake-nspkg==3.0.1 +azure-mgmt-datalake-store==1.1.0b1 +azure-mgmt-datamigration==10.0.0 +azure-mgmt-eventgrid==10.2.0b2 +azure-mgmt-eventhub==12.0.0b1 +azure-mgmt-extendedlocation==1.0.0b2 +azure-mgmt-hdinsight==9.1.0b2 +azure-mgmt-imagebuilder==1.3.0 +azure-mgmt-iotcentral==10.0.0b1 +azure-mgmt-iothub==5.0.0b1 +azure-mgmt-iothubprovisioningservices==1.1.0 +azure-mgmt-keyvault==13.0.0 +azure-mgmt-loganalytics==13.0.0b4 +azure-mgmt-managementgroups==1.0.0 +azure-mgmt-maps==2.0.0 +azure-mgmt-marketplaceordering==1.1.0 +azure-mgmt-media==9.0.0 +azure-mgmt-monitor==7.0.0b1 +azure-mgmt-msi==7.1.0 +azure-mgmt-netapp==10.1.0 +azure-mgmt-policyinsights==1.1.0b4 +azure-mgmt-postgresqlflexibleservers==3.0.0b1 +azure-mgmt-privatedns==1.0.0 +azure-mgmt-rdbms==10.2.0b17 +azure-mgmt-mysqlflexibleservers==1.1.0b2 +azure-mgmt-recoveryservices==4.0.0 +azure-mgmt-recoveryservicesbackup==9.2.0 +azure-mgmt-redhatopenshift~=3.0.0 +azure-mgmt-redis==14.5.0 +azure-mgmt-resource==24.0.0 +azure-mgmt-resource-deployments==1.0.0b1 +azure-mgmt-resource-deploymentscripts==1.0.0b1 +azure-mgmt-resource-deploymentstacks==1.0.0 +azure-mgmt-resource-templatespecs==1.0.0b1 +azure-mgmt-search==9.0.0 +azure-mgmt-security==6.0.0 +azure-mgmt-servicebus==10.0.0b1 +azure-mgmt-servicefabric==2.1.0 +azure-mgmt-servicefabricmanagedclusters==2.1.0b1 +azure-mgmt-servicelinker==1.2.0b3 +azure-mgmt-sql==4.0.0b22 +azure-mgmt-signalr==2.0.0b2 +azure-mgmt-sqlvirtualmachine==1.0.0b5 +azure-mgmt-storage==24.0.0 +azure-mgmt-synapse==2.1.0b5 +azure-mgmt-trafficmanager==1.0.0 +azure-mgmt-web==9.0.0 +azure-monitor-query==1.2.0 +azure-nspkg==3.0.2 +azure-storage-common==1.4.2 +azure-storage-blob==12.28.0b1 +azure-storage-file-datalake==12.23.0b1 +azure-storage-file-share==12.24.0b1 +azure-storage-queue==12.15.0b1 +azure-synapse-accesscontrol==0.5.0 +azure-synapse-artifacts==0.21.0 +azure-synapse-managedprivateendpoints==0.4.0 +azure-synapse-spark==0.7.0 +bcrypt==3.2.0 +certifi==2024.7.4 +cffi==2.0.0 +chardet==5.2.0 +colorama==0.4.6 +cryptography==44.0.1 +fabric==3.2.2 +humanfriendly==10.0 +idna==3.7 +invoke==2.2.0 +isodate==0.6.1 +javaproperties==0.5.1 +jmespath==0.9.5 +jsondiff==2.0.0 +knack==0.11.0 +msal-extensions==1.2.0 +msal[broker]==1.35.0b1 +msrest==0.7.1 +oauthlib==3.2.2 +packaging==25.0 +paramiko==3.5.0 +pbr==7.0.3 +pkginfo==1.8.2 +portalocker==2.3.2 +psutil==6.1.0 +pycomposefile==0.0.34 +PyGithub==1.55 +PyJWT==2.10.1 +PyNaCl==1.6.2 +pyOpenSSL==25.0.0 +PySocks==1.7.1 +python-dateutil==2.8.0 +requests-oauthlib==1.2.0 +requests==2.32.4 +scp==0.13.2 +semver==3.0.4 +six==1.16.0 +sshtunnel==0.1.5 +tabulate==0.8.9 +urllib3==2.6.3 +wcwidth==0.1.7 +websocket-client==1.8.0 +xmltodict==0.12.0