Repository: cloud-custodian/cloud-custodian

Issue:
azure.machine-learning-workspace resources fail when reported on
### Describe the bug

```
$ custodian report test-policy.yaml -s output
2025-07-02 21:43:20,111: custodian.azure.session:INFO Authenticated [Azure CLI | xxxxxxx]
Traceback (most recent call last):
  File "/home/jefflicquia/Projects/six-feet-up/stacklet/platform-initial-policies/main/venv/bin/custodian", line 8, in <module>
    sys.exit(main())
             ~~~~^^
  File "/home/jefflicquia/Projects/six-feet-up/stacklet/platform-initial-policies/main/venv/lib/python3.13/site-packages/c7n/cli.py", line 368, in main
    command(config)
    ~~~~~~~^^^^^^^^
  File "/home/jefflicquia/Projects/six-feet-up/stacklet/platform-initial-policies/main/venv/lib/python3.13/site-packages/c7n/commands.py", line 129, in _load_policies
    return f(options, list(policies))
  File "/home/jefflicquia/Projects/six-feet-up/stacklet/platform-initial-policies/main/venv/lib/python3.13/site-packages/c7n/commands.py", line 338, in report
    do_report(
    ~~~~~~~~~^
        policies, begin_date, options, sys.stdout, raw_output_fh=options.raw)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jefflicquia/Projects/six-feet-up/stacklet/platform-initial-policies/main/venv/lib/python3.13/site-packages/c7n/reports/csvout.py", line 69, in report
    formatter = Formatter(
        policies[0].resource_manager.resource_type,
    ...<3 lines>...
        include_policy=len(policy_names) > 1
    )
  File "/home/jefflicquia/Projects/six-feet-up/stacklet/platform-initial-policies/main/venv/lib/python3.13/site-packages/c7n/reports/csvout.py", line 170, in __init__
    if model.name != model.id:
       ^^^^^^^^^^
AttributeError: type object 'resource_type' has no attribute 'name'
```

### What did you expect to happen?

```
$ custodian report test-policy.yaml -s output
2025-07-02 21:47:10,891: custodian.azure.session:INFO Authenticated [Azure CLI | xxxxxxx]
"name","location"
"ml-ws-bad-2sem","eastus"
"ml-ws-good-2sem","eastus"
```

### Cloud Provider

Microsoft Azure

### Cloud Custodian version and dependency information

```shell
Custodian:   0.9.43
Python:      3.13.4 (main, Jun  3 2025, 15:34:24) [GCC 14.3.0]
Platform:    posix.uname_result(sysname='Linux', nodename='sfulap1', release='6.14.4-400.asahi.fc40.aarch64+16k', version='#1 SMP PREEMPT_DYNAMIC Wed Apr 30 21:41:19 UTC 2025', machine='aarch64')
Using venv:  True
Docker: False
Installed:

PyJWT==2.9.0
adal==1.2.7
applicationinsights==0.11.10
apscheduler==3.11.0
argcomplete==3.6.2
attrs==25.1.0
azure-common==1.1.28
azure-core==1.32.0
azure-cosmos==3.2.0
azure-cosmosdb-nspkg==2.0.2
azure-cosmosdb-table==1.0.6
azure-functions==1.21.3
azure-graphrbac==0.61.2
azure-identity==1.19.0
azure-keyvault==4.2.0
azure-keyvault-certificates==4.9.0
azure-keyvault-keys==4.10.0
azure-keyvault-secrets==4.9.0
azure-mgmt-advisor==9.0.0
azure-mgmt-apimanagement==1.0.0
azure-mgmt-appconfiguration==0.7.0
azure-mgmt-applicationinsights==1.0.0
azure-mgmt-appplatform==8.0.0
azure-mgmt-authorization==1.0.0
azure-mgmt-automation==0.1.1
azure-mgmt-batch==15.0.0
azure-mgmt-cdn==12.0.0
azure-mgmt-cognitiveservices==11.0.0
azure-mgmt-compute==19.0.0
azure-mgmt-containerinstance==7.0.0
azure-mgmt-containerregistry==10.3.0
azure-mgmt-containerservice==15.1.0
azure-mgmt-core==1.5.0
azure-mgmt-cosmosdb==6.4.0
azure-mgmt-costmanagement==1.0.0
azure-mgmt-databricks==1.0.0b1
azure-mgmt-datafactory==1.1.0
azure-mgmt-datalake-analytics==0.5.0
azure-mgmt-datalake-nspkg==3.0.1
azure-mgmt-datalake-store==0.5.0
azure-mgmt-desktopvirtualization==1.1.0
azure-mgmt-dns==8.0.0b1
azure-mgmt-eventgrid==10.3.0b4
azure-mgmt-eventhub==11.2.0
azure-mgmt-frontdoor==1.2.0
azure-mgmt-hdinsight==7.0.0
azure-mgmt-iothub==1.0.0
azure-mgmt-keyvault==8.0.0
azure-mgmt-kusto==2.2.0
azure-mgmt-logic==9.0.0
azure-mgmt-machinelearningservices==2.0.0b2
azure-mgmt-managementgroups==1.0.0b1
azure-mgmt-monitor==2.0.0
azure-mgmt-msi==1.0.0
azure-mgmt-network==28.1.0
azure-mgmt-nspkg==3.0.2
azure-mgmt-policyinsights==1.0.0
azure-mgmt-rdbms==10.2.0b17
azure-mgmt-recoveryservices==2.5.0
azure-mgmt-recoveryservicesbackup==7.0.0
azure-mgmt-redhatopenshift==1.5.0
azure-mgmt-redis==12.0.0
azure-mgmt-resource==16.1.0
azure-mgmt-resourcegraph==7.0.0
azure-mgmt-search==8.0.0
azure-mgmt-security==1.0.0
azure-mgmt-servicebus==8.2.1
azure-mgmt-servicefabric==1.0.0
azure-mgmt-signalr==0.4.0
azure-mgmt-sql==1.0.0
azure-mgmt-storage==17.1.0
azure-mgmt-streamanalytics==1.0.0
azure-mgmt-subscription==1.0.0
azure-mgmt-synapse==2.0.0
azure-mgmt-trafficmanager==0.51.0
azure-mgmt-web==3.0.0
azure-nspkg==3.0.2
azure-storage-blob==12.24.1
azure-storage-common==2.1.0
azure-storage-file==2.1.0
azure-storage-file-share==12.20.1
azure-storage-queue==12.12.0
boto3==1.36.11
botocore==1.36.11
c7n==0.9.43
cachetools==5.5.1
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.1
click==8.1.8
colorama==0.4.6
cryptography==43.0.3
deprecated==1.2.18
distlib==0.3.9
docutils==0.18.1
google-api-core==2.24.1
google-api-python-client==2.160.0
google-auth==2.38.0
google-auth-httplib2==0.2.0
google-cloud-appengine-logging==1.5.0
google-cloud-audit-log==0.3.0
google-cloud-core==2.4.1
google-cloud-logging==3.11.4
google-cloud-monitoring==2.26.0
google-cloud-storage==2.19.0
google-crc32c==1.5.0
google-resumable-media==2.7.2
googleapis-common-protos==1.66.0
grpc-google-iam-v1==0.14.0
grpcio==1.70.0
grpcio-status==1.70.0
httplib2==0.22.0
idna==3.10
importlib-metadata==8.4.0
isodate==0.7.2
jmespath==1.0.1
jsonschema==4.23.0
jsonschema-specifications==2023.12.1
msal==1.31.1
msal-extensions==1.2.0
msrest==0.7.1
msrestazure==0.6.4.post1
netaddr==0.7.20
oauthlib==3.2.2
opentelemetry-api==1.29.0
portalocker==2.10.1
proto-plus==1.26.0
protobuf==5.29.3
pyasn1==0.6.1
pyasn1-modules==0.4.1
pycparser==2.22
pyjwt==2.9.0
pyparsing==3.1.4
pyrate-limiter==2.10.0
python-dateutil==2.9.0.post0
pyyaml==6.0.2
referencing==0.35.1
requests==2.32.3
requests-oauthlib==2.0.0
retrying==1.3.4
rpds-py==0.20.1
rsa==4.9
s3transfer==0.11.2
six==1.17.0
tabulate==0.9.0
typing-extensions==4.12.2
tzlocal==5.2
uritemplate==4.1.1
urllib3==1.26.20
wrapt==1.17.2
zipp==3.20.2
```

### Policy

```shell
policies:
- name: test-reporting-machine-learning
  resource: azure.machine-learning-workspace
  filters: []
```

### Relevant log/traceback output

```shell
See above.
```

### Extra information or context

I was able to generate the expected output by editing my local copy of `c7n_azure/resources/machine_learning_workspace.py` and adding a `default_report_fields` declaration to the `resource_type` class with some suitable fields.


Discussion:

Patch:
diff --git a/tools/c7n_azure/c7n_azure/query.py b/tools/c7n_azure/c7n_azure/query.py
index e9cc575a552..6c358f004c2 100644
--- a/tools/c7n_azure/c7n_azure/query.py
+++ b/tools/c7n_azure/c7n_azure/query.py
@@ -186,8 +186,12 @@ class TypeInfo(metaclass=TypeMeta):
     client = ''
 
     resource = DEFAULT_RESOURCE_AUTH_ENDPOINT
-    # Default id field, resources should override if different (used for meta filters, report etc)
+    # Default id and name fields, resources should override if different
+    # (used for meta filters, report etc)
     id = 'id'
+    name = 'name'
+
+    default_report_fields = ()
 
     @classmethod
     def extra_args(cls, resource_manager):


Test patch:
diff --git a/tools/c7n_azure/tests_azure/test_report.py b/tools/c7n_azure/tests_azure/test_report.py
new file mode 100644
index 00000000000..bd25386e1b5
--- /dev/null
+++ b/tools/c7n_azure/tests_azure/test_report.py
@@ -0,0 +1,30 @@
+# Copyright The Cloud Custodian Authors.
+# SPDX-License-Identifier: Apache-2.0
+
+
+from c7n_azure.provider import Azure
+
+from c7n.config import Bag, Config
+from c7n.ctx import ExecutionContext
+from c7n.resources import load_resources
+
+from .azure_common import BaseTest
+
+
+class ReportMetadataTests(BaseTest):
+
+    def test_report_metadata(self):
+        load_resources(('azure.*',))
+        ctx = ExecutionContext(None, Bag(), Config.empty())
+        missing = set()
+        for k, v in Azure.resources.items():
+            model = v(ctx, Config.empty()).get_model()
+            if not getattr(model, "id", None):
+                missing.add("%s~%s" % (k, v))
+            if not getattr(model, "name", None):
+                missing.add("%s~%s" % (k, v))
+            if not hasattr(model, "default_report_fields"):
+                missing.add("%s~%s" % (k, v))
+
+        if missing:
+            raise AssertionError("Missing report metadata on \n %s" % (' \n'.join(sorted(missing))))