Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies = [
"azure-identity>=1.17,<2",
"azure-keyvault-secrets>=4.9,<5",
"azure-mgmt-resource>=23.1,<26",
"azure-mgmt-resource-deployments>=1.0.0b1,<2",
"azure-mgmt-resource-subscriptions>=1.0.0b1,<2",
"azure-mgmt-authorization>=4.0,<5",
"azure-mgmt-keyvault>=10.3,<11",
Expand Down
3 changes: 3 additions & 0 deletions src/azurefox/clients/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class AzureClients:
subscription_id: str
subscription: SubscriptionRef
resource: object
resource_deployments: object
authorization: object
automation: object
web: object
Expand Down Expand Up @@ -40,6 +41,7 @@ def build_clients(session: AuthSession, requested_subscription: str | None) -> A
from azure.mgmt.network import NetworkManagementClient
from azure.mgmt.postgresqlflexibleservers import PostgreSQLManagementClient
from azure.mgmt.resource import ResourceManagementClient
from azure.mgmt.resource.deployments import DeploymentsMgmtClient
from azure.mgmt.resource.subscriptions import SubscriptionClient
from azure.mgmt.sql import SqlManagementClient
from azure.mgmt.storage import StorageManagementClient
Expand Down Expand Up @@ -89,6 +91,7 @@ def build_clients(session: AuthSession, requested_subscription: str | None) -> A
subscription_id=subscription_id,
subscription=subscription_ref,
resource=ResourceManagementClient(session.credential, subscription_id),
resource_deployments=DeploymentsMgmtClient(session.credential, subscription_id),
authorization=AuthorizationManagementClient(session.credential, subscription_id),
automation=AutomationClient(session.credential, subscription_id),
web=WebSiteManagementClient(session.credential, subscription_id),
Expand Down
7 changes: 5 additions & 2 deletions src/azurefox/collectors/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,8 @@ def arm_deployments(self) -> dict:
deployments: list[dict] = []

try:
for deployment in self.clients.resource.deployments.list_at_subscription_scope():
iterator = self.clients.resource_deployments.deployments.list_at_subscription_scope()
for deployment in iterator:
deployments.append(
_deployment_summary(
deployment,
Expand All @@ -767,7 +768,9 @@ def arm_deployments(self) -> dict:

for resource_group in resource_groups:
try:
iterator = self.clients.resource.deployments.list_by_resource_group(resource_group)
iterator = self.clients.resource_deployments.deployments.list_by_resource_group(
resource_group
)
for deployment in iterator:
deployments.append(
_deployment_summary(
Expand Down
2 changes: 2 additions & 0 deletions tests/test_clients_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def __init__(self, credential: object) -> None:
"azure.mgmt.mysqlflexibleservers": ("MySQLManagementClient",),
"azure.mgmt.network": ("NetworkManagementClient",),
"azure.mgmt.postgresqlflexibleservers": ("PostgreSQLManagementClient",),
"azure.mgmt.resource.deployments": ("DeploymentsMgmtClient",),
"azure.mgmt.sql": ("SqlManagementClient",),
"azure.mgmt.storage": ("StorageManagementClient",),
"azure.mgmt.web": ("WebSiteManagementClient",),
Expand Down Expand Up @@ -96,3 +97,4 @@ def test_build_clients_supports_split_subscription_package(monkeypatch) -> None:
assert clients.subscription_id == "sub-b"
assert clients.subscription.display_name == "Beta"
assert clients.resource.subscription_id == "sub-b"
assert clients.resource_deployments.subscription_id == "sub-b"
81 changes: 81 additions & 0 deletions tests/test_collectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2306,6 +2306,87 @@ def test_collect_arm_deployments(fixture_provider, options) -> None:
assert output.deployments[2].name == "kv-secrets"


def test_collect_arm_deployments_uses_split_deployments_client(options) -> None:
def deployment(
name: str,
deployment_id: str,
*,
providers: list[str],
outputs_count: int = 0,
output_resource_count: int = 0,
) -> SimpleNamespace:
return SimpleNamespace(
id=deployment_id,
name=name,
properties=SimpleNamespace(
provisioning_state="Succeeded",
outputs={f"output-{index}": {} for index in range(outputs_count)},
output_resources=[object() for _ in range(output_resource_count)],
providers=[SimpleNamespace(namespace=namespace) for namespace in providers],
template_link=None,
parameters_link=None,
mode="Incremental",
timestamp="2026-04-18T00:00:00Z",
duration="PT1M",
),
)

provider = AzureProvider.__new__(AzureProvider)
provider.session = SimpleNamespace(
tenant_id="tenant-id",
token_source="fixture",
auth_mode="fixture",
)
provider.clients = SimpleNamespace(
subscription_id="subscription-id",
resource=SimpleNamespace(
resource_groups=SimpleNamespace(
list=lambda: [SimpleNamespace(name="rg-apps")],
)
),
resource_deployments=SimpleNamespace(
deployments=SimpleNamespace(
list_at_subscription_scope=lambda: [
deployment(
"sub-foundation",
(
"/subscriptions/subscription-id/providers/Microsoft.Resources/"
"deployments/sub-foundation"
),
providers=["Microsoft.Resources"],
outputs_count=2,
)
],
list_by_resource_group=lambda resource_group: [
deployment(
"rg-apps-deploy",
(
"/subscriptions/subscription-id/resourceGroups/rg-apps/providers/"
"Microsoft.Resources/deployments/rg-apps-deploy"
),
providers=["Microsoft.Web"],
output_resource_count=1,
)
]
if resource_group == "rg-apps"
else [],
)
),
)

output = collect_arm_deployments(provider, options)

assert [item.name for item in output.deployments] == [
"sub-foundation",
"rg-apps-deploy",
]
assert [item.scope_type for item in output.deployments] == [
"subscription",
"resource_group",
]
assert output.issues == []


def test_collect_arm_deployments_sorts_failures_and_linked_rows_first(options) -> None:
output = collect_arm_deployments(DriftOrderingFixtureProvider(Path(".")), options)

Expand Down
Loading