前陣子體驗到 Powershell 免編譯可直接修改調整的好處,而明文格式人眼可辨,列入 SOP 移交 OP 接受度較高,加上 Powershell 已取代 VBScript 成為微軟系統管理腳本主力,相關資源豐富,近期手邊有些排程應用,便計劃改用 Powershell 解決。 寫著寫著發現一個問題 - 一直沒認真把 Powershell 當成一門語言學習,只靠爬文找範例組拼裝車,改程式常卡在一些笨問題上,怎麼引用 .NET 物件?有沒有 Dictionary 可用?字串遇特殊字元怎麼 Escape?

馬步不蹲,武功難成,下了決心,在網路隨便找本Powershell 電子書利用周末啃完,算是有點基礎,改程式才不再心慌。

依照慣例邊讀邊做筆記備忘,再順手分享。

基本

 

  • 預設 .ps1 檔必須經數位簽章才能執行,開發測試時記得 Set-ExecutionPolicy RemoteSigned(本機檔案放行,網路下載者要簽章) 或 powershell.exe -ExecutionPolicy Bypass -File "c:\MyScript.ps1"
    此外,ExcutionPolicy 可針對本機、該使用者、該程式做不同設定,用 Get-ExcutionPolicy -List 可檢查
  • Write-Host 輸出至 stdout (如 Console 畫面),Write-Output 輸出至 stdout 及 Output Stream(可導向到檔案或其他管道)
  • PowerShell 很常用 Alias,例如:Write-Output 可寫成 Echo/Write,ls/dir 背後跑的是 Get-ChildItem,並不是 Linux ls 或 DOS dir (也可用 Set-Alias 自訂,但會在關閉 Session 後失效,若要永久有效需寫入 Profile )
  • Select 是 Select-Object 的別名,ForEach 是 ForEach-Object 的別名,甚至可寫成 % 就好,Where 寫 ? 則可。Get-Alias 可得完整清單
  • 可直接整合 .NET 物件 [System.IO.Path]::GetFileName('C:\Windows\explorer.exe'),在 VSCode 裡還可享用 .NET 物件的 Intellisense 提示,讚
  • Powershell 每行程式結尾的「;」可加可不加,一致即可。寫慣 C#、JavaScript,我傾向要加。
  • 建 .NET 物件的幾種寫法
    Add-Type -AssemblyName System.Drawing;
    $p1 = New-Object System.Drawing.Point -ArgumentList 1,1;
    $p2 = New-Object System.Drawing.Point(2, 2);
    $p3 = New-Object -TypeName System.Drawing.Point -Property @{
        X = 3;
        Y = 3;
    };
    $p4 = [System.Drawing.Point]@{ X=4; Y=4;};
  • PowerShell 也支援 Ubuntu Linux (WOW!)
  • 註解:# = //,<# ... #> = /* ... */
  • 陣列有些新玩法
    $ary1 = 1,2,3,4;
    $ary1 = $ary1 + 5; #變成五個元素
    $ary2 = 6,7,8;
    $ary3 = $ary1 + $ary2; #八個元素
    $p = "1.2.3.4".Split("."); $p[1]; #傳回2
    $a,$b,$c = "1-2-3".Split("-"); $c; #傳回"3"
  • 直接執行指令 PowerShell.exe -Command "(Get-Date).ToShortDateString(); 'PowerShell is fun!'",加 -Command 可吃 Pipeline 輸入
  • 執行安全原則
    AllSigned 限受信任發行者簽章過的才能執行
    Bypass 不限制,什麼都可以執行 Default 通常等於 RemoteSigned, 但由 ActiveDirectory 控制
    RemoteSigned 從網路下載的必須有簽章
    Restricted 一律不准。Windows PowerShell 限互動模式使用 Undefined 未使用 Unrestricted* 與 ByPass 相似

變數與運算子

 

  • 變數範圍一樣分全域跟區域,可用 $global:varName、$local:varName 寫法指定範圍
    $foo = "G";
    function myFunc {
     $foo = "L";
     Write-Host $global:foo; #"G"
     Write-Host $local:foo; #"L"
     Write-Host $foo; #"L"
    }
    myFunc;
    Write-Host $local:foo; #"G"
    Write-Host $foo; #"G"
  • 移除變數 Remove-Item Variable:\foo (磁碟機概念) 或 Remove-Varialbe -Name foo 或直接 rv foo
  • 運算子 -eq、-ne、-gt、-ge、-lt、-le、-like、-notlike、-match、-notmatch、-contains、-notcontains、-in、-notin、-shl、-shr、+-*/%、+=、-=
  • 導向:>、>>、2>&1。Powershell 3 再增加 3 Warning Output、4 Verbose Output、5 Debug Output、6 Information Output,*>&1
  • 陣列加與乘:1,2,3 + 4,5,6 == 1,2,3,4,5,6 1,2,3 * 2 == 1,2,3,1,2,3
  • 邏輯運算子:-and、-or、-xor、-not、!
  • 字串處理:
    "The rain in Seattle" -replace 'rain','hail' 
    "kenmyer@contoso.com" -replace '^[\w]+@(.+)', '$1' 
    "A B C" -split " " 
    "E","F","G" -join ":" 
  • 陣列表示式運算子@(Get-ChildItem $env:windir\System32)
  • 將字串視為指令執行(小心 Command Injection) $cmd='Get-ChildItem';& $cmd;
  • 「.」運算子:在現有 Scope 執行 ps1,沿用其中建立的函式、變數 . .\MyFunc.ps1
  • where / sort
    5,4,1,2,3 | sort | where { $_ -gt 1 }
  • Group-Object / group
    $names = @( "Aaron", "Albert", "Alphonse","Bernie", "Charlie", "Danny", "Ernie", "Frank")
    $names | group -Property Length

    Count Name Group
    4     5    {Aaron, Danny, Ernie, Frank}
    2     6    {Albert, Bernie}
    1     8    {Alphonse}
    1     7    {Charlie}
  • Select-Object / select dir "D:\" | select Name, FullName, Attribute

條件式與迴圈

 

  • if 否定寫法 if (-Not $a -eq 2) 或 if (!($a -eq 2)) 或 if ($a -ne 2)
  • if ($a) 何時不成立:$a = $false 或 $null 或 ""(空字串)
  • 迴圈
    ForEach ($name in @("a","b","c")) { <# ... #> }
    ForEach ($n in 1..20) { <# ... #> }
    for ($i = 0; $i -le 5; $i++) { <# ... #> }
    (1..10).ForEach({$_ * *_}); #得到 [1,4,9,16,...,100]
    @("Any","Bob","Celine","David") | ForEach-Object { "Hi, my name is $_!" } # ForEach-Object 可以寫 foreach、% 等 Alias
    @("Any","Bob","Celine","David") | % -Begin { $r=@(); } -Process { $r += "Hi, my name is $_!"; } -End { Write-Host "Count=$($r.Count)"; $r; }
    while ($cond) {
        if ($x) { continue; }
        if ($y) { break; }
    }
    do { some_code; } until ($cond);
    do { some_code; } while ($cond);
  • Switch
    #可加 -CaseSensitive 大小寫有別
    #加 -Wildcard 條件可寫 "Cond*" "C?onditi[a-z]n"
    #加 -Regex 條件可寫正則表示式
    #-File filename.txt ==> 逐行比對
    #花式用法:多個條件吻合都會執行,另可用 break 跳出
    #條件式還可寫 (2*3-1) 或 ($_ -gt -1 -and $_ -lt 1)
    switch ($v) {
      'a' { action1; }
      'b' { action2; }
      Default { action_other; }
    }

字串

 

  • 多行文字
    "Hello`r`nWorld";
    "Hello{0}World" -f [environment]::NewLine;
    "Hello
    World";
    @"
    Hello
    "World"
    "@; #可直接寫雙引號,不必 Escape
  • 單引號及雙引號
    "Timezone=$((Get-TimeZone).DisplayName)"; #顯示 Timezone=(UTC+08:00) 台北
    'Timezone=$((Get-TimeZone).DisplayName)'; #顯示 Timezone=$((Get-TimeZone).DisplayName)
    $a=123;
    "a=$a"; #顯示 a=123
  • 特殊字元
    "`0" #Null
    "`a" #Alert/Beep
    "`b" #Backspace
    "`f" #Form feed (used for printer output)
    "`n" #New line
    "`r" #Carriage return
    "`t" #Horizontal tab
    "`v" #Vertical tab (used for printer output)
    "`#" #Comment-operator
    "`$" #Variable operator
    "``" #Escape character
    "`'" #Single quote
    "`"" #Double quote
  • 格式化
    'You should really visit {0}' -f $hash.city;
    [String]::Format('You should really visit {0}', 'Taipei');

Hashtable

 

    $hashTable = @{
      'Key 1' = 'Value1';
      Key2 = 'Value2';
    };
    $hashTable += @{ Key3 = 'Value3' };
    $hashTable.Add("Key4", "Value4");
    $hashTable.Remove("Key2");
    foreach ($key in $var1.Keys) {
      $value = $var1[$key]
      # or
      $value = $var1.$key
    }
    foreach ($keyvaluepair in $var1.GetEnumerator()) {
      $key1 = $_.Key1;
      $val1 = $_.Val1;
    }

物件操作

 

    (Get-Item C:\Windows).GetType() #顯示 Name 為 DictionaryInfo
    Get-Item C:\Windows | Get-Member #列出 DictionaryInfo 的方法
    Get-Item . | Format-List -Property * #以 Key: Value 格式每項一行顯示
    # 動態捏物件
    $object = New-Object -TypeName PSObject -Property @{
    Name = $env:username
    ID = 12
    Address = $null
    }
    Add-Member -InputObject $object -Name "SomeNewProp" -Value "A value" -MemberType NoteProperty
    # 用 Select 擴充Property
    $newObject = $Object | Select-Object *, @{label='SomeOtherProp'; expression={'Another value'}}
    $newObject = $Object | Select *,@{l='SomeOtherProp';e={'Another value'}} #再縮寫
    #建集合加物件
    $newCollection = @()
    $newCollection += New-Object -TypeName PSObject -Property @{
        Name = $env:username
        ID = 12
        Address = $null
    }
    #指定屬性順序
    $newObject = [PSCustomObject][Ordered]@{
        Name = $env:Username
        ID = 12
        Address = $null
    }
    #泛型
    #Nullable System.DateTime
    [Nullable[datetime]]$nullableDate = Get-Date -Year 2012
    $nullableDate
    $nullableDate.GetType().FullName
    $nullableDate = $null
    $nullableDate
    #Dictionary<T, T>
    [System.Collections.Generic.SortedDictionary[int, String]]$dict =
    [System.Collections.Generic.SortedDictionary[int, String]]::new();

函數

 

    #嚴謹版
    function Write-Greeting {
        param(
            [Parameter(Mandatory,Position=0)]
            [String]$name,
            [Parameter(Mandatory,Position=1)]
            [Int]$age
        )
        "Hello $name, you are $age years old."
    }
    #簡易版
    function Write-Greeting ($name, $age) {
        "Hello $name, you are $age years old."
    }
    $greeting = Write-Greeting "Jim" 82; #依順序提供參數
    $greeting = Write-Greeting -name "Bob" -age 82; #具名提供參數
    
    #專業等級 用起來像Cmdlet (加[CmdletBinding])
    <#
    .Synopsis
        Short description
    .DESCRIPTION
        Long description
    .EXAMPLE
        Example of how to use this cmdlet
    .EXAMPLE
        Another example of how to use this cmdlet
    .INPUTS
        Inputs to this cmdlet (if any)
    .OUTPUTS
        Output from this cmdlet (if any)
    .NOTES
        General notes
    .COMPONENT
        The component this cmdlet belongs to
    .ROLE
        The role this cmdlet belongs to
    .FUNCTIONALITY
        The functionality that best describes this cmdlet
    #>
    function Verb-Noun
    {
        [CmdletBinding(DefaultParameterSetName='Parameter Set 1',
                        SupportsShouldProcess=$true,
                        PositionalBinding=$false,
                        HelpUri = 'http://www.microsoft.com/',
                        ConfirmImpact='Medium')]
        [Alias()]
        [OutputType([String])]
        Param
        (
            # Param1 help description
            [Parameter(Mandatory=$true, #必要參數
                        ValueFromPipeline=$true,
                        ValueFromPipelineByPropertyName=$true,
                        ValueFromRemainingArguments=$false,
                        Position=0,
                        ParameterSetName='Parameter Set 1')]
            [ValidateNotNull()]
            [ValidateNotNullOrEmpty()]
            [ValidateCount(0,5)]
            [ValidateSet("sun", "moon", "earth")] #預設選項
            [Alias("p1")]
            $Param1,
            # Param2 help description
            [Parameter(ParameterSetName='Parameter Set 1')] #可設定互斥,同Set參數只能給一個(RadioButton概念)
            [AllowNull()]
            [AllowEmptyCollection()]
            [AllowEmptyString()]
            [ValidateScript({$true})] #執行外部指令 Cmdlet,用 $_ 存取參數本身
            [ValidateRange(0,5)] #指定範圍
            [int]
            $Param2,
            # Param3 help description
            [Parameter(ParameterSetName='Another Parameter Set')]
            [ValidatePattern("[a-z]*")] #Regex驗證
            [ValidateLength(0,15)] #長度驗證
            [String]
            $Param3,
            [swith]$SomeFlag #呼叫時加上 -SomeFlag 使其為 true
        )
        Begin
        {
        }
        Process
        {
            if ($pscmdlet.ShouldProcess("Target", "Operation"))
            {
            }
        }
        End
        {
        }
    }
  • PS 函數可以不寫 return(寫了較明確),且過程輸出結果都會傳回
    function Get-Greeting{ "Hello World"; return "!"; }
    $greeting = Get-Greeting; #$greeting 變數會是Hello World及"!"兩行字串
  • 按 Ctrl-Space 列出可用參數(按鍵與中文輸入打架)

類別

 

    [DateTime]::new # 列出所有建構式, PS 5.0+,早期版本要 $type.GetConstructors()
    [String]::Format # 列出所有Overloading
    #宣告類別並使用,也可以繼承
    class Person {
        [string] $FirstName
        [string] $LastName
        [string] Greeting() {
            return "Greetings, {0} {1}!" -f $this.FirstName, $this.LastName    
            
        } 
    }
    $x = [Person]::new() 
    $x.FirstName = "Jane" 
    $x.LastName = "Doe" 
    $greeting = $x.Greeting() # "Greetings, Jane Doe!" 
  • Get-Member -InputObject $someObject 列舉物件成員

模組

 

  • PS 支援模組(Module, .psm1)以收納相同屬性共用函數重複利用,模組有 Manifest(.psd1) 定義名稱、作者、版本、說明、函數列表,New-ModuleManifest 可產生模版
  • .psm1 中用 Export-ModuleMember -Function funcName 對外公開方法,-Variable varName 公開變數,-Alias alias 公開別名
    預設自動公開所有函數,但變數與別名要自行用 Export-ModuleMember 公開,一但使用 Export-ModuleMember,連函數也改為手動公開
  • 使用 Import-Module
  • Tips - 查看 Cmdlet 參數,(Get-Command Export-ModuleMember).Parameters、Get-Help Export-ModuleMember 取得說明,看範例 Get-Help Get-Command -Examples,完整版加 -Full(沒啥用),看參數說明加 -Paramter paraName(沒啥用),線上版 -Online (好用)
  • 模組所在位置 $Env:PSModulePath
  • Get-Module -ListAvailable 列出所有可用模組
  • Import-Module Microsoft.PowerShell.Archive
    Get-Command -Module Microsoft.PowerShell.Archive #列出可用函數

Profile

 

  • PS 不會自動建立,New-Item -ItemType File $profile 或用文字檔編輯工具自己來
  • $profile 變數會回傳 Profile 設定(來自套用所有使用者的設定,Console/ISE/IDE...) $Profile | Format-List -Force

內建變數

 

  • $PSScriptRoot PS 所在的基本資料夾,相當於 "."
  • $Args 傳給函數、Script 區塊的參數陣列,事件中常用
  • $PSItem ForEach 時指向當次迴圈物件(亦可用 $_)
  • $? 上次執行結果 TRUE - 成功,FALSE - 失敗
  • $error 執行錯誤陣列,最近的一次可查 $error[0].Exception.Message,Format-List 顯示的話記得加上 -Force,否則會以 ExtendedTypeSystem(ETS)格式顯示
  • 若不希望錯誤被塞入 $error 可加上 -ErrorAction/-ErrorVariable 通用參數
    ErrorAction:Continue(有訊息但繼續) | Ignore(無錯誤訊息,繼續且不加入$Error) | Inquire(詢問使用者) | SilentlyContinue(無訊息繼續但加入$Error) | Stop | Suspend(僅 Workflow 適用)
  • $OFS Output Field Separator,欄位分隔符號,預設為空白
  • $null, $true, $false
  • $pid Process ID
  • $_ / $PSItem 迴圈 Iterator
  • $PSVersionTable 版本資料
  • $env:COMPUTERNAME 取得 DOS 環境變數

Splatting

 

  • Splatting 以 Key/Value 形式將多個參數包成物件傳遞,當用於多次呼叫時重複利用一組參數
    $splat = @{   
        Class = "Win32_SystemEnclosure"
        Property = "Manufacturer"
        ErrorAction = "Stop" 
        
    }
    Get-WmiObject -ComputerName $env:COMPUTERNAME @splat 
    Get-WmiObject -ComputerName "Computer2" @splat 
    Get-WmiObject -ComputerName "Computer3" @splat
    
    @{   
        ComputerName = $env:COMPUTERNAME
        Class = "Win32_SystemEnclosure"
        Property = "Manufacturer"
        ErrorAction = "Stop" 
    } | % { Get-WmiObject @_ }
    
    # 預設參數
    $MyParameters = @{ FileVersionInfo = $true }
    Get-Process @MyParameters -Name WmiPrvSE 
    Get-Process @MyParameters -Name explorer
  • @PSBoundParameters 在函數中將參數物件再傳給內層呼叫的其他函式

導向

 

    Write-Output 'My text' | Out-File -FilePath "$env:TEMP\Test.txt"
    'Hello world' # 沒加Cmdlet,相當於Write-Output
  • Write-Output 會將結果導向 Pipeline,Pipeline 的尾端會輸出到 Console
  • Write-Verbose、Write-Information、Write-Debug、Write-Progress、Write-Warning
  • 預設不同輸出的行為
    $VerbosePreference = "SilentlyContinue"
    $InformationPreference = "SilentlyContinue"
    $DebugPreference = "SilentlyContinue"
    $ProgressPreference = "Continue"
    $WarningPreference = "Continue"
  • 設為 "Inquire" 時
    Confirm
    Continue with this operation?
    [Y] Yes  [A] Yes to All  [H] Halt Command  [S] Suspend  [?] Help (default is "Y"):
  • $host.PrivateData.ErrorBackgroundColor = "Black"
    $host.PrivateData.ErrorForegroundColor = "Red"
  • 在函數中接入 Piepline
    function Write-FromPipeline {
        param(
            [Parameter(ValueFromPipeline)]  # This sets the ParameterAttribute
            [String]$Input    
        )    
        Write-Host $Input;
    }
    $foo = 'Hello World!'
    $foo | Write-FromPipeline
  • Pipeline 是同步執行的!! @( bigFile_1, bigFile_2, ..., bigFile_n) | Copy-File | Encrypt-File | Get-Md5 可以實現 Copy-File 在處理第三個檔案時,Encrypt 已在處理第二個檔案,Get-Md5 處理第一個檔案, 而非 Copy-File 全部做完才換 Encrypt-File

背景執行

 

    $job = Start-Job -ScriptBlock { SomeJob }
    $job = Start-Job -File "D:\Blah.ps1"
    $job = Invoke-Command -ComputerName "ComputerName" -ScriptBlock {Get-Service winrm} -JobName "WinRM" -ThrottleLimit 16 -AsJob #遠端
    Start-Job -ScriptBlock {Get-Process} -Credential "Domain\Username" #不同身分,會問密碼
    #如何寫死帳號密碼
    $username = "Domain\Username" 
    $password = "password" #明碼寫死有風險,請參考下方加密另存設定檔做法
    $secPassword = ConvertTo-SecureString -String $password -AsPlainText -Force 
    $credentials = New-Object System.Management.Automation.PSCredential -ArgumentList @($username, $secPassword) 
    Start-Job -ScriptBlock {Get-Process} -Credential $credentials 
    #取得作業項目
    Get-Job
    $job | Wait-job | Receive-Job #等結果
    $job | Wait-job -Timeout 10 #設定時限
    $job | Stop-Job #停止
    $job | Remove-Job 
    #以下只適用 Workflow Job
    $job | Suspend-Job
    $job | Resume-Job
  • Tips:保存帳密
    $credential = Get-Credential
    $credential | Export-CliXml -Path 'C:\My\Path\cred.xml' #加密後寫入
    $credential = Import-CliXml -Path 'C:\My\Path\cred.xml'

錯誤處理

 

  • 一樣有 Try ... Catch ....,但錯誤分 Terminating 及 Non-Terminating,前者可攔。像 1/0 為 Terminating,但 Stop-Process -Id 123456 不是,要改成 Stop-Process -Id 123456 -ErrorAction Stop
  • Write-Error 可觸發 Non-Terminating 錯誤,throw "Error Message" 可觸發 Terminating 錯誤

Package

 

  • 若不小心 PSGallery 預設模組被移除,用 Register-PSRepository -Default 註冊回來
  • Find-Module -Name name
  • Install-Module -Name name
  • Uninstall-Module -Name name -RequiredVersion version
  • Update-Module -Name name

Workflow

 

PS 3+ 支援,用 WWF 執行而非 PowerShell Engine

    workflow DoSomeWork {
        param (
            [string[]] $ComputerName  
        )
        Get-Process -ComputerName $ComputerName
    }
    DoSomeWork -ComputerName server01, server02, server03 

內嵌 C#

 

直接在 .ps1 內寫 C#

    $code = " using System;
    namespace MyNameSpace {
        public class Responder
        {
            public static void StaticRespond()
            {
                Console.WriteLine("Static Response");        
            }
            public void Respond()        
            {
                Console.WriteLine("Instance Respond");
            }
        }
    } "@
    # Check the type has not been previously added within the session, otherwise an exception is raised 
    if (-not ([System.Management.Automation.PSTypeName]'MyNameSpace.Responder').Type) {
        Add-Type -TypeDefinition $code -Language CSharp; 
    }
    [MyNameSpace.Responder]::StaticRespond();
    $instance = New-Object MyNameSpace.Responder; $instance.Respond(); 

常見工作

只記關鍵字,用到再查

  • 寄信 Send-MailMessage
  • 寫入 Registry New-ItemProperty -Path "HKCU:\" -Name "test" -Value "TestValue" -Type "String"
  • Import-Csv / Export-Csv
  • XML
    #方法一
    $xdoc = New-Object System.Xml.XmlDocument; $file = Resolve-Path(".\file.xml"); $xdoc.load($file);
    #方法二
    [xml]$xdoc = Get-Conent ".\file.xml"; 
    #方法三
    $xdoc = [xml]Get-Conent ".\file.xml";
    
    [xml]$xml=@"<people><person>....";
    
    $xdoc.people.person[0].name;
    $xdoc.SelectNodes("//people/person")
  • 呼叫 WebAPI Invoke-RestMethod
  • 執行 SQL Invoke-SqlCmd2 -ServerInstance ... -DataBase ... -Query ...
  • -match 支援 Regex,Select-String -Pattern 也可用 Regex,不然 [regex]::Match($text, $pattern) 亦可
  • -replace Regex 置換,或 [regex]::Replace($text, $pattern, $replace)
    # 動態決定置換內容
    $MatchEvalutor = {
        param($match)
        #Replace content with length of content
        $match.Value.Length
    }
    [regex]::Replace($text, $pattern, $MatchEvalutor)
  • 顯示進度條 Write-Progress
  • 執行外部程式
    gui_app.exe
    & gui_app.exe 
    & gui_app.exe | Out-Null
    Start-Process gui_app.exe 
    Start-Process gui_app.exe -Wait
  • 動態參數限制,當 A 參數符合條件時,必須要提供 B 參數 - Dynamic Paramters
  • 可整合 WPF GUI,動態捏出 WPF 對話框 (有點累,還是開專案吧)
  • [System.Web.HttpUtility]::UrlEncode($qs.Name) 或 [uri]::EscapeDataString("http://test.com?test=my value")
  • JFrog Artifactory Repository 有 Artifactory 可下載
  • 內建壓 ZIP 功能:Compress-Archive -Path C:\Documents\* -CompressionLevel Optimal -DestinationPath C:\Archives\Documents.zip
  • PSScriptAnalyzer 檢查 PS 寫法是否符合規範
  • Desired State Configuration (DSC)
    以下程式會生成 localhost.mof,Start-DscConfiguration -ComputerName localhost -Wait 執行它可自動部署
    configuration EnableIISFeature {
        node localhost
        {
            WindowsFeature IIS
            {
                Ensure = “Present”
                Name = “Web-Server”
            }
        }
    }

更多介紹 Windows PowerShell 預期狀態設定概觀

  • ShouldProcess 防呆機制,在 Cmdlet 裡實作後,加上 -WhatIf 預演,-Confirm 才真的執行。Implementing ShouldProcess For Your Functions
  • 排程:New-ScheduledTaskPrincipal、New-ScheduledTaskTrigger、New-ScheduledTaskAction、Register-ScheduledTask -Principal
  • WMI Get-CimInstance -ClassName Win32_Process、Get-WmiObject -Namespace "root\SecurityCenter2" -Class AntiVirusProduct
  • AD 管理:Get-ADUser、Get-ADGroup、Get-ADGroupMembe、Move-ADObject、Set-ADObject
  • SPS 管理:Add-PSSnapin "Microsoft.SharePoint.PowerShell"
  • Psack 用 PS 寫的自動編譯工具(似 rake(Ruby), bake(Boo))
  • Pester PS 自動測試
  • 簽章
    $cert = @(Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert)[0]
    $cert = Get-PfxCertificate -FilePath "C:\MyCodeSigningCert.pfx"
    Set-AuthenticodeSignature -Certificate $cert -FilePath c:\MyScript.ps1

雜項

 

  • 計算式屬性 Get-ChildItem C:\MyFolder | Select-Object Name, @{Name="Size_In_KB";Expression={$_.Length / 1Kb}}
  • 使用靜態型別,參照 DLL,Add-Type -AssemblyName "System.Math"; Add-Type -Path "D:\Blah.dll"; [System.Math]::Sqrt(49);
    .NET 內建的可直接用 [guid]::NewGuid() PS 5+ 有 New-Guid
  • Get-Help abount_CommonParameters 列出通用參數
  • 連上遠端主機 Enter-PSSession 192.168.1.1 -Credential $(Get-Credential)
  • 執行遠端主機程式
    Invoke-Command -ComputerName "RemoteComputerName" -ScriptBlock {
        Write-Host "Remote Computer Name: $ENV:ComputerName" 
    }
    
    $Session = New-PSSession -ComputerName "RemoteComputerName"
    $date = Invoke-Command -Session $Session -ScriptBlock { Get-Date } # 反序列化後只有資料沒有方法
  • Enable-PSRemoting -Force 開放遠端連入,不同網域時還需額外設定
  • PSSession 會耗資源,出錯會殘留,可用 try catch 強制清除
  • 接上 | Out-Null 或 > &null 可將輸出丟棄不流入 Ouptut Stream
  • 宣告最近版本要求 #requires -version 4
  • 宣告要以管理者權限執行 #requires -RunAsAdministrator
  • 更新說明 Update-Help、匯出說明 Save-Help
  • 命名原則:動詞-名詞
  • 用 PS 寫自動測試,System.Diagnostics.Process.Start Blah.exe,用 $process.StandardInput.WriteLine("1") 輸入, $process.StandardOutput.ReadToEnd() 讀結果。

Notes of Powershell basic


Comments

# by fredli

針對遠端部份補充個JEA(Just Enough Administration) 避免過高的權限,同時有稽核報告。 可作為高度資安要求環境下遠端命令的方案選項。 https://docs.microsoft.com/zh-tw/powershell/jea/

# by Jeffrey

to fredli, JEA 所提供的管控層級確實安全許多,值得另文介紹。感謝分享。

# by Jeff

很實用的參考資料,感謝分享!

# by 小鐵匠

請問可以把執行原則變更寫在腳本裡嗎? 如果可以,要用什麼指令回應原則變更回覆? 謝謝

# by Jeffrey

to 小鐵匠,「執行原則變更」是指 Set-ExecutionPolicy 嗎?試試 powershell.exe -ExecutionPolicy Bypass -File "c:\YourSetExecPolicyCommand.ps1"

# by Dean_Zhong

歡迎參考看看 $ExecutionPolicy = Get-ExecutionPolicy if ($ExecutionPolicy -ne "RemoteSigned") { Set-ExecutionPolicy RemoteSigned -Force }

Post a comment