AWS SDK for Python(boto3)でAmazon Managed Blockchainのブロックチェーンネットワークを作成してみた

Amazon Managed Blockchainでブロックチェーンネットワークを構築するのにAWS CloudFormationを利用したいなぁとドキュメントを読んでみたらリソースがありませんでした。oh...
AWS CloudFormationのカスタムリソースを利用すれば管理できるので、まずはAWS SDKでAmazon Managed Blockchainが取り扱えるのか確認してみました。

AWS CloudFormationのカスタムリソースについては下記が参考になります。

カスタムリソース - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources.html

CloudFormationで提供されていない処理をカスタムリソースで作ってみた。 ｜ DevelopersIO
https://dev.classmethod.jp/cloud/aws/cfn-api-custom/

AWS CloudFormationでCognitoユーザープールをMFAのTOTPを有効にして作成する - Qiita
https://qiita.com/kai_kou/items/f56bb13a5d47ee05d766


## Amazon Managed Blockchainってなんぞ？

Amazon Managed Blockchainってなんぞ？という方は下記をご参考ください。

Amazon Managed BlockchainがリリースされたのでHyperledger Fabricも合わせて情報をまとめてみた - Qiita
https://qiita.com/kai_kou/items/5a968f42c5f96296f6fe


## 前提

今回はAmazon Managed Blockchainのネットワーク、メンバー、ノードがAWS SDKで作成できるかの確認のみとなります。セキュリティグループ、インタフェースVPCエンドポイントやHyperledger Fabricの設定などは行いません。

- AWSアカウントがある
- AWSアカウントに以下の作成権限がある
  - Managed Blockchainネットワーク
- AWS CLIの`aws configure` コマンドでアカウント設定済み

ネットワーク構築して動作確認するまでの手順は下記が参考になります。

Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた - Qiita
https://qiita.com/kai_kou/items/e02e34dd9abb26219a7e


## AWS SDK for Python(boto3)でAmazon Managed BlockchainのAPIが提供されてる

ドキュメントを確認してみたらAWS SDKでAmazon Managed BlockchainのAPIが提供されていました。

boto/boto3: AWS SDK for Python
https://github.com/boto/boto3

ManagedBlockchain — Boto 3 Docs 1.9.152 documentation
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/managedblockchain.html


## Python環境の用意

Pipenvを利用したことがなかったので、ついでに初利用します(趣味)。
仮想環境じゃなくてもOKです。

Pipenvを使ったPython開発まとめ - Qiita
https://qiita.com/y-tsutsu/items/54c10e0b2c6b565c887a


```console
# pipenvがインストールされてなかったら
> pip install pipenv

(略)
Installing collected packages: virtualenv, virtualenv-clone, pipenv
Successfully installed pipenv-2018.11.26 virtualenv-16.6.0 virtualenv-clone-0.5.3


# 仮想環境の作成
> pipenv --python 3.7

Creating a virtualenv for this project…
Pipfile: /Users/xxx/dev/aws/managed-blockchain-use-sdk/Pipfile
Using /Users/xxx/.anyenv/envs/pyenv/versions/3.7.0/bin/python3 (3.7.0) to create virtualenv…
⠴ Creating virtual environment...
(略)
✔ Successfully created virtual environment! 
Virtualenv location: /Users/xxx/.local/share/virtualenvs/managed-blockchain-use-sdk-ijMKrYxz
Creating a Pipfile for this project…


# boto3のインストール
> pipenv install boto3

Installing boto3…
⠴ Installing...
(略)


# 仮想環境へ入る
> pipenv shell

Launching subshell in virtual environment…
Welcome to fish, the friendly interactive shell
```


## AWS SDK for Python(boto3)を利用して実装

ドキュメントを参考にネットワークなどを作成、取得できるか確認します。

ManagedBlockchain — Boto 3 Docs 1.9.152 documentation
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/managedblockchain.html


### ネットワークとメンバーの作成

`create_network` でネットワーク作成できます。パラメータはAWS CLIとほぼ同じなので下記が参考になります。
AWS CLIと同じく最初のメンバーも合わせて作成する必要があります。

Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた - Qiita
https://qiita.com/kai_kou/items/e02e34dd9abb26219a7e#%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%A8%E3%83%A1%E3%83%B3%E3%83%90%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B


```python:create_network.py
import boto3

client = boto3.client("managedblockchain")

def create_network():
  new_network = client.create_network(
    # ClientRequestToken="string",
    Name="TestNetwork",
    Description="TestNetworkDescription",
    Framework="HYPERLEDGER_FABRIC",
    FrameworkVersion="1.2",
    FrameworkConfiguration={
        "Fabric": {
            "Edition": "STARTER"
        }
    },
    VotingPolicy={
        "ApprovalThresholdPolicy": {
            "ThresholdPercentage": 50,
            "ProposalDurationInHours": 24,
            "ThresholdComparator": "GREATER_THAN"
        }
    },
    MemberConfiguration={
        "Name": "org1",
        "Description": "Org1 first member of network",
        "FrameworkConfiguration": {
            "Fabric": {
                "AdminUsername": "AdminUser",
                "AdminPassword": "Password123"
            }
        }
    }
  )

  print(new_network)
```

実行するとネットワークとメンバーの作成が開始されて`NetworkId` と`MemberId` が得られます。

```console
> python create_network.py

{'ResponseMetadata': {'RequestId': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Tue, 21 May 2019 03:24:32 GMT', 'content-type': 'application/json', 'content-length': '86', 'connection': 'keep-alive', 'x-amzn-requestid': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'x-amz-apigw-id': 'xxxxxxxxxxxxxxx=', 'x-amzn-trace-id': 'Root=1-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx;
Sampled=0'}, 'RetryAttempts': 0}, 'NetworkId': 'n-XXXXXXXXXXXXXXXXXXXXXXXXXX', 'MemberId': 'm-XXXXXXXXXXXXXXXXXXXXXXXXXX'}
```

### ネットワーク情報の取得

`list_networks` でネットワーク情報が取得できます。
パラメータ指定することで検索もできるみたいです。

```python:list_network.py
import boto3

client = boto3.client("managedblockchain")

def list_network():
  networks = client.list_networks(
    # Name="Test"
    # Name="string",
    # Framework="HYPERLEDGER_FABRIC",
    # Status="CREATING"|"AVAILABLE"|"CREATE_FAILED"|"DELETING"|"DELETED",
    # MaxResults=123,
    # NextToken="string"
  )

  print(networks)

list_networks()
```

```console
> python list_network.py

[{'Id': 'n-XXXXXXXXXXXXXXXXXXXXXXXXXX', 'Name': 'TestNetwork', 'Description': 'TestNetworkDescription', 'Framework': 'HYPERLEDGER_FABRIC', 'FrameworkVersion': '1.2', 'Status': 'CREATING', 'CreationDate': datetime.datetime(2019, 5, 21, 3, 24, 31, 929000, tzinfo=tzutc())}]
```

### ノードの作成

`create_node` でノード作成ができます。`ClientRequestToken` に関しては指定しない場合自動生成してくれるとのことでした。手動での生成方法は未調査です。

```python:create_node.py
import boto3

client = boto3.client("managedblockchain")

def create_node():
  new_node = client.create_node(
      # ClientRequestToken='string',
      NetworkId='n-5QBOS5ULTVEZZG6EMIPLPRSSQA',
      MemberId='m-OMLBOQIAMJDDPJV3FVGIAVUGBE',
      NodeConfiguration={
          'InstanceType': 'bc.t3.small',
          'AvailabilityZone': 'us-east-1a'
      }
  )

  print(new_node)

create_node()
```

```console
> python create_node.py

{'ResponseMetadata': {'RequestId': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Tue, 21 May 2019 04:04:44 GMT', 'content-type': 'application/json', 'content-length': '42', 'connection': 'keep-alive', 'x-amzn-requestid': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'x-amz-apigw-id': 'xxxxxxxxxxxxxxx=', 'x-amzn-trace-id': 'Root=1-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx;Sampled=0'}, 'RetryAttempts': 0}, 'NodeId': 'nd-XXXXXXXXXXXXXXXXXXXXXXXXXX'}
```

ネットワーク、メンバーが作成中(`Status` が`CREATING` )の場合は作成できませんでした。

```console:ネットワーク、メンバー作成中に実行
> python create_node.py

Traceback (most recent call last):
  File "main.py", line 43, in <module>
    'AvailabilityZone': 'us-east-1a'
  File "/Users/xxx/.local/share/virtualenvs/managed-blockchain-use-sdk-ijMKrYxz/lib/python3.7/site-packages/botocore/client.py", line 357, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/Users/xxx/.local/share/virtualenvs/managed-blockchain-use-sdk-ijMKrYxz/lib/python3.7/site-packages/botocore/client.py", line 661, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.ResourceNotReadyException: An error occurred (ResourceNotReadyException) when calling the CreateNode operation: Member m-XXXXXXXXXXXXXXXXXXXXXXXXXX is in CREATING. you cannot create nodes at this time..
```

### メンバー情報の取得

`list_members` でメンバーが取得できます。`NetworkId` が必須となります。


```python:list_members.py
import boto3

client = boto3.client("managedblockchain")

def list_members():
  members = client.list_members(
    NetworkId="n-XXXXXXXXXXXXXXXXXXXXXXXXXX"
    # Name="string",
    # Status="CREATING"|"AVAILABLE"|"CREATE_FAILED"|"DELETING"|"DELETED",
    # IsOwned=True|False,
    # MaxResults=123,
    # NextToken="string"
  )

  print(members["Members"])

list_members()
```

```console
> python list_members.py

[{'Id': 'm-XXXXXXXXXXXXXXXXXXXXXXXXXX', 'Name': 'demo', 'Description': 'demo member', 'Status': 'AVAILABLE', 'CreationDate': datetime.datetime(2019, 5, 21, 3, 24, 32, 271000, tzinfo=tzutc()), 'IsOwned': True}]
```

### ノード情報の取得

`list_nodes` でノードが取得できます。`NetworkId` 、`MemberId` が必須となります。

```python:list_nodes.py
import boto3

client = boto3.client("managedblockchain")

def list_nodes():
  nodes = client.list_nodes(
    NetworkId="n-XXXXXXXXXXXXXXXXXXXXXXXXXX",
    MemberId="m-XXXXXXXXXXXXXXXXXXXXXXXXXX"
    # Status="CREATING"|"AVAILABLE"|"CREATE_FAILED"|"DELETING"|"DELETED"|"FAILED",
    # MaxResults=123,
    # NextToken="string"
  )

  print(nodes["Nodes"])

list_nodes()
```

```console
> python list_nodes.py

[{'Id': 'nd-XXXXXXXXXXXXXXXXXXXXXXXXXX', 'Status': 'AVAILABLE', 'CreationDate': datetime.datetime(2019, 5, 21, 4, 4, 44, 468000, tzinfo=tzutc()), 'AvailabilityZone': 'us-east-1a', 'InstanceType': 'bc.t3.small'}]
```

特にハマることもなくboto3を利用してネットワーク、メンバー、ノードを作成することができました。
これで、AWS CloudFormationのカスタムリソースを利用してリソース管理することができそうです。
ただ、ネットワークとメンバー作成に20分程度かかるので、その待受処理などがAWS CloudFormationで実現できるのか、ちょっと心配です。(未調査)


## 参考

カスタムリソース - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources.html

CloudFormationで提供されていない処理をカスタムリソースで作ってみた。 ｜ DevelopersIO
https://dev.classmethod.jp/cloud/aws/cfn-api-custom/

AWS CloudFormationでCognitoユーザープールをMFAのTOTPを有効にして作成する - Qiita
https://qiita.com/kai_kou/items/f56bb13a5d47ee05d766

boto/boto3: AWS SDK for Python
https://github.com/boto/boto3

ManagedBlockchain — Boto 3 Docs 1.9.152 documentation
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/managedblockchain.html

Amazon Managed BlockchainがリリースされたのでHyperledger Fabricも合わせて情報をまとめてみた - Qiita
https://qiita.com/kai_kou/items/5a968f42c5f96296f6fe

Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた - Qiita
https://qiita.com/kai_kou/items/e02e34dd9abb26219a7e

Pipenvを使ったPython開発まとめ - Qiita
https://qiita.com/y-tsutsu/items/54c10e0b2c6b565c887a


Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた - Qiita
https://qiita.com/kai_kou/items/e02e34dd9abb26219a7e#%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%A8%E3%83%A1%E3%83%B3%E3%83%90%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B