2026年3月24日火曜日

FGT用スクリプト

  

構成と使い方

スクリプト概要

機能パラメータセット
JSONファイルで設定変更(変更前後の状態表示付き)-ConfigFile
全体コンフィグ取得(テキスト形式)-GetConfig
全体コンフィグ取得(JSON形式)-GetConfigJson

実行例

powershell
# ① 設定変更(APIトークン推奨)
.\Invoke-FGTConfig.ps1 -FGTHost 192.168.1.99 -ApiToken "xxxxxxxx" `
    -ConfigFile .\fgt-changes-sample.json

# ② テキストコンフィグ取得 → ファイル保存
.\Invoke-FGTConfig.ps1 -FGTHost 192.168.1.99 -ApiToken "xxxxxxxx" `
    -GetConfig -OutputFile .\backup.conf

# ③ JSONコンフィグ取得 → ファイル保存
.\Invoke-FGTConfig.ps1 -FGTHost 192.168.1.99 -ApiToken "xxxxxxxx" `
    -GetConfigJson -OutputFile .\backup.json

# ④ ユーザー/パスワード認証 + 自己署名証明書スキップ
.\Invoke-FGTConfig.ps1 -FGTHost 192.168.1.99 -Username admin -Password "P@ssw0rd" `
    -ConfigFile .\changes.json -SkipCertCheck
```

---

### 設定JSONファイルの書き方

| フィールド | 必須 | 説明 |
|-----------|------|------|
| `description` | 任意 | 変更セットの説明 |
| `onError` | 任意 | `continue`(継続)/ `stop`(中断) グローバルデフォルト |
| `changes[].endpoint` | **必須** | APIエンドポイント(例: `cmdb/firewall/address`) |
| `changes[].method` | **必須** | `GET` / `POST` / `PUT` / `DELETE` |
| `changes[].name` | 任意 | 対象オブジェクト名(PUT/DELETE時に必要) |
| `changes[].data` | 任意 | 設定データ(POST/PUT時) |
| `changes[].onError` | 任意 | 個別エラー時の動作(グローバルを上書き) |

---

### APIトークンの作成方法
```
FGT管理GUI → System → Administrators → Create New → REST API Admin
→ Profile: super_admin(または必要な権限)
→ 生成されたトークンをコピー

注意点

  • -GetConfig は /api/v2/monitor/system/config/backup を使用(FortiOS 6.4以降対応)
  • -GetConfigJson は主要 cmdb カテゴリを個別取得して結合。環境によって不要なカテゴリはスクリプト内の $categories 配列から削除可能
  • VDOM環境では -Vdom で対象VDOMを指定(デフォルト: root
  • 証明書エラーが出る場合は -SkipCertCheck を追加

---
{
  "description": "サンプル設定変更セット - 2025-03-24",
  "onError": "continue",
  "changes": [
    {
      "description": "ファイアウォールアドレス追加(新規作成)",
      "endpoint": "cmdb/firewall/address",
      "method": "POST",
      "name": "",
      "onError": "continue",
      "data": {
        "name": "HOST_192.168.100.10",
        "type": "ipmask",
        "subnet": "192.168.100.10/32",
        "comment": "サンプルホストアドレス"
      }
    },
    {
      "description": "ファイアウォールアドレス更新(既存変更)",
      "endpoint": "cmdb/firewall/address",
      "method": "PUT",
      "name": "HOST_192.168.100.10",
      "onError": "continue",
      "data": {
        "name": "HOST_192.168.100.10",
        "type": "ipmask",
        "subnet": "192.168.100.10/32",
        "comment": "更新済みホストアドレス"
      }
    },
    {
      "description": "スタティックルート追加",
      "endpoint": "cmdb/router/static",
      "method": "POST",
      "name": "",
      "onError": "stop",
      "data": {
        "dst": "10.0.0.0/8",
        "gateway": "192.168.1.254",
        "device": "port1",
        "comment": "内部ネットワーク向けルート"
      }
    },
    {
      "description": "DNS設定変更",
      "endpoint": "cmdb/system/dns",
      "method": "PUT",
      "name": "",
      "onError": "continue",
      "data": {
        "primary": "8.8.8.8",
        "secondary": "8.8.4.4"
      }
    },
    {
      "description": "管理者パスワードポリシー変更",
      "endpoint": "cmdb/system/password-policy",
      "method": "PUT",
      "name": "",
      "onError": "continue",
      "data": {
        "status": "enable",
        "min-length": 8,
        "must-contain": "upper-case-letter lower-case-letter number"
      }
    },
    {
      "description": "不要なアドレスオブジェクトを削除",
      "endpoint": "cmdb/firewall/address",
      "method": "DELETE",
      "name": "OLD_ADDRESS_TO_DELETE",
      "onError": "continue",
      "data": null
    }
  ]
}
---

#!/usr/bin/env pwsh <# .SYNOPSIS FortiGate (FGT) REST API 設定管理スクリプト .DESCRIPTION FortiGate の REST API を使用して設定変更・取得を行う。 - JSONファイルで指定した設定を適用(変更前に現状取得) - 全体コンフィグをテキスト形式で取得 - 全体コンフィグをJSON形式で取得 .PARAMETER ConfigFile 適用する設定を記述したJSONファイルのパス .PARAMETER GetConfig 全体コンフィグをテキスト形式で取得 .PARAMETER GetConfigJson 全体コンフィグをJSON形式で取得 .PARAMETER FGTHost FortiGate のIPアドレスまたはホスト名 .PARAMETER Port HTTPS ポート番号(デフォルト: 443) .PARAMETER ApiToken REST API トークン(推奨) .PARAMETER Username ログインユーザー名(ApiToken未指定時) .PARAMETER Password ログインパスワード(ApiToken未指定時) .PARAMETER Vdom 対象 VDOM(デフォルト: root) .PARAMETER SkipCertCheck TLS証明書の検証をスキップ(自己署名証明書対応) .PARAMETER OutputFile 出力ファイルパス(省略時は標準出力のみ) .PARAMETER ShowBefore 設定変更前の現状を表示する(デフォルト: $true) .EXAMPLE # 設定変更(APIトークン使用) .\Invoke-FGTConfig.ps1 -FGTHost 192.168.1.99 -ApiToken "xxxxx" -ConfigFile .\changes.json .EXAMPLE # 全体コンフィグをテキストで取得しファイル保存 .\Invoke-FGTConfig.ps1 -FGTHost 192.168.1.99 -ApiToken "xxxxx" -GetConfig -OutputFile .\fgt.conf .EXAMPLE # 全体コンフィグをJSON形式で取得 .\Invoke-FGTConfig.ps1 -FGTHost 192.168.1.99 -ApiToken "xxxxx" -GetConfigJson -OutputFile .\fgt.json .EXAMPLE # ユーザー/パスワード認証で設定変更 .\Invoke-FGTConfig.ps1 -FGTHost 192.168.1.99 -Username admin -Password "pass" -ConfigFile .\changes.json -SkipCertCheck .NOTES PowerShell 7.5+ 必須 FortiOS 6.4 / 7.x 対応 #> #Requires -Version 7.0 [CmdletBinding(DefaultParameterSetName = 'ApplyConfig')] param( # ---- モード選択 ---- [Parameter(ParameterSetName = 'ApplyConfig', Mandatory = $true, HelpMessage = '設定JSONファイルのパス')] [ValidateScript({ Test-Path $_ -PathType Leaf })] [string]$ConfigFile, [Parameter(ParameterSetName = 'GetConfig', Mandatory = $true)] [switch]$GetConfig, [Parameter(ParameterSetName = 'GetConfigJson', Mandatory = $true)] [switch]$GetConfigJson, # ---- 接続情報 ---- [Parameter(Mandatory = $true, HelpMessage = 'FGT IPまたはホスト名')] [string]$FGTHost, [Parameter(Mandatory = $false)] [ValidateRange(1, 65535)] [int]$Port = 443, [Parameter(Mandatory = $false)] [string]$ApiToken, [Parameter(Mandatory = $false)] [string]$Username, [Parameter(Mandatory = $false)] [string]$Password, [Parameter(Mandatory = $false)] [string]$Vdom = 'root', # ---- オプション ---- [Parameter(Mandatory = $false)] [switch]$SkipCertCheck, [Parameter(Mandatory = $false)] [string]$OutputFile, [Parameter(ParameterSetName = 'ApplyConfig', Mandatory = $false)] [bool]$ShowBefore = $true ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # ============================================================ # 定数・グローバル変数 # ============================================================ $script:BaseUrl = "https://${FGTHost}:${Port}/api/v2" $script:Headers = @{ 'Content-Type' = 'application/json' } $script:SessionCsrf = $null $script:SessionCookie = $null # ============================================================ # ログ出力ヘルパー # ============================================================ function Write-Log { param( [string]$Message, [ValidateSet('INFO','SUCCESS','WARN','ERROR','SECTION')] [string]$Level = 'INFO' ) $ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $color = switch ($Level) { 'SUCCESS' { 'Green' } 'WARN' { 'Yellow' } 'ERROR' { 'Red' } 'SECTION' { 'Cyan' } default { 'White' } } $prefix = switch ($Level) { 'SUCCESS' { '[OK] ' } 'WARN' { '[!!] ' } 'ERROR' { '[NG] ' } 'SECTION' { '=== ' } default { '[--] ' } } Write-Host "${ts} ${prefix}${Message}" -ForegroundColor $color } # ============================================================ # TLS 証明書スキップ設定 # ============================================================ function Set-TlsPolicy { if ($SkipCertCheck) { Write-Log 'TLS証明書の検証をスキップします' -Level WARN # PowerShell 7 では -SkipCertificateCheck パラメータを使用するため # ここでは HttpClientHandler の設定は不要 } } # ============================================================ # Invoke-RestMethod ラッパー(共通ヘッダ・エラー処理) # ============================================================ function Invoke-FGTApi { param( [string]$Uri, [string]$Method = 'GET', [hashtable]$Headers = @{}, [object]$Body = $null, [switch]$RawResponse ) $params = @{ Uri = $Uri Method = $Method Headers = $script:Headers + $Headers } if ($SkipCertCheck) { $params['SkipCertificateCheck'] = $true } if ($null -ne $Body) { $params['Body'] = ($Body | ConvertTo-Json -Depth 20 -Compress) } # セッションCookieがある場合はWebSession経由 if ($null -ne $script:SessionCookie) { $params['WebSession'] = $script:SessionCookie } try { if ($RawResponse) { $params['ResponseHeadersVariable'] = 'respHeaders' $result = Invoke-RestMethod @params return @{ Body = $result; Headers = $respHeaders } } return Invoke-RestMethod @params } catch { $statusCode = $_.Exception.Response?.StatusCode?.value__ $errBody = $null try { $errBody = $_.ErrorDetails.Message | ConvertFrom-Json } catch { } $errMsg = if ($errBody?.error) { $errBody.error } else { $_.Exception.Message } throw "API エラー [HTTP $statusCode] ${Method} ${Uri} : ${errMsg}" } } # ============================================================ # 認証 # ============================================================ function Connect-FGT { Write-Log '=== FortiGate 接続中 ===' -Level SECTION if ($ApiToken) { # APIトークン認証(推奨) $script:Headers['Authorization'] = "Bearer $ApiToken" Write-Log "APIトークン認証: ${FGTHost}:${Port}" -Level INFO } elseif ($Username -and $Password) { # ユーザー/パスワード認証(セッション) Write-Log "ユーザー認証: ${Username}@${FGTHost}:${Port}" -Level INFO $loginUri = "https://${FGTHost}:${Port}/logincheck" $loginBody = "username=${Username}&secretkey=${Password}&ajax=1" $webSession = $null $loginParams = @{ Uri = $loginUri Method = 'POST' Body = $loginBody ContentType = 'application/x-www-form-urlencoded' SessionVariable = 'webSession' } if ($SkipCertCheck) { $loginParams['SkipCertificateCheck'] = $true } $resp = Invoke-RestMethod @loginParams if ($resp -ne '0' -and $resp -notmatch '"loginstatus":1') { throw "ログイン失敗: レスポンス = $resp" } $script:SessionCookie = $webSession # CSRF トークン取得 $csrfCookie = $webSession.Cookies.GetCookies("https://${FGTHost}")['ccsrftoken'] if ($csrfCookie) { $csrfVal = $csrfCookie.Value -replace '"', '' $script:Headers['X-CSRFTOKEN'] = $csrfVal Write-Log "CSRFトークン取得: ${csrfVal}" -Level INFO } } else { throw '認証情報が不足しています。-ApiToken または -Username/-Password を指定してください。' } Write-Log '接続情報設定完了' -Level SUCCESS } # ============================================================ # ログアウト # ============================================================ function Disconnect-FGT { if ($null -ne $script:SessionCookie -and -not $ApiToken) { try { $logoutUri = "https://${FGTHost}:${Port}/logout" $p = @{ Uri = $logoutUri; Method = 'GET'; WebSession = $script:SessionCookie } if ($SkipCertCheck) { $p['SkipCertificateCheck'] = $true } Invoke-RestMethod @p | Out-Null Write-Log 'ログアウト完了' -Level INFO } catch { Write-Log "ログアウト中にエラー: $_" -Level WARN } } } # ============================================================ # 現状取得(変更前確認) # ============================================================ function Get-FGTCurrentState { param( [string]$Endpoint, [string]$Name = '' ) $uri = "$script:BaseUrl/${Endpoint}" if ($Name) { $uri += "/${Name}" } $uri += "?vdom=${Vdom}" try { $resp = Invoke-FGTApi -Uri $uri -Method 'GET' return $resp } catch { Write-Log "現状取得失敗 [${Endpoint}/$Name]: $_" -Level WARN return $null } } # ============================================================ # 設定適用(PUT / POST / DELETE) # ============================================================ function Invoke-FGTChange { param( [string]$Endpoint, [string]$Method, [string]$Name = '', [object]$Data = $null, [hashtable]$QueryParams = @{} ) $uri = "$script:BaseUrl/${Endpoint}" if ($Name -and $Method -in @('PUT','DELETE','GET')) { $uri += "/${Name}" } $query = @{ vdom = $Vdom } + $QueryParams $queryStr = ($query.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join '&' $uri += "?${queryStr}" $resp = Invoke-FGTApi -Uri $uri -Method $Method -Body $Data return $resp } # ============================================================ # 全体コンフィグ取得 (テキスト形式) # ============================================================ function Get-FGTFullConfig { Write-Log '=== 全体コンフィグ取得 (テキスト) ===' -Level SECTION $uri = "$script:BaseUrl/monitor/system/config/backup?scope=global&vdom=${Vdom}" Write-Log "URI: $uri" -Level INFO $params = @{ Uri = $uri Method = 'GET' Headers = $script:Headers } if ($SkipCertCheck) { $params['SkipCertificateCheck'] = $true } if ($null -ne $script:SessionCookie) { $params['WebSession'] = $script:SessionCookie } # テキストとして取得 $configText = Invoke-RestMethod @params Write-Log "コンフィグ取得完了 (文字数: $($configText.Length))" -Level SUCCESS if ($OutputFile) { $configText | Set-Content -Path $OutputFile -Encoding UTF8 Write-Log "ファイル保存: $OutputFile" -Level SUCCESS } else { Write-Output $configText } } # ============================================================ # 全体コンフィグ取得 (JSON形式) # ============================================================ function Get-FGTFullConfigJson { Write-Log '=== 全体コンフィグ取得 (JSON) ===' -Level SECTION # cmdb エンドポイントへのアクセス(主要カテゴリを収集) $categories = @( 'system/global', 'system/interface', 'system/dns', 'system/ntp', 'system/admin', 'firewall/address', 'firewall/addrgrp', 'firewall/service/custom', 'firewall/service/group', 'firewall/policy', 'firewall/vip', 'router/static', 'router/ospf', 'router/bgp', 'vpn/ipsec/phase1-interface', 'vpn/ipsec/phase2-interface', 'user/local', 'user/group' ) $fullConfig = [ordered]@{ '_metadata' = @{ host = $FGTHost vdom = $Vdom retrieved_at = (Get-Date -Format 'yyyy-MM-ddTHH:mm:sszzz') } } foreach ($cat in $categories) { $uri = "$script:BaseUrl/cmdb/${cat}?vdom=${Vdom}" Write-Log "取得中: $cat" -Level INFO try { $resp = Invoke-FGTApi -Uri $uri -Method 'GET' $key = $cat -replace '/', '_' $fullConfig[$key] = $resp.results ?? $resp } catch { Write-Log "スキップ [${cat}]: $_" -Level WARN $fullConfig[($cat -replace '/', '_')] = $null } } $json = $fullConfig | ConvertTo-Json -Depth 20 Write-Log "JSON生成完了 (文字数: $($json.Length))" -Level SUCCESS if ($OutputFile) { $json | Set-Content -Path $OutputFile -Encoding UTF8 Write-Log "ファイル保存: $OutputFile" -Level SUCCESS } else { Write-Output $json } } # ============================================================ # 設定変更 (JSONファイル読み込み) # ============================================================ function Invoke-FGTConfigFile { param([string]$FilePath) Write-Log "=== 設定変更開始: $FilePath ===" -Level SECTION # JSON 読み込み $raw = Get-Content -Path $FilePath -Raw -Encoding UTF8 $config = $raw | ConvertFrom-Json -AsHashtable $description = $config['description'] ?? '(説明なし)' Write-Log "変更セット: $description" -Level INFO $changes = $config['changes'] if (-not $changes -or $changes.Count -eq 0) { Write-Log 'changes が空です。処理を終了します。' -Level WARN return } $total = $changes.Count $success = 0 $failed = 0 foreach ($i in 0..($total - 1)) { $change = $changes[$i] $idx = $i + 1 $desc = $change['description'] ?? "(変更 $idx)" $ep = $change['endpoint'] $method = ($change['method'] ?? 'PUT').ToUpper() $name = $change['name'] ?? '' $data = $change['data'] ?? $null Write-Log "--- [$idx/$total] $desc ---" -Level SECTION # 変更前の現状取得 if ($ShowBefore -and $method -ne 'DELETE') { Write-Log "変更前の現状取得: ${ep}/$name" -Level INFO $before = Get-FGTCurrentState -Endpoint $ep -Name $name if ($before) { $beforeJson = $before | ConvertTo-Json -Depth 10 Write-Log '【変更前】' -Level INFO Write-Host $beforeJson -ForegroundColor DarkGray } else { Write-Log '現状データなし(新規作成の可能性)' -Level INFO } } # 設定変更実行 Write-Log "${method} ${ep}/$name を実行中..." -Level INFO try { $result = Invoke-FGTChange -Endpoint $ep -Method $method -Name $name -Data $data $httpStatus = $result.http_status ?? $result.status ?? 'unknown' Write-Log "完了 [status=$httpStatus]: $desc" -Level SUCCESS # 変更後の確認取得 if ($method -ne 'DELETE') { $afterName = $name ?: ($data?['name'] ?? '') $after = Get-FGTCurrentState -Endpoint $ep -Name $afterName if ($after) { $afterJson = $after | ConvertTo-Json -Depth 10 Write-Log '【変更後】' -Level INFO Write-Host $afterJson -ForegroundColor Green } } $success++ } catch { Write-Log "失敗: $desc | $_" -Level ERROR $failed++ # エラー時の動作(continue or stop) $onError = $change['onError'] ?? $config['onError'] ?? 'continue' if ($onError -eq 'stop') { Write-Log 'onError=stop のため処理を中断します。' -Level ERROR break } } } Write-Log "=== 処理完了: 成功=$success / 失敗=$failed / 合計=$total ===" -Level SECTION } # ============================================================ # メイン処理 # ============================================================ function Main { Set-TlsPolicy # 接続 Connect-FGT try { switch ($PSCmdlet.ParameterSetName) { 'ApplyConfig' { Invoke-FGTConfigFile -FilePath $ConfigFile } 'GetConfig' { Get-FGTFullConfig } 'GetConfigJson'{ Get-FGTFullConfigJson } } } finally { Disconnect-FGT } } # エントリポイント Main




2019年3月5日火曜日

文字列の扱いではまるポイント

Powershellが期待通りに動かずハマるポイント…
変数の型宣言が無くて楽だけど、いざというときにハマるポイントです。
Powershellに限らず、変数の型宣言が無い言語はみんな同じだと思う。
先ずは…文字、文字列、文字列配列の違いが分かってることが前提です。

# 文字列配列で間違いやすいポイント

# どれが文字列配列か
# ★実際に実行してみてね
## 変数
$String1 = 'これは文字列です。'         # 1つの文字列
$String2 = ('これも文字列です。')       # ()は先に処理するだけ。ここだと無いのと同じ。
$String3 = @('これは文字列配列です')    # @()は配列お宣言。中身が文字列なので、文字列配列
$String4 = @()                      # @()は配列お宣言。配列で初期化
$String4 += 'これも文字列配列です'      # 文字列を配列に追加
$String5 = 'じゃあ…これは?','これは文字列配列です。' # 2つの文字列なので、文字列配列になる。
## 配列長
$String1.Length
$String2.Length
$String3.Length
$String4.Length
$String5.Length
## 1つ目の配列の中身…
$String1[0]     # 文字列(文字配列)なので、1文字目を表示
$String2[0]     # 文字列(文字配列)なので、1文字目を表示
$String3[0]     # 文字列配列なので、1行目を表示
$String4[0]     # 文字列配列なので、1行目を表示
$String5[0]     # 文字列配列なので、1行目を表示

## 演算子の結果だと…
$String1 -match '文字'
$String5 -match '文字'
$String1 -match '?'
$String5 -match '?'
$String1 -match 'これ'
$String5 -match 'これ'
$String1 -ne 'a'
$String5 -ne 'a'

### ↑この結果の違いが理解できないと、バグを埋め込むことになります。
### 文字列として扱いたいのか、文字列配列として扱いたいのかを意識して扱うこと。

@($String1) -match '文字'   # 文字列配列化
$String5[0] -match '文字'   # 文字列配列の1つ目の文字列
$String5[1] -match '文字'   # 文字列配列の2つ目の文字列

2019年3月2日土曜日

文字列配列の抽出

Powershellでの文字列抽出の例
正規表現(match)が多機能で一番汎用性があります。
抽出条件がシンプルなのであれば、処理が軽いワイルドカード(like)が使えます。
ただ、数十万回とか回さないと差は分からないと思う…。
同じスクリプト内で、正規表現とワイルドカードを織り交ぜるとバグを埋め込みやすいので、基本は正規表現に統一した方が良いです。

# 文字列配列
$Array = @()
$Array += '1行目:a'
$Array += '2行目:bb'
$Array += '3行目:ccc'
## 要素数(行数)
$Array.Length
$Array
## 正規表現で抽出①
$Array -match 'b'
## 正規表現で抽出②(上①と同じ結果)
foreach ( $String in $Array ) {
    if ( $String -match 'b' ) {
        $String
    }
}
## ワイルドカードで抽出①
$Array -like '*c'
## ワイルドカードで抽出②(上①と同じ結果)
foreach ( $i in 0..($Array.Length -1) ) {
    if ( $Array[$i] -like '*c' ) {
        $Array[$i]
    }
}

# 要注意
## 演算子 -match や -like は左側が変数か配列化で結果が異なります。この違いを理解していないとハマります。(上の抽出①と②に関連)
$Example1 = '単なる文字列'
$Example2 = @( '1行だけの文字列配列' )
$Example1 -match '文字列'
$Example2 -match '文字列'


カスタムオブジェクト配列の作成と表示

カスタムオブジェクト配列の作成例
Powershellでのカスタムオブジェクトの作成例です。
ネットワーク機器のコンフィグファイルを読み込んで一覧化したりするのに超便利です。
オブジェクトからCSV化、CSVからオブジェクト化(全部文字列になるが…)できて便利です。

# オブジェクト配列化①
# 表の元ネタ
$InterfaceList = @( 'Port1','Port2','Port3' )   # 文字列配列
$IPAddressList = @( '192.168.0.1','192.168.255.1','172.16.255.1' )   # 文字列配列
$NetMaskList   = @( 30,24,24 )   # 数値配列
# オブジェクト化
$Array = New-Object System.Collections.ArrayList    # 配列リストの初期化
foreach ( $i in 0..($InterfaceList.Length -1) ) {   # $InterfaceListのループ
    $Object = [PSCustomObject] @{
        No = $i +1
        Interface = $InterfaceList[$i]
        IPAddress = $IPAddressList[$i]
        NetMask   = $NetMaskList[$i]
    }                       # オブジェクト化
    $Array.Add( $Object )   # オブジェクトの配列追加
}
$List = $Array.ToArray()    # 普通のオブジェクト配列に
$List.Length                # 配列長
$List                       # オブジェクト配列の表示
# CSV化
$CSV = $List | ConvertTo-Csv
$CSV.Length
$CSV


# オブジェクト配列化②
# 表の元ネタ
$InterfaceList = @( 'Port1','Port2','Port3' )   # 文字列配列
$IPAddressList = @( '192.168.0.1','192.168.255.1','172.16.255.1' )   # 文字列配列
$NetMaskList   = @( 30,24,24 )   # 数値配列
# オブジェクト化
$List = @()    # 配列リストの初期化
foreach ( $i in 0..($InterfaceList.Length -1) ) {   # $InterfaceListのループ
    $List += [PSCustomObject] @{
        No = $i +1
        Interface = $InterfaceList[$i]
        IPAddress = $IPAddressList[$i]
        NetMask   = $NetMaskList[$i]
    }                       # オブジェクト化
}
$List.Length                # 配列長
$List  
# CSV化
$CSV = $List | ConvertTo-Csv
$CSV.Length
$CSV


# オブジェクト配列化②改
# 表の元ネタ
$InterfaceList = @( 'Port1','Port2','Port3' )   # 文字列配列
$IPAddressList = @( '192.168.0.1','192.168.255.1','172.16.255.1' )   # 文字列配列
$NetMaskList   = @( 30,24,24 )   # 数値配列
# オブジェクト化
$List = @()    # 配列リストの初期化
$List += foreach ( $i in 0..($InterfaceList.Length -1) ) {   # $InterfaceListのループ
    [PSCustomObject] @{
        No = $i +1
        Interface = $InterfaceList[$i]
        IPAddress = $IPAddressList[$i]
        NetMask   = $NetMaskList[$i]
    }                       # オブジェクト化
}
$List.Length                # 配列長
$List  
# CSV化
$CSV = $List | ConvertTo-Csv
$CSV.Length
$CSV



#CSVデータからのオブジェクト配列化
## 元CSVデータの作成
$CSVData = @()
$CSVData += 'No,Interface,IPAddress,NetMask'
$CSVData += '1,Port1,192.168.0.1,30'
$CSVData += '2,Port2,192.168.255.1,24'
$CSVData += '3,Port3,172.16.255.1,24'
## CSVデータのオブジェクト配列化
$List = $CSVData | ConvertFrom-Csv
$List.Length
$List


文字列配列の作成と表示

Powershellでの文字列配列の処理例
 文字列配列の扱いはシンプル。基本は②で作成し、慣れてきたら③④を使い分けると良い。②も悪くは無いが、配列が1000行以上になると指数関数的に処理が遅くなるので③④に置き換える必要が出てきます。(①はソースが見にくくなるので推奨しない…)

 速度の問題から「System.Text.StringBuilder」を使うような例をよく見るが、文字列配列だけで無く、文字列の組み立てにまで対応していて、Powershellのシンプルな文字列配列の考え方とズレているので使いにくい。(改行を意識する必要がある…)

 どれか1つしか使わない縛りをかけるなら、汎用性が一番高い④しかない。

## 利点: 1行で記述できる、定数なら良い
## 欠点: 追加が出来ない、見づらい(要素数が分かりにくい)
$Array = @('1行目:a','2行目:bb','3行目:ccc')
## 要素数(行数)
$Array.Length
## 表示(全て同じ内容)
$Array
$Array[0..2]
$Array[0..($Array.Length -1)]


# 文字列配列②
## 利点: 見やすい、追加が容易
## 欠点: 加算代入の行数が多くなると、目に見えて遅くなる(多くても100行程度までに)
$Array = @()
$Array += '1行目:a'
$Array += '2行目:bb'
$Array += '3行目:ccc'
## 要素数(行数)
$Array.Length
## 表示(全て同じ内容)
$Array
$Array[0..2]
$Array[0..($Array.Length -1)]


# 文字列配列③
## 利点: 追加が容易、型チェックができる
## 欠点: 速い。遅くならない。
$Array = New-Object System.Collections.Generic.List[string]
$Array.Add( '1行目:a' )
$Array.Add( '2行目:bb' )
$Array.Add( '3行目:ccc' )
## 要素数(行数)
$Array.ToArray().Length
## 表示(全て同じ内容)
$Array
$Array[0..2]
$Array.ToArray()
$Array[0..($Array.ToArray().Length -1)]
## 型を文字列配列に変える
$StringArray = $Array.ToArray()
$StringArray.Length
$StringArray

# 文字列配列④
## 利点: 追加が容易、汎用性が高い
## 欠点: 速い。遅くならない。
$Array = New-Object System.Collections.ArrayList
## 表示
$Array.Add( '1行目:a' )
$Array.AddRange( @('2行目:bb','3行目:ccc') )
## 要素数(行数)
$Array.ToArray().Length
## 表示(全て同じ内容)
$Array
$Array[0..2]
$Array.ToArray()
$Array[0..($Array.ToArray().Length -1)]
## 型を文字列配列に変える
$StringArray = $Array.ToArray()
$StringArray.Length
$StringArray

2018年10月25日木曜日

コマンドレットを作る

1.モジュールの格納先フォルダ

モジュールパスの環境変数を確認し、Modulesフォルダを作成します。

コマンド:
$Env:PSModulePath -Split ';'

実行結果 例:
C:\Users\…\…\ドキュメント\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\6.0.4\Modules

 初期設定では3行のパスが表示さると思います。「C:\Users」から始まるパスがユーザー用のパスです。ここにモジュールを格納するのが無難です。
完全自己管理のパソコンで管理者権限があるのなら、2行目のパスでもいいです。


2.マニフェストファイルの作成

無くても自作コマンドレットは使えますが、インポートしたコマンドレットの新旧の判別が出来た方が便利です。
そのため、マニフェストファイルを作るのがおススメです。

コマンド 例:
New-ModuleManifest -Path New.psd1
Get-ChildItem

実行結果 例:
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2018/10/25      0:20           7644 New.psd1