How to make sure that VM Size used does have a sufficient available quota for usage in the region for the subscription id used.

Prem Jha 140 Reputation points
2025-10-17T07:34:08.11+00:00

I am planning to write a clean function effective function that can help me achieve the below tasks using Azure Python SDK Library Version 01-06-2018.

  1. pick only the general Purpose family VM Sizes.
  2. If hostEncryption flag is true then filter out only those general Purpose family VM Sizes that supports the 'encryption at host' feature else keep the already filtered out general Purpose family VM Sizes which we did in step 1
  3. Once we have the filtered VM Sizes as per the flag passed then check if the Subscription have sufficient available quota for that selected VM Size and that should be able to boot hypervisor gen1 VMs so that it does not gets errored out at the later stages of the AP creation SKD Library function call.
  4. Once all this is done than return the finally selected list of filtered VM Sizes. Help me with the right set of SDK Libraries that could help me achieve the above task, as I tried multiple times but the quota check fails most of the time and also error saying that it is not able to boot hypervisor gen1 VMs so that it does errored out at the and sometimes the correct set of VM Size are not returned.

TL:DR;

In short, how can I list only those VM Sizes that are available to use under my subscription, in the passed region and those who supports Hyper v1 images creation and who have cores quota limit available.

Azure Cost Management
Azure Cost Management
A Microsoft offering that enables tracking of cloud usage and expenditures for Azure and other cloud providers.
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Pradeep Kommaraju 940 Reputation points Microsoft Employee
    2025-10-17T19:30:46.11+00:00

    HI Prem,

    Easiest way it to access through portal

    To check if the VM size has sufficient quota:

    1. In the Azure Portal, go to Subscriptions.
    2. Select the relevant subscription.
    3. In the left pane, choose Usage + quotas.
    4. Set the Region and Resource type to Virtual Machines.
    5. Review the vCPU quota and current usage for the desired VM size family.

    If the quota is insufficient, select Request Increase directly from the same page to raise the limit.

    Feel free to get back if you have more questions, also don't forget to accept the answer if this was helpful.

    Thanks

    0 comments No comments

  2. Divyesh Govaerdhanan 9,355 Reputation points
    2025-10-17T19:55:56.3366667+00:00

    Hello,

    Welcome to Microsoft Q&A,

    I have attached the Python script below, which includes optional parameters that you can use to filter the VM sizes according to your needs.

    # Lists VM sizes you can actually deploy in a region for a subscription.
    # No Microsoft.Quota dependency; uses Compute Usage for quotas (stable).
    # Filters:
    #  - Subscription availability (no NotAvailableForSubscription in region)
    #  - Optional: Encryption-at-Host support
    #  - Optional: Hyper-V Gen1 support
    #  - Optional: Restrict to selected families (e.g., B/D/F)
    # Quota checks:
    #  - Family vCPU headroom (if a family row exists)
    #  - Regional total vCPU headroom (if present)
    # Graceful fallback:
    #  - If a family quota row is missing, keeps the size unless --strict-family-quota is set.
    
    import argparse
    import re
    import sys
    from typing import Dict, List, Optional, Tuple
    
    from azure.identity import DefaultAzureCredential
    from azure.mgmt.compute import ComputeManagementClient
    from azure.core.exceptions import HttpResponseError
    
    # ---------- helpers ----------
    def norm(s: str) -> str:
        return re.sub(r"[^a-z0-9]", "", (s or "").lower())
    
    def cap(capabilities, name: str, default=None):
        for c in capabilities or []:
            if (c.name or "").lower() == name.lower():
                return c.value
        return default
    
    def is_restricted_in_loc(sku, loc: str) -> bool:
        for r in (sku.restrictions or []):
            if r.reason_code == "NotAvailableForSubscription":
                info = r.restriction_info
                if info and info.locations and loc in info.locations:
                    return True
        return False
    
    def parse_args():
        p = argparse.ArgumentParser(description="List deployable VM sizes by capabilities & quota.")
        p.add_argument("--subscription", required=True, help="Subscription ID")
        p.add_argument("--location", required=True, help="Azure region, e.g., eastus")
        p.add_argument("--instances", type=int, default=1, help="How many VMs you plan to deploy")
        p.add_argument("--families", nargs="*", default=None,
                       help="Optional family prefixes to keep (e.g., B D F). Omit to allow all.")
        p.add_argument("--require-eah", action="store_true", help="Require Encryption-at-Host support")
        p.add_argument("--require-gen1", action="store_true", help="Require Hyper-V Gen1 support")
        p.add_argument("--strict-family-quota", action="store_true",
                       help="Drop sizes with no family quota row (default: keep, relying on regional)")
        p.add_argument("--verbose", action="store_true", help="Print debug summary")
        return p.parse_args()
    
    # ---------- quota via Compute Usage (stable; no RP registration needed) ----------
    def get_usage_quota(compute: ComputeManagementClient, location: str
                        ) -> Tuple[Tuple[Optional[int], Optional[int]], Dict[str, Tuple[int, int, str]]]:
        """
        Returns:
          regional: (used, limit) for total regional vCPUs (if found; otherwise (None,None))
          family_quota: dict key=normalized usage name ("standard<family>family") -> (used, limit, localized_name)
        """
        usage = list(compute.usage.list(location))
        regional_used = regional_limit = None
        family_map: Dict[str, Tuple[int, int, str]] = {}
    
        for u in usage:
            name_val = (getattr(u.name, "value", None) or "").lower()
            name_loc = (getattr(u.name, "localized_value", None) or "").lower()
            # Regional total: match “Total Regional vCPUs” or generic cores
            if "total" in name_loc and ("vcpu" in name_loc or "core" in name_loc):
                regional_used = u.current_value
                regional_limit = u.limit
            elif "cores" == name_val and ("total" in name_loc or "regional" in name_loc):
                regional_used = u.current_value
                regional_limit = u.limit
            # Family rows usually look like "Standard Dv5 Family vCPUs"
            if "family" in name_loc and ("vcpu" in name_loc or "core" in name_loc):
                k = norm(getattr(u.name, "value", "") or u.name.localized_value)
                family_map[k] = (u.current_value, u.limit, u.name.localized_value or u.name.value)
    
        return (regional_used, regional_limit), family_map
    
    def find_family_quota_row(family_map: Dict[str, Tuple[int,int,str]], family: str
                              ) -> Optional[Tuple[int,int,str]]:
        """
        Try exact normalized key; if not found, try fuzzy match against localized names.
        """
        exact_key = f"standard{norm(family)}family"
        if exact_key in family_map:
            return family_map[exact_key]
        # fuzzy: some families differ slightly (e.g., Dsv5 vs Dasv5 may share a family row naming)
        f_norm = norm(family)
        for k, v in family_map.items():
            if f_norm in k:
                return v
        return None
    
    # ---------- main ----------
    def main():
        args = parse_args()
        cred = DefaultAzureCredential()
        compute = ComputeManagementClient(cred, args.subscription)
    
        debug = {"skus_total": 0, "candidates_after_caps": 0, "restricted": 0,
                 "eah_filtered": 0, "gen1_filtered": 0, "fam_filtered": 0, "no_cores": 0,
                 "quota_family_missing": 0, "quota_family_blocked": 0, "quota_regional_blocked": 0}
    
        # 1) Gather candidate SKUs by capabilities & subscription availability
        candidates: List[dict] = []
        try:
            for sku in compute.resource_skus.list(filter=f"location eq '{args.location}'"):
                debug["skus_total"] += 1
                if sku.resource_type != "virtualMachines" or args.location not in (sku.locations or []):
                    continue
                if is_restricted_in_loc(sku, args.location):
                    debug["restricted"] += 1
                    continue
    
                fam = sku.family or ""
                if args.families:
                    if not any(fam.upper().startswith(p.upper()) for p in args.families):
                        debug["fam_filtered"] += 1
                        continue
    
                if args.require_eah:
                    if (cap(sku.capabilities, "EncryptionAtHostSupported", "False") or "False").lower() != "true":
                        debug["eah_filtered"] += 1
                        continue
    
                if args.require_gen1:
                    if "v1" not in (cap(sku.capabilities, "HyperVGenerations", "") or "").lower():
                        debug["gen1_filtered"] += 1
                        continue
    
                candidates.append({"name": sku.name, "family": fam})
        except HttpResponseError as e:
            print(f"Failed to list SKUs in {args.location}: {e}", file=sys.stderr)
            sys.exit(1)
    
        # 2) Add exact vCPU per size and drop unknown
        cores_by_size = {s.name: s.number_of_cores for s in compute.virtual_machine_sizes.list(args.location)}
        for c in candidates:
            c["vcpus"] = cores_by_size.get(c["name"])
        candidates = [c for c in candidates if c["vcpus"]]
        debug["candidates_after_caps"] = len(candidates)
    
        if not candidates:
            print(f"No candidate VM sizes after capability/availability filtering in {args.location}.")
            if args.verbose:
                print("[DEBUG]", debug)
            sys.exit(2)
    
        # 3) Quota headroom (family + regional if available)
        regional, family_map = get_usage_quota(compute, args.location)
        reg_used, reg_limit = regional
    
        usable: List[dict] = []
        for c in candidates:
            need = c["vcpus"] * args.instances
    
            fam_row = find_family_quota_row(family_map, c["family"])
            if fam_row is None:
                debug["quota_family_missing"] += 1
                if args.strict_family_quota:
                    # require an explicit family row; skip
                    continue
            else:
                fam_used, fam_limit, _label = fam_row
                if fam_limit is not None and fam_used is not None and (fam_used + need > fam_limit):
                    debug["quota_family_blocked"] += 1
                    continue
    
            if reg_used is not None and reg_limit is not None:
                if reg_used + need > reg_limit:
                    debug["quota_regional_blocked"] += 1
                    continue
    
            usable.append(c)
    
        if not usable:
            print(f"No VM sizes satisfy capabilities/availability/quota in {args.location}.")
            if args.verbose:
                print("[DEBUG]", debug)
                if not family_map:
                    print("[DEBUG] No family quota rows found in this region; "\
                          "either request per-family vCPU quotas or drop --strict-family-quota.")
            sys.exit(3)
    
        # 4) Print
        usable.sort(key=lambda x: (x["family"], x["vcpus"], x["name"]))
        print(f"Deployable VM sizes in {args.location} (instances={args.instances}):")
        print(f"{'Name':24} {'Family':10} {'vCPUs':>5}")
        print("-" * 44)
        for u in usable:
            print(f"{u['name']:24} {u['family']:10} {u['vcpus']:>5}")
    
        if args.verbose:
            print("\n[DEBUG]", debug)
            if reg_used is not None and reg_limit is not None:
                print(f"[DEBUG] Regional vCPUs: used {reg_used} / limit {reg_limit}")
            # Show a couple of detected family quota labels (for sanity)
            shown = 0
            for k, (_, lim, lbl) in family_map.items():
                if shown >= 6: break
                print(f"[DEBUG] Family quota row: {lbl} (limit={lim})")
                shown += 1
    
    if __name__ == "__main__":
        main()
    
    
    

    Please Upvote and accept the answer if it helps!!


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.