D365 · Power Platform ALM 學習站

Lab 導覽

開始,建立一條跨
多租戶(Tenant)的 Dynamics 365 交付流水線

即使你從沒用過 Azure DevOps,也能照著本站一步步:註冊帳號 → 版控方案 → 建立 Pipeline → 佈署到 Sandbox 測試 → 核准上線正式環境,最後統一佈署到多個獨立 Tenant。

0→1
從註冊帳號開始
8
Lab 操作階段
4
階段流水線
5
子公司 Tenant
怎麼用

左側是章節導覽。第一次接觸建議從上到下讀:先建立名詞概念(01–03),再進入「04 Lab 逐步操作手冊」實作。手機上點左上角 ☰ 開選單。

本站涵蓋什麼

01 · 入門

先懂這些名詞

在動手前,先花五分鐘把這些字認熟。之後每個操作步驟都會用到它們。

TENANT(一間子公司 = 一個租戶) ENVIRONMENT · Dev(開發) Solution(方案) Table · App · Flow · Plugin… Unmanaged(可編輯) ← 唯一可以改東西的地方 ENVIRONMENT · Prod(正式) Solution(方案) 同一份,但為 Managed Managed(唯讀 · 可乾淨移除) ← 只接受打包好的版本 Pipeline

圖:Tenant ⊃ Environment ⊃ Solution。開發在左邊 Dev 改,Pipeline 打包後送到右邊 Prod。

名詞白話解釋
Tenant一間公司在微軟雲上的「帳號世界」。每間子公司各有一個,彼此獨立。
EnvironmentPower Platform 的一個「資料庫+容器」。同一個 Tenant 內可以有多個,用來分 Dev / Sandbox / Prod。
DataverseEnvironment 背後存資料的平台(表格、欄位都放這)。
Solution 方案把你做的客製(表格、表單、App、流程、程式)包成一包,方便搬到別的環境。
Unmanaged「可編輯」的方案,只存在 Dev。這是你版控與修改的來源。
Managed「唯讀、可乾淨解除安裝」的方案,由 Pipeline 打包產生,只進 Test / Prod。
Azure DevOps微軟的開發協作平台:放程式碼(Repos)、跑自動化(Pipelines)、管測試(Test Plans)。
RepoGit 版控倉庫,存放拆解後的方案原始碼,可以看每次改了什麼。
Pipeline一段自動化流程腳本(YAML)。例如「把方案打包再佈署到某環境」。
Service Principal (SPN)給機器用的「應用程式帳號」,讓 Pipeline 不需要人輸入密碼就能登入某個 Tenant 佈署。
Application User把上面那個 SPN 加進某個 Environment,賦予它權限。
Service ConnectionAzure DevOps 裡設定的一條「通往某環境的連線」,Pipeline 靠它連上目標 Tenant。
pac CLIPower Platform 的命令列工具,本機用來匯出 / 匯入方案。
一句話串起來

你在 Dev 環境Solution(Unmanaged)→ 存進 RepoPipeline 打包成 Managed → 透過 Service Connection(背後是 SPN)佈署到各 TenantProd

02 · 概念

整體架構

一個集中的 Azure DevOps 管所有版控與自動化;透過各租戶的專屬連線,把方案佈署到多個獨立的 Tenant。

Azure DevOps Organization(集團) Repos · Pipelines · Test Plans 1 個起點,集中治理與稽核 export → build → release TENANT A TENANT B TENANT C TENANT D TENANT E Sandbox Prod Sandbox Prod Sandbox Prod Sandbox Prod Sandbox Prod

圖:單一 DevOps 起點,透過各租戶專屬連線統一佈署到多個獨立 Tenant,每個 Tenant 內有 Sandbox 與 Prod。

混合模式:共用核心 × 在地擴充

你們的情境同時有「集團共用邏輯」與「各子公司差異」,因此方案分兩層:

Core · 統一佈署

集團共用核心方案

中央團隊維護,一次開發,透過 Pipeline 同一版本統一佈到多個 Tenant,確保一致。

Regional · 獨立佈署

各子公司區域方案

各子公司自行擴充在地化欄位、報表、整合,各自一套 Dev→Sandbox→Prod,只推自己的 Tenant。

相依

Regional 方案相依於 Core,因此佈署順序永遠是先 Core、後 Regional。Pipeline 已內建此順序。

四階段流水線

01 EXPORT 匯出 → 進 Git 02 BUILD 打包 + 檢查 03 SANDBOX 佈署 + 測試 04 PROD 核准 + 上線

前兩段(01、02)不碰任何環境,是純版控與建置;後兩段(03、04)才真正佈署到租戶。04 Lab 手冊會帶你把這四段全部建起來。

03 · 概念

ALM 與方案策略

ALM(應用程式生命週期管理)的核心就四條規則。記住它們,後面的 Pipeline 設計就都合理了。

規則一

Dev 只放 Unmanaged

只有 Dev 環境可以編輯;它是你版控的唯一來源。

規則二

Test / Prod 只收 Managed

由 Pipeline 打包產出,唯讀、可乾淨解除安裝。

規則三

絕不在正式環境手改

要改就回 Dev 改,重走流程。

規則四

一切走 Git → PR → Pipeline

不走人工匯出匯入,才有稽核與品質關卡。

為什麼要拆解成原始碼?

方案匯出是一個 .zip,直接進 Git 無法比對差異。所以我們用 pac solution unpack 把它拆成上千個檔案,這樣每次改了哪個表單、哪個欄位,PR 上一目了然。

# 匯出並拆解(Lab 會用腳本 export-local.ps1 幫你做)
pac solution export  --name ContosoCore --managed false
pac solution unpack  --folder solutions/ContosoCore/src

跨環境最容易踩雷:連線與環境變數

Flow 的連線(Connection Reference)與環境相依的設定(Environment Variable),在搬到不同環境時不能把 Dev 的值帶過去。做法是用一份 deployment settings JSON,在匯入時重新綁定目標環境的正確值。

Lab 會示範

同一份打包好的方案,換一份 settings 檔 + 換一條 Service Connection,就能佈到不同 Tenant 的 Sandbox 或 Prod —— 這正是「一次建置、多處佈署」的關鍵。

04 · 動手做

Lab 逐步操作手冊

假設你完全沒用過 Azure DevOps。我們從註冊帳號開始,一路做到跨租戶正式上線。建議先用「一到兩間子公司」跑通,再複製到其餘子公司。全程約半天。

開始前

每張畫面都是「schematic 示意」並附圖片編號(如 image08)。當你實際操作後截圖,只要告訴我「image08 換這張」,我就能把對應的示意圖替換成你的真實截圖。點下方任一卡片可直接跳到該階段。

動手前:需要哪些授權?

做這個 Lab(以及日後正式導入)需要下列授權與角色到位。多間子公司各自獨立 Tenant,下列項目每個 Tenant 都要各自具備。Lab 練習可先用免費方案在一到兩間跑通。

項目需要的授權 / 角色用途Lab 免費起步
Azure DevOpsBasic 授權(每 Org 前 5 位使用者免費)Repos 版控、Pipelines 自動化✓ 免費
Azure Test PlansBasic + Test Plans 授權(付費)Sandbox 手動 UAT / 迴歸測試試用 30 天
Power Platform 環境Power Apps Developer Plan,或 Per-app / Per-user 授權;正式另需 Dataverse 容量Dev / Sandbox / Prod 環境✓ Developer Plan 免費
Power Platform 系統管理員Power Platform Administrator 角色建環境、加 Application User需管理員指派
Microsoft Entra IDApplication Developer 角色(或全域管理員)建立跨租戶佈署用的 App 註冊(SPN)需管理員指派
Premium 連接器(視需要)Power Automate Premium / Per-userFlow 若用到自訂 API、HTTP、Approvals 等進階連接器試用可用
Azure 訂用帳戶(建議)可建立 Key Vault 的訂用帳戶正式環境存放密鑰(免密鑰聯合認證亦需)正式階段
開發工具—(免授權)VS / VS Code、Git、pac CLI、.NET SDK✓ 全免費
最低可跑組合

只想先跑通流程:1 個 Azure DevOps Org(免費)+ 2 個 Power Platform Developer 環境(免費)+ 你自己是該租戶管理員即可完成 Phase 0–7。Test Plans 與 Key Vault 可先用試用或跳過自動化測試。

八個階段

Phase 0

準備帳號與工具

⏱ 約 60 分鐘 · 一次性設定

先確認授權

動手前,請先對照「概覽」頁的授權需求表,確認 Azure DevOps、Power Platform 環境與管理員角色到位。最低可跑組合:1 個免費 DevOps Org + 2 個 Developer 環境 + 你是該租戶管理員。

1

建立 Azure DevOps 帳號與組織(Organization)

用瀏覽器開 dev.azure.com,點「Start free」,用你的微軟帳號登入。第一次登入會請你建立一個 Organization(就是你團隊的最上層容器)。

image01 Azure DevOps 首頁 · 點 Start free 登入建立 Organization

https://dev.azure.com
Azure DevOps

開始免費使用,管理程式碼、Pipeline 與測試

Start free 🖱
▲ 點這裡,用微軟帳號登入後建立你的 Organization(例:contoso-group)
命名

Organization 名稱建議用集團代號,如 contoso-group。之後所有子公司的 Repo 與 Pipeline 都在這底下。

2

為每個 Tenant 建立 Power Platform 環境

admin.powerplatform.microsoft.com(Power Platform 系統管理中心),為練習先建立三個環境:Core-DevSubA-SandboxSubA-Prod。沒有付費環境可用免費的 Developer 類型起步。

image02 Power Platform 系統管理中心 · 建立環境(Sandbox/Developer)

admin.powerplatform.microsoft.com/environments
Power Platform 系統管理中心
環境 › 建立
名稱
SubA-Sandbox
類型
Sandbox(測試用)| 其他:Production / Developer
建立 Dataverse 資料庫
建立 🖱
重點

正式練習跨租戶時,SubA-* 要建在 SubA 那個 Tenant 底下(用該 Tenant 的帳號登入)。先在單一 Tenant 練也可以,之後再複製。

3

安裝本機工具

安裝 Git、VS Code,以及 Power Platform CLI(pac)。開一個終端機執行:

# 安裝 pac CLI(需先有 .NET SDK)
dotnet tool install --global Microsoft.PowerApps.CLI.Tool

# 驗證安裝成功
pac help
驗收

pac help 有列出指令清單,代表安裝成功,Phase 0 完成。

Phase 1

建立 Project 與 Repo

⏱ 約 20 分鐘

1

在 Organization 裡建立一個 Project

進入你的 Organization,右上角點「New project」。命名 Contoso-PowerPlatform,Visibility 選 Private。

image03 建立 Project(New project · Private)

dev.azure.com/contoso-group
contoso-group Overview
+ New project 🖱
Project name
Contoso-PowerPlatform
Visibility
Private
2

把本 Lab 的 repo 內容放進 Repos

左側選單點 Repos。把配套檔案(pipelines/、docs/、config/、scripts/)推上來。若你手邊已有本 Lab 資料夾:

git init
git add .
git commit -m "初始化 ALM Lab"
git remote add origin https://dev.azure.com/contoso-group/Contoso-PowerPlatform/_git/ContosoCore
git push -u origin main

image04 Repos 檔案結構(push 後)

.../Contoso-PowerPlatform/_git/ContosoCore
Repos › Files
pipelines/
docs/
config/
scripts/
README.md
3

安裝 Power Platform Build Tools 擴充

點右上齒輪 → Organization settingsExtensions → Browse marketplace,搜尋「Power Platform Build Tools」(發行者 microsoft-IsvExpTools),安裝到你的 Organization。裝一次,全 Org 的 Pipeline 都能用。

image05 安裝 Power Platform Build Tools 擴充

marketplace.visualstudio.com › Power Platform Build Tools
Power Platform Build Tools
Microsoft · 提供 export / import / pack / checker 等 Pipeline 工作
Get it free 🖱
驗收

Extensions 清單看得到已安裝的 Build Tools,Phase 1 完成。

Phase 2

跨租戶身分(最關鍵的一段)

⏱ 約 45 分鐘 · 每個目標環境做一次

這是整個 Lab 最容易卡關、也最重要的一段。目標:讓 Pipeline 不用人輸入密碼,就能登入某個 Tenant 的環境去佈署。做法是建立一個「機器人帳號」(Service Principal),讓三方彼此認得。

先看懂這張圖

身分驗證有「三隻腳」:① Entra ID 裡的 App(機器人本體 + 密鑰)② Dataverse 環境把它認成一個有權限的使用者 ③ Azure DevOps 用一條 Service Connection 拿著密鑰去登入。三者都對上,Pipeline 才佈得動。

① Entra ID App Client ID + Secret (機器人本體) ② Dataverse 環境 Application User + System Admin 角色 ③ DevOps 連線 Service Connection 加為使用者(步驟 4) 拿密鑰(步驟 5) 登入佈署
1

進入該 Tenant 的 App 註冊頁

該 Tenant 的系統管理員登入 portal.azure.com,上方搜尋列打「Microsoft Entra ID」進入 → 左側點 App registrations → 上方 + New registration

image06 Entra ID › App registrations › New registration

portal.azure.com › Microsoft Entra ID
Microsoft Entra ID Tenant:SubA ▾
Overview
App registrations
Enterprise applications
+ New registration 🖱
最常見錯誤

登錯 Tenant!右上角確認目前是 SubA 的 Tenant。各子公司 Tenant 各自獨立,App 一定要建在「要佈署的那個 Tenant」裡。

2

註冊 App 並記下兩組 ID

Name 填 PP-DevOps-SubA;「Supported account types」選第一個「僅此組織目錄」;Redirect URI 留空。按 Register。建立後在 Overview 記下 Application (client) IDDirectory (tenant) ID

image07 註冊 App 並記下 Client ID / Tenant ID

App registrations › Register an application
Register an application
Name
PP-DevOps-SubA
Supported account types
◉ 僅此組織目錄中的帳戶(單一租戶)
Register 🖱
複製這兩個值,等一下要用:
✓ Application (client) ID: a1b2c3d4-…-4444
✓ Directory (tenant) ID: t1t2t3t4-…-9999
3

建立 Client Secret(密鑰)

左側 Certificates & secrets+ New client secret → 填說明、選到期日(Lab 選 90 天或 1 年)→ Add。畫面會出現一組 Value立刻複製

image08 建立 Client Secret(Value 只顯示一次)

PP-DevOps-SubA › Certificates & secrets
Certificates & secrets
Client secrets › New
Description
devops-pipeline
Expires
90 days
Value: Qz8~kP…(只顯示這一次) 📋
只出現一次

離開頁面後 Value 就再也看不到。沒複製到只能刪掉重建。正式環境強烈建議改用免密鑰的 Workload Identity Federation(見「08 治理與資安」)。

4

把這個 App 加進目標環境,當 Application User

現在這個機器人存在於 Entra ID,但 Dataverse 環境還不認得它。到 Power Platform 系統管理中心把它加進去。兩種做法,擇一:

做法 A — 用畫面(推薦新手)

image09 於目標環境新增 Application User + 角色

admin.powerplatform › 環境 › SubA-Sandbox › 設定
SubA-Sandbox › 使用者 + 權限
應用程式使用者(Application users)
使用者
安全性角色
+ 新增應用程式使用者 🖱
選擇應用程式
PP-DevOps-SubA(貼上 client id 搜尋)
安全性角色
System Administrator

做法 B — 用腳本(快):先用系統管理員 pac auth create 登入該 Tenant,再跑本 Lab 腳本:

./scripts/setup-appuser.ps1 \
  -EnvironmentUrl https://subA-sbx.crm5.dynamics.com \
  -AppId a1b2c3d4-…-4444
白話

這步等於「幫機器人在該環境開一個管理員使用者」。Lab 用 System Administrator 最省事;正式環境建議改成剛好夠匯入方案的自訂角色。

5

在 Azure DevOps 建立 Service Connection

回到 DevOps 的 Project → 左下 Project settingsService connectionsNew service connection → 選 Power Platform,把前面收集的四樣東西填進去:環境 URL、Tenant ID、Client ID、Client Secret。

image10 建立 Power Platform Service Connection

Project settings › Service connections › New
New Power Platform connection
Authentication
Service Principal(或 Workload Identity Federation)
Server URL
https://subA-sbx.crm5.dynamics.com
Tenant ID
t1t2t3t4-…-9999
Application (client) ID
a1b2c3d4-…-4444
Client secret
••••••••••
Connection name
PP-Regional-SubA-Sandbox
Save 🖱
命名規範

PP-<範圍>-<子公司>-<階段>,例如 PP-Core-SubA-ProdPP-Regional-SubA-Sandbox。一致命名讓 Pipeline 參數化很乾淨。每個環境一條連線

6

驗證連線是否真的通

別急著往下!先確認三隻腳有對上。最快的方式:建一個一次性 Pipeline 用該連線跑 pac org who,或直接在 Service connection 頁點 Verify。若失敗,對照下表排查。

錯誤訊息原因回哪一步
401 / 未授權Secret 貼錯或已過期步驟 3 重建 secret
找不到使用者 / 無權限App 未加進環境步驟 4
環境找不到URL 打錯或登錯 Tenant步驟 5 檢查 Server URL
驗收

Verify 成功、或 pac org who 印出目標環境資訊 → Phase 2 完成。之後每多一個目標環境(SubA-Prod、SubB-Sandbox…)就重複這 6 步。

Phase 3

用範例程式碼建立方案並驗證

⏱ 約 90 分鐘

本階段會用配套 repo 的範例程式碼(Plugin、表單 JS、Power Automate 流程),在 Core-Dev 建立一個真正能動的方案,並逐項驗證。程式碼位置:solutions/ContosoCore/

1

請在 Core-Dev 建立 Solution 與 Publisher

請以瀏覽器開啟 make.powerapps.com,右上角切換到 Core-Dev 環境,左側點 SolutionsNew solution。名稱填 ContosoCore,並新增 Publisher,前綴填 contoso,按 Create

image11 建立 Solution 與 Publisher(前綴 contoso)

make.powerapps.com › Solutions
Power Apps環境:Core-Dev ▾
Solutions › New solution
Display name
ContosoCore
Publisher
Contoso(前綴 contoso)
Create 🖱
前綴一旦定了幾乎不能改

Publisher 前綴(如 contoso_)會出現在所有欄位名稱裡,Lab 一開始就要想好。

2

請建立 Customer 資料表與四個欄位(對應範例 Entity.xml

在方案內按 NewTable,名稱 Customer。接著按 + New column 依下表建立四個欄位——這些正是範例程式碼會用到的欄位。同時新增一個 Environment Variable contoso_ApiBaseUrl(示範跨環境重新綁定)。

顯示名稱邏輯名稱類型必填
客戶名稱contoso_name單行文字(主要欄位)
客戶編號contoso_accountnumber單行文字
電子郵件contoso_email單行文字(Email 格式)
信用額度contoso_creditlimit貨幣
✓ 驗證

在資料表的 Columns 清單看到以上四個 contoso_ 欄位,即完成。

3

請先在本機建置並測試範例 Plugin(伺服器端邏輯)

Plugin 是手寫的 C# 程式碼,先在本機驗證邏輯再上線。請開終端機進到 Plugin 專案,執行建置與單元測試:

# 首次:產生強式簽署金鑰
sn -k solutions/ContosoCore/plugins/ContosoCore.Plugins/ContosoCore.Plugins.snk

# 建置 + 跑 FakeXrmEasy 單元測試(不需連線環境)
dotnet test solutions/ContosoCore/plugins/ContosoCore.Plugins.Tests

image12 本機 dotnet test 結果(4 個測試通過)

terminal — dotnet test
Passed! - Failed: 0, Passed: 4, Skipped: 0
✓ 未填客戶編號時,應自動產生 CUST- 開頭的編號
✓ 已保留原有客戶編號,不覆蓋
✓ 同名客戶已存在時,應擲出例外
✓ 客戶名稱空白時,應擲出例外
✓ 驗證

看到 Passed: 4 代表 CustomerPreCreatePlugin 的邏輯正確。這一步的驗證之後也會由 Pipeline(Phase 5)自動重跑。

4

請把 Plugin 註冊到 Core-Dev,並掛在客戶建立事件上

用 pac 將組件推上環境,再於方案的 Customizations 依範例 Customizations.xml 的設定新增處理步驟(Message = Create、Table = contoso_customer、Stage = PreOperation、同步):

pac auth create --url https://core-dev.crm5.dynamics.com
pac plugin push --path solutions/ContosoCore/plugins/ContosoCore.Plugins/bin/Release/net462/ContosoCore.Plugins.dll
✓ 驗證(在真環境)

開 Model-driven App 新增一筆客戶:
不填客戶編號按 Save → 客戶編號自動變成 CUST-yyyyMMdd-nnnn
② 再建一筆同名客戶 → 出現錯誤「已存在相同名稱的客戶」被擋下。

5

請加入表單 JavaScript(用戶端驗證)

在方案內按 NewMoreWeb resource,類型選 JavaScript (JS),名稱 contoso_/customer/form.js,內容上傳範例檔 src/WebResources/contoso_/customer/form.js。接著打開 Customer 主表單,於 Form libraries 加入此 Web Resource,並掛事件:OnLoadContoso.Customer.onLoadOnSaveContoso.Customer.onSave、Email 欄位 OnChangeContoso.Customer.validateEmail

image13 表單事件掛載(OnLoad / OnSave / OnChange)

Form designer › Events › Event Handlers
Customer 主表單 › 事件
OnLoad → Contoso.Customer.onLoad
OnSave → Contoso.Customer.onSave
contoso_email · OnChange → Contoso.Customer.validateEmail
✓ 驗證(在真環境)

在客戶表單的 Email 欄輸入 abc(錯誤格式)→ 欄位即時出現紅字提示;按 Save 會被攔截無法儲存。改成 a@contoso.com 後即可存檔。

6

請加入 Power Automate 訂單核准流程(低程式碼)

在方案內按 NewAutomationCloud flow,參照範例 Workflows/OrderApproval/definition.json 的邏輯:觸發「當訂單建立」,判斷金額,> 50000 送 Approvals 核准、否則自動核准。連線請用 Connection Referencecontoso_dataversecontoso_approvals),不要直接綁個人連線。

為何用 Connection Reference

這樣跨環境搬移時,才能由 deployment settings 重新綁定各環境的連線(Phase 6 會用到)。直接綁個人連線會導致佈署後流程失效。

✓ 驗證(在真環境)

手動建立一筆金額 60000 的訂單 → 流程執行記錄顯示已送出核准要求;建立金額 1000 的訂單 → 狀態直接標記為已核准。

7

請發佈所有客製,準備進版控

回到方案,按 Publish all customizations。至此你已用範例程式碼做出一個能動的方案,下一階段(Phase 4)會把它匯出進 Git。

本階段完成 · 建立與驗證對照

資料表/欄位 → Columns 清單可見 ✓
Plugin(C#) → 本機 dotnet test 4 綠 + 真環境自動編號/擋重複 ✓
表單 JS → Email 錯誤即時提示、存檔被擋 ✓
Power Automate → 大額訂單送核准、小額自動核准 ✓

Phase 4

版控:匯出、Commit、開 PR

⏱ 約 25 分鐘

1

把 Dev 的方案匯出成原始碼

用本 Lab 腳本一鍵完成(也可用 Pipeline 01-export-and-commit.yml):

./scripts/export-local.ps1 -EnvironmentUrl https://core-dev.crm5.dynamics.com -SolutionName ContosoCore

它會匯出 Unmanaged 並拆解到 solutions/ContosoCore/src/

2

Commit 並開 Pull Request

git checkout -b feature/init-core
git add solutions/ContosoCore/src
git commit -m "新增 Customer 表與訂單流程"
git push origin feature/init-core

回 DevOps Repos → 會出現「Create a pull request」提示,點它,Target 選 main,建立 PR。

image14 開 Pull Request(feature → main)

Repos › Pull requests › New
New pull request
feature/init-core → main
Title
新增 Customer 表與訂單流程
Create 🖱
會發生什麼

開 PR 會自動觸發下一階段的 Build Pipeline 當作品質關卡。

Phase 5

Build Pipeline:打包 + 檢查

⏱ 約 20 分鐘

1

建立指向 YAML 的 Pipeline

左側 Pipelines → New pipeline → 選 Azure Repos Git → 選你的 repo → 選「Existing Azure Pipelines YAML file」→ 指向 /pipelines/02-build-managed.yml

image15 建立 Pipeline 指向 02-build-managed.yml

Pipelines › New pipeline
Where is your code?
Azure Repos Git
GitHub
Existing YAML file → /pipelines/02-build-managed.yml
Run 🖱
2

觀察它跑並產出 artifact

Pipeline 會:打包 Managed → 跑 Solution Checker → 蓋版本號 → 發佈 drop_ContosoCore artifact。若 Checker 發現嚴重問題,Pipeline 會失敗(這就是品質關卡)。修正後重跑通過,合併 PR。

驗收

Pipeline 綠燈,且在該次執行的 Artifacts 看到 ContosoCore_managed.zip

Phase 6

佈署到 Sandbox + 觸發測試

⏱ 約 40 分鐘

1

填好 deployment settings

編輯 config/deployment-settings/regional.subA.test.json,把連線 ID 與環境變數值填成 SubA-Sandbox 的正確值。

2

建立 release-sandbox Pipeline

同樣方式指向 /pipelines/03-release-sandbox.yml,設定為 Build 完成後觸發。它會用 PP-Regional-SubA-Sandbox 連線匯入方案,並套用 settings 重新綁定連線。

這就是重點

同一份 artifact,換 Service Connection + 換 settings 檔,就佈到不同 Tenant 的 Sandbox。要換成 SubB?換連線名稱即可。

3

在 Azure Test Plans 建立測試

左側 Test Plans → New Test Plan(SubA-Sandbox-UAT)→ 加 Test Suite 與 Test Case(一步一步的驗收步驟)。Pipeline 佈署完成會建立 Test Run,通知測試人員執行。

image16 Azure Test Plans 測試套件與結果

Test Plans › SubA-Sandbox-UAT
Test Plans
新增客戶並驗證必填欄位 Passed
建立訂單 → 審核 Flow 觸發 Passed
重複客戶偵測 Active
驗收

SubA-Sandbox 環境出現該方案,且 Flow 連線指向測試環境的值而非 Dev。測試案例可執行並標記通過。

Phase 7

核准後佈署到正式

⏱ 約 30 分鐘

1

建立帶核准的 Environment

Pipelines → Environments → New environment,命名 SubA-Prod。進去後 → 右上 ⋯ → Approvals and checks → 加 Approvals,指定核准人(先指定你自己)。

image17 設定 Environment 核准人(Approvals)

Pipelines › Environments › SubA-Prod › Approvals
SubA-Prod · Approvals and checks
Approvers
你(集團架構師)
Instructions
確認 Sandbox 測試已通過再核准
Create 🖱
2

執行 release-production Pipeline

指向 /pipelines/04-release-production.yml,手動觸發。它會在佈署 SubA-Prod停下等待核准;你按核准後才匯入。

image18 佈署前等待核准 → 按 Approve

Pipelines › release-production · Run
Deploy_SubA_Prod
⏸ Waiting for approval — 1 approver required
Approve 🖱
驗收

SubA-Prod 出現方案;DevOps 上可完整追溯「哪個 commit → 哪個 build → 誰核准」。

Phase 8

統一佈署到更多 Tenant

⏱ 約 20 分鐘 · 每多一間重複一次

1

為 SubB 重複身分設定

回到 Phase 2,為 SubB 的環境建立 SPN、Application User、Service Connection(PP-Core-SubB-Prod),並建 Variable Group env-subB

2

啟用統一佈署 stage

04-release-production.yml 啟用 Deploy_SubB_Prod stage,觸發一次,觀察同一版 Core 依序統一佈署:先導試佈 SubA,通過後再推 SubB 的正式環境。

收尾話術

「新增下一間子公司?建環境 + 一組 Service Connection + 複製一個 stage,一天內就能上線流程。」擴到更多 Tenant 只是重複這兩步。

卡關時看這裡

症狀可能原因解法
匯入失敗 missing dependencyRegional 先於 Core 匯入確保先佈 Core
Flow 匯入後不能跑Connection Reference 未綁定檢查 settings 的 ConnectionId
Pipeline 連不上環境SPN 未加為 Application User重跑 Phase 2 步驟 4
Checker 一直失敗ruleset 太嚴或真有問題調 ErrorLevel 或修正方案
恭喜完成

你已經跑通一條從註冊帳號到跨租戶上線的完整流水線。要複製到其餘子公司,只需重複 Phase 2 的身分設定與新增一個佈署 stage。

程式碼範例

Dynamics 365 / Power Platform 程式碼

Power Platform 的「程式碼」有四種形式,分屬不同層級。配套 repo 的 solutions/ 內都有實際範例;以下摘錄重點,完整檔案見對應路徑。

形式語言範例檔誰寫 / 怎麼進 Git
宣告式方案XMLSolution.xml · Entity.xml平台匯出,pac unpack 拆檔
伺服器端邏輯C#CustomerPreCreatePlugin.cs開發者手寫,dotnet 專案
用戶端邏輯JavaScriptcustomer/form.js開發者手寫,掛表單事件
低程式碼流程JSONOrderApproval/definition.jsonPower Automate 設計器,unpack
關鍵觀念

XML / Flow JSON 是平台產生的 —— 在 Dev 設計器點好、export 才變檔案,不要手改C# Plugin 與 JS Web Resource 才是你在 IDE 真正手寫、可直接編譯執行的程式碼。

① C# Plugin —— 伺服器端商業邏輯

solutions/ContosoCore/plugins/ContosoCore.Plugins/CustomerPreCreatePlugin.cs。建立客戶前:名稱必填、擋重複、自動產生客戶編號。註冊於 Create / contoso_customer / PreOperation / 同步。

public sealed class CustomerPreCreatePlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        var context = (IPluginExecutionContext)serviceProvider
            .GetService(typeof(IPluginExecutionContext));
        var factory = (IOrganizationServiceFactory)serviceProvider
            .GetService(typeof(IOrganizationServiceFactory));
        var service = factory.CreateOrganizationService(context.UserId);

        // 只處理 Target 是 Entity 的情況
        if (!context.InputParameters.TryGetValue("Target", out var obj)
            || !(obj is Entity target)) return;
        if (target.LogicalName != "contoso_customer") return;

        var name = target.GetAttributeValue<string>("contoso_name");
        if (string.IsNullOrWhiteSpace(name))
            throw new InvalidPluginExecutionException("客戶名稱為必填。");

        if (CustomerWithNameExists(service, name))
            throw new InvalidPluginExecutionException($"已存在相同名稱的客戶「{name}」。");

        if (string.IsNullOrWhiteSpace(
                target.GetAttributeValue<string>("contoso_accountnumber")))
        {
            target["contoso_accountnumber"] =
                $"CUST-{DateTime.UtcNow:yyyyMMdd}-{new Random().Next(1000, 9999)}";
        }
    }
    // … CustomerWithNameExists 用 QueryExpression 查同名啟用中的客戶
}

單元測試(FakeXrmEasy,不需連線環境)

[Fact(DisplayName = "未填客戶編號時,應自動產生 CUST- 開頭的編號")]
public void Generates_account_number_when_empty()
{
    var ctx = new XrmFakedContext();
    var target = new Entity("contoso_customer") { ["contoso_name"] = "Contoso 電子" };

    var pluginCtx = ctx.GetDefaultPluginContext();
    pluginCtx.InputParameters = new ParameterCollection { { "Target", target } };

    ctx.ExecutePluginWith<CustomerPreCreatePlugin>(pluginCtx);

    Assert.StartsWith("CUST-", target.GetAttributeValue<string>("contoso_accountnumber"));
}

CI 會執行 dotnet test,四個測試不過就擋 PR(見「07 測試」)。

② JavaScript Web Resource —— 表單邏輯

solutions/ContosoCore/src/WebResources/contoso_/customer/form.js。用 Xrm Client API 做即時驗證與儲存攔截。

function onSave(executionContext) {
    var formContext = executionContext.getFormContext();
    if (!isEmailValid(formContext)) {
        executionContext.getEventArgs().preventDefault(); // 取消儲存
        formContext.ui.setFormNotification(
            "Email 格式不正確,請確認後再儲存。", "ERROR", "contoso_email_invalid");
    }
}
function isEmailValid(formContext) {
    var value = formContext.getAttribute("contoso_email").getValue();
    return !value || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}

③ Power Automate 流程 —— 低程式碼(JSON)

solutions/ContosoCore/src/Workflows/OrderApproval/definition.json。訂單金額 > 50000 走主管核准,否則自動核准;連線用 Connection Reference,由 deployment settings 綁定各環境。

// 條件節點(節錄)
"Condition_amount_over_threshold": {
  "type": "If",
  "expression": { "greater": [ "@outputs('Get_order_total')?['body/contoso_totalamount']", 50000 ] },
  "actions": { "Start_and_wait_for_approval": { /* 送 Approvals 連接器 */ } },
  "else":    { "actions": { "Auto_approve_small_order": { /* 直接標記核准 */ } } }
}

④ 區域方案擴充(混合模式重點)

solutions/ContosoSubA/src/WebResources/csa_/order/localtax.js。SubA 在集團共用的訂單表單上,加自己的「當地稅額」計算——讀 Core 的欄位 contoso_totalamount、寫 SubA 的 csa_localtaxamount,稅率來自環境變數 csa_localtaxrate

為何先 Core 後 Regional

SubA 的程式參照了 Core 的欄位,Core 必須先存在——這正是佈署順序固定「先 Core、後 Regional」的原因。

這些程式碼如何進 CI

dotnet build 編譯 Plugin dotnet test FakeXrmEasy solution pack XML+JS+Flow Checker 靜態分析

對應 pipelines/02-build-managed.yml:先測 Plugin(不過就擋 PR),再打包整個方案,最後靜態分析。

05 · 參考手冊

環境與租戶策略

要規模化到多間子公司之前,先把環境數量、命名規範與連線對照表定清楚。這一章是實作時的查表依據。

環境數量規劃

混合模式下,環境分成 Core(集團)與 Regional(各子公司)兩類。

Core(集團共用)

環境類型位置用途
Core-DevDeveloper集團主 Tenant中央團隊唯一開發環境,Unmanaged
Core-BuildSandbox集團主 Tenant(選用)打包驗證 Managed 的乾淨環境
Core 的 Test/Prod在各子公司 Tenant 內Core managed 佈到各 Tenant 的 Sandbox / Prod
關鍵觀念

Core 沒有自己獨立的「Test/Prod」—— 它的測試與正式,就是跨多個 Tenant 的 Sandbox 與 Prod。

Regional(每間子公司 ×5)

環境類型用途
SubX-DevDeveloper該子公司開發,Unmanaged
SubX-SandboxSandbox整合測試 / UAT,跑 Azure Test Plans
SubX-ProdProduction正式營運

一間子公司 3 個 × 5 = 15 個 Regional 環境,加上 Core 1–2 個,總計約 16–17 個環境。Lab 練習先用一到兩間跑通即可。

Service Connection 命名規範

一致的命名讓 Pipeline 參數化與統一佈署迭代很乾淨:

# 格式
PP-<Scope>-<Sub>-<Stage>

# 範例
PP-Core-SubA-Sandbox      # Core 佈到 SubA 的 Sandbox
PP-Core-SubA-Prod
PP-Regional-SubA-Dev      # SubA 自己的 Dev(給 export 用)
PP-Regional-SubA-Sandbox
PP-Regional-SubA-Prod

環境對照表(填入客戶實際值)

SubTenant IDDev URLSandbox URLProd URL
SubAtenantA-guidsubA-dev…subA-sbx…subA-prod…
SubBtenantB-guid
SubC
SubD
SubE
別硬寫在 YAML

這張表建議放進 Azure DevOps 的 Variable GroupKey Vault,Pipeline 以變數引用,方便維護也較安全。

06 · 參考手冊

Azure DevOps 設定參考

Lab 手冊帶你點過一遍;這一章把 Project 結構、Variable Group、Environments 與核准的設定原則整理成查表用的參考。

Project 與 Repo 結構

Organization:Contoso-Group Project:Contoso-PowerPlatform ▸ Repos ContosoCore / SubA…E / templates ▸ Pipelines export / build / release ▸ Test Plans ▸ Environments Sandbox / Prod(含核准) Library(變數) env-subA:連線名稱 · URL · testPlanId env-subB:… 敏感值 → 連結 Azure Key Vault

單一 Project + Repo/Pipeline 權限做子公司隔離,通常已足夠;若集團要更強隔離,也可「一個 Project 一間子公司」。

Variable Group(集中管理環境值)

Pipelines → Library → 新增 Variable Group,例如 env-subA

變數加密
devServiceConnectionPP-Regional-SubA-Dev
sandboxServiceConnectionPP-Regional-SubA-Sandbox
prodServiceConnectionPP-Regional-SubA-Prod
sandboxUrlhttps://subA-sbx…
testPlanId1234
建議

Secret、connection id 這類敏感值放 Azure Key Vault 並在 Variable Group 連結,而非明文寫在群組裡。

Environments 與核准關卡

在 Pipelines → Environments 建立 SubA-SandboxSubA-Prod 等,並在 Prod 環境設定 Approvals and checks:

Approvals

指定核准人

如 SubA 負責人 + 集團架構師;佈署前強制人工放行。

Branch control

只允許 main

避免有人用非正式分支的內容佈到正式。

Gates

條件式放行

只在維護時窗佈署、或查詢變更單 / 測試通過率達標才放行。

供應鏈安全

把 Service Connection 設為「需核准才能被新 Pipeline 使用」,防止有人寫個 Pipeline 亂用正式連線。詳見「08 治理與資安」。

07 · 進階

測試與 Azure Test Plans

越底層的測試越快、越多、越早跑;Sandbox 用 Test Plans 做人工驗收與迴歸把關。

手動 UAT 自動化 UI 測試 整合測試(Flow / 外部) 單元測試 · 靜態分析(最多、最快)
層級工具何時跑
單元測試xUnit + FakeXrmEasyBuild 階段(不需環境)
靜態分析Solution CheckerBuild 階段
自動化 UIPower Apps Test EngineSandbox 佈署後
手動 UAT / 迴歸Azure Test PlansSandbox 佈署後

與 Pipeline 串接

Sandbox 佈署成功後,Pipeline 呼叫 REST API 建立 Test Run、通知測試人員在 Test Plans 執行手動驗收;進階可把 Test Case 關聯自動化測試由 Pipeline 直接跑並回填結果。

品質門檻

正式佈署(Phase 7)的核准可設定條件:Test Plan 通過率 ≥ 95% 才放行,未過就擋在 Sandbox。

08 · 進階

治理與資安

跨多個獨立 Tenant 的自動化佈署,身分與可追溯特別重要。

面向Lab 起步正式建議
Pipeline 登入Client SecretWorkload Identity Federation(免密鑰 OIDC)
密鑰儲存DevOps Variable Group(加密)Azure Key Vault + Variable Group 連結
SPN 權限System Administrator最小權限自訂角色
正式佈署單人核准多方核准 + 分支控制 + 維護時窗 Gate
回滾重新匯入前一版 Managed方案 / 環境備份 + 保留前版 artifact

為何建議免密鑰(Workload Identity Federation)

讓 DevOps 與 Entra ID 建立聯合信任,佈署時換發短期 token,沒有長期 secret 可外洩,設定一次後也免除輪替維護。每個 Tenant 各建一次。

資料隔離的好處

各子公司各自獨立 Tenant,若跨法遵區域(如 GDPR),資料落地與隔離天生分離,反而有利合規。

09 · 配套

配套檔案

本學習站對應一套可直接取用的 repo。網頁是視覺導覽,實作細節與可複製的程式碼在這些檔案裡。

docs/

九份中文說明

名詞、架構、環境策略、ALM、DevOps 設定、Pipeline 設計、測試、Lab 步驟、治理。

pipelines/

四支主 Pipeline + 樣板

export / build / release-sandbox / release-production,加三個共用樣板。

config/

deployment settings

各環境的連線參照 / 環境變數綁定 JSON。

scripts/

pac CLI 腳本

建立 Application User、本機匯出、身分除錯。

solutions/

方案原始碼與程式碼

C# Plugin + 單元測試、表單 JS、Power Automate 流程、方案 XML。見「程式碼範例」章。

搭配使用

先讀網頁建立概念與跟著 Lab 操作,需要精確參數或要複製貼上時,回去對應的 docs/pipelines/ 檔案。

Dynamics 365 · Power Platform ALM Lab — 混合模式多租戶參考範例