pythonazurepaginationazure-resource-graph

Azure python sdk resourcegraph no skip_token


no matter what i try skip_token is None. my code:

from azure.identity import DefaultAzureCredential
from azure.mgmt.resourcegraph import ResourceGraphClient
from azure.mgmt.resourcegraph.models import QueryRequest, QueryRequestOptions

credential = DefaultAzureCredential()
client = ResourceGraphClient(credential)

query = """
Resources
| where type == "microsoft.network/networkinterfaces"
| where isnotnull(managedBy)
| mv-expand ipConfig = properties.ipConfigurations
| where isnotnull(ipConfig.properties.privateLinkConnectionProperties.fqdns)
| project fqdns = ipConfig.properties.privateLinkConnectionProperties.fqdns
"""

fqdns = []
skip_token = None

while True:
    request = QueryRequest(
        query=query,
        options=QueryRequestOptions(
            skip_token=skip_token,
            top=1000,
        ),
    )
    response = client.resources(request)
    # do smth here

    skip_token = response.skip_token # always None

sample response:

{'additional_properties': {}, 'total_records': 10070, 'count': 1000, 'result_truncated': 'true', 'skip_token': None, 'data': [ xyz ], 'facets': [] }

Python 3.10.12, pip freeze:

azure-common==1.1.28
azure-core==1.34.0
azure-identity==1.22.0
azure-mgmt-core==1.5.0
azure-mgmt-resourcegraph==8.0.0
certifi==2025.4.26
cffi==1.17.1
charset-normalizer==3.4.2
cryptography==44.0.3
idna==3.10
isodate==0.7.2
msal==1.32.3
msal-extensions==1.3.1
msrest==0.7.1
oauthlib==3.2.2
pycparser==2.22
PyJWT==2.10.1
requests==2.32.3
requests-oauthlib==2.0.0
six==1.17.0
typing_extensions==4.13.2
urllib3==2.4.0

Solution

  • Apparently skip_token is only populated when you sort results:

    To enable proper pagination and receive a valid skip_token, modify your query to include a stable order by clause.
    The response won't include the $skipToken if:

    The query contains a limit or sample/take operator.
    All output columns are either dynamic or null type.

    also keep in mind that result_truncated is true only if you do not receive a skip_token. which makes no sense, because you cant paginate without one. skipping first X results only makes sense if you sort, because else results are random. extremely weird implementation.

    None of the docs I was able to find cover the quirks above.