72 require_allowed_fields(prerequisites, fields, label="File CU prerequisites")
73 if any(not _embedding._text(prerequisites.get(key)) for key in fields):
74 raise _cu_failure("cu-prerequisite-missing", "Owner-verified CU capability/region, selected processing/required deployments, auth/access and reachability evidence is required.")
90 or normalize_azure_location(account.get("location")) is None
91 or properties.get("provisioningState") != "Succeeded"
92 or (choice["auth"] != "system-assigned" and properties.get("disableLocalAuth") is not False)
93 or properties.get("publicNetworkAccess") not in ("Enabled", "Disabled")
94 or choice["endpoint"].rstrip("/") not in [v.rstrip("/") for v in candidates if isinstance(v, str)]
95 ):
96 raise _cu_failure("cu-prerequisite-invalid", "Readback must bind the selected ready AIServices account/endpoint/location/network. Key modes also need enabled local auth; MI does not. Any required setup change needs separate approval.")
108 if properties["publicNetworkAccess"] != "Enabled" or (
109 acl is not None and (not isinstance(acl, dict) or acl.get("defaultAction") != "Allow")
110 ):
111 raise _cu_failure("cu-mi-network-unverified", "This MI path requires existing public CU reachability without default-deny ACLs. Restricted/private network compatibility needs separate verified setup and approval; no network changes or key fallback.")
143 parameters = current.get("fileParameters") if isinstance(current, dict) else None
144 ingestion = parameters.get("ingestionParameters") if isinstance(parameters, dict) else None
145 ai = ingestion.get("aiServices") if isinstance(ingestion, dict) else None
146 if (
147 not isinstance(ai, dict) or ingestion.get("contentExtractionMode") != "standard"
148 or not isinstance(ai.get("uri"), str)
149 or ai["uri"].rstrip("/") != choice["endpoint"].rstrip("/")
150 or ingestion.get("identity") is not None
151 or (choice["auth"] == "system-assigned" and ai.get("apiKey") not in file_cu_mi.REDACTED)
152 ):
153 raise _cu_failure("cu-readback-mismatch", "Observed File CU endpoint/extraction/auth conflicts with the selected configuration; credential details withheld.")
260 elif source.get("ai_services_key_acquisition") is not None or source.get("ai_services_managed_identity") is not None or "cu_identity_state" in plan:
261 raise _cu_failure("cu-plan-mismatch", "Automatic acquisition requires the complete versioned File CU workflow.")
267 if not all(isinstance(p, str) and p for p in paths):
268 raise file_cu_mi.fail("cu-mi-provenance-required", "A redacted source GET cannot establish key versus MI auth. Retain the original approved version 1.2 File input and completed creation result for exact reuse.")
269 prior, result = (_bootstrap_io.read_json(path) for path in paths)
270 if (
271 not isinstance(prior, dict) or not isinstance(result, dict)
272 or not isinstance(prior.get("plan"), dict) or not isinstance(prior.get("approval"), dict)
273 or not isinstance(result.get("resources"), dict)
274 or not isinstance(result["resources"].get("created"), list)
275 ):
276 raise file_cu_mi.fail("cu-mi-provenance-mismatch", "Retain complete approved input and completed File result objects.")
283 created = result.get("resources", {}).get("created", [])
284 source = pp.get("source", {})
285 if (
286 prior.get("schema_version") != "1.0" or pp.get("file_cu_plan_version") != "1.2"
287 or approved != {"confirmed": True, "fingerprint": fingerprint}
288 or source.get("action") != "create" or source.get("endpoint") != plan["source"]["endpoint"]
289 or source.get("name") != plan["source"]["name"] or pp.get("owner") != plan["owner"]
290 or pp.get("cu_identity_state") != plan["cu_identity_state"]
291 or pp.get("cu_resource_state") != plan["cu_resource_state"]
292 or result.get("status") != "completed"
293 or result.get("approved_plan") != {"confirmed": True, "fingerprint": fingerprint}
294 or not _search_read.definitions_match(source.get("desired"), current)
295 or not any(isinstance(item, dict) and item.get("type") == "knowledge-source"
296 and item.get("name") == source["name"] and item.get("etag") == current.get("@odata.etag")
297 and item.get("definition_digest") == digest(_search_read._definition(current))
298 for item in created)
299 ):
300 raise file_cu_mi.fail("cu-mi-provenance-mismatch", "Retained MI creation evidence does not bind the fresh source/ETag/account/identity. No auth inference, reingestion or ownership claim.")