适用于 PowerShell 7.0 及以上版本
在 DevOps 和基础设施即代码的实践中,配置文件管理是核心能力。无论是应用部署、容器编排还是 CI/CD 流水线,JSON 和 YAML 格式的配置文件无处不在。PowerShell 原生支持 JSON 的读写与转换,配合 powershell-yaml 模块也能轻松处理 YAML,包括 Kubernetes 风格的多文档格式。
本文将从实际场景出发,逐步介绍如何用 PowerShell 完成 JSON 配置读取与修改、YAML 解析、Schema 验证、模板渲染以及环境配置切换。
读取 JSON 配置
日常运维中,我们经常需要从 JSON 文件中提取特定的配置项。PowerShell 的 ConvertFrom-Json 可以将 JSON 文本转换为对象,然后用属性访问语法直接取值。下面封装了一个通用函数,支持按层级路径读取指定区段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function Get-JsonConfig { param( [Parameter(Mandatory)] [string]$Path,
[string[]]$Sections )
$json = Get-Content $Path -Raw | ConvertFrom-Json
if ($Sections) { $result = $json foreach ($section in $Sections) { $result = $result.$section } return $result }
return $json }
|
假设我们有一个应用配置文件 appsettings.json,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| { "server": { "host": "0.0.0.0", "port": 8080 }, "database": { "host": "db.internal", "port": 5432, "name": "app_prod" }, "logging": { "level": "INFO", "path": "/var/log/app.log" } }
|
读取整个配置或某个区段:
1 2 3 4 5 6 7
| $config = Get-JsonConfig -Path ".\appsettings.json" $config.database.host
$dbConfig = Get-JsonConfig -Path ".\appsettings.json" -Sections "database" $dbConfig
|
执行结果示例:
1 2 3 4 5
| db.internal
host port name
db.internal 5432 app_prod
|
修改 JSON 配置
读取只是第一步,更多时候我们需要修改配置项并写回文件。下面的函数支持用点号分隔的路径(如 database.host)来定位嵌套属性,并可选地在修改前备份原文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| function Set-JsonConfig { param( [Parameter(Mandatory)] [string]$Path,
[Parameter(Mandatory)] [hashtable]$Properties,
[switch]$Backup )
if ($Backup) { Copy-Item $Path "$Path.bak.$(Get-Date -Format 'yyyyMMddHHmmss')" }
$json = Get-Content $Path -Raw | ConvertFrom-Json
foreach ($key in $Properties.Keys) { $parts = $key -split '\.' $obj = $json for ($i = 0; $i -lt $parts.Count - 1; $i++) { $obj = $obj.$($parts[$i]) } $obj.$($parts[-1]) = $Properties[$key] }
$json | ConvertTo-Json -Depth 10 | Set-Content $Path -Encoding UTF8 }
|
将数据库主机和日志级别修改为新值:
1 2 3 4
| Set-JsonConfig -Path ".\appsettings.json" -Backup -Properties @{ "database.host" = "db-new.internal" "logging.level" = "DEBUG" }
|
执行结果示例:
1 2 3 4 5 6 7
| # 原文件已备份为 appsettings.json.bak.20250402100000 # appsettings.json 中的值已更新 Get-Content .\appsettings.json | ConvertFrom-Json | Select-Object -ExpandProperty database
host port name
db-new.internal 5432 app_prod
|
读取 YAML 配置
YAML 在 Kubernetes、CI/CD 等场景中广泛使用。PowerShell 本身不支持 YAML,但 powershell-yaml 模块弥补了这个缺口。下面的函数封装了 YAML 读取逻辑,并在模块缺失时给出友好提示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function Get-YamlConfig { param( [Parameter(Mandatory)] [string]$Path )
if (-not (Get-Module -ListAvailable -Name powershell-yaml)) { Write-Warning "需要安装 powershell-yaml 模块: Install-Module powershell-yaml -Scope CurrentUser" return $null }
Import-Module powershell-yaml $content = Get-Content $Path -Raw return $content | ConvertFrom-Yaml }
|
使用方式很简单:
1 2 3 4 5 6
|
$config = Get-YamlConfig -Path ".\config.yaml" $config.server.host
|
执行结果示例:
解析 Kubernetes 多文档 YAML
Kubernetes 的资源清单文件通常包含多个文档,用 --- 分隔。标准的 YAML 解析器只处理第一个文档,因此需要先分割再逐一解析。下面的函数会将每个文档提取出 kind、name、namespace 等关键信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function Get-YamlMultiDocument { param([Parameter(Mandatory)][string]$Path)
Import-Module powershell-yaml $content = Get-Content $Path -Raw
$documents = $content -split '(?m)^---\s*$' | Where-Object { $_.Trim() }
foreach ($doc in $documents) { $yaml = $doc | ConvertFrom-Yaml [PSCustomObject]@{ Kind = $yaml.kind Name = $yaml.metadata.name Namespace = $yaml.metadata.namespace Content = $yaml } } }
|
假设有一个 deploy.yaml 包含 Namespace 和 Deployment 两个资源:
1 2 3 4 5 6 7 8 9 10 11 12
| apiVersion: v1 kind: Namespace metadata: name: my-app --- apiVersion: apps/v1 kind: Deployment metadata: name: web-server namespace: my-app spec: replicas: 3
|
解析结果如下:
1
| Get-YamlMultiDocument -Path ".\deploy.yaml" | Format-Table Kind, Name, Namespace
|
执行结果示例:
1 2 3 4
| Kind Name Namespace
Namespace my-app Deployment web-server my-app
|
Schema 验证
配置项的错误往往在运行时才暴露,提前做 Schema 验证可以有效减少故障。下面的函数支持必填检查、类型校验、长度限制、枚举值和正则匹配等规则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| function Test-ConfigSchema { param( [Parameter(Mandatory)] [PSCustomObject]$Config,
[Parameter(Mandatory)] [hashtable]$Schema )
$errors = @()
foreach ($rule in $Schema.GetEnumerator()) { $key = $rule.Key $constraints = $rule.Value $value = $Config.$key
if ($constraints.Required -and -not $value) { $errors += "缺少必填字段: $key" continue }
if ($null -ne $value) { if ($constraints.Type -and $value.GetType().Name -ne $constraints.Type) { $errors += "$key 类型错误: 期望 $($constraints.Type), 实际 $($value.GetType().Name)" } if ($constraints.MinLength -and $value.Length -lt $constraints.MinLength) { $errors += "$key 长度不足: 最小 $($constraints.MinLength)" } if ($constraints.AllowedValues -and $value -notin $constraints.AllowedValues) { $errors += "$key 值非法: $value, 允许值: $($constraints.AllowedValues -join ', ')" } if ($constraints.Pattern -and $value -notmatch $constraints.Pattern) { $errors += "$key 格式不匹配: $($constraints.Pattern)" } } }
if ($errors) { Write-Host "配置验证失败:" -ForegroundColor Red $errors | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow } return $false }
Write-Host "配置验证通过" -ForegroundColor Green return $true }
|
定义验证规则并执行检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| $schema = @{ serverName = @{ Required = $true; Type = "String"; MinLength = 3 } port = @{ Required = $true; Type = "Int32" } environment = @{ Required = $true; AllowedValues = @("dev", "staging", "prod") } database = @{ Required = $true; Pattern = "^[a-z][a-z0-9_]+$" } }
$config = [PSCustomObject]@{ serverName = "prod-sql-01" port = 1433 environment = "production" database = "app_db" }
Test-ConfigSchema -Config $config -Schema $schema
|
执行结果示例:
1 2 3
| 配置验证失败: - environment 值非法: production, 允许值: dev, staging, prod False
|
模板渲染
在管理 Nginx、HAProxy 等配置时,硬编码不利于多环境复用。模板渲染可以将占位符替换为实际值,还支持条件块来控制内容是否输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| function Resolve-ConfigTemplate { param( [Parameter(Mandatory)] [string]$TemplatePath,
[Parameter(Mandatory)] [hashtable]$Variables,
[string]$OutputPath )
$template = Get-Content $TemplatePath -Raw
foreach ($var in $Variables.GetEnumerator()) { $template = $template -replace "\{\{$($var.Key)\}\}", $var.Value }
$template = [regex]::Replace( $template, '\{\{#if\s+(\w+)\}\}(.*?)\{\{/if\}\}', { param($m) if ($Variables[$m.Groups[1].Value]) { $m.Groups[2].Value } else { "" } }, [System.Text.RegularExpressions.RegexOptions]::Singleline )
if ($OutputPath) { $template | Set-Content $OutputPath -Encoding UTF8 Write-Host "配置已渲染: $OutputPath" }
return $template }
|
准备一个 Nginx 配置模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| $template = @" server { listen {{PORT}}; server_name {{HOST}}; {{#if SSL}}listen 443 ssl; ssl_certificate {{SSL_CERT}};{{/if}} location / { proxy_pass http://{{BACKEND}}; } } "@
$template | Set-Content "$env:TEMP\nginx.tpl"
Resolve-ConfigTemplate -TemplatePath "$env:TEMP\nginx.tpl" ` -Variables @{ PORT = "8080" HOST = "app.example.com" SSL = $true SSL_CERT = "/etc/ssl/cert.pem" BACKEND = "127.0.0.1:3000" } ` -OutputPath ".\nginx.conf"
|
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12
| 配置已渲染: .\nginx.conf
server { listen 8080; server_name app.example.com; listen 443 ssl; ssl_certificate /etc/ssl/cert.pem; location / { proxy_pass http://127.0.0.1:3000; } }
|
环境配置切换
在不同环境(开发、预发布、生产)之间切换时,需要加载对应的配置并注入到环境变量中。下面的函数根据环境名称查找对应配置文件,将所有配置项导出为以 APP_ 为前缀的环境变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| function Switch-AppEnvironment { param( [Parameter(Mandatory)] [ValidateSet("dev", "staging", "prod")] [string]$Environment,
[string]$ConfigDir = ".\config" )
$configFile = Join-Path $ConfigDir "$Environment.json"
if (-not (Test-Path $configFile)) { throw "配置文件不存在: $configFile" }
$config = Get-JsonConfig -Path $configFile
foreach ($prop in $config.PSObject.Properties) { [Environment]::SetEnvironmentVariable( "APP_$($prop.Name.ToUpper())", $prop.Value, "Process" ) }
[Environment]::SetEnvironmentVariable("APP_ENV", $Environment, "Process")
Write-Host "已切换到 $Environment 环境" -ForegroundColor Green return $config }
|
在开发环境与生产环境之间切换:
1 2 3 4 5 6
| Switch-AppEnvironment -Environment dev -ConfigDir ".\config"
$env:APP_ENV $env:APP_SERVER_HOST
|
执行结果示例:
1 2 3 4
| 已切换到 dev 环境
dev 127.0.0.1
|
管理配置文件时,建议将敏感信息(密码、密钥)从配置文件中分离,使用环境变量或密钥管理服务替代。模板渲染前务必做 Schema 验证,避免无效配置上线。通过本文介绍的工具函数,你可以构建一套完整的配置管理流程:读取配置、Schema 校验、模板渲染、环境切换,覆盖从开发到生产的全链路。