PowerShellでImport-CsvとForEachループを使用してCSVデータを処理する方法

同じタスクを複数回実行することはありましたか? たとえば、GUIを使用して1つずつ複数のActive Directoryユーザーを作成することや、サーバーにログインして選択したフォルダから古いログを削除することはありましたか? もしはいと答えるなら、あなたは一人ではありません。 PowerShellのImport-Csvとforeachループをマスターする時が来ました。

手動のタスクには問題はありません。それは時には必要です。しかし、CSVファイルの読み取りと処理に関しては、PowerShellのコマンドレットImport-CsvForEachループを使用することができます。

PowerShellのImport-Csvコマンドレットは、CSVファイルのような表形式のデータソースからデータを読み取るための優れた方法です。その後、ForEachループを使用してCSVデータの各行を反復処理することができます。

この記事では、この強力な組み合わせを使用して、一括、単調で繰り返しのタスクを自動化する方法を学びます。

Import-CsvコマンドレットとForEachループが初めての場合、または既に知っていることの復習が必要な場合は、以下のリンクを参照して詳しく学ぶことができます。

Import-Csvを使用したPowerShellでのCSVファイルの管理 (foreachループ)

基本に戻る: PowerShellのforeachループ

前提条件

この記事にはいくつかの例とデモがあります。一緒に進めるには、最初にいくつかの準備が必要です。

  • A script editor such as Visual Studio Code, Atom, or Notepad++. Use whichever one you’re comfortable with.
  • Windows PowerShell 5.1またはPowerShell Core 6+
  • Exchange Onlineへのアクセス(Exchange Online関連の例を実際に試す場合はオプションです)。

Import-CsvとForEachループを活用する

次のセクションでは、実世界のシナリオで遭遇する可能性のある、Import-CsvコマンドレットとForEachループの使用例がいくつかあります。これらのPowerShellのインポートCSVforeachの例はそれぞれの目的に特化していますが、使用されている概念と技術が同じであることを理解することが重要です。

CSVからのレコードの読み取りと表示

おそらく、Import-CsvForEachループの最も基本的な使用法は、ファイルからレコードを読み取り、コンソールに表示することです。CSVファイルの構造はデータベースに似ています。列見出しを持ち、各行はレコードとして扱われます。

たとえば、以下はemployee.csvというファイルの内容で、EmployeeIDNameBirthdayの3つの列と4つのレコードからなります。

CSV containing employee records

以下のコードは、employee.csvファイルの内容をインポートし、インポートされたデータをForEach-Objectコマンドレットにパイプします。その後、ForEach-ObjectはインポートされたCSVの各レコードを処理し、コンソールに連結された値を表示します。以下のコードをコピーしてlist-employee.ps1として保存してください。

注意:以下の例で使用されているForEachループのタイプは、ForEach-Objectコマンドレットです。この記事の「The ForEach-Objectコマンドレット」セクションを参照してください。

Import-Csv .\employee.csv | ForEach-Object {
    Write-Host "$($_.Name), whose Employee ID is $($_.EmployeeID), was born on $($_.Birthday)."
}

スクリプトを保存したら、PowerShellでファイル名を呼び出して実行します。スクリプトを実行すると、以下のスクリーンショットに似た出力が表示されるはずです。

Imported CSV records displayed in the console

CSVからレコードを検索して表示する

前の例では、CSVからすべてのレコードを読み取り表示する方法を学びました。この例では、同じCSVファイルemployee.csvを使用しますが、今回はCSVからEmployeeIDを検索するためにPowerShell関数を作成します。

以下のコードスニペットは、Find-Employeeという名前のPowerShell関数で、パラメータは1つだけです。パラメータ名はEmployeeIDであり、CSVから検索するための従業員IDの値を受け入れます。

この関数を使用する前に、まずPowerShellセッションにインポートする必要があります。以下の2つの方法でFind-Employee関数をメモリにインポートすることができます:

  1. 以下のコードをPowerShellにコピーして貼り付ける。
  2. スクリプトをFind-Employee.ps1として保存し、ドットソーシングテクニックを使用してインポートする。

注意:以下の例で使用されているForEachループのタイプはForEachステートメントです。この記事の「ForEachステートメント」セクションを参照してください。

Function Find-Employee {
    param (
        # パラメータは検索する従業員のIDを受け入れます。
        [Parameter(Mandatory)]
        $EmployeeID
    )

    # employee.csvファイルの内容をインポートし、$employee_list変数に格納します。
    $employee_list = Import-Csv .\employee.csv

    # CSVのすべてのレコードをループします。
    foreach ($employee in $employee_list) {

        # 現在のレコードの従業員IDがEmployeeIDパラメータの値と等しいかどうかをチェックします。
        if ($employee.EmployeeID -eq $EmployeeID) {

            # EmployeeIDが見つかった場合、レコードをコンソールに表示します。
            Write-Host "$($employee.Name), whose Employee ID is $($employee.EmployeeID), was born on $($employee.Birthday)."
        }
    }
}

関数コードをPowerShellセッションにインポートした後、以下のように関数名を入力して呼び出します。

Find-Employee

EmployeeIDパラメータを使用せずに関数を実行すると、EmployeeIDの値を検索するためにプロンプトが表示されます。以下は例の出力です。

Function to search a CSV file for a specific record

または、関数を実行する際にランタイムでEmployeeIDパラメータの値を指定することもできます。以下のように表示されます。

Find-Employee -EmployeeID 'E-2023'

複数のサーバーからディスク容量の使用状況を取得する

システム管理者の間でよく行われるルーチンのタスクの1つは、複数のサーバーのディスク容量の使用状況を監視することです。ディスク容量の使用状況を確認するために、各サーバーにログインする代わりに、サーバー名、ドライブレター、閾値を含むCSVリストを作成し、PowerShellを使用してCSVファイルをインポートし、各行をループしてクエリを実行します。

リモートサーバーからディスクスペース使用量を取得するスクリプトを作成するには、まず以下のヘッダーを持つCSVリストを作成します:

  • servername – これはクエリする名前サーバーです。
  • disk – これはスペース使用量を取得するドライブのレターです。
  • threshold – これはしきい値をGB単位で定義します。ディスクの空き容量がこの値よりも低い場合、レポートには警告ステータスが表示されます。

以下のサンプルでは、4つのレコードがリストアップされています。サーバー数や読み取るディスクの数に応じて、CSVファイルは異なります。

servername,disk,threshold
au-dc01,c,120
au-mail01,c,100
au-mail01,d,6
au-file01,c,120

CSVが完成したら、ファイルをservers.csvとして保存します。これはPowerShellスクリプトによってインポートされる入力リストとして機能します。

ディスクスペース使用量の例を提供するために、以下のコードをスクリプトエディタにコピーしてdiskReport.ps1として保存します。スクリプトはCSVパスと同じ場所に保存する必要があります。

$server_list = Import-Csv -Path .\servers.csv

foreach ($server in $server_list) {
    Get-WmiObject -Class Win32_logicaldisk -ComputerName ($server.servername) | `
        Where-Object { $_.DeviceID -match ($server.disk) } | `
        Select-Object `
    @{n = 'Server Name'; e = { $_.SystemName } }, `
    @{n = 'Disk Letter'; e = { $_.DeviceID } }, `
    @{n = 'Size (GB)'; e = { $_.Size / 1gb -as [int] } }, `
    @{n = 'Free (GB)'; e = { $_.FreeSpace / 1gb -as [int] } }, `
    @{n = 'Threshold (GB)'; e = { $server.Threshold } }, `
    @{n = 'Status'; e = {
            if (($_.FreeSpace / 1gb) -lt ($server.Threshold)) {
                return 'Warning'
            }
            else {
                return 'Normal'
            }
        }
    }
}

上記のスクリプトは、実行されると以下のアクションを実行します。

  • 名前がservers.csvのcsvファイルをインポートし、$server_list変数に格納します。
  • $server_list変数に格納されたサーバーリストをループします。
  • foreachループの各イテレーションでは、現在の行は$server変数で表されます。
  • Get-WmiObjectコマンドレットを使用して、サーバーからディスク情報を取得します。
  • 表示するために関連するプロパティのみ選択してください。
    サーバー名 – クエリされているシステムの名前です。
    ディスクの文字 – ドライブに割り当てられた文字です。
    サイズ(GB) – GB単位のディスクのサイズです。
    空き容量(GB) – GB単位の空き容量のサイズです。
    しきい値(GB) – GB単位の定義されたしきい値です。
    ステータス空き容量(GB)の値がしきい値(GB)の値よりも小さい場合、ステータスは「警告」となります。それ以外の場合、ステータスは「正常」となります。

スクリプトファイルdiskReport.ps1を保存したので、PowerShellでその名前を呼び出して実行する準備が整いました。

./diskReport.ps1

実行されると、以下のスクリーンショットにスクリプトの出力が表示されます。

Disk space information gathered from multiple servers

出力はCSVにエクスポートすることもできます。CSVにエクスポートすると、レポートを共有する際に便利です。エクスポートされたCSVファイルはメールで送信したり、ファイル共有やSharePointサイトにアップロードしたりすることができます。CSVへのエクスポートについての詳細は、Export-Csv: The PowerShell Way to Treat CSV Files as First Class Citizensを参照してください。

複数のActive Directoryユーザーの作成

この時点で、Import-CsvForEachの使用方法についての良いアイデアを既に持っていると思います。次の例では、New-ADUserGet-ADUserのコマンドレットを組み合わせて学習をさらに進めます。

new_employees.csvというCSVファイルを受け取りました。このファイルには、HR部門から提供された新入社員のリストが含まれています。CSVファイルの各行は、オンボーディングするユーザーを表し、以下の列があります:FirstNameLastNameDepartmentStateEmployeeID、およびOffice

ユーザー名は、従業員の名前の最初の文字と姓を連結したものにする必要があります(例:bparrはユーザーBob Parrの場合)。

CSV file containing new employees information for on-boarding

CSVファイルが保存されたら、以下のスクリプトはImport-Csvを使用してCSVファイルnew_employees.csvを読み取ります。その後、各行を反復処理し、値をNew-ADUserの適切なパラメーターに渡します。

Import-Csv .\new_employees.csv | ForEach-Object {
    New-ADUser `
        -Name $($_.FirstName + " " + $_.LastName) `
        -GivenName $_.FirstName `
        -Surname $_.LastName `
        -Department $_.Department `
        -State $_.State `
        -EmployeeID $_.EmployeeID `
        -DisplayName $($_.FirstName + " " + $_.LastName) `
        -Office $_.Office `
        -UserPrincipalName $_.UserPrincipalName `
        -SamAccountName $_.SamAccountName `
        -AccountPassword $(ConvertTo-SecureString $_.Password -AsPlainText -Force) `
        -Enabled $True
}

スクリプトを実行すると、新しいユーザーアカウントは既にActive Directoryに存在するはずです。ただし、ユーザーアカウントが実際に作成されたことを確認することは良い慣行です。

同じCSVファイルnew_employees.csvを参照して、以下のスクリプトはCSVをインポートし、foreachを使用してリスト内のADUserオブジェクトを取得します。

Import-Csv .\new_employees.csv | ForEach-Object {
	Get-AdUser $_.SamAccountName
}

Office 365メールボックスにプロキシメールアドレスを追加する

Office 365のメールボックス管理では、複数のユーザーに対してプロキシアドレスを追加する要求は珍しくありません。通常、この種の要求では、管理者はユーザーのリストと追加するメールアドレスを受け取ります。以下は、CSVの例です。

The new_address.csv file contents

注意: PowerShellでExchange Onlineのコマンドレットを実行する前に、まずExchange Online Managementシェルにログインする必要があります。

Import-CsvForEachループを使用して、PowerShellスクリプトでリストを一括処理することができます。以下のスクリプトは、その方法を示しています。

以下のスクリプトでは、new_address.csvファイルの内容をインポートし、$user_list変数に格納します。そして、foreach()メソッドを使用して、PowerShellはユーザーリスト全体をループし、各レコードのusernameemailの値を使用して各メールボックスに新しいメールアドレスを追加します。

$user_list = Import-Csv .\new_address.csv

$user_list.foreach(
    {
        Set-Mailbox -Identity $_.username -EmailAddresses @{add="$($_.email)"}
    }
)

スクリプトを実行すると、コンソールには出力が表示されません。画面出力がない場合、新しいメールアドレスが正常に追加されたことを意味します。しかし、メールアドレスが追加されたことを確認するにはどうすればよいでしょうか?

同じCSVファイルnew_address.csvを参照して、新しいアドレスが追加されたかどうかをImport-CsvForEachを使用して確認できます。

以下のスクリプトでは、new_address.csvファイルの内容をインポートし、$user_list変数に格納します。そして、foreach()メソッドを使用して、PowerShellはユーザーリスト全体をループし、新しいメールアドレスがメールボックスのプロキシアドレスのリストに存在するかどうかを確認します。見つかった場合、ステータスはTrueとなります。それ以外の場合は、結果はFalseとなります。

$user_list = Import-Csv .\new_address.csv

$user_list.foreach(
    {
        $emailObj = (Get-Mailbox -Identity $_.username).EmailAddresses
        if ($emailObj -contains $_.email) {
            Write-Host "$($_.username) --> $($_.email) --> TRUE"
        }
        else {
            Write-Host "$($_.username) --> $($_.email) --> FALSE"
        }
    }
)

バリデーションスクリプトが実行されると、以下のスクリーンショットに示されているような出力が表示されるはずです。以下の出力では、ステータスがすべてTRUEとなっており、これは新しいメールアドレスが各メールボックスに正常に追加されたことを意味しています。

Running the new email address verification

メーリングリストへの毎日の天気予報の送信

この例では、購読者のメールアドレスとその地域または場所のリストが含まれたCSVファイルがあることを前提としています。これらの購読者は、その場所に特化したその日の天気予報を含む毎日のメールを期待しています。以下は、ファイル名がsubscribers.csvのサンプルCSVをご覧ください。

Weather forecast subscribers list

以下のCSVには、2人の購読者のみが含まれています。1人はロサンゼルスにおり、もう1人はマニラにいます。

以下のスクリプトでは、以下のアクションを実行します:

  • subscribers.csvファイルからメールアドレスと地域情報をインポートする
  • 各購読者に対して:
    – 購読者の地域に基づいてhttps://wttr.in/から天気予報画像をダウンロードする
    – 天気予報画像を購読者のメールアドレスに送信する

以下のスクリプトでは、上記のアクションを実行します。最初の3つの変数$senderAddress$smtpServer、および$smtpPortを変更する必要があります。その後、このコードをコピーしてSend-WeatherInfo.ps1として保存します。スクリプトの各セクションの上にあるコメントを参照して、コードの詳細について学ぶことができます。

# SMTP設定はここから開始
$senderAddress = '<SENDER EMAIL ADDRESS HERE'
$smtpServer = '<SMTP RELAY SERVER HERE>'
$smtpPort = 25
# SMTP設定はここから開始

# 購読者のメールリストをインポート
$subscriber_list = Import-Csv .\subscribers.csv

# 天気予報を取得し、メールを送信する
$subscriber_list.foreach(
    {
				
        # エリアに基づいて天気情報のURLを作成
        $uri = ('<https://wttr.in/>' + $_.Area + '_u1F.png')
        # エリアの天気予報画像を取得し、画像を<AREA>.pngとして保存
        $imageFile = (((Resolve-Path .\).Path) + "$($_.Area).png")
        Start-BitsTransfer $uri -Destination $imageFile

        # 添付ファイルオブジェクトを作成
        $attachment = New-Object System.Net.Mail.Attachment -ArgumentList $imageFile
        $attachment.ContentDisposition.Inline = $true
        $attachment.ContentDisposition.DispositionType = "Inline"
        $attachment.ContentType.MediaType = "image/png"
        $attachment.ContentId = "weatherImage"

        # メッセージを作成
        $emailMessage = New-Object System.Net.Mail.MailMessage
        $emailMessage.From = $senderAddress
        $emailMessage.To.Add($_.Email)
        $emailMessage.Subject = ('Weather Forecast - ' + $_.Area)
        $emailMessage.IsBodyHtml = $true
        $emailMessage.Body = '<img src="cid:weatherImage">'
        $emailMessage.Attachments.Add($attachment)

        # メッセージを送信
        Write-Output "Sending Weather Info to $($_.Email)"
				$smtp = New-Object Net.Mail.SmtpClient($smtpServer, $smtpPort)
        $smtp.Send($emailMessage)

        # オブジェクトを破棄
        $attachment.Dispose()
        $emailMessage.Dispose()
    }
)

スクリプトを実行すると、以下の例のメールスクリーンショットと同様のメールが各購読者に送信されます。

Weather forecast for Manila sent as an email to the subscriber
Weather forecast for Los Angeles sent as an email to the subscriber

概要

Import-CsvForEachループの組み合わせは、適用できるタスクの制限はありません。デリミタで区切られた列のリストが関わるかぎり、この強力な組み合わせを活用することができます。

この記事では、CSVファイルがデータベースと似ていること、データのインポートにImport-Csvを使用し、ForEachループの反復中に値を参照する方法を学びました。

I hope that with the many examples provided in this article, you now understand more of the Import-Csv and ForEach. And, that you would be able to use the knowledge you gained in this article in your administration and automation tasks.

さらに読む

Source:
https://adamtheautomator.com/import-csv-and-the-foreach-loop/