BestillingsportalenBestillingsportalen

Drift

Endringslogg

Vis på GitHub

Sjekk ut release notes for høydepunkter og mer detaljert endringslogg for siste hovedversjon.

1.0.0 - 10.09.2026

Sikkerhet

  • Gjeste-webdelen kan ikke lenger gi eksterne rollen Eier — rollen er låst til Gjest: Hele invitasjonskjeden er bygget for eksterne brukere (ProcessGuests poster alltid til Graph /invitations), men webdelen tilbød likevel fire roller opp til Eier — og gjester kan ikke eie en Microsoft 365-gruppe, så runbooken feilet. Rollen er nå låst til Gjest (gjestemedlemskap i M365-gruppen via Add-PnPMicrosoft365GroupMember — standardmodellen for eksterne i Microsoft 365, gir tilgang til team, område, Planner osv.; lagres som Member i listen) eller Ingen rolle, i alle lag: radioknappene og property pane-dropdownen viser kun de to valgene, lagret Owner fra eldre versjoner klemmes til Gjest ved retry og i webdel-konfigurasjonen, og AddGuestToSite-runbooken avviser Owner med tydelig feilmelding som siste skanse. Det gamle Besøkende-valget (Visitor) er fjernet fra UI — mer begrenset tilgang gis via SP-gruppevalget (Besøkende-gruppen pre-velges som før) — men gamle Visitor-rader æres fortsatt av runbooken ved retry. AccessPreviewPanel er forenklet tilsvarende (Eier-grenen og warning-varianten er død kode og fjernet), og statuslisten viser fortsatt historiske Besøkende/Eier-rader korrekt.

  • Sensitivitetsmerking flyttet ut av Logic App-en — hemmeligheter eksponeres ikke lenger i kjørehistorikken: Apply_sensitivity_label-scopet i ProcessProvisionRequest gjorde en 12-actions ROPC-dans (legg tjenestekontoen til som gruppe-eier, vent 2 minutter, hent fire Key Vault-secrets, URL-enkod dem, hent token, PATCH mot Graph beta, fjern eieren). secureData var feilplassert inne i inputs på Key Vault-kallet for passordet, og manglet helt på URL-enkodingen, token-kallet og PATCH-en. Tjenestekontoens passord, appens client secret og et levende delegert Graph-token var dermed lesbare for alle med lesetilgang til Logic App-ens kjørehistorikk, ved hver bestilling med merke. Hele scopet er fjernet, og operasjonen gjøres nå i ConfigureSpace-runbooken, der Automation-jobboutput bare inneholder det skriptet selv skriver. Har du kjørt en tidligere versjon med enableSensitivity = true, bør du rotere både tjenestekontoens passord og app-secreten etter oppgradering.

  • ROPC-flyten, Key Vault-en og Entra ID-appen er fjernet — løsningen har ingen hemmeligheter i drift: sensitivitetsmerker settes nå app-only med Automation-kontoens managed identity via Set-PnPTenantSite -SensitivityLabel, som går gjennom SharePoints tenant-admin-API og propagerer container-merket til gruppen på tjenersiden. Det omgår Graph-begrensningen for assignedLabels (som fortsatt står, re-verifisert august 2026 — men gjelder en annen vei enn den vi bruker). Målt 4. august 2026 i testtenant: virker, og merket er på gruppen innen 15 sekunder; feilmodusen i pnp/powershell#4917 reproduserte ikke. Runbooken leser alltid tilbake assignedLabels og feiler med en melding som navngir de sannsynlige årsakene hvis merket ikke landet.

    Kravet om en tjenestekonto uten MFA er dermed borte — en tilbakevendende innvending i kundenes sikkerhetsgjennomganger. Fjernet i sin helhet: Azure Key Vault med secretene appid/appSecret/sausername/sapassword, API-tilkoblingen bestillingsportalen-kv (6 tilkoblinger → 5), Entra ID-app-registreringen med client secret, createentraidapp.ps1, appmanifest.json, Get-CredentialEndDates.ps1, Refreshing-app-secret.md, parametrene appName/keyVaultName, -SkipCreateEntraIDAppSecret, og ~230 linjer i deploy.ps1 (secret-generering, Key Vault-validering med globalt unikt navnefallback, credential-prompt, pingback av secret-/sertifikatutløp). Installasjonen har dermed ett steg mindre og krever ikke lenger Global Administrator for å opprette en app-registrering.

    Tjenestekontoen beholdes — den er fortsatt områdeeier, identiteten bak de fire delegerte API-tilkoblingene og den som poster velkomstmeldingen i Teams. Det som falt bort er kravet om at MFA må være avslått.

    enableSensitivity-parameteren er også fjernet. Etter at ROPC-apparatet forsvant hadde den bare én effekt: å seede listeelementet EnableSensitivityLabels ved førstegangs listepopulering — og den effekten uteble i upgrade-modus og når man svarte n på template-prompten. Ingenting deployes betinget: SyncLabels, IP Labels-listen og InformationProtectionPolicy.Read.All settes opp uansett, og Logic App-en har alltid lest bryteren fra innstillinger-listen. Funksjonaliteten skrus nå av og på utelukkende der, uten å kjøre deploy.ps1 på nytt. Også ute: -EnableSensitivity-switchen i GenerateParameters.ps1 og «Sensitivity labels»-linja i pre-flight, som antydet at noe ble installert.

    Eksisterende installasjoner: ARM sletter ikke ressurser som fjernes fra en mal, så Key Vault, KV-tilkoblingen og app-registreringen blir liggende og må fjernes manuelt. Har miljøet kjørt med enableSensitivity = true, bør tjenestekontoens passord roteres — se Oppgraderingsveiledningen.

    Sidefiks: Get-Credential returnerer $null når den avbrytes, noe som ville krasjet skriptet på .UserName.

  • Tjenestekontoen etterlates ikke lenger som gruppe-eier ved feil: Remove_service_account_from_group kjørte kun runAfter: Succeeded, så en feilet merking ga tjenestekontoen permanent eierskap på gruppen. Nå try/finally i runbooken. Samme feil i Post_Welcome_Message er rettet ved å utløse fjerningen på Succeeded/Failed/TimedOut.

Ny funksjonalitet

  • Bestilleren registreres som gjestens sponsor i Entra ID: sponsors er Entras innebygde felt for hvem i organisasjonen som er ansvarlig for en ekstern bruker. Inviterer man manuelt fra Entra-portalen, setter Microsoft automatisk den som inviterer som sponsor — men Bestillingsportalen inviterer app-only med managed identity, så det finnes ingen brukerinviterer å registrere, og feltet har til nå stått tomt på alle BP-inviterte gjester. Løsningen sender nå RequestedBy fra Guest Requests-listen gjennom ProcessGuestRequest til ProcessGuests, som slår opp bestilleren i Entra (mail eller userPrincipalName) og registrerer vedkommende med POST /users/{id}/sponsors/$ref. Gjelder både nye og eksisterende gjester, og er standardfunksjonalitet uten bryter: sponsor gir ingen rettigheter — den dokumenterer ansvar — så i motsetning til guestEntraGroup er det ingen sikkerhetsbeslutning kunden må ta stilling til. Ingen nye tillatelser: UAMI-ens eksisterende User.ReadWrite.All dekker kallet, og sponsor er kjerne-B2B, ikke Entra ID Governance.

    Best effort by design. Hele steget ligger i et eget Set_guest_sponsor-scope, og Response kjører videre på Succeeded/Failed/TimedOut. En sponsor som ikke lar seg sette kan dermed aldri velte en ellers vellykket invitasjon. Skipped er bevisst utelatt: scopet hoppes kun over når selve invitasjonen feilet, og da skal kjøringen feile slik at kalleren markerer forespørselen som Failed. Det er relevant fordi Entra tillater maks fem sponsorer per gjest, og en mye brukt ekstern konsulent treffer det taket. Rader uten RequestedBy (retry, elementer opprettet direkte i listen) og bestillere som ikke lar seg slå opp hoppes over på samme måte.

    ProcessGuests er lagt til i oppgraderingssettet (deploy.ps1 -Upgrade), som til nå bare deployet ProcessProvisionRequest og ProcessGuestRequest. Uten det ville en oppgradering sendt RequestedByEmail til en flyt som ikke leser feltet.

  • Valgfri felles Entra ID-gruppe for inviterte gjester (guestEntraGroup): Ny valgfri installasjonsparameter som gjør at hver gjest invitert via InviteGuests-webdelen — i tillegg til tenant-invitasjonen og tilgangen på det inviterte området — legges inn i en angitt Entra ID-gruppe. Organisasjonen kan dermed gi alle gjester en felles grunntilgang ett sted, f.eks. lesetilgang på hub-området og app-katalogen (etterspurt av en kunde som har opprettet en egen Entra ID-gruppe for formålet). Parameteren tar gruppens objekt-ID (anbefalt) eller visningsnavn — et visningsnavn må matche nøyaktig én gruppe, ellers feiler forespørselen med en tydelig melding i stedet for å gjette. Verdien flyter fra parameters.json via deploy.ps1 og ProcessGuestRequest Logic App inn i AddGuestToSite-runbooken som et nytt [STEP 3/3]: gjestens objekt-ID slås opp (samme UPN-utledning som M365-gruppesteget, nå felles i Resolve-GuestObjectId), og medlemskapet legges til med POST /groups/{id}/members/$ref — idempotent, «already exist» fra Graph behandles som suksess ved retry. Krever ingen nye tillatelser (Automation-identitetens eksisterende Group.ReadWrite.All dekker kallet); sikkerhetsgrupper og M365-grupper støttes, rolletildelbare og on-prem-synkroniserte grupper kan ikke skrives via Graph. Tom/utelatt parameter = funksjonen er av, og eldre parameters.json-filer fungerer uendret.

  • Tenant-register for Bestillingsportalen-instanser (bp_ProvisionUrls) — URL-en er ikke lenger hardkodet, og flere installasjoner kan dele tenant: Webdelene og Teams-appen finner nå området via en tenant-omfattende storage entity som deploy.ps1 vedlikeholder automatisk (upsert per installasjon, med visningsnavn fra ny valgfri parameter provisionInstanceTitle; kan også administreres manuelt med Set-PnPStorageEntity -Key bp_ProvisionUrls mot tenant app-katalogen). Verdien er en ren URL eller et JSON-array av {title, url} der første element er standardinstansen. URL-oppløsning: på SharePoint-sider vinner property pane-verdien, med registeret som fallback (standardverdien er fjernet fra webdel-manifestet, så nye sider følger registeret); i Teams, der det ikke finnes noe property pane, vinner registeret. requestsSiteAlias-kravet er tilsvarende myket opp — ethvert alias fungerer, og pre-flight-fiksen ved aliaskollisjon er nå bare å velge et ledig alias (rename-workarounden er historie). Gjeste-webdelens guestRequestSiteUrl-fallback bruker også registeret. Erstatter PP365 sin aldri-utgitte pp365_ProvisionUrl (PR #1762), som ikke fulgte med porten.

  • Instansvelger i Teams-appen: Har tenanten flere registrerte instanser, tilgangsfiltreres de (lesetilgang sjekkes per instans, parallelt) — brukere med tilgang til én instans sendes rett inn, brukere med tilgang til flere får en velger (valget huskes per bruker, med Bytt bestillingsportal-meny i appen), og brukere uten tilgang får korrekt feilmelding. TeamsAppConfig.json og områdetyper lastes først etter at instansen er valgt, og bytte remonterer webdelen slik at alle data hentes på nytt.

  • Bestillings-webdelen (ProjectProvision) er flyttet inn i dette repoet fra Prosjektportalen 365: Webdelen som lar brukere bestille nye samarbeidsområder (og Teams-appen den utgjør) bygges og distribueres nå som del av bp-provision-web-parts.sppkg — Prosjektportalen 365 er ikke lenger en forutsetning for bestillingsflaten. Komponent-id-en (e88cea29-09a0-4ce4-a38c-e0d74b65f619) er beholdt, så eksisterende Bestillingsportalen-sider og Teams-appen fortsetter å virke etter oppgradering. Viktig oppgraderingsrekkefølge for tenants med Prosjektportalen 365: oppgrader først PP365 til en versjon uten webdelen, og distribuer deretter denne pakken i samme vedlikeholdsvindu — appkatalogen tillater ikke to pakker med samme komponent-id, og siden viser «finner ikke komponenten» i mellomtiden. Ny webdel-egenskap provisionAccessGroupTitle styrer hvilken SharePoint-gruppe som gir tilgang når tilgangsstyring er på (standard «Bestillingsportalen»).

  • Migrering til managed identity: Logic Apps autentiserer nå mot Microsoft Graph, SharePoint REST og Azure Automation med en delt user-assigned managed identity (bestillingsportalen-uami) i stedet for client secret og sertifikat. Sertifikatet og sertifikat-fornying (renew-certificate.ps1, Renewing-certificate.md) er fjernet. Migreringen etterlot først én client secret til ROPC-flyten for sensitivitetsmerker; den flyten, Key Vault-en og app-registreringen er fjernet senere i samme versjon, se Sikkerhet-seksjonen over. deploy.ps1 oppretter identityen via bicep, tildeler app-roller (AssignUamiPermissions) og sender uamiName til alle Logic App-maler. Eksisterende installasjoner må kjøre én full deploy.ps1 (uten -Upgrade) og deretter re-autorisere de delegerte API-tilkoblingene — se Oppgraderingsveiledningen.

  • InviteGuests-webdel (SPFx 1.22 + Fluent UI v9): Ny frittstående SharePoint-webdel for å invitere eksterne gjester til et eksisterende område uten å gå via bestillingsskjemaet. Bygget med OverlayDrawer + TagPicker (basert på Prosjektportalen 365s ProvisionDrawer/Guest-mønster) og DataGrid for statusvisning (basert på ProvisionStatus-mønsteret). Prosjektet ligger under Source/SharePointFramework/ProvisionWebParts/.

  • Guest Requests SharePoint-liste: Ny støtteliste på Bestillingsportalen-admin-området som lagrer alle gjesteforespørsler med felter for status, GuestId, InviteRedeemUrl og ErrorMessage. Opprettes automatisk via PnP-templaten (Source/Templates/Objects/Lists/Guest Requests.xml).

  • ProcessGuestRequest Logic App: Ny wrapper-Logic App som lytter på Guest Requests-listen (1-min polling), kaller eksisterende ProcessGuests Logic App, lagrer GuestId/InviteRedeemUrl, kjører deretter AddGuestToSite-runbooken og setter til slutt Status til Invited (eller Failed med feilmelding via Catch-blokken hvis noe i kjeden feiler — Status reflekterer dermed sluttresultatet inkl. tilgangstildeling). Azure Automation-konnektoren returnerer alltid HTTP 200 selv når runbookens interne status er Failed, så en eksplisitt Check_runbook_status-If-handling leser body('Add_Guest_To_Site')?.properties?.status etterpå og setter Failed + runbook-exception som ErrorMessage hvis runbooken faktisk feilet.

  • Site-fokusert e-post fra ProcessGuestRequest med to varianter: Etter at runbooken har lagt til gjesten på siten sender Logic App-en en branded HTML-e-post via office365-konnektoren (POST /Mail) — Check_if_guest_is_new-If grener på @empty(InviteRedeemUrl):

    • Ny i tenanten (InviteRedeemUrl ikke-tom): Send_guest_invitation_email med emne «Du har fått tilgang til {SiteTitle}» og «Kom i gang»-knapp som peker på InviteRedeemUrl (gjesten må akseptere tenant-invitasjonen først).
    • Eksisterende tenant-gjest (InviteRedeemUrl tom): Send_site_access_email med samme emne, men «Gå til området»-knapp som peker rett på triggerBody()?['SiteUrl'] (ingen invitasjon å akseptere — gjesten er allerede inne i tenanten og kan gå direkte til området).

    Begge har samme HTML-template og hilsen tilpasses med fornavn dersom det er satt. Speiler Send_guest_invitation_email-mønsteret fra ProcessProvisionRequest, men med site-fokus i stedet for generisk «collaboration invitation» — og legger til den eksisterende-gjest-varianten som upstream ikke har. Ny tenantName ARM-parameter (tildelt via spoTenantName i deploy.ps1) brander e-posten med firmanavn, og ny office365-API-connection (gjenbruker bestillingsportalen-o365 opprettet av apiconnections.json).

  • Tilgangstildeling på siten ved invitasjon: Webdelen viser nå to nye seksjoner i invite-drawer for å gi gjesten faktisk tilgang utover bare tenant-invitasjonen:

    • Områderolle-seksjon: radio-velger med Ingen/Besøkende/Medlem/Eier (default Besøkende). Hybrid-routing i runbooken — på M365-gruppe-koblede siter brukes Graph-cmdletene (Add-PnPMicrosoft365GroupOwner/Member) for Eier/Medlem, mens Besøkende alltid håndteres via SP AssociatedVisitorGroup. På andre site-typer går alle tre roller via tilsvarende SP-associated-grupper.
    • SharePoint-brukergruppe-seksjon (valgfritt): "Ikke legg til", "Legg til i eksisterende gruppe" (dropdown), eller "Opprett ny gruppe" (navn + tilgangsnivå Read/Contribute/Edit/Full Control).
  • AddGuestToSite runbook: Ny PowerShell-runbook (Source/Runbooks/AddGuestToSite.ps1) som kalles av ProcessGuestRequest Logic App etter vellykket tenant-invitasjon. Kjører først CSOM Web.EnsureUser($guestEmail) for å materialisere gjesten i målsitens user info-liste (uten dette feiler gruppe-cmdletene siden en fersk B2B-gjest enda ikke er en SP-prinsipal på siten), deretter hybrid-routing for M365GroupRole (Graph-cmdlet på gruppe-koblede siter, SP-associated-gruppe ellers) og endelig SP-brukergruppe via Get-PnPGroup + Add-PnPGroupMember/New-PnPGroup. Hver kjøring logger med [INIT]/[STEP 1/2]/[STEP 2/2]/[DONE]-prefikser for å enklere kunne se hvilke steg som fullførte ved delvis feil. Runbook-RESSURSEN opprettes via ny Source/ARMTemplates/runbooks.bicep som deploy.ps1 alltid kjører (også med -SkipBicepDeploy i upgrade-mode), og innholdet lastes opp fra Source/Runbooks/ og publiseres automatisk av skriptet.

  • Guest Requests-skjema utvidet: Syv nye felt — FirstName (Text), LastName (Text), Company (Text), M365GroupRole (Choice), SPGroupAction (Choice), SPGroupName (Text), SPPermissionLevel (Choice) — som bærer profil-data og brukerens valg fra drawer-en gjennom Logic App-en og inn i runbooken.

  • Profil-data per gjest i InviteDrawer: Drawer-en viser nå Fornavn/Etternavn/Selskap-felter per gjest. GraphService slår opp e-postadressen mot Entra ID via MSGraphClientV3 (/users?$filter=mail eq … or userPrincipalName eq … or otherMails/any(o:o eq …)); hvis brukeren finnes, fylles Fornavn/Etternavn fra givenName/surname og blir readonly med "Finnes i Entra ID"-badge. Krever User.ReadBasic.All-permission registrert i package-solution.json (admin må godkjenne i SharePoint Admin → API access etter første deploy). invitedUserDisplayName settes fra Fornavn+Etternavn på Graph-invitasjonen.

  • inviteMode-property på webdelen (Single/Multi, default Multi): Single = én gjest om gangen med vanlig <Input>; Multi = TagPicker som støtter lim-inn med komma/semikolon/linjeskift som separatorer. I Multi-modus vises en <GuestTabList> (en Tab per gjest) når 2+ gjester er valgt + per-gjest-bryterne er på.

  • perGuestProfileMode + perGuestRoleMode-properties (begge Disabled/Optional/Enforced, default Optional): To brytere på toppen av drawer-en — "Detaljer per gjest" viser/skjuler profilfelter (Fornavn/Etternavn/Selskap); "Rolle og brukergruppe per gjest" flytter M365- og SP-gruppe-seksjonene inn i per-gjest-området. Admin kan tvinge på (Enforced), tvinge av (Disabled), eller la bruker styre (Optional).

  • inviteAccessLevel-property på webdelen (Owner/Member/Anyone, default Owner): Styrer hvem som ser Inviter-knappen. Default skjuler knappen for alle som ikke er M365-gruppe-eier (eller site collection admin). SiteService.getCurrentUserAccessLevel() sjekker mot sitens AssociatedOwnerGroup / AssociatedMemberGroup ved bruk av web.currentUser.groups. Merk at dette er en UI-sjekk — server-side dekkes behovet av list-permissions på Guest Requests-listen (se punktet under om fjernet server-side sjekk).

  • (Fjernet) Server-side autorisasjonsjekk i ProcessGuestRequest: Et tidligere forsøk på SP REST-basert autorisasjon mot målsitens AssociatedOwner/MemberGroup ble fjernet — SharePoint-connectoren authentiserer som connection-brukeren, som typisk mangler tilgang til mål-sitene → 403 og workflow stuck. Erstatning: list-permissions på Guest Requests + SPFx UI-sjekk dekker sikkerhetsbehovet.

  • PATCH av Entra-profilfelter etter invitasjon: ProcessGuests Logic App PATCH-er givenName/surname/companyName/users/{invitedUser.id} rett etter at invitasjonen er opprettet — slik at navn og selskap fra drawer-en faktisk settes på Entra-brukeren (ikke bare lagres på listen). Bruker managed identityens User.ReadWrite.All. PATCH-en er wrappet i en If så den hoppes over hvis alle tre feltene er tomme.

  • Kopier-invitasjonslenke i InviteStatus: Ny kolonne med en Copy16Regular-knapp på Invited-rader som kopierer InviteRedeemUrl til clipboard via navigator.clipboard.writeText. Tooltip viser "Kopier invitasjonslenke" / "Kopiert!" som feedback. Siden sendInvitationMessage: false, må admin uansett dele lenken manuelt — denne knappen gjør det til ett klikk.

  • AssignManagedIdentityPermissions i upgrade-mode: deploy.ps1s funksjon for å tildele Sites.FullControl.All + Group.ReadWrite.All + User.Read.All til automation account-managed identityen kjøres nå også i upgrade-flyten (tidligere kun i full deploy). Idempotent — sjekker per AppRoleId. User.Read.All trengs fordi Add-PnPMicrosoft365GroupMember/Owner slår opp gjesten via GET /users/{email} før den poster til members/$ref; uten dette returnerer brukeroppslaget 403 «Insufficient privileges».

  • Automatisk SPFx-bygging og publisering i deploy-scriptet: Ny DeploySPFxPackages-funksjon kjører npm install (ved behov) + npm run build for alle SPFx-løsninger under Source/SharePointFramework/*/ og publiserer .sppkg til tenant app-katalog via Add-PnPApp -Overwrite -Publish. Kan hoppes over med -SkipSPFxDeploy.

Forbedringer

  • Repoet er klargjort for open source: README er skrevet om for eksterne lesere (engelsk sammendrag, målgruppe, kortversjon av forutsetningene, anbefalt leserekkefølge og forholdet til Provision Assist), ny SECURITY.md med privat sårbarhetsrapportering, og CONTRIBUTING beskriver hvordan webdelene bygges lokalt. Dokumentasjonen omtaler konsekvent «organisasjonen»/«tenanten» i stedet for «kunden», skjermbilder med reelle miljøer er redigert, og opphavsangivelsene bruker SoftwareOne gjennomgående. Standardverdien for innstillingen CompanyName (firmanavnet i gjesteinvitasjons-e-poster) er endret fra «Puzzlepart» til «SoftwareOne» – gjelder kun nye installasjoner, siden eksisterende elementer aldri overskrives; sett din egen verdi i Provisioning Request Settings.

  • Teams-appen publiseres nå automatisk av deploy.ps1Sync to Teams-knappen brukes ikke lenger: Knappen i SharePoint-appkatalogen er upålitelig (deaktivert eller «Failed to sync» i mange tenanter), så løsningen skiper nå sitt eget Teams-app-manifest (ProvisionWebParts/teams/manifest.json, med versjon synkronisert fra package-solution.json). Etter opplasting av .sppkg pakker skriptet manifest + ikoner til bestillingsportalen-teams-app.zip og publiserer den til Teams-appkatalogen via Graph — appen matches på komponent-id (externalId), så en app som tidligere ble synkronisert fra PP365 oppdateres i stedet for å dupliseres, og re-kjøring med samme versjon er no-op. Krever delegert AppCatalog.ReadWrite.All på PnP-appen (lagt til i registreringskommandoen i Deployment-guide.md); mangler tillatelsen produseres zip-en likevel, med instruksjoner for manuell opplasting i Teams admin center. developer-metadataene i package-solution.json er samtidig oppdatert til SoftwareOne med reelle privacyUrl/termsOfUseUrl — tomme verdier der gir ugyldig Teams-manifest og er en kjent årsak til at Sync to Teams feiler.

  • SPFx-verktøykjeden er løftet fra 1.22.1 til 1.23.2: eslint 9 med flat config, oppdaterte @rushstack-pakker og @types/jest 30. Ingen funksjonell endring for sluttbrukere.

  • Standard listeelementer seedes nå av PnP-malen (<pnp:DataRows>) i stedet for Excel-import: de fem listene Provisioning Request Settings, Provisioning Types, Teams Templates, Time Zones og Locales (411 elementer) ble populert av deploy.ps1 via Import-Excel fra Source/Settings/SharePoint List items.xlsx — en destruktiv slett-og-reseed som bare kjørte ved nyinstallasjon, slik at nye innstillinger aldri nådde oppgraderte miljøer uten manuelle copy-paste-steg (jf. den gamle prosedyren i Approval-flow.md). Verdiene ligger nå som <pnp:DataRows KeyColumn="..." UpdateBehavior="Skip"> i Source/Templates/Objects/Lists/*.xml: eksisterende elementer røres aldri (heller ikke runtime-verdier skrevet av SyncGroupSettings), og manglende standardelementer legges til automatisk ved re-apply — også i upgrade-modus. TenantURL løses med {hosturl}-tokenet, Image/Icon-URL-ene med {hosturl}{site}/SiteAssets/..., og SPOManagedPath er malens eneste parameter (default sites i pnp:Preferences, overstyres av deploy.ps1 via -Parameters). Excel-fila og ImportExcel-modulkravet er fjernet, og ~170 linjer seed-kode i deploy.ps1 er borte. Tre stille skavanker rettet i samme slengen: ExternalSharing-kolonnen for Provisioning Types ble aldri importert (alle typer endte som false uansett regnearkverdi — nå seedes de riktige verdiene), den døde HideSiteClassifications-grenen er fjernet (raden fantes ikke og $global:siteClassifications ble aldri satt), og det ødelagte EDU-filteret for Teams-maler er fjernet (det sjekket en kolonne som ikke fantes, så alle 18 maler ble alltid seedet — det er nå den dokumenterte oppførselen). Merk: kunder som bevisst har slettet et standardelement (f.eks. en områdetype) får det tilbake ved oppgradering, og standardelementer må ikke gis nytt navn — Title er nøkkelen (unntatt Time Zones som bruker TimeZoneId pga. duplikat visningsnavn). Med skipApplySPOTemplate = true seedes ingenting lenger — seedingen følger malen.

  • GenerateParameters.ps1 foreslår managedPath fra tenantens egen innstilling: verdien kom fra malen (sites) uansett hva tenanten var satt opp med, og deploy.ps1 bygger område-URL-er fra den — en tenant med Opprett gruppeområder under satt til /teams/ fikk dermed feil URL-er uten at noe protesterte. Skriptet tilbyr nå å lese innstillingen fra tenanten. Dette er det ene steget Azure CLI ikke kan gjøre (az account get-access-token mot https://<tenant>-admin.sharepoint.com feiler med InteractionRequired uten en egen SPO-scopet pålogging), så det går via Get-PnPTenant og koster én interaktiv pålogging som SharePoint-administrator — derfor en prompt, ikke noe som skjer uansett. Verdien leses fra NewTeamSiteManagedPathGet-PnPTenantInternalSetting — ikke Get-PnPTenant, som ikke har den, og heller ikke NewSiteManagedPath, som gjelder ikke-gruppekoblede områder; løsningens område er et gruppekoblet team site, og NewTeamSiteManagedPath er det admin-senterets «Opprett gruppeområder under» skriver. Verken Set-SPOTenant eller Set-PnPTenant dokumenterer innstillingen, så kandidatlista har fallback til NewSiteManagedPath og til andre *ManagedPath*-properties, og verdien kontrolleres mot AvailableManagedPathsForSiteCreation. Finnes ingen verdi, spør skriptet om den framfor å beholde en mulig feil standard. Hoppes over med -Force, uten PnP.PowerShell, eller hvis PnP-appen ikke finnes i tenanten.

  • -SkipAppRoles: installasjon uten Global Administrator: app-rolletildelingen er det eneste steget i deploy.ps1 som krever GA (ev. Privileged Role Administrator + Cloud Application Administrator) — i mange kommuner får ikke konsulenten den rollen. Med -SkipAppRoles installeres alt annet, og skriptet skriver ut en ferdig kommando med tenant-ID og begge identitetenes object-ID-er utfylt, som kundens GA kjører etterpå. AssignPermissionsToManagedIdentity.ps1 er utvidet til handover-modus (-AutomationIdentityId/-UamiId/-TenantId) med begge rollelistene innbakt — GA-en trenger den ene fila, PowerShell 7+ og Microsoft.Graph-modulen, ingenting annet fra installasjonen. Graph-innloggingen i skriptet ber nå kun om AppRoleAssignment.ReadWrite.All + Application.Read.All (tidligere ba den i tillegg om RoleManagement/Application.ReadWrite/DelegatedPermissionGrant — unødvendig, og consent-dialogen så mer dramatisk ut enn operasjonen er). Logic Apps får 401/403 frem til kommandoen er kjørt; DEPLOYMENT SUMMARY viser SKIPPED med kommandoen i detaljene. Dokumentert i installasjonsveiledningen.

  • PRE-DEPLOYMENT CHECKLIST og -Preflight: forhåndssjekkene stoppet på første feil, så mangler ble oppdaget én re-kjøring om gangen — RBAC i én runde, resource providers i neste, hver med sin egen bestilling til kundens admin. Nå registrerer alle sjekkene resultatet sitt og kjøres til ende, og resultatet vises som en samlet sjekkliste med OK/MISSING/WARNING/SKIPPED per punkt og en Fix:-linje med konkret løsning for hver mangel — hele mangellisten i én kjøring. MISSING stopper fortsatt før noe opprettes; WARNING gjør ikke. Tre nye sjekker i samme rennet: app-rolle-rettigheter (aktiv GA eller PRA+CAA via kontoens directory-roller — best-effort, siden PIM-eligible roller ikke synes før aktivering, derfor WARNING og aldri MISSING), tenant app-katalog (Get-PnPTenantAppCatalogUrl på admin-tilkoblingen) og Node.js 22.14+ (SPFx-bygget er siste deploy-steg, så manglende Node dukket ellers opp etter at alt annet var ferdig). Ny -Preflight-parameter kjører kun sjekkene og avslutter med exit-kode 0/1 — rask oversikt over hva som mangler før man booker tid til den ekte kjøringen. Krever samme innlogginger som en deploy, men ingenting opprettes eller endres.

  • Pre-flight sjekker og registrerer resource providers: et ferskt abonnement har ikke Microsoft.Automation/Microsoft.Logic registrert, og deployen feilet med MissingSubscriptionRegistration — først for Automation-kontoen, med Logic Apps som neste vegg. ValidateResourceProviders sjekker de fire namespacene løsningen bruker (Microsoft.Automation, Microsoft.ManagedIdentity, Microsoft.Logic, Microsoft.Web, filtrert etter hvilke deploy-steg som faktisk skal kjøre). Uregistrerte providers registreres automatisk — startet i bakgrunnen rett etter bekreftelsesprompten og ventet på rett før Azure-deployen, så områdeopprettelsen absorberer ventetiden. Registrering er en abonnementsoperasjon (Owner på ressursgruppen hjelper ikke), så når permissions-endepunktet bekrefter at kontoen mangler abonnementsrettigheter, stopper pre-flight med de nøyaktige az provider register-kommandoene en abonnementsadministrator må kjøre. Wildcard-matchingen er trukket ut i felles TestActionMatch/TestAzureAction som RBAC-sjekken også bruker.

  • Pre-flight verifiserer at kontoen kan opprette RBAC-rolletildelinger: azureresources.bicep inneholder to Microsoft.Authorization/roleAssignments (Automation Job/Runbook Operator for managed identityen), og ARM autoriserer disse ved innsending — en konto med bare Contributor får hele deployen synkront avvist, uten deployment-record, og azure-cli (verifisert på 2.77.0) sluker 400-kroppen og viser bare «The content for this response was already consumed». Ny ValidateAzureRbac sjekker roleAssignments/write mot permissions-endepunktet (ressursgruppen hvis den finnes, ellers abonnementet) og stopper med en tydelig melding om Owner/User Access Administrator før noe kjøres. En feilet sjekk blokkerer ikke — kun en bekreftet manglende rettighet. Hoppes over med -SkipBicepDeploy, der ingen rolletildelinger deployes. I samme gate: ValidateArmTemplates kompilerer nå også begge bicep-malene lokalt, som fanger blokkert Bicep-nedlasting og kompileringsfeil — den andre veien til samme uleselige feilbilde.

  • PRE-FLIGHT SUMMARY viser rot-områdets språk: provisjonering av områder med et annet språk enn tenantens rot-område har gitt problemer, men språket var ikke synlig noe sted under installasjonen. Ny Root site language-linje viser LCID og språknavn for https://<tenant>.sharepoint.com, lest fra rot-webbens Web.Language over en separat PnP-tilkobling (-ReturnConnection, så admin-tilkoblingen pre-flight står på er urørt, og tokenet fra første pålogging gjenbrukes — ingen ekstra nettleser-prompt). Første forsøk brukte SiteProperties.Lcid fra Get-PnPTenantSite, men den populeres ikke pålitelig — en reell tenant rapporterte 0. Språknavnet slås opp fra LCID-en via CultureInfo framfor en oppslagstabell, siden løsningens egen Locales-liste ligger på området kjøringen er i ferd med å opprette. Ren visningsverdi: feiler oppslaget skrives årsaken (første linje) og installasjonen fortsetter.

  • -ParametersPathdeploy.ps1: skriptet leste hardkodet .\parameters.json, så det å jobbe mot flere kundemiljøer krevde at man kopierte riktig fil over parameters.json før hver kjøring — med den åpenbare feilmuligheten at man kjørte deploy mot én kunde med en annens parametre. Filen kan nå angis eksplisitt (./deploy.ps1 -ParametersPath .\parameters-contoso.json), default er uendret. Stien valideres før modulsjekk og de tre innloggingene, så en skrivefeil stopper umiddelbart i stedet for etter tre MFA-runder, og feilmeldingen lister parameterfilene som faktisk ligger i mappen. PRE-FLIGHT SUMMARY viser hvilken fil verdiene kom fra. GenerateParameters.ps1 skriver ut den ferdige deploy.ps1-kommandoen inkludert -ParametersPath når -OutputPath ikke er standardfilen, og .gitignore dekker nå parameters-*.json (unntatt parameters.template.json).

  • StatusReason inneholder nå den faktiske feilen: de ti Handle_Error-scopene skrev en hardkodet streng, så den egentlige Graph-/SPO-feilmeldingen aldri nådde listeelementet. De åtte container-scopene bruker nå result() + Filter_failed_actions_*/Compose_error_message_* (samme mønster som ProcessGuestRequest) og skriver f.eks. «Failed during space type validation: Provision_Group: Insufficient privileges …». result() rapporterer bare de umiddelbare barne-actionene, så ved dypt nestede feil navngis grenen framfor det innerste kallet. For Configure_space (en connector-action, ikke et scope) leses jobbens properties.exception, som er runbookens egen oppsummering med navn på hvert feilende steg. Skipped er bevisst ikke lagt til i utløseren — dokumentasjonen hevdet feilaktig at den var med.

  • Runbook-jobber som feiler med HTTP 200 fanges nå i provisjoneringsflyten: Azure Automation-connectoren returnerer HTTP 200 selv når jobben internt er Failed. ProcessGuestRequest har hatt en eksplisitt Check_runbook_status-sjekk siden denne versjonen; ProcessProvisionRequest hadde den ikke, så en feilet ConfigureSpace kunne gå videre som om alt gikk bra. Ny Check_runbook_status etter Configure_space, og Update_status_to_Space_Created er kjedet etter sjekken i stedet for parallelt med den (ellers kunne bestillingen bli stemplet «Space Created» før Terminate rakk å stoppe kjøringen).

  • ConfigureSpace.ps1 kjører nå fail-fast per steg: $ErrorActionPreference var aldri satt, så ikke-terminerende PnP-feil gikk rett forbi alle 22 catch-blokkene og kjøringen rapporterte suksess selv om enkeltsteg ikke hadde gjort noe. Nå settes Stop, og en ny Invoke-Step-hjelper gjenoppretter «registrer og gå videre»-oppførselen på ett sted i stedet for 22 identiske try/catch. Write-StepSummary skriver en per-steg-tabell til jobbloggen. Merk: steg som tidligere feilet stille vil nå rapporteres — en bestilling som «alltid har fungert» kan begynne å feile, og det er reelle feil som var skjult.

  • Tre bugs i ConfigureSpace.ps1: (1) externalSharing, joinHub og applyPnPTemplate er strenger fra Logic App-en men ble testet med if ($x)/-eq $true — strengen "false" er truthy, så ekstern deling ble slått når den skulle vært av; (2) dokumentbiblioteket var hardkodet til "Dokumenter" i tre steg, som feilet på alle ikke-norske områder — slås nå opp via BaseTemplate/IsDefaultDocumentLibrary; (3) AddSiteCollectionAdmins sendte "" videre til Add-PnPSiteCollectionAdmin fordi "" -split "," gir ett tomt element.

  • Eksplisitte PnP-tilkoblinger i ConfigureSpace.ps1: 11 implisitte Connect-PnPOnline-bytter er erstattet av Connect-Admin/Connect-Site som hvert steg kaller selv. Stegene er dermed rekkefølge-uavhengige, og tenant-admin-cmdletene kjører ikke lenger mens tilkoblingen står på selve området. Sensitivitetsmerket settes nå før SetExternalSharing, siden container-merket overstyrer områdets privacy- og delingsinnstillinger. Tenantnavnet utledes fra [System.Uri].Host i stedet for Substring/IndexOf('.'), som brakk på alt som ikke var https://tenant.sharepoint.com/….

  • Medlemskapstest og paging i Process_Owners_and_Members: testene var contains(string(body('Get_group_members')), id) — substring-match mot serialisert JSON. Id-ene projiseres nå ut med to Select-actions og testene gjør et reelt array-oppslag. Get_group_members/Get_group_owners hentet dessuten uten $top, så Graph returnerte standard sidestørrelse 100 — grupper med flere enn 100 medlemmer fikk stille feil svar for alle forbi første side. Satt til $top=999.

  • Kortere provisjonering: Delay_while_owner_is_added lå inne i eier-løkken med ett minutt per iterasjon, og kunne ikke hjelpe «er allerede eier»-sjekken uansett (den leser et øyeblikksbilde tatt før løkken). Erstattet av én 30-sekunders venting etter løkken. Wait_for_viva_engage_community_to_provision var 2 minutter rett før Until-løkken som allerede poller operasjonsstatus — redusert til 30 sekunder. Ventingene rundt gruppe-, team-, teamify-, clone- og områdeopprettelse er bevisst urørt: de gater asynkron propagering i Teams/SPO og krever måling i testtenant før de kan gjøres om til polling.

  • Dokumentasjonen konsolidert: Architecture.md (diagrammene flyttet til Teknisk løsningsbeskrivelse kap. 1), Bestillingsportalen.md (unike Guest Requests-feltbeskrivelser flyttet til Data-stores.md; resten dupliserte CHANGELOG/Data-access-security) og Cost-estimates.md (2020-tall uten informasjonsverdi) er fjernet. Tillatelsestabellene i Managed-identity-migration.md er erstattet med lenke til Data-access-security.md, som er kanonisk kilde for tillatelseslistene — tre kopier hadde allerede sklidd fra hverandre.

  • Managed-identity-migration.md fjernet: dokumentet beskrev migreringen fra client secret/sertifikat til managed identity i 1.0.0, og hadde etter at ROPC-flyten og Key Vault forsvant flere avsnitt som omtalte Key Vault som gjeldende — inkludert en verifikasjonssjekkliste som ba leseren bekrefte en Key Vault-policy som ikke finnes. Bakgrunnen dekkes av Datatilgang og sikkerhet, oppgradering av eldre installasjoner av Oppgraderingsveiledningen, og designer-roundtrip-advarselen er flyttet til CONTRIBUTING.md. Alle innkommende lenker er pekt om.

  • Minimerte runtime-tilganger på user-assigned managed identity: Alle HTTP-kallene i Logic Apps og alle runbook-operasjoner er kartlagt mot dokumentert minste Graph-tillatelse. To roller er strammet inn: Directory.ReadWrite.AllGroupSettings.ReadWrite.All (eneste bruk var å deaktivere gjestedeling per gruppe) og Graph Sites.FullControl.AllSites.Read.All (eneste bruk var lesekallene i CheckSiteExists). Identiteten har dermed ingen tenant-vid katalog-skrivetilgang. SharePoint Sites.FullControl.All beholdes på både UAMI (områdeopprettelse — Sites.Selected er umulig mot områder som ikke finnes ennå) og Automation-kontoens identity (tenant-admin-cmdlets i ConfigureSpace + tilpasninger mot provisjonerte områder). Eksisterende migrerte miljøer: fjern de to gamle rollene manuelt fra UAMI-en i Entra (deploy tildeler kun manglende roller, fjerner aldri). Se kall-for-kall-begrunnelse i Data-access-security.md.

  • Trygg re-kjøring av deploy.ps1 mot eksisterende miljø: Svarer du n på «Site already exists»-prompten hoppes nå også liste-reseedingen over (før ble Settings-, Provisioning Types-, Teams Templates- og Time Zones-listene alltid slettet og gjenopprettet fra pakkens standardverdier i fresh-modus — også ved n — slik at tilpasninger som AdminGroupId, ApproverEmail og egne provisioning types gikk tapt). Liste-ID-ene hentes fortsatt, så Logic Apps kan deployes. Bestillingsdata har aldri vært berørt.

  • GenerateParameters.ps1 – automatisk generering av parameters.json: Nytt hjelpeskript forankret i en eksplisitt mål-tenant: det spør hvilken tenant (kunde) parametrene gjelder (-Tenant eller prompt), logger Azure CLI inn i den tenanten, viser hvem du er logget inn som, og tilbyr kun abonnementer som tilhører mål-tenanten (med tenant-domene i stedet for bare GUID i velgeren) — laget for konsulenter som jobber mot flere kundetenants. Skriptet henter tenantId/subscriptionId fra konteksten og fullTenantName/spoTenantName fra Graph (initial-domenet), og fyller resten med standardverdier fra parameters.template.json (beskrivelser og nye parametre holdes dermed i sync). Tjenestekonto-UPN promptes eller angis som parameter (-ServiceAccountUPN; -Force for uovervåket kjøring). Skriptet gjør kun lesekall mot Azure.

  • PnP.PowerShell 3.2+ og PowerShell 7.4+ kreves og håndheves: VerifyModules sjekker nå minimumsversjon (før kun at modulen fantes) og gir presis beskjed om Update-Module ved for gammel versjon; skriptet krever PowerShell 7.4+ (PnP 3.x-krav, før kun «Core»). Skriptet laster i tillegg PnP.PowerShell før første Az-cmdlet for å unngå assembly-konflikten mellom modulene (Method 'get_Services' in type '...LoggingBuilder' ... does not have an implementation — oppstår når nyere Az lastes først i økten; start nytt PowerShell-vindu hvis den likevel treffer).

  • Runbooks kjører nå på PowerShell 7.4 med PnP.PowerShell 3.2: Alle tre runbookene (ConfigureSpace, GetSiteTemplates, AddGuestToSite) er samlet i runbooks.bicep og kjører i et nytt Azure Automation runtime environment (bestillingsportalen-ps74: PowerShell 7.4, PnP.PowerShell 3.2, Az-pakke) — erstatter den klassiske PowerShell 7.2-modellen med PnP 2.4.0 og den eldgamle Az.Accounts 1.6.2. Siden runbooks.bicep deployes i både full- og upgrade-modus, får eksisterende miljøer runtime-oppgraderingen via deploy.ps1 -Upgrade.

  • Power Automate-flyten er nå med i leveransen: Provisioning Request Approval fulgte upstream sin Power Apps-løsningspakke, som ble droppet da SPFx-webdelen erstattet appen — repoet har aldri inneholdt den, og installasjonsveiledningen forutsatte stilltiende at den fantes i miljøet (stemte kun der Bestillingsportalen hadde vært installert før). Ny Source/Flows/Bestillingsportalen-Flows_unmanaged.zip (32 KB) er avledet fra upstream-pakken og trimmet til kun det som brukes: flyt-definisjonen, de fire connection references og de fire environment variables flyten faktisk bruker. Canvas-appen, dens ni miljøvariabler og upstream-flyten Check Space Availability er fjernet — sistnevnte har PowerApps-trigger og kalles kun fra canvas-appen (SPFx-webdelen sjekker tilgjengelighet via CheckSiteExists-Logic Appen), og en importert kopi lar seg heller ikke aktivere (shared_logicflows-tilkoblingsfeil). Flyten pakkes i Draft-tilstand slik at importen ikke gjør et aktiveringsforsøk som feiler med FlowNotOriginalAuthor; aktivering gjøres eksplisitt i konfigurasjonsveiledningens Steg 2. Miljøer med tilpassede flyter kan fortsatt eksportere/importere egne pakker.

  • Authorize-ApiConnections.ps1 — guidet autorisering av API-tilkoblingene: Nytt hjelpeskript som sjekker status på de fire delegerte API-tilkoblingene, genererer samtykkelenker via ARM (listConsentLinks), åpner dem i nettleseren (logg inn som tjenestekontoen), fanger samtykket på en lokal lytter (http://localhost:8155/) og fullfører autoriseringen via confirmConsentCode — hele veien til verifisert Connected, uten portalnavigasjon. Håndterer begge redirect-variantene til API-hubben (med og uten kvitteringskode), og varsler om den forventede «created from a different organization»-advarselen (lenken genereres av admin-sesjonen mens tjenestekontoen samtykker). Selve innloggingen kan ikke automatiseres (delegert OAuth krever at tjenestekontoen selv logger inn); flyt-aktivering/-deling forblir manuelt av samme grunn.

  • CustomerSpecific runbook — fast utvidelsespunkt for kundetilpasninger: ProcessProvisionRequest kjører nå runbooken CustomerSpecific rett etter ConfigureSpace, med samme parametersett (unntatt spaceImage). Runbooken opprettes av deploy.ps1 med et tomt no-op-innhold kun hvis den ikke finnes, og overskrives aldri av senere deploys/upgrades — i motsetning til de øvrige runbookene som holdes i sync med repoet. Kundespesifikke etterkonfigurasjonssteg (ekstra lister, tilganger, integrasjoner) legges her, og kjører som Automation-kontoens system-assigned managed identity.

  • Runbook-innholdet deployes direkte fra repoet — manuelt innlimingssteg fjernet: Plassholder-URI-ene mot upstream-repoet er borte; runbooks.bicep oppretter tomme runbook-skall, og deploy.ps1 laster opp innholdet fra Source/Runbooks/ via management-APIet og publiserer. Innholdet er dermed alltid i sync med repoet ved hver deploy/upgrade. Merk: endringer gjort direkte på runbookene i Azure Portal overskrives — kundetilpasninger i runbooks skal gjøres i repoet det deployes fra.

  • Robust tenant-validering i GenerateParameters.ps1: Tenant-inputen resolves nå til tenant-ID via det offentlige OpenID-discovery-endepunktet før innlogging — skrivefeil gir umiddelbar, tydelig feil, en UPN limt inn ved et uhell auto-korrigeres til domenedelen, feilet az login stopper skriptet (før fortsatte det stille på forrige cachede sesjon), og etter innlogging verifiseres at sesjonen faktisk står i mål-tenanten.

  • Gjenbruk av eksisterende innloggingssesjoner — færre MFA-runder ved gjentatte kjøringer: Både deploy.ps1 og GenerateParameters.ps1 sjekker nå om det finnes cachede sesjoner som matcher mål-miljøet før de starter ny innlogging: Az PowerShell-konteksten (matcher tenant → tilbys gjenbruk med stille subscription-bytte), Azure CLI (az account list-cachen sjekkes mot tenant/subscription → tilbys gjenbruk), og PnP-tokenet gjenbrukes innenfor kjøringen slik at kun første PnP-tilkobling viser nettleserprompt (bevisst ikke persistert på tvers av økter, for å unngå kundetenant-tokens liggende på disk). Du blir alltid spurt (y = gjenbruk / n = logg inn på nytt); med -SkipConfirmation/-Force gjenbrukes matchende sesjoner automatisk.

  • PnP-autentisering er nå utelukkende interaktiv — sertifikatstøtten er fjernet: Installasjonen er attended by design (interaktive prompts hele veien), så app-only/sertifikat-støtten er fjernet helt i tråd med PnP sin egen veiledning: pnpCertPath-parameteren er borte, alle PnP-tilkoblinger (admin-site, nytt område, app-katalog, -SkipSharepointSite-grenen som tidligere ignorerte sertifikatet) går gjennom én felles ConnectPnP-funksjon med Connect-PnPOnline -Interactive, og sertifikatpassord-prompten er fjernet. Før falt tom pnpCertPath dessuten tilbake på credential-basert pålogging som feiler med MFA. Installasjonsveiledningen viser Register-PnPEntraIDAppForInteractiveLogin (delegerte tilganger: Graph Group.ReadWrite.All + SharePoint AllSites.FullControl) — eller gjenbruk av Prosjektportalen-appen. Oppgradering fra eldre versjon: fjern pnpCertPath fra parameters.json.

  • Entra ID-appen er nå valgfri uten sensitivitetsmerker (mellomsteg — appen og enableSensitivity er begge fjernet helt senere i samme versjon, se Sikkerhet-seksjonen): deploy.ps1 krevde at appen fantes selv om den etter managed identity-migreringen kun brukes av ROPC-flyten for sensitivitetsmerker (dokumentasjonen sa allerede at den kunne slettes). Nå: mangler appen og enableSensitivity er false, hopper deploy over den med tydelig melding (SKIPPED i rapporten) — createentraidapp.ps1 trengs altså ikke i standardoppsettet. Mangler den og enableSensitivity er true, feiler deploy tidlig med presis beskjed. GenerateParameters.ps1 sjekker i tillegg om både PnP-appen (Prosjektportalen-standard) og Entra ID-appen finnes i tenanten, og next-steps-teksten sier eksakt hva som trengs for konfigurasjonen din.

  • Tidlig validering av tjenestekontoen: deploy.ps1 verifiserer nå at serviceAccountUPN finnes i tenanten før noe opprettes (før krasjet installasjonen kryptisk midt i site-opprettelsen når kontoen manglet, siden den brukes som -Owners i New-PnPSite), med advarsel hvis kontoen mangler lisenser eller en brukbar Power Automate-plan — godkjenningsflyten krever seeded Power Automate (E1/E3/E5); frontline-planer (F1/F3) gir kryptiske feil (FlowNotOriginalAuthor, shared_logicflows) først ved aktiveringen i konfigurasjonsveiledningens Steg 2. GenerateParameters.ps1 validerer at kontoen finnes ved generering, med re-prompt ved skrivefeil. Kontoen vises også (verifisert med visningsnavn) i pre-flight-oppsummeringen.

  • Pre-flight-bekreftelse i deploy.ps1: Etter innlogging — men før noe opprettes — viser skriptet en oppsummering av hvilket miljø du faktisk er koblet til (Entra ID-tenant, Azure-subscription med navn, innlogget konto, SharePoint-tenant) og hva som vil bli satt opp/oppdatert (per komponent, med skip-flagg reflektert), og krever eksplisitt y for å fortsette. Beskytter mot deploy til feil tenant/subscription. Hopp over med -SkipConfirmation for automatiserte kjøringer.

  • Deployment summary i deploy.ps1: Skriptet skriver nå ut en statusrapport på slutten av hver kjøring (også ved feilavbrudd) med én linje per delkomponent — OK/FAILED/WARNING/SKIPPED. Alle az deployment-kall exit-kode-sjekkes (før kunne en feilet Logic App-deployment passere ubemerket og skriptet rapportere suksess), feilede app-rolletildelinger samles opp og rapporteres i stedet for å forsvinne i utskriften, en feilet SPFx-bygging stopper ikke lenger resten av deployen, og skriptet avslutter med exit-kode 1 hvis noen komponenter feilet. Gjenstående manuelle steg (autorisere API-tilkoblinger, aktivere flyter) listes opp til slutt.

  • Forbedret feilhåndtering i bestillingsflyt: Implementert omfattende feilhåndtering som automatisk oppdaterer Provisioning Requests-listen med status "Space Creation Failed" når feil oppstår i både Logic App og ConfigureSpace runbook. Dette gir bedre synlighet på feiltilstander og enklere feilsøking.

  • Upgrade-mode utvidet: DeployUpgradeLogicApp deployer nå BÅDE ProcessProvisionRequest OG ProcessGuestRequest. SPFx-løsninger bygges og publiseres også i upgrade-mode (med mindre -SkipSPFxDeploy er satt).

  • Navigasjon bevares i upgrade-mode: Invoke-PnPSiteTemplate kjøres uten -ClearNavigation når -Upgrade er aktiv, slik at egendefinerte nav-lenker ikke slettes.

  • Tydeligere "Site already exists"-prompt: Prompten i CreateRequestsSharePointSite forklarer nå tydelig at y = re-anvend PnP-template (oppdaterer lister/felter/innstillinger) og n = hopp over template men fortsett med Logic Apps / SPFx. n stopper ikke lenger hele scriptet.

  • Race-condition-guard i DeployUpgradeLogicApp: Kaster nå tydelig feilmelding hvis $global:requestsListId eller $global:guestRequestsListId er tomme før Logic Apps deployes.

  • InviteDrawer UX-justeringer: M365-gruppe-seksjonen skjules helt på siter uten M365-gruppe (i stedet for gråtonet variant). Default SP-gruppe-valg er nå Legg til i eksisterende gruppe med sitens associated Visitors-gruppe pre-valgt (fra SiteService.getSiteContext). GuestPicker re-skrevet etter PP365-mønster med useTagPickerFilter — den maskinskrevne e-posten vises som klikkbar option med Avatar i dropdown.

  • AccessPreviewPanel — tilgangsoversikt i invite-drawer: Alltid-synlig <MessageBar> som dynamisk lister hva gjesten faktisk får tilgang til basert på valgt M365-rolle + SP-gruppe + om siten er M365-gruppe-koblet. Skifter mellom intent='info' (begrenset tilgang) og intent='warning' (Owner/Member på gruppe-koblet site = Teams + alle gruppe-ressurser + OneNote/Planner) med advarsels-footer som oppmuntrer til mer begrenset rolle. Adresserer det grunnleggende M365-gruppe-medlemskaps-kaskade-problemet hvor "Member" gir mye mer tilgang enn brukere intuitivt forstår.

Feilrettinger

  • Bestillingsskjemaet skiller nå «området finnes ikke» fra «du har ikke tilgang»: Peker den konfigurerte URL-en på et område som ikke finnes (HTTP 404), vises den nye meldingen «Fant ikke Bestillingsportalen på {url}» i stedet for den misvisende tilgangsmeldingen — feilkonfigurasjon maskeres ikke lenger som tilgangsproblem. (Port av PP365 PR #1762-delen som ikke fulgte med flyttingen av webdelen.)

  • MinimumOwners-innstillingen fra Innstillinger for bestilling håndheves nå i bestillingsskjemaet: Verdier ≥ 2 gir inline valideringsmelding på eierfeltet («Det kreves minst {0} eiere …») og deaktivert bestill-knapp til kravet er oppfylt, i begge skjemavarianter. Manglende/ugyldig/0/1 gir nøyaktig dagens oppførsel. (Port av PP365 PR #1763.)

  • Navnesjekken i bestillingsskjemaet fanger nå også ventende bestillinger, gjenbrukte aliaser og områder i papirkurven — og revalideres ved innsending: Tilgjengelighetssjekken på områdenavnet er debounced med sekvensvakt (utdaterte svar kan ikke overskrive nyere), respekterer 64-tegnsgrensen for gruppealias (delt calculateAliasValue), og sjekker i tillegg til levende områder både bestillinger underveis i Provisioning Requests-listen (ny melding «Det finnes allerede en bestilling med samme adresse») og aliaser som er opptatt av eksisterende M365-grupper eller områder i tenant-papirkurven (via GetValidSiteUrlFromAlias). Ved innsending revalideres alt — konflikt gir egen toast («Adressen er ikke ledig») i stedet for generisk feil — og en dobbeltklikk-vakt med spinner hindrer to identiske bestillinger. Samtidig rettet: dobbel skråstrek i beregnede område-URL-er (//sites/) og literal «undefined» i navn/alias når navnekonvensjon ikke er konfigurert. (Port av gjenstående PP365-endringer som ikke fulgte med flyttingen av webdelen — upstream-deltaet er med dette fullt innhentet.)

  • Personfelter sendes nå via validateUpdateListItem i stedet for klientside ensureUser — bestilling virker for brukere med kun lesetilgang: Eiere/medlemmer/bestilt-på-vegne-av ble tidligere oppløst med ensureUser før innsending, som krever mer enn lesetilgang på bestillingsområdet og feilet for vanlige brukere. Nå sendes vanlige felter først, deretter oppløses personfeltene server-side; feiler oppløsningen slettes den halvferdige bestillingen og en egen feilmelding vises («Personer kunne ikke legges til»). Personvelgeren dedupliserer også søketreff og identifiserer valg på e-post/principal-nøkkel i stedet for visningsnavn, så to brukere med samme navn kan skilles (e-post vises på valgte brukere). (Port av PP365 PR #1766.)

  • SP-gruppeseksjonen forklarer nå sin rolle i forhold til gjesterollen: Med rollen Gjest får brukeren allerede tilgang via M365-gruppen (som er medlem av områdets medlemsgruppe) — seksjonen presenteres da som et rent tillegg, standard handling er ingen ekstra gruppe, og standardvalget heter «Bare legg til i standard SharePoint-medlemsgruppe» (siden gjesten uansett lander der via M365-gruppen) — mot «Ikke legg til i SharePoint-gruppe» når ingen rolle er valgt. Uten rollen forklarer seksjonen at en SP-gruppe (eller senere direkte deling) er eneste vei til tilgang, og velges verken rolle eller gruppe viser tilgangsforhåndsvisningen en advarsel om at invitasjonen alene ikke gir tilgang til området.

  • Gjesterollen feilet med 404 på gruppekoblede områder: Add-PnPMicrosoft365GroupMember fikk gjestens e-postadresse, men Graph adresserer brukere med objekt-id eller UPN — og en gjests UPN er navn_domene#EXT#@tenant, aldri e-posten, så oppslaget ga Not Found (404) for samtlige gjester. Runbooken utleder nå UPN-en fra claims-loginName som EnsureUser allerede returnerer, slår opp objekt-id-en via Graph og bruker den i kallet. Re-invitasjon av en som alt er medlem (Graph 400 «already exist») behandles nå som suksess i stedet for å feile forespørselen.

  • Gjesteinvitasjon med «Opprett ny gruppe» feilet på ikke-engelske områder: Runbooken sendte webdelens engelske nivånavn (Edit, Read …) rett til Set-PnPGroupPermissions -AddRole, men rolledefinisjonsnavn er språkavhengige per område — på norsk heter nivået «Redigering», og jobben stoppet med Role definition 'Edit' not found. Nivået slås nå opp via den språkuavhengige RoleTypeKind (Reader/Contributor/Editor/Administrator) med fallback til eksakt navnematch for egendefinerte nivåer, og områdets faktiske rollenavn brukes i kallet.

  • Tom guestRequestSiteUrl peker nå på /sites/bestillingsportalen: Fallbacken var gjeldende område — som bare fungerte når webdelen sto på portalen selv. Tomt felt betyr nå standard-aliaset fra installasjonen på gjeldende tenant, og feltet viser den utledede URL-en som placeholder. Miljøer med annet alias setter URL-en eksplisitt, som før.

  • Per-gjest-modus i invite-draweren starter nå med de konfigurerte standardene: Nye gjesterader arvet ikke defaultM365GroupRole, defaultSpGroupName/Besøkende-fallbacken (autoSelectVisitorGroup) eller defaultSpPermissionLevel — de falt stille tilbake til hardkodede verdier ved innsending. Radene seedes nå fra samme standarder som delt modus.

  • Manglende gruppenavn i per-gjest-modus ga stille blokkert innsending: Valideringsmeldingen «Gruppenavn er påkrevd» ble kun vist i delt modus; i per-gjest-modus ble innsendingen blokkert uten synlig tilbakemelding. Meldingen vises nå på den aktive gjestens SP-gruppeseksjon også.

  • Installasjonsveiledningen delt i to: Deployment-guide.md dekker nå kun den skriptede delen (forutsetninger, parametre, deploy.ps1, API-autorisering, SPFx) — leseren er den tekniske installatøren med Azure-tilganger. Ny Configuration-guide.md tar over derfra med alt som gjøres uten Azure-tilganger: godkjenningsoppsett, flyt-import/-aktivering, deling, støtte-Logic Apps, aktivering av maler/huber, administratorgruppe, auto-approval og Teams-appen (tidligere Steg 4–10, omnummerert til 1–7). Nytt Steg 8: Verifiser med en testbestilling beskriver ende-til-ende-testen (som vanlig bruker, ikke tjenestekontoen), statusløpet, hvor feil leses (StatusReason + runbookens stegtabell) og opprydding. Begge veiledningene advarer nå om token-forsinkelsen første døgn: managed identity-tokens caches i opptil ~24 timer, så en fersk installasjon kan gi sporadiske 401/403 og Roles on the request '' som leger seg selv — ikke et konfigurasjonsproblem. Alle kryssreferanser (Approval-flow, Teknisk løsningsbeskrivelse, Flows/README, deploy-output, ConfigureSpace-meldinger, README-navigasjonen) er pekt om.

  • F-lisenser friskmeldt for tjenestekontoen: dokumentasjonen og lisenssjekken advarte mot frontline-lisenser (F1/F3) basert på én FlowNotOriginalAuthor-feil — som viste seg å være miljøspesifikk for et utviklingsmiljø. En F3-lisensiert tjenestekonto er nå verifisert gjennom hele løpet i en reell kundetenant (import, aktivering og kjøring av godkjenningsflyten). FLOW_O365_S1 regnes derfor som brukbar plan i pre-flight-sjekken; kun uprovisjonerte viral-prøvelisenser (FLOW_P2_VIRAL) gir fortsatt advarsel. Forutsetningene, parameterbeskrivelsen og feilsøkingsboksen i konfigurasjonsveiledningens Steg 2 er justert tilsvarende — lisensbytte er siste utvei ved aktiveringsfeil, ikke førstevalg.

  • Velkomstmeldingen feilet når tjenestekontoen selv var bestiller: Post_Welcome_Message legger tjenestekontoen til som midlertidig gruppe-eier for å poste i Teams-kanalen — men når tjenestekontoen selv sendte inn bestillingen er den allerede eier fra opprettelsen, og Graph svarer 400 «One or more added object references already exist». Å bare svelge feilen ville vært verre: Remove_service_account_from_group etterpå hadde fjernet et legitimt eierskap. Flyten leser nå eierlisten først og hopper over både tillegget (inkludert to-minutters-ventingen) og fjerningen når kontoen allerede eier gruppen — midlertidig eierskap ryddes kun når flyten selv la det til.

  • ConfigureSpace feilet hele bestillingen når JoinHub var aktiv uten valgt hub: JoinHub kommer fra provisioning-typen og hub-ID-en fra brukerens valg i webdelen — er Hub Sites-listen tom (GetHubSites aldri kjørt, eller ingen hub med Enabled = true), kommer ID-en tom inn, og runbooken feilet med det kryptiske «Hub site with id '' was not found in the tenant». Det er et konfigurasjonshull, ikke en provisjoneringsfeil: steget hopper nå over med en melding som peker på GetHubSites og Enabled-flagget (konfigurasjonsveiledningens Steg 4–5).

  • Retry på tilkobling i ConfigureSpace mot transiente token-feil: PnP henter token lat — første kall etter Connect-PnPOnline — og identity-endepunktet i Automation-sandboxen har gitt tomme/uparserbare svar under raske påfølgende token-forespørsler (hvert steg kobler til på nytt). Steget feilet da med [Managed Identity] The error response was either empty or could not be parsed — sett i praksis på to av stegene i én kjøring. Connect-hjelperne tvinger nå token-hentingen umiddelbart (Get-PnPWeb) inne i en retry-løkke med økende backoff (4 forsøk), så hikken aldri når stegresultatene.

  • GenerateParameters.ps1 fant ikke ressursgruppa og regionen til et miljø som allerede var installert: resourceGroupName og region kom rett fra mal-defaultene, så en parameterfil generert for et eksisterende miljø sa rg-bestillingsportalen i norwayeast uansett hva kunden faktisk brukte (sett i praksis: ressursgruppa het Bestillingsportalen og sto i northeurope). Å deploye med det feiler ikke — det bygger en komplett andre installasjon i en ny ressursgruppe og etterlater den gamle. Skriptet slår nå opp ProcessProvisionRequest-Logic Appen i abonnementet — navnet er identisk i alle versjoner av løsningen, også provisionassist-*-generasjonen, mens ressursgruppenavn og region er frie valg ingen default kan gjette — og fyller begge verdiene fra funnet. Flere treff rapporteres i stedet for å gjettes på, ingen treff behandles som nyinstallasjon, og et eksplisitt -Region beholdes med en advarsel om avviket.

  • n på template-prompten lastet likevel opp pakkens bilder: valget lover å «leave the site untouched (only reads the list ids)», men UploadAssets var gated på upgrade-modus alene — ikke på svaret. Pakkens bilder og ikoner ble derfor skrevet over områdets egne uansett. Nå hopper den over når malen hoppes over.

  • New-AzResourceGroup ba om bekreftelse mot en eksisterende ressursgruppe: «Provided resource group already exists. Are you sure you want to update it?» er en interaktiv ShouldContinue som verken -Force eller -SkipConfirmation demper, så en uovervåket kjøring mot et eksisterende miljø ville stått og ventet på et svar — og «Created resource group» etterpå var direkte misvisende. Ressursgruppa opprettes nå bare når den mangler; finnes den, sier kjøringen det og oppgir regionen den står i.

  • Områdekonfigurasjonen slettet kundens egne bilder ved hver re-deploy: steget som oppretter SiteAssets/Provisioning Request med undermappene ProvTypesImages og ProvTypesIcons sjekket først om hovedmappa fantes — og slettet den rekursivt (Remove-PnPFolder -Force) hvis den gjorde, før den bygde strukturen på nytt. På en nyinstallasjon var det et no-op; på en re-deploy eller en full oppgradering av et miljø i drift kastet det alle bilder og ikoner kunden hadde lastet opp for sine egne provisioning types, og Image/Icon-URL-ene på de listeelementene pekte etterpå på slettede filer. Ingenting krevde nullstillingen: UploadAssets legger ut pakkens egne filer og overskriver sine egne navn uansett. Mappene resolves nå med Resolve-PnPFolder, som oppretter det som mangler og returnerer det som finnes — eksisterende innhold røres ikke.

  • «Connected to SPO» sa ikke hvor eller som hvem: linja bekreftet bare at Connect-PnPOnline ikke kastet. Siden den innloggingen kan fullføre stille fra PnPs token-cache på disk, er det nettopp tenanten og identiteten man trenger å se — begge skrives nå ut, sammen med admin-URL-en kjøringen er koblet til.

  • Et feilet områdeoppslag kunne ende i tilbudet om å slette kundens område: Get-PnPTenantSite feiler både når området ikke finnes og når den innloggede kontoen ikke får lese tenantens områdeegenskaper — feil konto plukket i PnP-nettleserprompten (den gjenbruker gjerne en cachet konto fra en annen tenant), manglende SharePoint-administrator-rolle, eller throttling. Lest med -ErrorAction SilentlyContinue var de to umulige å skille: begge ga $null, altså «ingen site her». En kjøring mot et eksisterende miljø gikk dermed inn i opprettelsesløpet, og to prompts senere sto tilbudet «PERMANENTLY delete this group (including its site)» på en produksjonsportal — sett i praksis i en dev-tenant der PnP hadde logget inn med en gjestekonto fra en annen tenant. Tre lag med retting: ny GetSiteExistenceState klassifiserer feilen og skiller NotFound (kun «File Not Found»/«Cannot get site»/404) fra Unknown, og et Unknown stopper kjøringen med årsak og sannsynlig konto-forklaring i stedet for å gjette; pre-flight rapporterer det samme som MISSINGSharePoint tenant site lookup, så kjøringen stopper før noe er endret; og gruppe-sjekken sammenligner nå gruppas faktiske område-URL (over Graph, som virker uavhengig av SPO-admin-tilgangen) med målområdet — eier gruppa nettopp det området vi sikter på, er det installasjonen vi oppgraderer og ikke rester fra et mislykket forsøk, og sletting tilbys ikke. PRE-FLIGHT SUMMARY viser i tillegg Signed in as (PnP) ved siden av Az- og CLI-identitetene, siden det er den identiteten som avgjør om området kan leses og malen anvendes.

  • GA-sjekken meldte «ingen Global Administrator» til en permanent Global Administrator: CheckAppRoleRights spurte /me/transitiveMemberOf/microsoft.graph.directoryRole og leste .value fra første side. Den transitive varianten paginerer over kontoens hele medlemskapssett og bruker directoryRole-castet per side, så en konto med mange gruppemedlemskap får side etter side med "value": [] og en @odata.nextLink — verifisert i kundetenant: 20 sider, alle tomme, på en konto med GA, Privileged Role Administrator, SharePoint-, Teams- og Power Platform-administrator. Å lese første side «beviste» dermed at kontoen ikke hadde noen roller i det hele tatt, og sjekken ba en GA om å kjøre -SkipAppRoles. Bytter til /me/memberOf/…, som returnerer alle rollene i ett svar. Advarselen navngir nå også rollene kontoen faktisk har, og sier at PIM-roller må aktiveres og at en rolle arvet via en gruppe ikke er synlig i dette oppslaget — «ingen GA funnet» alene lot deg ikke skille de tre.

  • Modulsjekken så ikke en modul som var lastet i økten: VerifyModules brukte Get-InstalledModule, som er den smaleste av de tre måtene en modul kan være brukbar — den ser bare det PowerShellGet har registrert, og altså ikke en kopi pakket ut for hånd eller importert fra en sti utenfor PSModulePath. Kjører du en side-lastet versjon (måten man tester en ny PnP-release), stoppet skriptet med «PnP.PowerShell module not installed» om en modul som var lastet og virket. Sjekken ser nå på lastet modul først (en lastet modul vinner uansett — PowerShell laster ikke en annen versjon inn i samme økt), deretter PSModulePath og Get-InstalledModule, og skriver ut versjon og hvor den kom fra. Av samme grunn hopper også Import-Module PnP.PowerShell over seg selv når modulen alt er lastet: import etter navn feiler for en side-lastet kopi.

  • MFA-kravet på Azure-skriving oppdages nå før kjøringen, ikke midt i den: tenanter som håndhever «MFA for Azure» avviser skriveoperasjoner fra en sesjon som autentiserte med passord alene. Lesing går fint, så ingenting merkes før første deployment — og den ligger på azureresources.bicep, altså etter at hele SharePoint-delen er kjørt. Symptomet er AADSTS50076 fra CLI-en eller RequestDisallowedByAzure fra ARM. Ny pre-flight-sjekk MFA on the Azure session leser amr-claimet ut av ARM-tokenet (kun claimet; tokenet logges aldri) og oppgir kommandoene som fikser det. Merk at --scope alene ikke holder: utfordringen CLI-en får tilbake er en Conditional Access authentication context ({"acrs":{"essential":true,"values":["p1"]}}), og uten --claims-challenge leverer Entra tilbake det samme passord-tokenet uten å spørre om noe — sett i praksis: iat blir nyere, amr står fortsatt pwd. Sjekken ber derfor om at man kjører az logout og deretter nøyaktig den az login-kommandoen CLI-en selv skriver ut, inkludert --claims-challenge. WARNING og ikke MISSING med vilje: håndhevingen avhenger av tenantens policy, og en passord-sesjon ble sett fullføre en hel installasjon samme dag — å blokkere ville stoppet kjøringer som virker.

  • Provisjonering stoppet etter en pre-1.0-oppgradering: runbook-kallet gikk uten Authorization-header: bestillingsportalen-automation er den eneste tilkoblingen som skiftet autentiseringsmodell i 1.0 — fra Entra ID-appens credentials til den user-assigned managed identityen. På en nyinstallasjon opprettes den for managed identity og melder Ready; på en oppgradering finnes den fra før med app-registreringens sertifikat-credentials, og ARM bytter bare parameterValueType til Alternative. Den gamle credentialen blir liggende, klarer ikke å fornye seg (AADSTS700027 når Key Vault-sertifikatet er rotert) og låser tilkoblingen i Error — designeren kaller den «Invalid connection», og Logic App-runtime sender runbook-kallet uten Authorization-header, så ConfigureSpace aldri starter og bestillingen feiler med «Authentication failed. The 'Authorization' header is missing.» Logic App-definisjonen var korrekt hele tiden (connectionProperties.authentication.type = ManagedServiceIdentity verifisert i den deployede arbeidsflyten); feilen satt i selve tilkoblingsressursen. deploy.ps1 sletter nå tilkoblingen og gjenoppretter den gjennom apiconnections.json når statusen ikke er Ready/Connected, og verifiserer resultatet. Sjekken ligger etter mal-deployen, ikke før: en tilkobling med en død credential står med den statusen den sist rakk å registrere — typisk Connected fra den opprinnelige installasjonen, fordi ingenting har prøvd å fornye den siden — og det er malens egen oppdatering som utløser fornyelsen og først da setter Error. En sjekk før deployen finner altså ingenting å reparere på nettopp den kjøringen som ødelegger tilkoblingen, og lar miljøet stå igjen ødelagt til neste kjøring (sett i praksis i et kundemiljø). Trygt å gjøre: en managed identity-tilkobling holder ingen credentials og trenger ingen autorisering, i motsetning til de fire delegerte. Verifisert mot både et oppgradert miljø (Error) og en nyinstallasjon (Ready).

  • To PnP.PowerShell 1.x-cmdletnavn i ConfigureSpace som ikke finnes i 3.x: Add-PnPUserToGroup (linje 301, besøkende-steget) og Set-PnPLabel (linje 507, oppbevaringsmerke-steget) ble fjernet av PnP før 3.x og finnes ikke i verken 3.2.0 eller 3.4.0 — heller ikke som aliaser. Begge stegene feilet altså med «not recognized» hver gang en bestilling hadde besøkende eller et oppbevaringsmerke; med fail-fast per steg felte de hele bestillingen. Erstattet med Add-PnPGroupMember -Group og Set-PnPRetentionLabel, som tar samme argumenter. Funnet ved å parse alle PnP-kall i repoet (AST) og verifisere cmdlet- og parameterflaten mot den importerte modulen — samme sjekk mot 3.2.0 som kontroll, så metodens blindsoner (dynamiske parametre på New-PnPSite) kunne skilles fra reelle funn.

  • «Connected to SPO» ble skrevet ut uten å være verifisert: Connect-PnPOnline kaster ikke når den fullfører fra token-cachen som en konto uten rettigheter i tenanten — feilen kommer først på neste tenant-admin-lesing, langt nok ned i kjøringen at den leses som et rettighetsproblem og ikke som en innlogging som traff feil konto. Linja bekreftet altså noe den ikke hadde sjekket. Nå gjøres en lesing mot tenant-admin-API-et rett etter innloggingen: går den, skrives URL og konto ut som før; går den ikke, sier linja eksplisitt at tilkoblingen ikke er brukbar, og den nye pre-flight-sjekken SharePoint admin access blokkerer kjøringen med kontoen, feilen og kommandoen som tømmer cachen. Alias-sjekken rapporterer SKIPPED med en peker dit i stedet for å legge på en andre rød linje for samme årsak.

  • PnP-innloggingen kan binde seg stille til en konto fra en annen tenant: PnP.PowerShell lagrer MSAL-cachen på disk (%LOCALAPPDATA%\.m365pnppowershell\pnp.msal.cache) uavhengig av -PersistLogin, så -Interactive fullfører uten å spørre som den kontoen som ligger der. Symptomet er ikke en innloggingsfeil, men Unauthorized på første tenant-admin-lesing — og å logge inn på nytt hjelper ikke, siden ingenting spør. Connect-PnPOnline -ForceAuthentication løser det heller ikke i praksis; det som virker er å slette cachefila. Pre-flight-sjekken over oppgir kommandoen når den treffer. PRE-FLIGHT SUMMARY viser i tillegg Signed in as (PnP), siden Az-, CLI- og PnP-innloggingene er tre uavhengige identiteter.

  • $deployUser ble lest før az account set og kunne komme fra feil tenant: UPN-en brukes til å gi installasjonsbrukeren site collection admin og til deployment-pingbacken, men az ad signed-in-user show kjørte før abonnementet ble valgt — den returnerte altså den kontoen Azure CLI tilfeldigvis stod på, som med cachede sesjoner i flere tenanter godt kan være en bruker i en helt annen tenant. Oppslaget er flyttet etter az account set. Samtidig er Set-PnPTenantSite -Owners pakket i egen try/catch: tildelingen er en bekvemmelighet, den kan legitimt feile for en gjestekonto, og en feil der skal ikke rive med seg hele områdesteget og dermed installasjonen.

  • Alias-valideringen blokkerte re-deploy etter URL-endring på området: ValidateSiteAlias sjekket kollisjon mot tjenestekontoen uansett, men kollisjonen er bare relevant når et nytt område skal opprettes — aliaset blir gruppens mailNickname ved opprettelse og aldri igjen. Etter en URL-endring i SharePoint admin-senter (som endrer URL-en men ikke gruppens mailNickname) må requestsSiteAlias settes til det nye URL-segmentet for at Logic Apps skal redeployes mot riktig URL — og det segmentet kan gjerne være tjenestekontoens navn, nøyaktig kollisjonen sjekken finnes for ved nyinstallasjon. Sjekken hopper nå over seg selv når området allerede finnes på den beregnede URL-en.

  • New-PnPSite hang til evig tid fordi tjenestekontoen var eneste eier: området ble opprettet med -Owners <tjenestekontoen>, og gruppekoblede områder gir kun tilgang via gruppemedlemskap — installasjonsbrukeren som kjørte cmdlet-en hadde dermed ingen tilgang til det nye området, og New-PnPSite sin «vent til området er klart»-løkke polte et område den aldri fikk åpnet. Symptomet var at skriptet hang på «Creating the site …» mens admin-senteret viste området som opprettet med tjenestekontoen som eier. Nå opprettes området uten -Owners (kalleren blir automatisk gruppe-eier og -medlem, så ventingen fullfører), og tjenestekontoen legges til rett etterpå som både eier og medlem — eierskap gir ikke medlemskap i Microsoft 365-grupper, og godkjenningsflyten og Teams-velkomstmeldingen kjører som denne kontoen. Tildelingen gjøres idempotent med az CLI (fungerer uavhengig av PnP-appens delegerte Graph-tillatelser), og feiler den, fortsetter kjøringen med en tydelig manuell instruks i stedet for å stoppe.

  • Områdeopprettelsen kollapset når aliaset var opptatt av en bruker: kunder har gjerne en tjenestekonto som bestillingsportalen@kunde.no, og aliaset ble utledet fra requestsSiteName — altså bestillingsportalen. Aliaset blir Microsoft 365-gruppens mailNickname og deler navnerom med alle brukere og grupper, så det var opptatt. SharePoint feiler ikke på det: det opprettet gruppen som bestillingsportalen1. Skriptet leste ikke URL-en New-PnPSite returnerte, så alt videre pekte på /sites/bestillingsportalen, som ikke fantes — og kjøringen døde i Set-PnPTenantSite med Object reference not set to an instance of an object, etter fem minutter uten spor av den egentlige årsaken. Tre rettinger: (1) nytt requestsSiteAlias-parameter, som faller tilbake til gammel utledning når det er tomt slik at eksisterende installasjoner peker på området sitt — standardverdien er fortsatt bestillingsportalen, fordi Teams-appen har URL-en /<managedPath>/bestillingsportalen hardkodet og området må ligge der; er aliaset opptatt, er framgangsmåten å opprette området på et ledig alias og deretter endre URL-en (URL-endringen rører ikke gruppens mailNickname), dokumentert i installasjonsveiledningen; (2) ny ValidateSiteAlias i pre-flight som stopper kjøringen før noe opprettes hvis aliaset kolliderer med tjenestekontoens UPN/mailNickname eller med en annen bruker i tenanten, med den framgangsmåten som Fix-tekst — de eksisterende sjekkene så bare etter grupper på aliaset, som var nettopp det som glapp; (3) New-PnPSite-returverdien brukes nå, så en omdøpt URL gir en tydelig advarsel og resten av kjøringen følger området som faktisk ble opprettet i stedet for å kollapse.

  • throw('... {0}', $melding) formaterte ikke: feilmeldingen fra områdeopprettelsen nådde konsollet med en bokstavelig {0} i seg, fordi throw med komma kaster en to-elements array framfor å formatere. Bruker -f nå.

  • 403 «Insufficient privileges» når gjest legges i Medlem-/Eier-gruppen på M365-gruppe-koblet site: Add-PnPMicrosoft365GroupMember/Owner resolver gjesten via GET /users/{email} før den poster til members/$ref. Managed identityen hadde bare Group.ReadWrite.All, så brukeroppslaget feilet med 403. User.Read.All lagt til i AssignManagedIdentityPermissions — kjør deploy.ps1 -Upgrade for å tildele den ekstra appen til eksisterende automation account.

  • Sitelogoparameteren virket ikke: deploy.ps1 leste logoUrl fra parameters.json, men parameteren heter siteLogoPath — verdien ble alltid tom. Rettet til å lese siteLogoPath.

  • Entra ID-appen ba om unødvendige application-tillatelser (mellomsteg — appmanifest.json og hele app-registreringen er fjernet senere i samme versjon, se Sikkerhet-seksjonen): appmanifest.json er trimmet til kun den delegerte Group.ReadWrite.All-tillatelsen (ROPC for sensitivitetsmerker). Alle application-rollene ligger på managed identities etter migreringen — nye installasjoner får dermed en app uten tenant-vide app-tillatelser. Eksisterende installasjoner: se fase 2 i Oppgraderingsveiledningen.

  • AssignPermissionsToManagedIdentity.ps1: default-scopene matcher nå det deploy.ps1 tildeler automation-kontoens identity (Group.ReadWrite.All + User.Read.All, pluss -IncludeSharePointSitesFullControl), og skriptet returnerer exit-kode 1 hvis én eller flere tildelinger feiler (før kunne alle feile med exit 0).

  • api.id i apiconnections.json: tre av tilkoblingene manglet ledende / i managed API-referansen; normalisert for alle seks.

Om versjonshistorikken

Versjonshistorikken for Bestillingsportalen starter på 1.0.0. Taggen v0.9.0 i repoet er arvet fra oppstrøms pnp/provision-assist-m365-linjen og svarer ikke til en Bestillingsportalen-release — den peker på en commit fra februar 2025, lenge før arbeidet som utgjør 1.0.0. Den er beholdt som den er, siden den er publisert, men skal ikke brukes til å resonnere om hva et miljø kjører.

Versjonsnummeret ligger i VERSION i repo-rot. Se CONTRIBUTING.md for versjonsskjemaet og release-rutinen.