2021-02-05

2020-10-15

技術ノート

eyecatch
AWS が公開している、AWS サービスの IP アドレスレンジ (ip-range.json) に PowerShell でアクセスし、プレフィックスリストを自動生成する方法を紹介します。

はじめに

本記事の趣旨ですが、前提として以下のエントリがすでに存在することを冒頭でお伝えしておくべきかと思います。

AWS の公開 IP アドレスレンジをいかに利用するかというテーマでセキュリティグループを自動生成する方法を紹介した記事ですが、同じテーマで考えた場合、最近アップデートされた機能であるプレフィックスリストも同じノリで自動生成できたほうがなにかと便利そうだなと感じました。そこで、今度はセキュリティグループではなくプレフィックスリストを自動生成する方法を紹介したいと思います。なお、PowerShell 関数でやってみるという方式自体は踏襲しています。本記事の性格上、派生元と内容が被る部分も多く、そういった部分に関しては記述を省略していますのでご了承ください。

作業環境は元記事とまったく一緒のため、割愛します。また、AWS Tools for PowerShell のユースケースは以下のリポジトリで管理しています。日々ブラッシュアップするため、記事の内容と相違している可能性がありますのでご了承ください。

プレフィックスリストを自動生成してみる

AWS が公開している IP アドレスレンジから任意のレンジを抽出し、それらをもとにプレフィックスリストを自動生成します。

スクリプトの全体像

コードの全体像を見てみましょう。

スクリプト

Function New-EC2ManagedPrefixListFromAWSPublicIpAddressRange
{
    [OutputType([System.Object])]
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$True)]
        [ValidateSet(
            "af-south-1",
            "ap-east-1",
            "ap-northeast-1",
            "ap-northeast-2",
            "ap-south-1",
            "ap-southeast-1",
            "ap-southeast-2",
            "ca-central-1",
            "eu-central-1",
            "eu-north-1",
            "eu-south-1",
            "eu-west-1",
            "eu-west-2",
            "eu-west-3",
            "me-south-1",
            "sa-east-1",
            "us-east-1",
            "us-east-2",
            "us-west-1",
            "us-west-2",
            "us-iso-east-1",
            "us-isob-east-1"
        )]
        [string]$Region,

        [Parameter(Mandatory=$True)]
        [ValidateNotNullOrEmpty()]
        [string[]]$ServiceKey,

        [Parameter(Mandatory=$True)]
        [ValidateSet(
            "Ipv4",
            "Ipv6"
        )]
        [string]$IpAddressFormat,

        [Parameter(Mandatory=$True)]
        [ValidateNotNullOrEmpty()]
        $MaxEntry,

        [Parameter(Mandatory=$True)]
        [ValidateNotNullOrEmpty()]
        $PrefixListName
    )

    Begin
    {
        Set-DefaultAWSRegion -Region $Region -Scope Script
        Import-Module -Name AWS.Tools.EC2

        $Ipv4Entries = @()
        $Ipv6Entries = @()
    }

    Process
    {
        $AWSPublicIpAddresses = Get-AWSPublicIpAddressRange -ServiceKey $ServiceKey -Region $Region

        ForEach($AWSPublicIpAddress In $AWSPublicIpAddresses)
        {
            If ($AWSPublicIpAddress.IpPrefix -like "*.*" -and $IpAddressFormat -eq "Ipv4")
            {
                $Ipv4Entry = New-Object -TypeName Amazon.EC2.Model.AddPrefixListEntry
                $Ipv4Entry.Cidr = $AWSPublicIpAddress.IpPrefix
                $Ipv4Entry.Description = $AWSPublicIpAddress.Service
                $Ipv4Entries += $Ipv4Entry
            }

            If ($AWSPublicIpAddress.IpPrefix -like "*:*" -and $IpAddressFormat -eq "Ipv6")
            {
                $Ipv6Entry = New-Object -TypeName Amazon.EC2.Model.AddPrefixListEntry
                $Ipv6Entry.Cidr = $AWSPublicIpAddress.IpPrefix
                $Ipv6Entry.Description = $AWSPublicIpAddress.Service
                $Ipv6Entries += $Ipv6Entry
            }
        }

        $Tags = @{ Key="Name"; Value=$PrefixListName }
        $NameTag = New-Object -TypeName Amazon.EC2.Model.TagSpecification
        $NameTag.ResourceType = "prefix-list"
        $NameTag.Tags.Add($Tags)

        $PrefixListParams = @{
            AddressFamily = $IpAddressFormat
            MaxEntry = $MaxEntry
            PrefixListName = $PrefixListName
            TagSpecification = $NameTag
        }

        If ($IpAddressFormat -eq "Ipv4")
        {
            $PrefixListParams.Add("Entry", $Ipv4Entries)
        }

        If ($IpAddressFormat -eq "Ipv6")
        {
            $PrefixListParams.Add("Entry", $Ipv6Entries)
        }

        $PrefixList = New-EC2ManagedPrefixList @PrefixListParams
    }
    
    End
    {
        return $PrefixList
    }
}

# Example

$Params = @{
    Region = "ap-northeast-1"
    ServiceKey = "S3", "AMAZON_CONNECT"
    IpAddressFormat = "Ipv4"
    MaxEntry = 30
    PrefixListName = "test-prefix-01"
 }

New-EC2ManagedPrefixListFromAWSPublicIpAddressRange @Params

#Get-Variable | Remove-Variable -ErrorAction SilentlyContinue

ほぼほぼセキュリティグループのときと同じですが、こちらのほうが少し簡単です。

Begin ブロック


# デフォルトリージョンを設定
Set-DefaultAWSRegion -Region $Region -Scope Script

# 必要なモジュールを読み込む
Import-Module -Name AWS.Tools.EC2

# IPv4 アドレス1個に対して1個作成される Amazon.EC2.Model.AddPrefixListEntry オブジェクトを格納する配列を初期化
$Ipv4Entries= @()

# IPv6 アドレス1個に対して1個作成される Amazon.EC2.Model.AddPrefixListEntry オブジェクトを格納する配列を初期化
$Ipv6Entries= @()

Ipv4 と Ipv6 で扱うオブジェクトは同一ですが、New-EC2ManagedPrefixListのパラメータでアドレスファミリとして Ipv4/Ipv6 のいずれかを選択する必要があるので、配列はふたつ初期化しておくのがポイントです。

Process ブロック


# AWS パブリック IP レンジをサービス・リージョンで絞り込む
$AWSPublicIpAddresses = Get-AWSPublicIpAddressRange -ServiceKey $ServiceKey -Region $Region

# 絞り込んだ AWS パブリック IP レンジをひとつずつ処理する
ForEach($AWSPublicIpAddress In $AWSPublicIpAddresses)
{
    # 対象 IP レンジが Ipv4、かつパラメータでの指定が Ipv4 の場合は作成したオブジェクトを Ipv4 用の配列に加算する
    If ($AWSPublicIpAddress.IpPrefix -like "*.*" -and $IpAddressFormat -eq "Ipv4")
    {
        $Ipv4Entry = New-Object -TypeName Amazon.EC2.Model.AddPrefixListEntry
        $Ipv4Entry.Cidr = $AWSPublicIpAddress.IpPrefix
        $Ipv4Entry.Description = $AWSPublicIpAddress.Service
        $Ipv4Entries += $Ipv4Entry
    }

    # 対象 IP レンジが Ipv6、かつパラメータでの指定が Ipv6 の場合は作成したオブジェクトを Ipv6 用の配列に加算する
    If ($AWSPublicIpAddress.IpPrefix -like "*:*" -and $IpAddressFormat -eq "Ipv6")
    {
        $Ipv6Entry = New-Object -TypeName Amazon.EC2.Model.AddPrefixListEntry
        $Ipv6Entry.Cidr = $AWSPublicIpAddress.IpPrefix
        $Ipv6Entry.Description = $AWSPublicIpAddress.Service
        $Ipv6Entries += $Ipv6Entry
    }

    # プレフィックスリストに付与する Name タグを定義
    $Tags = @{ Key="Name"; Value=$PrefixListName }
    $NameTag = New-Object -TypeName Amazon.EC2.Model.TagSpecification
    $NameTag.ResourceType = "prefix-list"
    $NameTag.Tags.Add($Tags)

    # 必要なパラメータを定義する
    $PrefixListParams = @{
        AddressFamily = $IpAddressFormat
        MaxEntry = $MaxEntry
        PrefixListName = $PrefixListName
        TagSpecification = $NameTag
    }

    # パラメータでの指定が Ipv4 の場合は Ipv4 用のオブジェクト配列を Add する
    If ($IpAddressFormat -eq "Ipv4")
    {
        $PrefixListParams.Add("Entry", $Ipv4Entries)
    }

    # パラメータでの指定が Ipv6 の場合は Ipv6 用のオブジェクト配列を Add する
    If ($IpAddressFormat -eq "Ipv6")
    {
        $PrefixListParams.Add("Entry", $Ipv6Entries)
    }

    # 実行する
    $PrefixList = New-EC2ManagedPrefixList @PrefixListParams
}

コメントを読んでいただければ流れはわかると思います。強いてポイントをあげるとすれば、最後の$IpAddressFormatの値によって連想配列にAddする内容を制御しているところでしょうか。

End ブロック

特に言うことはありません。


return $PrefixList

実行してみる

実行してみましょう。パラメータは例によってバンドル(スプラッティング)します。


$Params = @{
    Region = "ap-northeast-1"
    ServiceKey = "s3", "AMAZON_CONNECT"
    IpAddressFormat = "Ipv4"
    MaxEntry = 30
    PrefixListName = "test-prefix-01"
 }

New-EC2ManagedPrefixListFromAWSPublicIpAddressRange @Params

プレフィックスリストがうまく生成されているか確認してください。

おわりに

今回は AWS の公開 IP アドレスレンジを使ってプレフィックスリストを作りましたが、プレフィックスリストの機能が本当の実力を発揮するのはオンプレ環境と AWS 環境が VPN 接続されているインフラ基盤でオンプレ側の IP アドレスレンジを管理するシチュエーションである気がします。なんらかの方法でオンプレの領域も自動化できたら・・・最高ですね。オンプレのネットワークに関してはあまり知見がないため、現時点で実装方法が思いつきませんが、チャレンジしてみたいテーマではあります。