30 raise fail("cu-mi-prerequisite-missing", "Resolve existing Search resource ID and exact CU-scoped Cognitive Services User assignment. Missing identity/role changes need packaged bootstrap plans and explicit approval.")
31 require_allowed_fields(value, {"search_resource_id", "role_assignment_id"}, label="File CU managed identity")
36 not isinstance(search, str) or SEARCH_ID.fullmatch(search) is None
37 or not isinstance(role, str) or not role.casefold().startswith(prefix.casefold())
38 or cu_ingestion_auth.UUID.fullmatch(role[len(prefix):]) is None
39 ):
40 raise fail("cu-mi-binding-invalid", "Select exact Search and CU-account-scoped role assignment IDs; no inferred account, inherited/custom role or user-assigned identity.")
56 if response.status != 200 or not isinstance(response.body, dict):
57 raise fail("cu-mi-prerequisite-unavailable", "Cannot GET the exact Search identity or scoped CU role. Have its owner resolve read access or plan missing setup; no key fallback or permission changes.",
84 or str(rp.get("principalId", "")).casefold() != identity["principalId"].casefold()
85 or rp.get("principalType") != "ServicePrincipal"
86 or str(rp.get("scope", "")).casefold() != choice["resource_id"].casefold()
87 or expected_role.fullmatch(str(rp.get("roleDefinitionId", ""))) is None
88 or rp.get("condition") not in (None, "")
89 ):
90 raise fail("cu-mi-role-unverified", "Require the selected Search principal's unconditional Cognitive Services User assignment on this CU account. Plan a missing scoped role explicitly; no self-grant or key fallback.")
124 if not all(isinstance(p, str) and p for p in paths):
125 raise 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.")
126 prior, result = (_bootstrap_io.read_json(path) for path in paths)
127 if (
128 not isinstance(prior, dict) or not isinstance(result, dict)
129 or not isinstance(prior.get("plan"), dict) or not isinstance(prior.get("approval"), dict)
130 or not isinstance(result.get("resources"), dict)
131 or not isinstance(result["resources"].get("created"), list)
132 ):
133 raise fail("cu-mi-provenance-mismatch", "Retain complete approved input and completed File result objects.")
143 created = result.get("resources", {}).get("created", [])
144 source = pp.get("source", {})
145 if (
146 prior.get("schema_version") != "1.0" or pp.get("file_cu_plan_version") != "1.2"
147 or approved != {"confirmed": True, "fingerprint": fingerprint}
148 or source.get("action") != "create" or source.get("endpoint") != plan["source"]["endpoint"]
149 or source.get("name") != plan["source"]["name"] or pp.get("owner") != plan["owner"]
150 or pp.get("cu_identity_state") != plan["cu_identity_state"]
151 or pp.get("cu_resource_state") != plan["cu_resource_state"]
152 or result.get("status") != "completed"
153 or result.get("approved_plan") != {"confirmed": True, "fingerprint": fingerprint}
154 or not search_reconcile.definitions_match(source.get("desired"), current)
155 or not any(isinstance(item, dict) and item.get("type") == "knowledge-source"
156 and item.get("name") == source["name"] and item.get("etag") == current.get("@odata.etag")
157 and item.get("definition_digest") == digest(search_reconcile._definition(current))
158 for item in created)
159 ):
160 raise 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.")
183 raise fail("cu-mi-ack-version-unverified", "MI creation was acknowledged, but its version evidence is invalid. Retain ownership; do not replay.") from None
184 if created_etag is None:
185 raise fail("cu-mi-ack-version-unverified", "MI creation was acknowledged without an ETag; redacted auth cannot be bound to a created version. Retain ownership; do not replay.")
186
187 def guarded(method, target, token, **kwargs):
188 nonlocal acknowledged, created_etag
189 if method == "PUT" and target == url:
190 recheck()
191 result = transport(method, target, token, **kwargs)
192 if target == url:
193 if method == "PUT" and result.status in (200, 201):
194 acknowledged = True
195 if method == "GET" and result.status == 200 and plan["source"]["action"] == "create" and not acknowledged:
196 raise fail("cu-mi-source-drift", "Expected source absence changed before creation; do not infer MI from redacted readback. Refresh discovery and provenance.")
197 if method == "GET" and result.status == 200 and acknowledged:
201 raise fail("cu-mi-readback-version-unverified", "Created MI source readback has invalid version evidence.") from None
202 if observed_etag != created_etag:
203 raise fail("cu-mi-source-drift", "Source version changed after acknowledged MI creation; redacted auth is unproven. Inspect ownership before cleanup.")