AWS GreengrassにComponentをデプロイする #2 (Raspberry PiからS3にファイルを共有する)

#### 前回の記事 

https://qiita.com/Buffalo46/items/5d1e27a08c4bded6e581

## まとめ
- Subscribeした信号をトリガーにAWSサービスにアクセスするコンポーネントの作成及びデプロイした。具体的には、`raspi/request`というトピックに来る信号をトリガーにして、簡単なファイルを作ってS3にアップロードする、というコンポーネント。
- S3にアクセスするところで苦戦。前回のデプロイメントのように、IAM RoleにS3の`PutObject`権限を与えても意味なし。Greengrassと関係ないところで走っているboto3のアクセス権限の問題だから。
- StreamManagerというコンポーネントを入れて解決。

## 次のステップ
- 次はpicameraで写真を撮ってS3にあげる。Raspberry PiのリソースをGreengrassから動かすテスト。

## 難しかったところ：S3へのアクセス

`botocore.exceptions.NoCredentialsError: Unable to locate credentials.`というエラーでうまくいかない。エラーから見る限り、boto3のアクセス権限の問題。

#### そもそもGreengrassと関係ない問題であることを確認する
まずはGreengrassの中のやりとりの問題ではないことを確認。具体的には、Raspberry Piでpythonを立ち上げてboto3を動かしてみる。
以下のコードは、boto3が機能していないのは権限が与えられていないことを確認するためだけ。この方法を使えば、CLIを入れなくても一時的にUser権限を与えられる。これを走らせた後で、一度pythonを閉じて、もう一回開いた後、今度はクレデンシャルを与えずに走らせようとしても、きちんと(?)エラーメッセージを返してくれる。(参考: [How To Specify Credentials When Connecting to AWS S3 Using Boto3?](https://www.stackvidhya.com/specify-credentials-in-boto3/))

```python:boto3のアクセス確認
import boto3
s3_client = boto3.client('s3',
    aws_access_key_id='<YOUR_ACCESS_KEY>',
    aws_secret_access_key='<YOUR_SECRET_ACCESS_KEY>',
    region_name='<YOUR_REGION>'
)
s3_client.put_object(Body='Text Contents', Bucket='<BUCKET_NAME>', Key='test.jpg')
```

#### じゃあ、どうやってS3へのアクセス権限をboto3に与えるか？

調べてみると、どうやら以下の3パターンは選択肢としてありそう。

1. Raspberry PiをIAM Userとして登録してしまう
1. Public Componentとして用意されているStream Managerを使う（参考: [[AWS IoT Greengrass V2] ストリームマネージャーを使用してコンポーネントからKinesis Data Streamsへデータを送ってみました](https://dev.classmethod.jp/articles/greengrass-v2-send-kinesis-by-stream-manager/)）
1. Public Componentとして用意されているToken Exchange Serviceを使う（参考: [[AWS IoT Greengrass V2] トークン交換サービスでコンポーネントからDynamoDBにアクセスしてみました](https://dev.classmethod.jp/articles/greengrass-v2-token-exchange-service/)）

まず#1について。今回の一連の試験は、例えば製造現場のIPCにGreengrassを入れて動かすことを念頭に置いている。その状況を考えると、デバイスの数だけIAM Userを増やしていくのは現実的ではないので却下。
次に#2について。これは、製造現場での使用シナリオを考えると一番理にかなっているように見える。ただ、残念ながら実装方法がよく分からない。。。ということで今のところ却下。
で、#3。簡単だしサービスを走らせるときだけにRoleを一時的に使うというのは、セキュリティの観点からも問題ないのではないかということで、これを採用。

## 実装
今回の肝はレシピでToken Exchange Serviceを使用することを明示すること。具体的には、`ComponentDependencies`の箇所を追加する。

```json:レシピ
{
  "RecipeFormatVersion": "2020-01-25",
  "ComponentName": "<COMPONENT_NAME>",
  "ComponentVersion": "1.0.4",
  "ComponentType": "aws.greengrass.generic",
  "ComponentDependencies": {
    "aws.greengrass.TokenExchangeService": {
      "VersionRequirement": ">=2.0.0 <3.0.0",
      "DependencyType": "HARD"
    }
  },
  "Manifests": [
    {
      "Lifecycle": {
        "Install": "pip3 install --user awsiotsdk boto3",
        "Run": "python3 -u {artifacts:path}/pubsub.py greengrass-component-artifact-918139609371-2022jan14"
      },
      "Artifacts": [
        {
          "Uri": "s3://greengrass-component-artifact-918139609371-2022jan14/artifacts/com.feb10pubsub/1.0.1/pubsub.py",
          "Digest": "NHcqqMdH5+J1vP7XgGD7wYYqU1nyiypLh4ra09uYDuQ=",
          "Algorithm": "SHA-256",
          "Unarchive": "NONE",
          "Permission": {
            "Read": "OWNER",
            "Execute": "NONE"
          }
        }
      ]
    }
  ],
  "Lifecycle": {}
}
```

## 一言
難しそうな方法を提示されると「勉強しなければ」と思う一方で簡単な方法を探してしまう。。。これで良いのか？？

## 参考資料

https://www.stackvidhya.com/specify-credentials-in-boto3/

https://dev.classmethod.jp/articles/greengrass-v2-send-kinesis-by-stream-manager/

https://dev.classmethod.jp/articles/greengrass-v2-token-exchange-service/

https://docs.aws.amazon.com/greengrass/v2/developerguide/interact-with-aws-services.html