Day 25 我們替自己的:
SysAdminToolkit
加入了:
Pester Tests
現在流程已經開始變成:
修改 PowerShell
↓
執行 Pester
↓
測試通過
↓
準備部署
但還有一個很現實的問題。
假設昨天:
Get-ServerHealth
正常
今天我修改了一段:
if ($CPUUsage -ge $CPUCritical) {
$CPUStatus = "Critical"
}
結果明天發現:
Server Health Report 判斷異常
這時候我可能會問:
昨天正常的版本到底長什麼樣子?
如果平常的版本管理方式是:
ServerHealth.ps1
ServerHealth_old.ps1
ServerHealth_new.ps1
ServerHealth_v2.ps1
ServerHealth_v2_final.ps1
ServerHealth_final2.ps1
ServerHealth_真的最後.ps1
那其實:
已經沒有真正的版本管理。
所以 Day 26 要開始把前 25 天的:
SysAdmin Automation Toolkit
正式放進:
Git
讓我們以後可以回答:
誰改了?
改了什麼?
什麼時候改?
哪一版正常?
哪個 Commit 開始出問題?
能不能回頭看上一版?
今天會從最基本的:
git init
git status
git add
git commit
git log
git diff
開始。
為什麼系統工程師也需要 Git?
很多人看到 Git 第一個想到:
Developer
Source Code
GitHub
但我們現在維護的:
PowerShell Module
Config Template
Pester Tests
Automation Scripts
Documentation
其實全部都是:
Code。
而且這些 Code 已經會:
查 Windows Server
查 AD
產生報表
判斷告警
操作帳號
自動排程
重要性甚至可能比一般的小工具還高。
所以 PowerShell Automation 沒有 Version Control,久了很容易出現:
不知道哪份最新版
改壞不知道改哪裡
兩個工程師互相覆蓋
Production 跟測試版本不同
問題發生後沒辦法追改動
Git 就是在解決這些問題。
Git 跟 GitHub 不完全一樣
先分清楚。
Git
是一套:
Version Control System
可以只在你自己的電腦使用。
例如:
C:\Projects\SysAdmin-Automation
直接:
git init
就能開始記錄版本。
GitHub
則是一個可以存放 Git Repository 的平台。
同類型還有:
GitLab
Azure DevOps
Bitbucket
公司內部 Git Server
所以:
Git
≠
GitHub
今天先把:
Local Git Repository
搞懂。
先確認 Git
PowerShell:
git --version
例如:
git version 2.x.x.windows.x
表示 Git 可以使用。
如果出現:
git is not recognized
表示目前:
Git 沒安裝
或
Git 不在 PATH
企業受管控電腦則應依公司的軟體安裝政策處理。
今天要管理哪個專案?
前面已經慢慢整理成:
SysAdmin-Automation/
│
├── Modules/
│ └── SysAdminToolkit/
│ ├── SysAdminToolkit.psd1
│ ├── SysAdminToolkit.psm1
│ ├── Public/
│ └── Private/
│
├── Config/
│ ├── Servers.csv
│ └── NotificationConfig.csv
│
├── Input/
│
├── Scripts/
│
├── Tests/
│
├── Reports/
│
├── Logs/
└── State/
今天就把:
SysAdmin-Automation
變成 Git Repository。
第一步:進入專案目錄
例如:
Set-Location `
C:\Projects\SysAdmin-Automation
確認:
Get-Location
C:\Projects\SysAdmin-Automation
git init
執行:
git init
可能看到:
Initialized empty Git repository in ...
這時候目錄裡會多:
.git
概念:
SysAdmin-Automation/
│
├── .git/
├── Modules/
├── Scripts/
├── Tests/
└── ...
.git 是 Git 儲存 Repository 歷史與中繼資料的地方。
平常:
不要手動進 .git 裡亂改東西。
git status
初始化之後:
git status
可能看到:
On branch main
Untracked files:
Modules/
Config/
Scripts/
Tests/
關鍵字:
Untracked
意思就是:
Git 現在看到這些檔案,但還沒有開始追蹤。
Git 可以想像成三個區域
這個觀念很重要。
Working Directory
↓
git add
↓
Staging Area
↓
git commit
↓
Repository History
Working Directory
就是你現在實際在修改的檔案:
Get-ServerHealth.ps1
Write-Log.ps1
Module.Tests.ps1
Staging Area
代表:
我已經決定這些修改要放進下一個 Commit。
使用:
git add
Commit
代表:
把目前選好的變更建立成一個版本紀錄。
使用:
git commit
自動產生版本
而是:
修改
↓
選擇要記錄什麼
↓
Commit
先不要 git add .
這裡先不要急著:
git add .
因為目前專案裡有:
Logs/
Reports/
State/
Input/
其中一些東西:
根本不應該放進 Git。
所以我們要先建立:
.gitignore
什麼東西不應該進 Git?
目前這個 SysAdmin Automation 專案,至少要考慮:
Logs
Reports
Runtime State
Temporary Files
Real Production Input
Credential
Secret
Password
Token
例如:
Logs/
HealthCheck_20261004.log
每天都會變。
沒有必要每次:
Commit
Reports 也通常不需要 Version Control
例如:
Reports/
ServerHealth_20261004.csv
Daily_Health_Report_20261004.html
這些屬於:
Runtime Output
而不是:
Source Code
所以也可以 Ignore。
State 更不適合 Commit
Day 23 的:
State/
ServerHealthState.json
代表:
目前 Production 的 Monitoring State
如果 Commit 進 Git,
某天:
git checkout
切換版本時,
可能連 Monitoring State 都跟著回到以前。
這不是我們想要的。
所以:
State/
應該跟 Source Code 分開。
建立 .gitignore
在 Repository Root:
SysAdmin-Automation/
└── .gitignore
內容可以先:
Logs/*
Reports/*
State/*
Input/*
*.tmp
*.temp
*.bak
.vscode/
.DS_Store
Thumbs.db
*.secret
*.credential
*.key
*.pfx
.p12
.env
.env.
為什麼是 Logs/* 而不是 Logs/?
這裡可以做一個實用的小技巧。
如果寫:
Logs/
整個目錄都被 Ignore。
但 Git 本身又:
不追蹤空目錄。
結果 Clone Repository 之後可能根本沒有:
Logs
Reports
State
如果我們希望資料夾結構保留下來,
可以:
Logs/*
!Logs/.gitkeep
Reports/*
!Reports/.gitkeep
State/*
!State/.gitkeep
然後建立:
Logs/.gitkeep
Reports/.gitkeep
State/.gitkeep
.gitkeep 不是 Git 官方特殊功能。
它只是大家常用的:
空白 placeholder file。
Input 也要小心
例如 Day 18:
Input/NewUsers.csv
真實檔案可能包含:
員工姓名
帳號
Department
Email
Day 19:
Offboarding.csv
可能包含:
離職帳號
Ticket
日期
這些通常不應該無條件放進 Source Repository。
但沒有 Sample,別人不知道格式
所以一個更好的方式是:
Input/
├── NewUsers.example.csv
└── Offboarding.example.csv
Repository 裡保存:
範例格式
而真正 Production:
NewUsers.csv
Offboarding.csv
被 .gitignore 排除。
例如:
Input/*.csv
!Input/*.example.csv
這樣可以兼顧:
Documentation
+
Data Protection
Config 也不能一概而論
例如:
Config/Servers.csv
可能只是:
SERVER01
SERVER02
Threshold
Role
有些公司可以放 Git。
但如果包含:
內部主機名稱
IP
敏感系統資訊
帳號
就需要依公司政策判斷。
比較成熟的方式可以:
Servers.example.csv
進 Repository。
真實:
Servers.csv
留在部署環境。
Secrets 一定要特別注意
例如:
Password
API Token
SMTP Password
Private Key
Certificate Private Key
不應該:
git add
↓
git commit
不要覺得:
Repository 是 Private,所以密碼放進去沒關係。
因為 Git 最重要的特性就是:
History。
即使你後來把檔案刪掉,
Secret 仍然可能存在舊 Commit 裡。
.gitignore 不是 Secret 保險箱
這點非常重要。
假設:
password.txt
已經:
git add
git commit
後來才加入:
password.txt
Git 並不會自動忘記以前的版本。
所以:
.gitignore 是防止未來誤加入,不是清除已經 Commit 的 Secret。
如果真正的 Credential 已經進入 Repository,應該依公司的憑證事件處理方式:
撤銷 / Rotate Secret
清理 Repository History
檢查曝光範圍
不要只刪檔案就當作處理完成。
現在重新 git status
建立 .gitignore 後:
git status
應該主要看到:
.gitignore
Modules/
Scripts/
Tests/
Config Example
Input Example
而不是:
100 個 Log
100 個 CSV Report
第一次 git add
如果確認沒問題:
git add .
再次:
git status
可能看到:
Changes to be committed:
new file: .gitignore
new file: Modules/...
new file: Scripts/...
new file: Tests/...
現在這些檔案已經在:
Staging Area
git diff
在 Commit 前最好養成:
git diff
但要注意:
git diff
主要看:
Working Directory 還沒有 Stage 的修改。
如果已經:
git add .
想看下一個 Commit 準備包含什麼,
使用:
git diff --staged
或:
git diff --cached
Commit 前先 Review
所以我會養成這個流程:
git status
git diff --staged
確認:
沒有 Password
沒有 Production CSV
沒有大量 Log
沒有不該進去的檔案
再 Commit。
第一次 Commit
例如:
git commit -m "Initial SysAdmin automation toolkit"
這時 Git 就建立:
第一個版本紀錄。
如果 Git 要求 User Name / Email
第一次使用 Git 可能看到:
Please tell me who you are
可以設定:
git config --global user.name "Your Name"
git config --global user.email "your-email@example.com"
如果是公司環境,建議使用公司規定的 Git Identity。
也可以只設定目前 Repository:
git config user.name "Your Name"
git config user.email "your-email@example.com"
差別:
--global
→ 這個 Windows User 的所有 Repository
沒有 --global
→ 只影響目前 Repository
git log
Commit 完:
git log
可能:
commit abc123...
Author: ...
Date: ...
Initial SysAdmin automation toolkit
簡潔一點:
git log --oneline
例如:
a15f732 Initial SysAdmin automation toolkit
前面的:
a15f732
就是 Commit Hash 的縮寫。
Commit 可以想成 Snapshot
假設:
Commit A
Initial Toolkit
之後修改:
Get-ServerHealth
再 Commit:
Commit B
Improve server health error handling
再加入:
Pester
Commit C:
Add Pester tests for health state logic
Git History 就變成:
C Add Pester tests
↑
B Improve server health error handling
↑
A Initial toolkit
每個 Commit 都代表:
專案在某一個時間點的狀態。
修改一個 Function 看看
假設修改:
Modules/
SysAdminToolkit/
Public/
Get-ServerHealth.ps1
增加:
Write-Verbose `
"Health check started: $ComputerName"
存檔後:
git status
會看到:
modified:
Modules/.../Get-ServerHealth.ps1
git diff 看修改內容
git diff
可能看到:
如果不小心改錯:
當下就可以看到。
修改前後差異非常適合 Troubleshooting
假設昨天正常,今天出問題。
可以:
git diff
快速回答:
我剛剛到底改了什麼?
這比靠記憶:
我好像只改了一點點。
可靠很多。
第二個 Commit
例如:
git add Modules/SysAdminToolkit/Public/Get-ServerHealth.ps1
然後:
git commit -m "Add verbose output to server health check"
再:
git log --oneline
可能:
2a491fa Add verbose output to server health check
a15f732 Initial SysAdmin automation toolkit
現在版本歷史開始建立。
Commit Message 不要寫「update」
不太建議:
update
fix
change
test
123
final
過三個月看:
8f23aaa update
83ac221 update
35c87df fix
2184c9b update
完全不知道在幹嘛。
比較好:
Add inactive AD user report
Fix state transition for Unknown status
Add Pester tests for health events
Improve WinRM error handling
Add SMTP notification configuration
Commit Message 最好簡單回答:
這個 Commit 做了什麼?
一個 Commit 不要塞十種事情
例如今天一次改:
Server Health
HTML CSS
AD Offboarding
Email
README
最後:
git commit -m "Update scripts"
未來要找:
哪個 Commit 改壞 Email?
會非常困難。
比較好:
Commit 1
Fix email notification logic
Commit 2
Improve HTML report layout
Commit 3
Add offboarding validation
這叫:
Logical Commit。
Git 跟 Pester 可以一起使用
Day 25:
改 Code
↓
Pester
Day 26 可以變:
Modify Code
↓
git diff
↓
Invoke-Pester
↓
All Passed?
↓
git add
↓
git diff --staged
↓
git commit
這就是一個很實際的小型開發流程。
我會養成這套順序
git status
先知道目前狀態。
修改。
git diff
確認修改。
執行:
Invoke-Pester -Path ".\Tests"
-Output Detailed
全部 Pass 後:
git add .
檢查:
git diff --staged
最後:
git commit -m "..."
不要測試失敗還直接 Commit Production Ready
Git 本身不會阻止你:
Tests Failed
還是:
git commit
所以目前仍然靠:
工作習慣。
流程:
Tests Pass
↓
Commit
到了後面 CI,我們才可以讓系統自動幫忙擋。
git show
如果想看某個 Commit 做了什麼:
git show 2a491fa
會看到:
Commit
Author
Date
Message
Diff
這在查:
這一版到底改什麼?
非常方便。
git log 某一個檔案
例如:
git log --oneline -- Modules/SysAdminToolkit/Public/Get-ServerHealth.ps1
可以看到:
這個 Function
歷史上有哪些 Commit 修改過?
如果想看 Patch:
git log -p -- Modules/SysAdminToolkit/Public/Get-ServerHealth.ps1
這對維運工具 Troubleshooting 很有用。
git blame
有一個名字比較特別:
git blame Modules/SysAdminToolkit/Public/Get-ServerHealth.ps1
它可以看到:
每一行
最後是哪個 Commit 修改
誰修改
它真正的用途不是:
找人背鍋。
而是:
找到某一行程式碼的歷史背景。
例如看到:
$CPUWarning = 80
不知道為什麼是 80。
透過 Commit 可以找到:
Add configurable CPU thresholds
再看當時的變更背景。
Git 不只保存 Code,也保存「修改原因」
這就是為什麼 Commit Message 重要。
程式碼只能告訴你:
現在怎麼寫
Commit History 可以告訴你:
以前怎麼寫
後來為什麼改
什麼時候改
這對長期維護非常重要。
Branch 是什麼?
目前所有修改都直接發生在:
main
但如果今天想做一個比較大的功能:
加入 Teams Notification
我不一定希望:
做到一半
↓
main 已經都是半成品
這時候可以建立:
Branch
例如:
git switch -c feature/teams-notification
概念:
main
│
├── stable
│
└──── feature/teams-notification
↓
開發中
查看 Branch
git branch
可能:
feature/teams-notification
main
代表:
目前所在 Branch。
切回 main
git switch main
這樣就可以:
main
→ 穩定版
feature branch
→ 新功能開發
Branch 最大的價值
假設我要大改:
Get-ServerHealth
如果直接改 Main:
Production Toolkit
跟著變動
如果用 Branch:
main
→ 原本穩定版本
feature/new-health-check
→ 開發新版本
測完再合併。
Merge
假設 Feature 完成:
git switch main
然後:
git merge feature/teams-notification
這樣修改才正式進入 Main。
目前不用把 Branch 搞得太複雜
不需要一開始就:
Git Flow
Develop
Release
Hotfix
Feature
十幾種 Branch Policy
這個 30 天系列先掌握:
main
→ 穩定
feature/*
→ 開發
就足夠建立好習慣。
我要回到上一個版本怎麼辦?
這裡要特別小心。
如果只是:
看一下以前某個版本。
可以:
git show :
例如:
git show a15f732:Modules/SysAdminToolkit/Public/Get-ServerHealth.ps1
不用真的把目前整個 Repository 倒回去。
如果只是還沒 Commit 的修改寫壞
假設:
Get-ServerHealth.ps1
你改了一堆,但:
還沒 git add
還沒 commit
而且確定這些修改全部不要。
可以使用:
git restore Modules/SysAdminToolkit/Public/Get-ServerHealth.ps1
它會把 Working Directory 的檔案恢復到目前 Commit 的版本。
但要注意:
未 Commit 的修改可能會消失。
所以先:
git diff
確認真的不要。
不要一遇到問題就 git reset --hard
網路教學很常看到:
git reset --hard
它確實很強。
但也可能直接丟掉:
未保存的工作
所以初學時不要把:
git reset --hard
git clean -fd
當作萬用解法。
先弄清楚:
Working Tree 有什麼?
Staging 有什麼?
哪些修改可以丟?
再處理。
Git 不等於 Backup
這也是一個重要觀念。
如果 Repository 只存在:
C:\Projects
硬碟壞掉:
.git
+
全部檔案
一起沒了。
所以:
Local Git
提供:
Version History。
但如果希望:
異地保存
多人協作
集中管理
還需要 Remote Repository。
Remote Repository
例如:
GitHub
GitLab
Azure DevOps
Internal Git Server
概念:
Local Repository
↓
git push
↓
Remote Repository
另一台電腦:
Remote Repository
↓
git clone
↓
Local Repository
公司內部 Automation 不一定適合公開 GitHub
我們現在的 Toolkit 未來可能出現:
Internal Server Name
OU Structure
Group Name
Internal Mail Relay
Infrastructure Logic
所以正式公司環境應先依:
資訊安全政策
Source Code Policy
Data Classification
決定 Repository 放哪裡。
不要看到 Git 就直接:
Public GitHub
README 也應該進 Git
到現在專案已經有不少東西。
可以建立:
README.md
至少寫:
PowerShell automation toolkit for Windows Server
and Active Directory operations.
Invoke-Pester -Path .\Tests -Output Detailed
Usage
Import-Module .\Modules\SysAdminToolkit\SysAdminToolkit.psd1
README 也屬於:
> **Source Project 的一部分。**
---
# CHANGELOG 也可以開始有
例如:
```text id="4w5ak7"
CHANGELOG.md
內容:
# Changelog
## 1.1.0
- Added health state tracking
- Added recovery notification
- Added Pester tests
## 1.0.0
- Initial server health check
- Added logging
- Added HTML report
這跟 Git Log 不完全相同。
Git Log 是:
工程修改紀錄
CHANGELOG 更偏:
使用者需要知道的版本變化
Module Version 與 Git Tag
Day 24 有:
ModuleVersion = 1.0.0
Git 也可以對某個 Commit 做:
Tag
例如:
git tag v1.0.0
查看:
git tag
可能:
v1.0.0
這代表:
這個 Commit 是正式 1.0.0 版本。
Tag 的價值
如果半年後已經:
v1.5.0
但想知道:
1.0.0 當時長什麼樣?
Git 就能很清楚知道。
因此可以讓:
SysAdminToolkit.psd1
ModuleVersion = 1.1.0
跟:
Git Tag
v1.1.0
對應。
比較兩個版本
例如:
git diff v1.0.0 v1.1.0
就可以知道:
從 1.0.0 到 1.1.0 到底改了什麼?
這對 Production Troubleshooting 很有用。
真正的修改流程現在開始完整
以前:
打開 .ps1
↓
修改
↓
存檔
↓
跑 Production
現在我們可以:
git switch -c feature/new-function
↓
Modify Code
↓
git diff
↓
Invoke-Pester
↓
Tests Passed
↓
git add
↓
git diff --staged
↓
git commit
↓
Merge
↓
Update Module Version
↓
Tag Release
↓
Deploy
這已經很接近一個小型 Software Engineering Workflow。
PowerShell 專案的 .gitignore 範例
把今天內容整理一下:
# ==========================================
# Runtime Logs
# ==========================================
Logs/*
!Logs/.gitkeep
# ==========================================
# Generated Reports
# ==========================================
Reports/*
!Reports/.gitkeep
# ==========================================
# Runtime State
# ==========================================
State/*
!State/.gitkeep
# ==========================================
# Real Input Data
# ==========================================
Input/*.csv
!Input/*.example.csv
# ==========================================
# Environment-specific Config
# ==========================================
Config/*.local.csv
Config/*.production.csv
# ==========================================
# Temporary Files
# ==========================================
*.tmp
*.temp
*.bak
*.log
# ==========================================
# Secrets / Credentials
# ==========================================
*.secret
*.credential
*.key
*.pfx
*.p12
.env
.env.*
# ==========================================
# OS / Editor
# ==========================================
Thumbs.db
.DS_Store
.vscode/
實際要 Ignore 哪些 Config,仍然要依公司的環境與資料分類調整。
今天完整的 Git Workflow
初始化:
git init
查看:
git status
建立:
.gitignore
加入:
git add .
確認:
git status
git diff --staged
Commit:
git commit -m "Initial SysAdmin automation toolkit"
查看:
git log --oneline
修改 Function:
git status
git diff
執行 Test:
Invoke-Pester `
-Path ".\Tests" `
-Output Detailed
Pass:
git add Modules Tests
git diff --staged
git commit -m "Improve health event validation"
查看:
git log --oneline
這就是今天最核心的一套流程。
Git 最重要的其實不是「備份檔案」
很多人一開始會把 Git 理解成:
可以存舊版本。
沒有錯。
但對我們目前的 SysAdmin Automation 來說,Git 更大的價值是:
Change Traceability
Reproducibility
Collaboration
Rollback Reference
Code Review
Release History
例如某一天:
2026/10/04
Automation 正常
2026/10/05
Health Status 判斷異常
我們可以:
git log
↓
找期間 Commit
↓
git show
↓
看改了什麼
↓
Pester 重現
而不是:
大家一起回想昨天誰改了哪一支 ps1。
Git + Pester 開始產生很大的價值
現在兩個工具放在一起:
Git
→ 告訴我 Code 怎麼改變
Pester
→ 告訴我修改有沒有破壞預期行為
組合起來:
修改
↓
Diff
↓
Test
↓
Commit
這才開始真正有:
Change Control for Code
的概念。
Day 26 小結
今天我們把前 25 天累積的:
PowerShell
Windows Server
Active Directory
Module
Pester
正式放進:
Git Version Control
主要接觸:
git init
git status
git add
git diff
git diff --staged
git commit
git log
git show
git switch
git branch
git merge
git tag
但真正重要的不是背指令。
而是開始建立這個流程:
Code
↓
Change
↓
Review Diff
↓
Test
↓
Stage
↓
Review Again
↓
Commit
↓
History
今天也建立 .gitignore,把:
Logs
Reports
State
Production Input
Temporary Files
Secrets
跟 Source Code 分開。
尤其要記住:
Password、Token、Private Key 不應該 Commit 到 Git。
而且:
.gitignore 無法讓已經進入 Git History 的 Secret 自動消失。
如果 Credential 已經被 Commit,就應該視情況進行 Rotate / Revoke 與 History Cleanup。
另外今天開始讓:
ModuleVersion
可以跟:
Git Tag
對應:
SysAdminToolkit 1.0.0
↕
Git Tag v1.0.0
整套專案目前已經從:
PowerShell Script
一路變成:
PowerShell Module
+
Pester Testing
+
Git Version Control
我們已經不只是:
寫一支可以執行的 Script。
而是開始思考:
這套工具半年後還能不能安全地修改、追蹤與維護?
Day 27 預告
Day 27|PowerShell CI:Git Commit 之後自動跑 Pester,不讓壞掉的 Module 進入下一步
Day 25:
Pester
讓我們可以自己執行:
Invoke-Pester
Day 26:
Git
讓我們知道:
誰改了什麼。
但現在仍然有一個問題。
工程師可能:
修改
↓
忘記跑 Pester
↓
Commit
↓
Push
所以 Day 27 會把:
Git
+
Pester
正式串起來。
流程變成:
git push
↓
CI Pipeline
↓
Import SysAdminToolkit
↓
Test-ModuleManifest
↓
Invoke-Pester
↓
┌───────────────┐
│ Tests Passed? │
└───────┬───────┘
│
┌────┴────┐
↓ ↓
Yes No
↓ ↓
Pass Fail
↓
Stop Pipeline
也會開始談:
Jenkins
GitHub Actions
跟我們前面 Task Scheduler 的差別:
Task Scheduler
→ 執行「維運工作」
CI
→ 驗證「Automation Code」
Day 27 會是這個系列從 PowerShell Automation 正式往 DevOps / Platform Tooling 接軌的一天。