部署到 Azure:同一份程式碼,多一個家
學會用 Vercel 上線之後,這一課把同一個 GitHub 儲存庫再部署到 Microsoft Azure 的 Static Web Apps,最後替網站綁上自己的網址。學生會看到:程式碼只有一份,卻能同時部署到兩個雲端平台;也會親手讀懂一次部署失敗的錯誤訊息並修好它。本頁所有截圖都來自實際部署派課室網站(pyclass.bangcoding.com)的過程。
本課結束時你會
| 0:00–0:15 | 觀念:代管服務 vs 虛擬機器、一份程式碼部署到多個平台 |
| 0:15–0:30 | 步驟 1:登入 Azure、確認訂用帳戶與權限 |
| 0:30–1:00 | 步驟 2:建立 Static Web App【跟著做】 |
| 1:00–1:10 | 休息 |
| 1:10–1:40 | 步驟 3:第一次部署失敗 → 讀錯誤 → 修 YAML【一起除錯】 |
| 1:40–2:10 | 步驟 4–5:網域與 DNS、綁定自訂網域【老師示範】 |
| 2:10–2:30 | 驗收、費用檢查、回顧 |
學生帳號需要能在 Azure 建立資源。學校訂用帳戶建議由老師先建好一個資源群組(例如 rg-class-2026),在「存取控制 (IAM)」把學生加為該資源群組的參與者,不要給整個訂用帳戶的權限。也可以讓學生使用個人的 Azure for Students 方案。買網域只需要老師示範一次,學生之後可以分到老師網域底下的子網域。
觀念:Azure 上「放網站」有很多種方式
Azure 的服務多達數百種,同樣是「把網站放上網」,選錯服務的代價可能是每個月好幾千元。我們的課程網站和學生專案的前端都是寫好的 HTML、CSS、JavaScript,伺服器只要把檔案送出去,Python 與 SQL 練習也都在學生的瀏覽器裡執行,所以最適合的是靜態 Web 應用程式(Static Web Apps):Azure 全權代管、免費方案就夠用,而且和 Vercel 一樣會從 GitHub 自動部署。
| Static Web App(本課使用) | 虛擬機器 | |
|---|---|---|
| 誰管主機 | Azure 全部代管 | 自己安裝系統、架網頁伺服器、更新、顧資安 |
| 費用 | 免費方案 $0 | 只要開著就按小時計費 |
| 更新網站 | 推上 GitHub 自動部署 | 自己把檔案傳上主機 |
| 網址與 HTTPS | 自動提供,可綁自訂網域並免費核發憑證 | 自己設定網域與憑證 |
在 Marketplace 搜尋時,判斷一張卡片只要看兩個地方:發行者是不是 Microsoft,以及類型是 Azure Service(代管服務)、Virtual Machine(虛擬機器)還是 SaaS(訂閱軟體)。下圖第一張「靜態 Web 應用程式」才是我們要的;其他寫著 Nginx、Next.js 的卡片都是虛擬機器,旁邊還標著每小時的價格。

最常見的意外支出:首頁上方常有熱門產品的推薦(例如 AI 模型服務),按下「設定並訂閱」就會開始計費;建立表單的「方案類型」預設是付費的「標準」。每一步都要看清楚再按。

步驟 1:登入並確認訂用帳戶
- 打開 portal.azure.com,用學校發的 Microsoft 帳號登入(和 Office 365 同一組)。
- 首頁點「訂用帳戶」,確認看得到學校的訂用帳戶。
- 點進訂用帳戶 →「存取控制 (IAM)」→「角色指派」,確認自己的角色是「擁有者」或「參與者」。只有「讀者」就不能建立資源,要請管理者開權限。


名詞:看得到訂用帳戶不等於能動它,能做什麼由「角色」決定,分配權限要依「最小權限原則」。清單中的 Foreign Principal(外部群組)通常是經銷商保留的管理權限,用來處理帳務與支援,不要自己刪除。
可以順便帶學生看既有的虛擬機器:狀態要是「已停止(已解除配置)」才不收運算費;在 VM 的作業系統裡按關機只會變成「已停止」,運算費照算。停止後的磁碟與固定公用 IP 仍會收少量費用。

步驟 2:建立 Static Web App【跟著做】
- 「建立資源」→ 搜尋
Static Web App→ 選發行者為 Microsoft 的「靜態 Web 應用程式」→「建立」。 - 依下表填寫「基本」頁籤:
| 欄位 | 填寫 | 為什麼 |
|---|---|---|
| 資源群組 | 按「新建」→ rg-pyclass(學生用老師開好的群組) | 同一個網站的東西放在一起,日後一次刪除也不會誤刪別人的資源 |
| 名稱 | pyclass-site | Azure 內的資源名稱 |
| 方案類型 | 免費 | ⚠️ 預設是付費的「標準」,一定要改 |
| 來源 | GitHub → 以 GitHub 登入並授權 | 讓 Azure 能在儲存庫加入部署設定 |
| 組織/存放庫/分支 | 自己的帳號 / 專案儲存庫 / main | |
| 組建預設 | Custom | 純 HTML 網站不需要框架組建;React、Vite 等專案要選對應框架 |
| 應用程式位置 / API 位置 / 輸出位置 | / / 留空 / . | 網站檔案就在儲存庫最外層,不需另外產生輸出資料夾 |



/、API 留空、輸出 .- 按「檢閱 + 建立」,確認 SKU 為 Free 後按「建立」,約 1 分鐘完成。


地區顯示 centralus 沒關係:靜態檔案會自動發佈到全球節點,台灣的使用者會從最近的節點取得;地區只影響 API 與管理資料。部署權杖是一組密碼,Azure 把它存在 GitHub 儲存庫的 Secrets,不會出現在程式碼裡,這和「金鑰只放環境變數」是同一個觀念。
步驟 3:第一次部署失敗了——讀錯誤、修設定【一起除錯】
按下建立後,Azure 會在 GitHub 儲存庫加入 .github/workflows/azure-static-web-apps-….yml,並立刻觸發第一次部署。到 GitHub 儲存庫上方的「Actions」分頁,會看到第一次是紅色叉叉。點進去讀錯誤紀錄:
Detected following platforms: nodejs: 22.23.2 Error: Could not find either 'build' or 'build:azure' node under 'scripts' in package.json.
Azure 的組建工具看到儲存庫裡有 package.json(這是給 Vercel 研究紀錄 API 用的),就以為這是需要先執行 npm run build 的 Node.js 專案,結果找不到組建指令而失敗。但我們的網站是寫好的 HTML,直接上傳就可以,所以只要告訴它跳過組建:
- 在 GitHub 打開
.github/workflows/azure-static-web-apps-….yml,按鉛筆圖示編輯。 - 在
output_location: "."的下一行加入(前面的空白要和上一行對齊):
output_location: "." # Built app content directory - optional
skip_app_build: true
- 「Commit changes」,訊息寫「跳過組建,直接上傳靜態網站」。Actions 會自動重跑,約 1 分鐘出現綠色勾勾。

skip_app_build: true,縮排與上一行對齊
除錯的固定流程:Actions 分頁 → 點失敗的那一次 → 找紅字 → 依訊息修改 → 推送後再看一次。YAML 的縮排有意義,空白對不齊設定就會失效,這是學生最常犯的錯。修完之後如果要在自己電腦繼續工作,記得先 git pull 把網頁上的修改拉下來。
這時網站已經可以用 Azure 給的網址打開,例如 https://white-sand-02103a410.3.azurestaticapps.net。同一個 commit 也讓 Vercel 重新部署了一次,兩邊內容永遠一樣:每個平台只讀自己的設定檔(Vercel 讀 vercel.json,Azure 讀 workflow 與 staticwebapp.config.json),互不干擾。

這個失敗是真實發生的,建議不要事先修好,讓學生自己在 Actions 讀到紅字。可以問:「錯誤訊息說它偵測到什麼平台?為什麼它會這樣以為?」Azure 會把 xxx.html 轉址成不含副檔名的 xxx,相對連結仍然正常,可以讓學生觀察兩個平台網址的差別。
步驟 4:網域與 DNS【老師示範】
Azure 給的網址又長又難記。要換成自己的網址,需要三樣東西互相配合:網域像門牌,向註冊商「租」來,每年續約;DNS 像查號台,記錄每個門牌要指向哪裡;網站主機則是真正的房子。一個網域可以分出很多子網域,各自指向不同的網站:
bangcoding.com(網域,一年 11.99 美元) ├── pyclass.bangcoding.com → Azure Static Web App(本課) ├── foodie.bangcoding.com → 吃貨評分站示範 └── 學號.bangcoding.com → 學生期末專題
Azure 也有賣網域,服務名稱是「App Service 網域」。在 Azure 買的好處是會自動建立 Azure DNS 區域並完成連結;限制是只有 .com、.net、.org 等國際網域(沒有 .tw),而且點數型或部分經銷商訂閱可能不符合購買資格,送出時才會檢查。
- 「建立資源」→ 搜尋「App Service 網域」→ 選 Microsoft 的那一張 →「建立」。
- 資源群組選
rg-pyclass,在「網域」欄位搜尋想要的名稱。出現「此網域無法使用」代表已被註冊,下方會列出可買的類似名稱。 - 「連絡資訊」要填真實資料(國際網域管理機構 ICANN 的規定),並確認隱私權保護為啟用,個資就不會出現在公開的 WHOIS 查詢裡。
- 在「進階」頁籤把自動續約打開,檢閱後按「建立」。網域註冊是最晚完成的一項,狀態會先顯示 Accepted。






費用提醒:網域費每年約 11.99 美元,通常不能用 Azure 點數抵扣;Azure DNS 每個區域每月約 0.5 美元,區域內加幾筆紀錄都一樣。Static Web App 與 HTTPS 憑證免費。學生不需要自己買網域,可以由老師分配子網域。
不在 Azure 買網域也可以:向 Cloudflare(.com 以成本價販售,美元計價、開英文 Invoice)或台灣的註冊商(可買 .tw、開統一發票,學校報帳較方便)購買,再把名稱伺服器改成 Azure DNS 提供的 4 組位址。學校子網域則可請資訊組把整個子網域「委派」給你的 Azure DNS,一次設定後就能自己新增網址。
步驟 5:把子網域綁到網站
網域和網站都在 Azure 裡,所以 Azure 會自動新增 DNS 紀錄、驗證網域擁有權、核發 HTTPS 憑證,不需要手動設定 CNAME。
- 進入 Static Web App →「自訂網域」→「+ 新增」→「Azure DNS 上的自訂網域」。
- DNS 區域選
bangcoding.com,第三層網域填pyclass,下方會顯示完整網域pyclass.bangcoding.com。 - 按「新增」,停留在頁面上等候約 5 分鐘,出現「已成功將 DNS 記錄和自訂網域新增」後關閉。
- 清單中狀態變成「已驗證」,到期日欄位是 HTTPS 憑證的期限,Azure 會自動續期。





如果網域在外面買:選「其他 DNS 上的自訂網域」,Azure 會給你一筆 CNAME(子網域指向 xxx.azurestaticapps.net),你到註冊商的 DNS 設定頁手動新增,再回 Azure 按驗證。DNS 生效可能要幾分鐘到幾小時。學校網路有時會擋對外的 DNS 查詢,在校內用 nslookup 查不到紀錄不一定是設定錯誤。
常見問題與錯誤
Actions 失敗:Could not find either 'build' or 'build:azure'
儲存庫有 package.json 但其實不需要組建。在 workflow 加 skip_app_build: true(步驟 3)。
建立後才發現選了「標準」方案
Static Web App →「主控方案」可以改回免費。改之前先確認沒有用到標準方案才有的功能(例如超過 2 個自訂網域)。
在 GitHub 網頁改了 workflow,回到電腦 git push 被拒絕
網頁上的修改產生了新的 commit,本機沒有。先 git pull 再推送。
買網域時出現「此訂用帳戶不符合建立 App Service 網域的資格」
點數型、試用或部分經銷商訂閱不能在 Azure 買網域。改向外部註冊商購買,再用「其他 DNS 上的自訂網域」綁定。
自訂網域一直停在「驗證中」
網域在 Azure:確認 DNS 區域就是網域買來時自動建立的那一個。網域在外部:確認 CNAME 拼字正確、名稱伺服器已改好,並等待 DNS 生效。
本課檢核
帶走的觀念:程式碼只維護一份,可以部署到很多地方;每個平台只讀自己的設定檔。雲端操作最重要的習慣是:看清楚發行者、類型與方案再按建立,資源放進同一個資源群組,用完知道怎麼刪除。