pythonで Azure Blob、AWS S3からMD5ハッシュ値を取得する

## はじめに
本年度から業務内容がWindowsアプリ開発からAWS関連の業務に代わり、
最初のタスクが「**Azure Blob Storageから AWS S3に移行されたデータが正しく移行されているかの検証**」でした。

aws s3にはアップロード時に、アップロード前と後のオブジェクトの整合性を検証する手段があるのですが、今回は既に**全データ移行済み**だったため、BlobとS3の**メタデータ**を取得して格納されている**MD5ハッシュ値**で検証を実施しました。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/data-integrity-s3/

ちなみに言語はpythonを使っています。

## 結論
* オブジェクトのメタデータ取得(S3編)
* オブジェクトのメタデータ取得(Blob編)
* 終わり



## オブジェクトのメタデータ取得(S3編)
pythonかつaws s3のメタデータを取得する方法は探せば多くの情報が見つかると思います。
下記のコードもその一例になります。

```python
    from azure.storage.blob import BlobServiceClient

    # AWS CLIの aws configure コマンドで .aws/credentialsファイルに書き込むこと
    aws_access_key_id = ''
    aws_secret_access_key = ''

    bucket_name = ''
    key_name = ''
    
    s3_cli = boto3.client('s3',aws_access_key_id, aws_secret_access_key)
    # オブジェクトのメタデータを取得
    s3_resp = s3_cli.head_object(bucket_name, key_name)
    # レスポンスからMD5ハッシュが格納されているEtagを取得
    s3obj_etag = s3_resp['ETag'].strip('"')
    return s3obj_etag
```

`key_id`や`bucket_name`は自分の環境にあわせて値を代入してください。
`boto3.client()`でs3へアクセスするオブジェクトをまず取得します。
その後、`head_object()`でオブジェクトのメタデータを取得します。

```python
    # オブジェクトのメタデータを取得
    s3_resp = s3_cli.head_object(bucket_name, key_name)
```

s3_respは`json`が返ってきます。返り値の中身は下記のようなデータが入っています。

```json
"s_respの中身の例"
{'ResponseMetadata': {'RequestId': '9dad3fc3-0e30-1dbc-a754-06bdfcde1d5e',
  'HostId': '',
  'HTTPStatusCode': 200,
  'HTTPHeaders': {'date': 'Sun, 13 Dec 2020 22:43:10 GMT',
   'x-amz-request-id': '9dad3fc3-0e30-1dbc-a754-06bdfcde1d5e',
   'last-modified': 'Sun, 13 Dec 2020 22:40:57 GMT',
   'etag': '"669fdad9e309b552f1e9cf7b489c1f73-2"',
   'content-type': 'binary/octet-stream',
   'x-amz-server-side-encryption': 'AES256',
   'x-amz-version-id': 'fe14c26b-bba0-6edf-a754-06bdfcde1d5e',
   'accept-ranges': 'bytes',
   'content-length': '10485760',
   'server': 'CloudianS3'},
  'RetryAttempts': 0},
 'AcceptRanges': 'bytes',
 'LastModified': datetime.datetime(2020, 12, 13, 22, 40, 57, tzinfo=tzutc()),
 'ContentLength': 10485760,
 'ETag': '"669fdad9e309b552f1e9cf7b489c1f73-2"',
 'VersionId': 'fe14c26b-bba0-6edf-a754-06bdfcde1d5e',
 'ContentType': 'binary/octet-stream',
 'ServerSideEncryption': 'AES256',
 'Metadata': {}}
```
- 返り値の参考例  
https://zenn.dev/nowko/books/550bcd398f3122/viewer/ce44b4

``head_object``の返り値に``ETag``というキーがあります。この値がMD5ハッシュ値になります。

```json
'ETag': '"669fdad9e309b552f1e9cf7b489c1f73-2"'
```

数字型として使う場合は、下記のようにstrip()で **""** を削除して使いましょう。

```python
# レスポンスからMD5ハッシュが格納されているEtagを取得
# ""を除去
s3obj_etag = s3_resp['ETag'].strip('"')
```

以上でaws s3のMD5ハッシュ取得については終了です。

## オブジェクトのメタデータ取得(Blob編)

s3と違ってazure blobのメタデータ取得については、探すのに時間がかかりました。
なので、ここからは皆さんのお役になる情報だと思います。

```python
    from azure.storage.blob import BlobServiceClient

    # BLOB接続文字列
    connectionString = ''
    container_name = ''
    blob_name = ''

    blob_service_client = BlobServiceClient.from_connection_string(connectionString)
    blob_client = blob_service_client.get_blob_client(container_name, blob_name)
    
    # BLOBのすべてのユーザー定義メタデータ、標準HTTPプロパティ、およびシステムプロパティを受け取る
    prop = blob_client.get_blob_properties()
    bytearray = prop['content_settings']['content_md5']
    return bytearray.hex()
```

以下、解説になります。
`from_connection_string()`、`get_blob_client()`を使って`BlobClient`を取得します。

```python
blob_service_client = BlobServiceClient.from_connection_string(connectionString)
blob_client = blob_service_client.get_blob_client(container_name, blob_name)
```

https://docs.microsoft.com/en-us/python/api/azure-storage-blob/azure.storage.blob.blobclient?view=azure-python#get-blob-properties---kwargs-

`BlobClient`クラスには`get_blob_properties()`というAPIがあり、このAPIがBLOBのメタデータ(`BlobProperties`)を返してくれます。
ここで注意点なのですが、`BlobProperties`にはetagというキーがありますが、今回欲しいハッシュ値は、`content_settings`キーの値`ContentSettings`に`content_md5`というキーがありこちらになります。


```python
    prop = blob_client.get_blob_properties()
    bytearray = prop['content_settings']['content_md5']
    return bytearray.hex()
```

content_md5は`bytearray`型なので、`hex()`で変換することでs3で取得した値と同じものになるかと思います。

## 終わり

以上になります。
先にs3のハッシュを取得できていたため、`BlobProperties`にはetagを見つけて返り値を確認した時は、なぜs3の返り値と値が異なるのかと頭を抱えていました。
Microsoftのドキュメントをきちんと確認することはやはり大事ですね。

皆さんはデータ移行時は必ずアップロード時に整合性を確認して、失敗した場合はリトライなどの処理をしましょう。データ数が多いので、移行後の整合性確認はお金の無駄でしかありません。