[アップデート] AWS DataSync の ロケーション と タスク の リストを行う API でフィルタリングが可能になりました!自動化が捗る!

ロケーションとタスクの一覧を取得する際に条件を設定できるようになったため、システム化するのに役立ちます。

コンバンハ、千葉(幸)です。

一週間程度前のアップデートですが、 AWS DataSync の以下の API を呼び出す際に、フィルタリングを行うことが可能になりました!

  • ListLocations
  • ListTasks

これにより、「特定のパスを含むロケーション」「 S3 バケット A からデータ転送を行うタスク」「昨日の 18:00 以降に設定されたタスク」などを簡単に取得できるようになります。

タスクの作成・実行のインプットとしたり、意図せぬタスク・ロケーションの設定が行われていないかの監視を行ったりといった、取得した結果を活用した自動化がシンプルに実現できるようになりました!

目次

何が変わったのかをもうちょい詳しく

簡単におさらいすると、AWS Datasync は各種ストレージサービス間でのデータ転送をシンプルかつ高速に実現してくれるサービスです。

データ転送の送信元、送信先はロケーションというコンポーネントとして作成します。同一のリソースであっても、設定するパス(path)が異なれば別物のロケーションとして扱われます。

ロケーション間のデータ転送はエージェントによって中継されます。一部のロケーションのタイプでは作成時にエージェントと紐付ける必要があります。

ロケーションやエージェントは、AWS 環境・オンプレ環境どちらでも構成可能です。

データ転送を実行する際には、タスクというコンポーネントを定義します。主に以下の内容を定めます。

  • 送信元のロケーション
  • 送信先のロケーション
  • 各種オプション

タスクに設定する送信元もしくは送信先のロケーションのいずれかでエージェントとの紐付けが行われている必要があります。

タスクを実行すると、その履歴は Task Execution (タスクの実行)という単位で管理されます。

タスクや Task Execution を含め、 AWS DataSync において List を行う API としてはいくつかありますが、そのうちロケーションタスクをリストするものが今回のアップデートでより便利になりました。

以下のイメージです。

従来は一覧を頭から順に出力するだけでしたが、特定の条件に基づいてフィルタリングできるようになったというものです。

出力結果を利用して他の操作のトリガーとしたり、設定内容の監視を行うといった自動化をシンプルに実現できるようになりました。

フィルターできるものをもうちょい詳しく

AWS CLI のリファレンスを参照するのが分かりやすいので、そちらを確認していきます。

今回アップデートされた2つの API に対応する CLI コマンドにおいて、--filters というオプションを追加できるようになっています。

--filtersオプションにおいては、以下の三要素からなるパラメータを引き渡します。

  • Nameフィルタリングする項目(ロケーションタイプ、作成時刻 など)
  • Valuesフィルタリングする項目の値(efs、2020-07-07 12:00:00 など)
  • Operatorフィルタリングの条件(完全一致、〜より大きい など)

Name,Values

一つのオプションの固まりの中で、Valuesだけは複数定義できます。また、Operatorで指定する値によっては、Valuesに完全な値を入れる必要はありません。

list-locations

Name名 概要 Values例
LocationUri ロケーションのURI efs://ap-northeast-1.fs-xxxxxxxx/hoge/
LocationType ロケーションタイプ efs,nfs,s3
CreationTime 作成時刻 2020-07-07 12:00:00

list-tasks

Name名 概要 Values例
LocationId タスクに設定されているロケーションのARN arn:aws:datasync:ap-northeast-1:000000000000:location/loc-xxxxxxxxxxxxxxxxx
CreationTime 作成時刻 2020-07-07 12:00:00

Operator

CreationTime用の値と、それ以外の値用のものに分かれます。

CreationTime 用

概要
LessThanOrEqual Values の値以下
LessThan Values の値未満
GreaterThanOrEqual Values の値以上
GreaterThan Values の値より大きい

それ以外の値 用

概要
Equals Values の値に等しい
NotEquals Values の値に等しくない
In Values の値のいずれかに合致する
Contains Values の値を含む
NotContains Values の値を含まない
BeginsWith Values の値から始まる

なお、Valuesの値を複数定義する場合、Operatorで選択できるのはInのみです。それ以外の場合、以下のようなエラーが発生します。

$ aws datasync list-locations --filter Name=LocationType,Values=efs,nfs,Operator=BeginsWith

An error occurred (InvalidRequestException) when calling the ListLocations operation: Filter validation failed for com.amazonaws.fmrs.commons.exception.FmrsFilterValidationException: Only one value is allowed in values for comparison operator BEGINS_WITH : Only one value is allowed in values for comparison operator BEGINS_WITH

オプションの指定の仕方

以下のような形式で指定します。

$ aws datasync list-locations \
  --filter ”Name=LocationType,Values=efs,Operator=Equals”

複数のオプションの固まりを羅列する時は以下のように書きます。

$ aws datasync list-locations \
  --filter ”Name=LocationType,Values=efs,Operator=Equals” \
           ”Name=LocationUri,Values=/hoge,Operator=Contains”

留意点としては以下があります。

  • 同じ Name を複数羅列することはできない
    • Nameによってはエラーが発生する
      • LocationTypeを複数指定した例:An error occurred (InvalidRequestException) when calling the ListLocations operation: Filter validation failed for com.amazonaws.fmrs.commons.exception.FmrsFilterValidationException: Found more than one filter for filter type LocationType : Found more than one filter for filter type LocationType
    • エラーが発生しない場合でも、後に付与したもののみが解釈される
  • 異なるNameを羅列した場合 AND 条件となる
    • 上記の例の場合、「タイプが EFS 」かつ「 URI に/hogeを含む」ものが対象となる
  • ""で囲むようにした方がベター
    • Name=CreationTimeの場合、囲まないとエラーが発生する

3点目について補足します。以下のようにName=LocationType の場合、""で囲まない形で書いても正常に解釈されます。これは他のほとんどのNameにおいても同様です。

$ aws datasync list-locations \
  --filter Name=LocationType,Values=efs,Operator=Equals
{
    "Locations": [    
 ーーー略ーーー
    ]
}

Name=CreationTimeの場合、""で囲まない形で書くと以下のようなエラーが発生します。

$ aws datasync list-locations \
  --filter Name=CreationTime,Values=2020-07-07 12:00:00,Operator=GreaterThan

Error parsing parameter '--filters': Expected: '=', received: ',' for input:
12:00:00,Operator=GreaterThan
        ^

""で囲むことで、正常に解釈されます。

$ aws datasync list-locations \
  --filter "Name=CreationTime,Values=2020-07-07 12:00:00,Operator=GreaterThan"
{
    "Locations": [
 ーーー略ーーー
    ]
}

(私はここで 2 時間くらい溶かしました。悲しい。)

やってみた

今回は以下のような環境で確認してみます。(わざわざフィルタリングをするほどの規模ではないですが、サンプルということで……)

  • ロケーション
    • NFSloc-0xxxxxxxxxxxxxxxx
      • エージェント agent-0aaaaaaaaaaaaaaaa と紐付け
    • EFSloc-0yyyyyyyyyyyyyyyy
  • タスク
    • task-0zzzzzzzzzzzzzzzz

AWS CLI のバージョンは以下です。

$ aws --version
aws-cli/2.0.42 Python/3.7.4 Darwin/19.6.0 exe/x86_64

通常のリスト

フィルタリングを行わない場合の結果を確認してみます。

list-locations

ロケーションの ARNURI のみが結果として返されます。一度のリクエストで返却されるのは最大で 100 ロケーションまでです。

$ aws datasync list-locations
{
    "Locations": [
        {
            "LocationArn": "arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0xxxxxxxxxxxxxxxx",
            "LocationUri": "nfs://10.nn.nn.nnn/fuga/"
        },
        {
            "LocationArn": "arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0yyyyyyyyyyyyyyyy",
            "LocationUri": "efs://ap-northeast-1.fs-xxxxxxx/hoge/"
        }
    ]
}

list-tasks

タスクの ARNステータス名称が返却されます。こちらも一度のリクエストで返却されるのは最大で 100 タスクです。

$ aws datasync list-tasks
{
    "Tasks": [
        {
            "TaskArn": "arn:aws:datasync:ap-northeast-1:000000000000:task/task-0zzzzzzzzzzzzzzzz",
            "Status": "AVAILABLE",
            "Name": "Task-no-Namae"
        }
    ]
}

フィルタリングができないとただ単に一覧が返却されるのみ(しかも上限あり)で、一緒に取れる値も最小限のため、このままだと自動化を行いにくいというのはよく分かります。

個々のリソースの Describe

今回のアップデートにはあまり関係のない部分ですが、ロケーションやタスクはどのような値を持っているかを確認してみます。

describe-location

ロケーションを Describe する場合、ロケーションタイプごとにコマンドが分かれています。

EFS language=ロケーションの場合

$ aws datasync describe-location-efs --location-arn arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0yyyyyyyyyyyyyyyy
{
    "LocationArn": "arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0yyyyyyyyyyyyyyyy",
    "LocationUri": "efs://ap-northeast-1.fs-xxxxxxxx/hoge/",
    "Ec2Config": {
        "SubnetArn": "arn:aws:ec2:ap-northeast-1:000000000000:subnet/subnet-0ssssssssssssssss",
        "SecurityGroupArns": [
            "arn:aws:ec2:ap-northeast-1:000000000000:security-group/sg-0ssssssssssssssss",
            "arn:aws:ec2:ap-northeast-1:000000000000:security-group/sg-0gggggggggggggggg"
        ]
    },
    "CreationTime": "2020-07-07T10:10:10.236000+09:00"
}
$ aws datasync describe-location-nfs --location-arn arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0xxxxxxxxxxxxxxxx
{
    "LocationArn": "arn:aws:datasync:ap-northeast-1:000000000000:location/loc-xxxxxxxxxxxxxxxx",
    "LocationUri": "nfs://10.nn.nn.nnn/fuga/",
    "OnPremConfig": {
        "AgentArns": [
            "arn:aws:datasync:ap-northeast-1:000000000000:agent/agent-0aaaaaaaaaaaaaaaa"
        ]
    },
    "MountOptions": {
        "Version": "AUTOMATIC"
    },
    "CreationTime": "2020-07-07T10:30:10.236000+09:00"
}

ロケーションタイプのうちいくつかは、作成時にエージェントと紐付ける必要があります。送信元もしくは送信先いずれかのロケーションでエージェントが紐づいている組み合わせでのみ、タスクを定義できます。

ロケーションの操作-AWS DataSync

describe-task

$ aws datasync describe-task --task-arn arn:aws:datasync:ap-northeast-1:000000000000:task/task-0zzzzzzzzzzzzzzzz
{
    "TaskArn": "arn:aws:datasync:ap-northeast-1:000000000000:task/task-00zzzzzzzzzzzzzzzz",
    "Status": "RUNNING",
    "Name": "Task-no-Namae",
    "CurrentTaskExecutionArn": "arn:aws:datasync:ap-northeast-1:000000000000:task/task-00zzzzzzzzzzzzzzzz/execution/exec-0aaaaaaaaaaaaaaaa",
    "SourceLocationArn": "arn:aws:datasync:ap-northeast-1:000000000000:location/loc-xxxxxxxxxxxxxxxx",
    "DestinationLocationArn": "arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0yyyyyyyyyyyyyyyy",
    "CloudWatchLogGroupArn": "arn:aws:logs:ap-northeast-1:000000000000:log-group:/aws/datasync",
    "SourceNetworkInterfaceArns": [],
    "DestinationNetworkInterfaceArns": [
        "arn:aws:ec2:ap-northeast-1:000000000000:network-interface/eni-0eeeeeeeeeeeeeeee",
        "arn:aws:ec2:ap-northeast-1:000000000000:network-interface/eni-0nnnnnnnnnnnnnnnn",
        "arn:aws:ec2:ap-northeast-1:000000000000:network-interface/eni-0iiiiiiiiiiiiiiii",
        "arn:aws:ec2:ap-northeast-1:000000000000:network-interface/eni-00000000000000000"
    ],
    "Options": {
        "VerifyMode": "ONLY_FILES_TRANSFERRED",
        "OverwriteMode": "ALWAYS",
        "Atime": "BEST_EFFORT",
        "Mtime": "PRESERVE",
        "Uid": "INT_VALUE",
        "Gid": "INT_VALUE",
        "PreserveDeletedFiles": "PRESERVE",
        "PreserveDevices": "NONE",
        "PosixPermissions": "PRESERVE",
        "BytesPerSecond": 10485760,
        "TaskQueueing": "ENABLED",
        "LogLevel": "BASIC",
        "TransferMode": "CHANGED"
    },
    "Excludes": [],
    "CreationTime": "2020-07-08T10:10:10.236000+09:00"
}

送信元/送信先のロケーションのほか、各種オプションが確認できます。

また、エージェントはアクティベートの際に紐づくエンドポイントを設定しますが、VPC エンドポイントと紐付けを行った場合、中継用のクライアント が用いる ENI が生成されます。

今回の構成では送信元ロケーションにエージェントが紐付けてあり、VPC エンドポイントを用いてアクティベートしているため、送信先の ENI として 4 つの ENI が確認できます。

フィルターを用いたリスト

ようやく本題です。いくつかのパターンを試してみます。

In を使用した複数 Values でのフィルタリング

LocationTypeefsもしくはnfsのロケーションをリストしてみます。どちらのロケーションも返却されました。

$ aws datasync list-locations \
  --filter Name=LocationType,Values=efs,nfs,Operator=In
{
    "Locations": [
        {
            "LocationArn": "arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0xxxxxxxxxxxxxxxx",
            "LocationUri": "nfs://10.nn.nn.nnn/fuga/"
        },
        {
            "LocationArn": "arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0yyyyyyyyyyyyyyyy",
            "LocationUri": "efs://ap-northeast-1.fs-xxxxxxx/hoge/"
        }
    ]
}

ここでOperator=Equalsにしてみると、Valuesを複数指定してはいけない旨のエラーが出ます。

$ aws datasync list-locations \
  --filter Name=LocationType,Values=efs,nfs,Operator=Equals

An error occurred (InvalidRequestException) when calling the ListLocations operation: Filter validation failed for com.amazonaws.fmrs.commons.exception.FmrsFilterValidationException: Only one value is allowed in values for comparison operator EQ : Only one value is allowed in values for comparison operator EQ

Valuesefsのみに指定すると、片方のみ返却されます。

$ aws datasync list-locations \
  --filter Name=LocationType,Values=efs,Operator=In
{
    "Locations": [
        {
            "LocationArn": "arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0yyyyyyyyyyyyyyyy",
            "LocationUri": "efs://ap-northeast-1.fs-xxxxxxx/hoge/"
        }
    ]
}

複数条件を用いたフィルタリング

LocationTypeefsもしくはnfsで、LocationUri/hogeを含む、という条件でリストしてみます。

今回の構成では NFS ロケーションは /hogeというパスを含まないため、 EFS ロケーションのみがリストされます。

$ aws datasync list-locations \
  --filter "Name=LocationType,Values=efs,nfs,Operator=In" \
           "Name=LocationUri,Values=/hoge,Operator=Contains"
{
    "Locations": [
        {
            "LocationArn": "arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0yyyyyyyyyyyyyyyy",
            "LocationUri": "efs://ap-northeast-1.fs-xxxxxxx/hoge/"
        }
    ]
}

条件に合致しないフィルタリング

Name=LocationIdとして、存在しないロケーション ARN を指定すると、以下のような結果が返ってきます。条件に合致するものがない場合は、全てこのような形のレスポンスとなります。

$ aws datasync list-tasks \
  --filter "Name=LocationId,Values=arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0pppppppppppppppp,Operator=Equals"
{
    "Tasks": []
}

上記の例を実行する際に付随して気づいたのですが、Name=LocationIdを指定する際は注意が必要そうです。タスクに設定してあるロケーションの ARN を指定してOperator=Equalsでフィルタリングすると、結果が返ってきます。これは違和感がない挙動です。

$ aws datasync list-tasks \
  --filter Name=LocationId,Values=arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0yyyyyyyyyyyyyyyy,Operator=Equals
{
    "Tasks": [
        {
            "TaskArn": "arn:aws:datasync:ap-northeast-1:000000000000:task/task-0zzzzzzzzzzzzzzzz",
            "Status": "AVAILABLE",
            "Name": "Task-no-Namae"
        }
    ]
}

同じケースでOperator=NotEqualsにしても、結果が返ってきます。これはちょっと想定外の挙動です。

$ aws datasync list-tasks \
  --filter Name=LocationId,Values=arn:aws:datasync:ap-northeast-1:000000000000:location/loc-0yyyyyyyyyyyyyyyy,Operator=NotEquals
{
    "Tasks": [
        {
            "TaskArn": "arn:aws:datasync:ap-northeast-1:000000000000:task/task-0zzzzzzzzzzzzzzzz",
            "Status": "AVAILABLE",
            "Name": "Task-no-Namae"
        }
    ]
}

ContainsNotContainsにしても同じ挙動でした。ロジックは理解しきれませんでしたが、Name=LocationIdを使用する際はNot系のOperatorを指定しない方がよいかと思います。

CreationTime を用いたフィルタリング

個人的に今回一番ハマった部分です。

Values に指定する値として、Describe した際のレスポンスと同じ形式の 2020-07-06T10:10:10.236000+09:00を指定すると、以下のようなエラーが出ます。

% aws datasync list-locations \
  --filter Name=CreationTime,Values=2020-07-06T10:10:10.236000+09:00,Operator=GreaterThan

An error occurred (ValidationException) when calling the ListLocations operation: 1 validation error detected: Value '[2020-07-07T10:10:10.236000+09:00]' at 'filters.1.member.values' failed to satisfy constraint: Member must satisfy constraint: [Member must have length less than or equal to 255, Member must have length greater than or equal to 1, Member must satisfy regular expression pattern: ^[0-9a-zA-Z_\ \-\:\*\.\\/\?-]*$]

上記のエラーによると、+が正規表現パターンに含まれていないことが原因と読めます。

そのため末尾を削った2020-07-07T10:10:10.236000で試してみるのですが、また違うエラーが出ます。いくつか違う形式で試しても同一です。

$ aws datasync list-locations \
  --filter Name=CreationTime,Values=2020-07-06T10:10:10.236000,Operator=GreaterThan

An error occurred (InternalFailure) when calling the ListLocations operation (reached max retries: 2):

2020-07-07 12:00:00 の形式が正しいのですが、"" で囲まないで試行するとエラーが出る、というのは先述の通りです。

$ aws datasync list-locations \
  --filter Name=CreationTime,Values=2020-07-06 12:00:00,Operator=GreaterThan

Error parsing parameter '--filters': Expected: '=', received: ',' for input:
12:00:00,Operator=GreaterThan
        ^

""で囲むことで、ようやく正常に解釈されます。

$ aws datasync list-tasks \
  --filter "Name=CreationTime,Values=2020-07-06 12:00:00,Operator=GreaterThan"
{
    "Tasks": [
        {
            "TaskArn": "arn:aws:datasync:ap-northeast-1:000000000000:task/task-0zzzzzzzzzzzzzzzz",
            "Status": "AVAILABLE",
            "Name": "Task-no-Namae"
        }
    ]
}

いろいろと遠回りをしましたが、新しく追加されたフィルターオプションの使い方が確認できました!

終わりに

AWS DataSync の ListLocations と ListTasks の API がフィルタリングに対応したことを確認しました。

本エントリでは AWS CLI を例に取りましたが、SDK など、他の手段においてもフィルタリング可能です。(ちなみにマネジメントコンソールでは対応していません。)

ある程度 多数のロケーションやタスクを管理している環境でないとありがたみは薄いかもしれませんが、単純に一覧が出力されるだけだった従来と比較すると、自動化に組み込みやすくなったかと思います。活用していきましょう。

あとはもう少しリファレンスに細かい仕様の情報が充実すると嬉しいな……と思いました。

以上、コマンドと数時間ずっと格闘していた千葉(幸)がお送りしました。