PowerShell 技能连载 - 多云成本优化自动化系统

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
53
54
55
56
57
58
59
60
61
62
63
64
65
function Get-CloudCostReport {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string[]]$SubscriptionIds,

[ValidateSet('Daily','Monthly')]
[string]$Granularity = 'Monthly'
)

$costReport = [PSCustomObject]@{
Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
TotalCost = 0
ServiceBreakdown = @{}
OptimizationSuggestions = @()
}

try {
# 获取跨云成本数据
$costData = $SubscriptionIds | ForEach-Object {
Invoke-RestMethod -Uri "https://management.azure.com/subscriptions/$_/providers/Microsoft.CostManagement/query?api-version=2023-03-01" \
-Headers @{ Authorization = "Bearer $env:AZURE_TOKEN" } \
-Body (@{
type = "ActualCost"
timeframe = "MonthToDate"
dataset = @{
aggregation = @{
totalCost = @{
name = "Cost"
function = "Sum"
}
}
grouping = @(
@{
type = "Dimension"
name = "ServiceName"
}
)
}
} | ConvertTo-Json)
}

# 分析成本结构
$costReport.TotalCost = ($costData.properties.rows | Measure-Object -Property [0] -Sum).Sum
$costReport.ServiceBreakdown = $costData.properties.rows |
Group-Object { $_[1] } -AsHashTable |
ForEach-Object { @{$_.Key = [math]::Round($_.Value[0],2)} }

# 生成优化建议
$costData.properties.rows | Where-Object { $_[0] -gt 1000 } | ForEach-Object {
$costReport.OptimizationSuggestions += [PSCustomObject]@{
Service = $_[1]
Cost = $_[0]
Recommendation = "考虑预留实例或自动缩放配置"
}
}
}
catch {
Write-Error "成本数据获取失败: $_"
}

# 生成Excel格式报告
$costReport | Export-Excel -Path "$env:TEMP/CloudCostReport_$(Get-Date -Format yyyyMMdd).xlsx"
return $costReport
}

核心功能

  1. 跨云成本数据聚合分析
  2. 服务维度费用结构分解
  3. 智能优化建议生成
  4. Excel格式报告输出

应用场景

  • 多云环境成本监控
  • 预算超支预警
  • 资源使用效率优化
  • 财务部门合规报告

PowerShell参数传递机制详解

参数传递基础原理

1
2
3
4
5
6
7
8
9
# 位置参数示例
function Get-FullName {
param($FirstName, $LastName)
"$FirstName $LastName"
}
Get-FullName '张' '三'

# 命名参数示例
Get-FullName -LastName '李' -FirstName '四'

管道参数绑定

1
2
3
4
1..5 | ForEach-Object { 
param([int]$num)
$num * 2
}

实用技巧

  1. 使用[Parameter(Mandatory)]标记必选参数
  2. 通过ValueFromPipeline实现流式处理
  3. 验证集ValidateSet限制参数取值范围
  4. 类型转换失败时的错误处理策略

PowerShell集合类型操作指南

基础数据结构

1
2
3
4
5
6
7
# 数组初始化与索引
$numbers = 1..5
$numbers[0] = 10 # 索引从0开始

# 哈希表键值操作
$user = @{Name='Alice'; Age=25}
$user['Department'] = 'IT'

操作对比表

方法 数组适用 哈希表适用 时间复杂度
Add() × O(1)
Remove() × O(1)
Contains() O(n)/O(1)

典型应用场景

  1. 使用ArrayList实现动态数组
  2. 通过OrderedDictionary保持插入顺序
  3. 利用哈希表进行快速键值查找
  4. 多维数组存储表格数据

常见错误解析

1
2
3
4
5
6
7
# 固定大小数组修改错误
$fixedArray = @(1,2,3)
$fixedArray.Add(4) # 抛出异常

# 正确使用动态集合
$list = [System.Collections.ArrayList]@(1,2,3)
$list.Add(4) | Out-Null

PowerShell管道过滤器实战指南

管道过滤器原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 基础过滤示例
Get-Process | Where-Object {$_.CPU -gt 100} | Select-Object Name, Id

# 自定义过滤函数
function Filter-LargeFiles {
param([int]$SizeMB=50)
Process {
if ($_.Length -gt $SizeMB*1MB) {
$_
}
}
}

Get-ChildItem -Recurse | Filter-LargeFiles -SizeMB 100

性能优化要点

  1. 尽量在管道前端过滤
  2. 避免在过滤器中执行耗时操作
  3. 合理使用流式处理与缓存机制

典型应用场景

1
2
3
4
5
6
# 实时监控进程创建
Get-WmiObject -Query "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'" |
ForEach-Object {
$process = $_.TargetInstance
Write-Host "新进程: $($process.Name) (PID: $($process.ProcessId))"
}

调试技巧

1
2
3
# 查看中间结果
Get-Service | Tee-Object -Variable temp | Where-Object Status -eq 'Running'
$temp | Format-Table -AutoSize

PowerShell JEA安全架构解析

JEA端点配置

1
2
3
4
5
6
7
8
# 创建会话配置文件
$sessionParams = @{
Path = '.JEAConfig.pssc'
SessionType = 'RestrictedRemoteServer'
RunAsVirtualAccount = $true
RoleDefinitions = @{ 'CONTOSO\SQLUsers' = @{ RoleCapabilities = 'SqlReadOnly' } }
}
New-PSSessionConfigurationFile @sessionParams

角色能力定义

1
2
3
4
5
6
7
8
9
# 创建角色能力文件
$roleCapability = @{
Path = 'Roles\SqlReadOnly.psrc'
VisibleCmdlets = 'Get-Sql*'
VisibleFunctions = 'ConvertTo-Query'
VisibleProviders = 'FileSystem'
ScriptsToProcess = 'Initialize-SqlEnv.ps1'
}
New-PSRoleCapabilityFile @roleCapability

会话沙箱验证

1
2
3
4
5
6
# 连接JEA端点并验证权限
$session = New-PSSession -ConfigurationName JEAConfig
Invoke-Command -Session $session {
Get-Command -Name * # 仅显示白名单命令
Get-ChildItem C:\ # 触发权限拒绝
}

典型应用场景

  1. 数据库只读访问控制
  2. 第三方供应商操作审计
  3. 生产环境故障诊断隔离
  4. 跨域资源受限访问

安全加固要点

  • 虚拟账户生命周期控制
  • 转录日志完整性校验
  • 模块白名单签名验证
  • 会话超时熔断机制

PowerShell 技能连载 - Azure Functions 管理技巧

在 PowerShell 中管理 Azure Functions 是一项重要任务,本文将介绍一些实用的 Azure Functions 管理技巧。

首先,让我们看看基本的 Azure Functions 操作:

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
# 创建 Azure Functions 信息获取函数
function Get-AzFunctionInfo {
param(
[string]$FunctionAppName,
[string]$ResourceGroupName
)

try {
$functionApp = Get-AzFunctionApp -Name $FunctionAppName -ResourceGroupName $ResourceGroupName
$functions = Get-AzFunction -FunctionAppName $FunctionAppName -ResourceGroupName $ResourceGroupName

return [PSCustomObject]@{
Name = $functionApp.Name
ResourceGroup = $functionApp.ResourceGroup
Location = $functionApp.Location
Runtime = $functionApp.Runtime
OS = $functionApp.OSType
State = $functionApp.State
FunctionCount = $functions.Count
Functions = $functions | ForEach-Object {
[PSCustomObject]@{
Name = $_.Name
Trigger = $_.Trigger
Status = $_.Status
}
}
}
}
catch {
Write-Host "获取函数应用信息失败:$_"
}
}

Azure Functions 部署:

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
# 创建 Azure Functions 部署函数
function Deploy-AzFunction {
param(
[string]$FunctionAppName,
[string]$ResourceGroupName,
[string]$PackagePath,
[string]$Runtime,
[hashtable]$AppSettings
)

try {
# 创建部署包
$zipPath = "$PackagePath.zip"
Compress-Archive -Path $PackagePath -DestinationPath $zipPath -Force

# 部署函数应用
$functionApp = New-AzFunctionApp -Name $FunctionAppName `
-ResourceGroupName $ResourceGroupName `
-Runtime $Runtime `
-StorageAccountName (New-AzStorageAccount -Name "$($FunctionAppName)storage" `
-ResourceGroupName $ResourceGroupName `
-Location (Get-AzResourceGroup -Name $ResourceGroupName).Location `
-SkuName Standard_LRS).StorageAccountName `
-FunctionsVersion 4 `
-OSType Windows `
-RuntimeVersion 7.0 `
-AppSettings $AppSettings

# 部署函数代码
Publish-AzWebApp -Name $FunctionAppName `
-ResourceGroupName $ResourceGroupName `
-ArchivePath $zipPath

# 清理临时文件
Remove-Item $zipPath

Write-Host "函数应用部署完成"
}
catch {
Write-Host "部署失败:$_"
}
}

Azure Functions 监控:

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
# 创建 Azure Functions 监控函数
function Monitor-AzFunction {
param(
[string]$FunctionAppName,
[string]$ResourceGroupName,
[int]$Interval = 60,
[int]$Duration = 3600
)

try {
$startTime = Get-Date
$metrics = @()

while ((Get-Date) - $startTime).TotalSeconds -lt $Duration {
$invocations = Get-AzMetric -ResourceId (Get-AzFunctionApp -Name $FunctionAppName -ResourceGroupName $ResourceGroupName).Id `
-MetricName "FunctionExecutionUnits" `
-TimeGrain 1 `
-StartTime (Get-Date).AddMinutes(-5) `
-EndTime (Get-Date)

$errors = Get-AzMetric -ResourceId (Get-AzFunctionApp -Name $FunctionAppName -ResourceGroupName $ResourceGroupName).Id `
-MetricName "FunctionExecutionCount" `
-TimeGrain 1 `
-StartTime (Get-Date).AddMinutes(-5) `
-EndTime (Get-Date)

$metrics += [PSCustomObject]@{
Timestamp = Get-Date
Invocations = $invocations.Data | Measure-Object -Property Total -Sum | Select-Object -ExpandProperty Sum
Errors = $errors.Data | Measure-Object -Property Total -Sum | Select-Object -ExpandProperty Sum
MemoryUsage = Get-AzMetric -ResourceId (Get-AzFunctionApp -Name $FunctionAppName -ResourceGroupName $ResourceGroupName).Id `
-MetricName "MemoryUsage" `
-TimeGrain 1 `
-StartTime (Get-Date).AddMinutes(-5) `
-EndTime (Get-Date) |
Select-Object -ExpandProperty Data |
Measure-Object -Property Total -Average |
Select-Object -ExpandProperty Average
}

Start-Sleep -Seconds $Interval
}

return $metrics
}
catch {
Write-Host "监控失败:$_"
}
}

Azure Functions 日志收集:

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
# 创建 Azure Functions 日志收集函数
function Get-AzFunctionLogs {
param(
[string]$FunctionAppName,
[string]$ResourceGroupName,
[string]$FunctionName,
[datetime]$StartTime,
[datetime]$EndTime,
[ValidateSet("Information", "Warning", "Error")]
[string[]]$LogLevels
)

try {
$logs = Get-AzLog -ResourceId (Get-AzFunctionApp -Name $FunctionAppName -ResourceGroupName $ResourceGroupName).Id `
-StartTime $StartTime `
-EndTime $EndTime

if ($FunctionName) {
$logs = $logs | Where-Object { $_.ResourceName -eq $FunctionName }
}

if ($LogLevels) {
$logs = $logs | Where-Object { $_.Level -in $LogLevels }
}

return [PSCustomObject]@{
FunctionApp = $FunctionAppName
Function = $FunctionName
Logs = $logs
Summary = [PSCustomObject]@{
TotalCount = $logs.Count
ByLevel = $logs | Group-Object Level | ForEach-Object {
[PSCustomObject]@{
Level = $_.Name
Count = $_.Count
}
}
}
}
}
catch {
Write-Host "日志收集失败:$_"
}
}

Azure Functions 配置管理:

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
# 创建 Azure Functions 配置管理函数
function Manage-AzFunctionConfig {
param(
[string]$FunctionAppName,
[string]$ResourceGroupName,
[hashtable]$AppSettings,
[switch]$RemoveUnspecified
)

try {
$functionApp = Get-AzFunctionApp -Name $FunctionAppName -ResourceGroupName $ResourceGroupName

if ($RemoveUnspecified) {
# 删除未指定的设置
$currentSettings = $functionApp.AppSettings
$currentSettings.Keys | ForEach-Object {
if (-not $AppSettings.ContainsKey($_)) {
$AppSettings[$_] = $null
}
}
}

# 更新应用设置
Update-AzFunctionApp -Name $FunctionAppName `
-ResourceGroupName $ResourceGroupName `
-AppSettings $AppSettings

Write-Host "配置更新完成"
}
catch {
Write-Host "配置更新失败:$_"
}
}

这些技巧将帮助您更有效地管理 Azure Functions。记住,在处理 Azure Functions 时,始终要注意应用的安全性和性能。同时,建议使用适当的监控和日志记录机制来跟踪函数的运行状态。

PowerShell 技能连载 - 高级参数处理

动态参数机制

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
function Get-EnvironmentData {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$EnvironmentName
)

dynamicparam {
$paramDictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary

if ($EnvironmentName -eq 'Production') {
$attribute = [System.Management.Automation.ParameterAttribute]@{
Mandatory = $true
HelpMessage = "生产环境专属参数"
}
$param = New-Object Management.Automation.RuntimeDefinedParameter(
'ProductionKey',
[string],
$attribute
)
$paramDictionary.Add('ProductionKey', $param)
}
return $paramDictionary
}
}

参数集应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Set-SystemConfiguration {
[CmdletBinding(DefaultParameterSetName='Basic')]
param(
[Parameter(ParameterSetName='Basic', Mandatory=$true)]
[string]$BasicConfig,

[Parameter(ParameterSetName='Advanced', Mandatory=$true)]
[string]$AdvancedConfig,

[Parameter(ParameterSetName='Advanced')]
[ValidateRange(1,100)]
[int]$Priority
)
# 根据参数集执行不同逻辑
}

最佳实践

  1. 使用Parameter属性定义清晰的参数关系
  2. 为复杂场景设计参数集
  3. 通过dynamicparam实现条件参数
  4. 保持参数命名的语义清晰

```powershell
function Invoke-CustomOperation {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true)]
[ValidateScript({
if ($_.OperationType -notin ‘Read’,’Write’) {
throw “无效的操作类型”
}
$true
})]
[object]$InputObject
)
# 管道参数处理逻辑
}

PowerShell 技能连载 - 音频处理技巧

在 PowerShell 中处理音频文件可能不是最常见的任务,但在某些场景下非常有用。本文将介绍一些实用的音频处理技巧。

首先,让我们看看基本的音频操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 创建音频处理函数
function Get-AudioInfo {
param(
[string]$AudioPath
)

# 使用 NAudio 库获取音频信息
Add-Type -Path "NAudio.dll"
$reader = [NAudio.Wave.AudioFileReader]::new($AudioPath)

$info = [PSCustomObject]@{
FileName = Split-Path $AudioPath -Leaf
Duration = $reader.TotalTime
SampleRate = $reader.WaveFormat.SampleRate
Channels = $reader.WaveFormat.Channels
BitsPerSample = $reader.WaveFormat.BitsPerSample
FileSize = (Get-Item $AudioPath).Length
}

$reader.Dispose()
return $info
}

音频格式转换:

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 Convert-AudioFormat {
param(
[string]$InputPath,
[string]$OutputPath,
[ValidateSet("mp3", "wav", "ogg", "aac")]
[string]$TargetFormat
)

try {
# 使用 FFmpeg 进行格式转换
$ffmpeg = "C:\ffmpeg\bin\ffmpeg.exe"

switch ($TargetFormat) {
"mp3" {
& $ffmpeg -i $InputPath -codec:a libmp3lame -q:a 2 $OutputPath
}
"wav" {
& $ffmpeg -i $InputPath -codec:a pcm_s16le $OutputPath
}
"ogg" {
& $ffmpeg -i $InputPath -codec:a libvorbis $OutputPath
}
"aac" {
& $ffmpeg -i $InputPath -codec:a aac -b:a 192k $OutputPath
}
}

Write-Host "音频转换完成:$OutputPath"
}
catch {
Write-Host "转换失败:$_"
}
}

音频剪辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 创建音频剪辑函数
function Split-AudioFile {
param(
[string]$InputPath,
[TimeSpan]$StartTime,
[TimeSpan]$Duration,
[string]$OutputPath
)

try {
$ffmpeg = "C:\ffmpeg\bin\ffmpeg.exe"
$start = $StartTime.ToString("hh\:mm\:ss")
$duration = $Duration.ToString("hh\:mm\:ss")

& $ffmpeg -i $InputPath -ss $start -t $duration -acodec copy $OutputPath
Write-Host "音频剪辑完成:$OutputPath"
}
catch {
Write-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
# 创建音频合并函数
function Merge-AudioFiles {
param(
[string[]]$InputFiles,
[string]$OutputPath
)

try {
# 创建临时文件列表
$tempFile = "temp_list.txt"
$InputFiles | ForEach-Object {
"file '$_'" | Add-Content $tempFile
}

# 使用 FFmpeg 合并文件
$ffmpeg = "C:\ffmpeg\bin\ffmpeg.exe"
& $ffmpeg -f concat -safe 0 -i $tempFile -c copy $OutputPath

# 清理临时文件
Remove-Item $tempFile

Write-Host "音频合并完成:$OutputPath"
}
catch {
Write-Host "合并失败:$_"
}
}

一些实用的音频处理技巧:

  1. 音频批量处理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # 批量处理音频文件
    function Process-AudioBatch {
    param(
    [string]$InputFolder,
    [string]$OutputFolder,
    [scriptblock]$ProcessScript
    )

    # 创建输出目录
    New-Item -ItemType Directory -Path $OutputFolder -Force

    # 获取所有音频文件
    $audioFiles = Get-ChildItem -Path $InputFolder -Include *.mp3,*.wav,*.ogg,*.aac -Recurse

    foreach ($file in $audioFiles) {
    $outputPath = Join-Path $OutputFolder $file.Name
    & $ProcessScript -InputPath $file.FullName -OutputPath $outputPath
    }
    }
  2. 音频效果处理:

    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
    # 应用音频效果
    function Apply-AudioEffect {
    param(
    [string]$InputPath,
    [string]$OutputPath,
    [ValidateSet("normalize", "fade", "echo", "reverb")]
    [string]$Effect,
    [hashtable]$Parameters
    )

    try {
    $ffmpeg = "C:\ffmpeg\bin\ffmpeg.exe"
    $filter = switch ($Effect) {
    "normalize" { "loudnorm" }
    "fade" { "afade=t=in:st=0:d=$($Parameters.Duration)" }
    "echo" { "aecho=0.8:0.88:60:0.4" }
    "reverb" { "aecho=0.8:0.9:1000:0.3" }
    }

    & $ffmpeg -i $InputPath -af $filter $OutputPath
    Write-Host "已应用效果:$Effect"
    }
    catch {
    Write-Host "效果处理失败:$_"
    }
    }
  3. 音频分析:

    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
    # 分析音频波形
    function Analyze-AudioWaveform {
    param(
    [string]$AudioPath,
    [int]$SamplePoints = 100
    )

    Add-Type -Path "NAudio.dll"
    $reader = [NAudio.Wave.AudioFileReader]::new($AudioPath)

    # 读取音频数据
    $buffer = New-Object float[] $SamplePoints
    $reader.Read($buffer, 0, $SamplePoints)

    # 计算波形数据
    $waveform = @()
    for ($i = 0; $i -lt $SamplePoints; $i += 2) {
    $waveform += [PSCustomObject]@{
    Time = $i / $reader.WaveFormat.SampleRate
    Left = $buffer[$i]
    Right = $buffer[$i + 1]
    }
    }

    $reader.Dispose()
    return $waveform
    }

这些技巧将帮助您更有效地处理音频文件。记住,在处理音频时,始终要注意文件格式的兼容性和音频质量。同时,建议在处理大型音频文件时使用流式处理方式,以提高性能。

PowerShell 技能连载 - Pester 测试技巧

在 PowerShell 中使用 Pester 进行测试是一项重要任务,本文将介绍一些实用的 Pester 测试技巧。

首先,让我们看看基本的 Pester 测试操作:

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
# 创建 Pester 测试函数
function New-PesterTest {
param(
[string]$TestName,
[string]$TestPath,
[string]$ModuleName,
[string[]]$Functions
)

try {
# 创建测试文件
$testContent = @"
Describe '$ModuleName' {
BeforeAll {
Import-Module $ModuleName
}

Context '基本功能测试' {
BeforeEach {
# 测试前的准备工作
}

AfterEach {
# 测试后的清理工作
}

It '应该成功导入模块' {
Get-Module -Name $ModuleName | Should -Not -BeNullOrEmpty
}

It '应该包含所有必需的函数' {
$module = Get-Module -Name $ModuleName
foreach ($function in $Functions) {
$module.ExportedFunctions.Keys | Should -Contain $function
}
}
}
}
"@

$testContent | Out-File -FilePath $TestPath -Encoding UTF8
Write-Host "测试文件 $TestPath 创建成功"
}
catch {
Write-Host "创建测试文件失败:$_"
}
}

Pester 模拟测试:

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 创建 Pester 模拟测试函数
function New-PesterMockTest {
param(
[string]$TestName,
[string]$TestPath,
[string]$FunctionName,
[hashtable]$MockData
)

try {
$testContent = @"
Describe '$FunctionName 模拟测试' {
BeforeAll {
Import-Module $ModuleName
}

Context '模拟外部依赖' {
BeforeEach {
Mock Get-Process {
return @(
@{
Name = 'Process1'
Id = 1001
CPU = 10
},
@{
Name = 'Process2'
Id = 1002
CPU = 20
}
)
}

Mock Get-Service {
return @(
@{
Name = 'Service1'
Status = 'Running'
},
@{
Name = 'Service2'
Status = 'Stopped'
}
)
}
}

It '应该正确模拟进程数据' {
$result = Get-Process
$result.Count | Should -Be 2
$result[0].Name | Should -Be 'Process1'
$result[1].Name | Should -Be 'Process2'
}

It '应该正确模拟服务数据' {
$result = Get-Service
$result.Count | Should -Be 2
$result[0].Status | Should -Be 'Running'
$result[1].Status | Should -Be 'Stopped'
}
}
}
"@

$testContent | Out-File -FilePath $TestPath -Encoding UTF8
Write-Host "模拟测试文件 $TestPath 创建成功"
}
catch {
Write-Host "创建模拟测试文件失败:$_"
}
}

Pester 参数测试:

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
53
54
55
# 创建 Pester 参数测试函数
function New-PesterParameterTest {
param(
[string]$TestName,
[string]$TestPath,
[string]$FunctionName,
[hashtable]$Parameters
)

try {
$testContent = @"
Describe '$FunctionName 参数测试' {
BeforeAll {
Import-Module $ModuleName
}

Context '参数验证' {
It '应该验证必需参数' {
{ & $FunctionName } | Should -Throw
}

It '应该验证参数类型' {
{ & $FunctionName -Name 123 } | Should -Throw
}

It '应该验证参数范围' {
{ & $FunctionName -Count -1 } | Should -Throw
}

It '应该验证参数格式' {
{ & $FunctionName -Email 'invalid-email' } | Should -Throw
}
}

Context '参数组合' {
It '应该处理有效的参数组合' {
$result = & $FunctionName -Name 'Test' -Count 5
$result | Should -Not -BeNullOrEmpty
}

It '应该处理边界条件' {
$result = & $FunctionName -Name 'Test' -Count 0
$result | Should -BeNullOrEmpty
}
}
}
"@

$testContent | Out-File -FilePath $TestPath -Encoding UTF8
Write-Host "参数测试文件 $TestPath 创建成功"
}
catch {
Write-Host "创建参数测试文件失败:$_"
}
}

Pester 性能测试:

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
53
54
55
56
57
58
59
60
61
62
63
64
65
# 创建 Pester 性能测试函数
function New-PesterPerformanceTest {
param(
[string]$TestName,
[string]$TestPath,
[string]$FunctionName,
[int]$Iterations = 1000,
[int]$Timeout = 30
)

try {
$testContent = @"
Describe '$FunctionName 性能测试' {
BeforeAll {
Import-Module $ModuleName
}

Context '执行时间测试' {
It '应该在指定时间内完成' {
$measurement = Measure-Command {
for ($i = 0; $i -lt $Iterations; $i++) {
& $FunctionName
}
}

$measurement.TotalSeconds | Should -BeLessThan $Timeout
}

It '应该保持稳定的执行时间' {
$times = @()
for ($i = 0; $i -lt 10; $i++) {
$measurement = Measure-Command {
& $FunctionName
}
$times += $measurement.TotalMilliseconds
}

$average = ($times | Measure-Object -Average).Average
$standardDeviation = [math]::Sqrt(($times | ForEach-Object { [math]::Pow($_ - $average, 2) } | Measure-Object -Average).Average)

$standardDeviation | Should -BeLessThan ($average * 0.1)
}
}

Context '资源使用测试' {
It '应该控制内存使用' {
$before = (Get-Process -Id $PID).WorkingSet64
for ($i = 0; $i -lt $Iterations; $i++) {
& $FunctionName
}
$after = (Get-Process -Id $PID).WorkingSet64

($after - $before) / 1MB | Should -BeLessThan 100
}
}
}
"@

$testContent | Out-File -FilePath $TestPath -Encoding UTF8
Write-Host "性能测试文件 $TestPath 创建成功"
}
catch {
Write-Host "创建性能测试文件失败:$_"
}
}

Pester 测试报告:

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
# 创建 Pester 测试报告函数
function New-PesterTestReport {
param(
[string]$TestPath,
[string]$ReportPath,
[string]$Format = 'HTML'
)

try {
$testResults = Invoke-Pester -Path $TestPath -PassThru

switch ($Format) {
'HTML' {
$testResults | ConvertTo-Html -Title "Pester 测试报告" | Out-File -FilePath $ReportPath -Encoding UTF8
}
'XML' {
$testResults | ConvertTo-Xml | Out-File -FilePath $ReportPath -Encoding UTF8
}
'JSON' {
$testResults | ConvertTo-Json -Depth 10 | Out-File -FilePath $ReportPath -Encoding UTF8
}
}

return [PSCustomObject]@{
TotalTests = $testResults.TotalCount
PassedTests = $testResults.PassedCount
FailedTests = $testResults.FailedCount
SkippedTests = $testResults.SkippedCount
ReportPath = $ReportPath
}
}
catch {
Write-Host "生成测试报告失败:$_"
}
}

这些技巧将帮助您更有效地使用 Pester 进行测试。记住,在编写测试时,始终要注意测试覆盖率和可维护性。同时,建议使用适当的测试数据和模拟来确保测试的可靠性。

PowerShell 技能连载 - 基于ATT&CK框架的进程行为分析

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
function Invoke-ProcessBehaviorAnalysis {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true)]
[string]$ComputerName = $env:COMPUTERNAME
)

$techniques = Invoke-RestMethod -Uri 'https://attack.mitre.org/api/techniques/'
$processes = Get-CimInstance -ClassName Win32_Process -ComputerName $ComputerName

$report = [PSCustomObject]@{
Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
SuspiciousProcesses = @()
MITRETechniques = @()
}

$processes | ForEach-Object {
$behaviorScore = 0
$matchedTechs = @()

# 检测隐藏进程
if ($_.ParentProcessId -ne 1 -and -not (Get-Process -Id $_.ParentProcessId -ErrorAction SilentlyContinue)) {
$behaviorScore += 20
$matchedTechs += 'T1055' # Process Injection
}

# 检测异常内存操作
if ($_.WorkingSetSize -gt 1GB) {
$behaviorScore += 15
$matchedTechs += 'T1056' # Memory Allocation
}

if ($behaviorScore -gt 25) {
$report.SuspiciousProcesses += [PSCustomObject]@{
ProcessName = $_.Name
ProcessId = $_.ProcessId
Score = $behaviorScore
CommandLine = $_.CommandLine
}
$report.MITRETechniques += $matchedTechs | Select-Object @{n='TechniqueID';e={$_}}, @{n='Description';e={$techniques.techniques.Where{$_.id -eq $_}.name}}
}
}

$report | ConvertTo-Json -Depth 3 | Out-File "$env:TEMP/ProcessAnalysis_$(Get-Date -Format yyyyMMdd).json"
return $report
}

核心功能

  1. ATT&CK技术特征匹配
  2. 进程行为异常评分
  3. 自动化威胁检测
  4. JSON报告生成

典型应用场景

  • 企业终端安全监控
  • 红队攻击痕迹分析
  • 蓝队防御策略验证
  • 安全事件快速响应