【Python×AWS】botocore.exceptions.NoCredentialsErrorエラー対応。clientとresourceの違いも

# 概要
* Pythonのライブラリboto3でAWSのSecretManagerを使って実装していたら、下記エラーが表示されたので、その解決方法を記します。

```
botocore.exceptions.NoCredentialsError: Unable to locate credentials
```

# 原因の追求

* 上記エラーの原因は、boto3の認証権限。AWSに繋ぐための認証情報が設定されていないエラーです。
    * boto3とは：PythonからAWSを操作するためのライブラリ
* 認証情報を設定するってどういう意味かというと、boto3に「Python3を動かすためのIAMユーザー」を認識させてあげる必要があります。結論、以下の2つの手順が必要です。
    * ①Python3を動かすためのIAMユーザーを作成（設定）
    * ②boto3が認識する`credentials`に認証情報を記載
        * （他にも方法がありますがわかりやすく解説するため上記のように記載。詳細は後述）

## ①Python3を動かすためのIAMユーザーを作成（設定）
* boto3にIAMユーザーを認識してもらうには、APIキー(アクセスキーとシークレットキー)が必要です。
* すでにIAMユーザーが存在する場合、設定する方法は以下の通りです。
    * ①AWSログイン→IAM→ユーザー→該当のユーザーをクリック→「セキュリティ認証情報」タブをクリック→少し下にいくと「アクセスキー」の欄に「アクセスキーを作成」というボタンがあるのでそれをクリック→作成後、CSVをダウンロード可能。アクセスキーとシークレットアクセスキーはこの時しか取得するチャンスがありませんのでご注意。

## ②boto3が認識する`credentials`に認証情報を記載
* IAMユーザーの設定を行ったら、次は、boto3が認識してくれる場所にアクセスキーとシークレットキーを記載してあげます。その場所が「`\.aws\config`と`\.aws\credentials`」です。
    * 正確には他の方法もあるので後述しますが、一旦後回しにします
* ローカルの`\Users\<ユーザー名>/.aws`に`credentials`ファイルを作成し、そこに以下の情報を記載します。

```text
[default]
aws_access_key_id = XXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXX
```

* `\.aws\config`にも必要な情報があれば記載します。
    * ここに認証情報を記載することもできますが、一般的な使い方ではないようです。

```text
[default]
region = ap-northeast-1
output = json
```

* profileを使って管理することも可能です。
    * 参考：[Boto3の設定あれこれ（profile名、アクセスキー、Config、DEBUGログの設定とか）](https://zenn.dev/sugikeitter/articles/how-to-use-boto3-various-settings)

* ちなみに、上記`credentials`の認証情報が不十分な場合、以下メッセージが表示されます。

```
botocore.exceptions.ConfigParseError: Unable to parse config file:
```


## CLIから実行可能なコマンド"aws configure"
* 上記の①②をより簡単に行う方法として、AWS CLIを利用して`aws configure`コマンドから以下の項目を順番に登録する方法があります。これにより、自動的に`credentials`が作成されます。
     * AWS Access Key ID [None]:<アクセスキー>
     * AWS Secret Access Key [None]:<シークレットキー>
     * Default region name [None]:<リージョン>
     * Default output format [None]:<json/table/textのどれか>
* 参考：[設定ファイルと認証情報ファイルの設定](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-files.html)

## 解決策
* 上記の「①Python3を動かすためのIAMユーザーを作成（設定）」と「②boto3が認識する`credentials`に認証情報を記載」を行った後であれば、エラーは表示されずその後問題なく作業できました。もちろん、AWS　CLIを使っても良いと思います。ただし、`credentials`に記載する以外にも認証情報を取ってくる方法がありますので、以下の補足をご参照ください。 
* 参考記事
    * ["botocore.exceptions.NoCredentialsError: Unable to locate credentials"　認証情報エラーの対処方法](https://koboldtodragon.hatenablog.com/entry/2016/07/12/000000)
    * [boto3でawsアクセス時にconfigとcredentialsを使う](https://hacknote.jp/archives/56506/)
    * [【Python3入門】boto3を使ってPython3とAWSに触れてみよう！【60分】](https://blog.serverworks.co.jp/tech/2019/08/21/post-73231/)
    * [Python boto3 でAWSを自在に操ろう ~入門編~](https://qiita.com/kimihiro_n/items/f3ce86472152b2676004)

## 補足①：重要情報のベタうちは避けるように！
* 以下の書き方はベタうちの事例ですが、githubなどに挙げる場合は、重要な情報であるアクセスキーIDとシークレットアクセスキーが外部に公開されてしまうので、要注意です。
* ベタうちの場合の記載:

```python
import boto3

s3 = boto3.client('s3',
        aws_access_key_id=XXXXXXXXXXXXX,
        aws_secret_access_key=XXXXXXXXXXXXX,
        region_name='ap-northeast-1'
)
```

* ベタうちを避ける場合にも色々方法があります。上記解決策の方法は、`credentials`ファイルに認証情報を記載する方法でしたが、AWS CLIによって環境変数でセットする方法やSTSのAssumeRoleによって一時的な認証情報を取得する方法がありますので、以下記事をご参照ください。

* AWS CLIによって環境変数でセットする場合などの記載:
    * 参考：[AWS SDK for python(Boto3)は認証情報をどこに置くか？](https://zenn.dev/hashito/articles/81a02a803ed7c4)

```python
export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXX
export AWS_DEFAULT_REGION=XXXXXXXXXXXXX
```

* STSのAssumeRoleで一時的な認証情報を取得する場合の記載:
    * 参考：[Boto3の設定あれこれ（profile名、アクセスキー、Config、DEBUGログの設定とか）](https://zenn.dev/sugikeitter/articles/how-to-use-boto3-various-settings)
    * 参考：[AWS SDK For Python (Boto3) で AssumeRole を使ってみた](https://dev.classmethod.jp/articles/aws-sdk-for-python-boto3-assumerole/)

```python
import boto3

    s3 = boto3.client('sts')

    assumed_role_object = s3.assume_role(
        RoleArn=IAM_ROLE_ARN, # IAMロールのARN
        RoleSessionName=IAM_ROLE_SESSION_NAME # 識別できる名前
    )
    credentials = assumed_role_object['Credentials']
    s3_resource = boto3.client(
        's3',
        aws_access_key_id=credentials['AccessKeyId'],
        aws_secret_access_key=credentials['SecretAccessKey'],
        aws_session_token=credentials['SessionToken'],
    )
```

* どの指定方法がどんな優先順位で検索されるのか？については以下記事をご参照ください。
    * [Boto3（AWS SDK for Python）の利用する認証情報](https://qiita.com/tsukamoto/items/00ec8ef7e9a4ce4fb0e9)


## 補足②：clientとresourceの違い
* clientとresourceの違いは、以下記事に詳しく記載があります。ラップする形でもらうのかオブジェクト指向的にもらうのかという違いが主な差らしいです。基本的にresource使ったら良さげという記事がいくつかありましたが、resourceAPIが未対応なサービスもあるのと、2023/1/19をもってリソース・インターフェースが改修凍結されましたので、これからコーディングする場合は、clientを使うことが推奨されています(既存のresourceが動かなくなるわけではないです)。
    * [boto3のclient API とresource APIについて](https://analytics-note.xyz/aws/boto3-client-resource/)
    * [AWS Python SDK(boto3)のリソース・インターフェースが改修凍結されました](https://dev.classmethod.jp/articles/aws-python-sdk-resource-interface-deperecation-warning/)

* 両者の違いについてより詳細に知りたい方は、以下記事にわかりやすく比較されていますので、ぜひ。
    * [AWS SDK for Python (Boto3) の “Client API” と “Resource API” の違いについて調べてみた](https://dev.classmethod.jp/articles/boto3-client-api-and-resource-api/)

>Client API
REST APIと1対1で対応しているため、理論上は「できないことはない。」
結果が基本的に辞書型で得られるので、必要な情報を取り出すのが若干めんどう。

>Resource API
オブジェクト指向で処理を記述できるので、コードの見通しが良くなる。
サービスやリソースの種類によってはResource APIが用意されていないことがある。(その場合はClient APIを使う必要がある)

* 何か気になるところや指摘ありましたら、ぜひコメントいただければ幸いです。