amplifyで発生した Export EXPORT_NAME cannot be updated as it is in use by STACK_NAME の対処

# amplifyで発生した `Export EXPORT_NAME cannot be updated as it is in use by STACK_NAME` の対処

- amplify cli version: 7.6.3
- AWSコンソール操作 : 2022/03/05 時点

amplify pushをしたところ以下のエラーが発生した

```markdown
Export EXPORT_NAME cannot be updated as it is in use by STACK_NAME
```

このメッセージ単体だけで調査したところAWSの以下の記事が引っかかるが、解決の方法の一部でしかなかったので対処方法を残す。

[https://aws.amazon.com/premiumsupport/knowledge-center/cloudformation-stack-export-name-error/?nc1=h_ls](https://aws.amazon.com/premiumsupport/knowledge-center/cloudformation-stack-export-name-error/?nc1=h_ls)

## なぜこのエラーが発生したか？

### 構成していた環境

- DynamoDB(Storage)とLambda(Function)を連携させていた

### 発生した原因

あるときAWSコンソール上で、Streamで無効化してLambdaトリガーをAWSコンソール上から停止させた。(緊急だったのでコンソール上から行った)

その後Streamを有効化してLambdaトリガーを復活させるが、このときテーブルのStreamARNが変更されてしまう

![img1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240634/df356d5e-6659-501d-a773-1513d275dcc1.png)


- ARNが変わったことによりCloudFormationの差異（ドリフト）が発生
- amplify pushした際に、StreamARNが合わずエラーが発生した。
    - amplify pushによりCloudFormationでLambdaとDynamoテーブルのスタックが動く
        - Dynamoテーブルのスタックで実行中スタックを見てみると出力タブにあるStreamARN情報は変わるのだが、Lambdaのスタックが `変更前のStreamARN設定` を読み取ってしまうようで、存在しないARNのためエラーになっている模様
- Lambdaの変更前にテーブルのStreamARN定義が変わっていればエラーが出ないはずだが、テーブルのスタックを更新しようとするとLambdaで呼び出されているため、cannot be updated as it is in use が発生してしまう。
    - このために次の手順が必要だった

## 対処方法

もっとも解決方法に近づいた記事は以下のIssue

[https://github.com/aws-amplify/amplify-cli/issues/4054#issuecomment-747119091](https://github.com/aws-amplify/amplify-cli/issues/4054#issuecomment-747119091)

これでも完全ではないが解決方法のメイン作業になる。

Issueに書いているがAWSコンソールでCloudFormationを触る必要がある

### 対処１．エラーが発生しているLambda関数のトリガー設定からDynamoDBの設定を削除する

- CloudFormationで再度新しいStreamARNで設定されるので、１度削除する必要がある
    
![img2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240634/e383120b-7580-46da-1cdf-acd6c05175b5.png)
    
- 削除しないと対処２で既に存在しているとエラーが出る

```markdown
Resource handler returned message: "The event source arn (" ${テーブルのStreamARN} ") and function (" ${Lambda関数名} ") provided mapping already exists. 
Please update or delete the existing mapping with UUID XXXXXXXXXXXXXXXXXXXXXXXXXX (Service: Lambda, Status Code: 409, Request ID: YYYYYYYYYYYYYYYYYYYYYYYY, Extended Request ID: null)" (RequestToken: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ, HandlerErrorCode: AlreadyExists
```

### 対処２．Lambdaを作るtemplate.jsonでStreamARNを直接指定したtemplate.jsonファイルを作成して実行させる

- LambdaのCloudFormationテンプレートファイル(関数名-template.json)がamplifyプロジェクトに生成されているので、そのファイルをコピーし手動変更用のtemplateファイルを作成する（今回しか使わないので元ファイルとは別のところに置いておく）
    - 例）amplify/backend/function/mylambda/mylambda-cloudformation-template.json

- コピーしたtemplate.jsonファイルでStreamARNの設定を修正する

```markdown
# もともとの設定では以下のようImportValueを使ってDynamoテーブルのスタックで出力された値を参照するようになっている
# GetAtt:${テーブル名}:StreamArnの場所を調べたところ２箇所あった

	 "Action": [
	      "dynamodb:DescribeStream",
	      "dynamodb:GetRecords",
	      "dynamodb:GetShardIterator",
	      "dynamodb:ListStreams"
	    ],
	    "Resource": {
	      "Fn::ImportValue": {
	        "Fn::Sub": "${XXXXXXXXXXXXXXXXXXXXXXX}:GetAtt:${テーブル名}:StreamArn"
	      }
	    }

......

"Properties": {
  "BatchSize": 100,
  "Enabled": true,
  "EventSourceArn": {
    "Fn::ImportValue": {
      "Fn::Sub": "${XXXXXXXXXXXXXXXXXXXXXXX}:GetAtt:${テーブル名}:StreamArn"
    }
  },
```

このファイルのImportValue部分を以下のように変更する

```markdown
	"Action": [
      "dynamodb:DescribeStream",
      "dynamodb:GetRecords",
      "dynamodb:GetShardIterator",
      "dynamodb:ListStreams"
    ],
    "Resource":  "arn:aws:dynamodb:xxxxxx(DynamoDBに設定されているStreamARN)"

......

  "Properties": {
    "BatchSize": 100,
    "Enabled": true,
    "EventSourceArn": "arn:aws:dynamodb:xxxxxx(DynamoDBに設定されているStreamARN)",
```

- Lambdaを作るCloudFormationスタックを探す
- 上記で修正したtemplate.jsonをLambdaのスタック＞更新＞ネストされたスタックを更新＞既存テンプレートを置き換える＞JSONを指定してアップロードして更新を行う。

![img3-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240634/7499dc3b-deb9-4cb7-09c7-31c705f34eb0.png)

### 対処３．DynamoDBテーブルのCloudFormationスタックを再実行する

スタックが古いStreamARNになっているので最新化させる(元々これをやりたかったが対処２を実施したことで参照元がなくなり、更新できるようになった）

- DynamoDBのテーブルを作るCloudFormationスタックを探す
- 詳細の出力タブからStreamARNが古いことを確認する

![img4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240634/317acbf7-d1d7-ab36-25db-fb2d03011b99.png)

- 更新＞ネストされたスタックを更新する＞現在のテンプレートを使用＞スタックの詳細(設定は変更せず)次へ＞スタックオプションの設定でタグを適当に追加して、スタックの更新を行う
    - これはtemplateを再実行するためだけの処置。再実行すると元からあるtemplate.jsonにタグ情報が書いてないので再実行された際に設定したタグは消える
- 更新するとStreamARNの最新値に更新される（出力タブから確認）

これで問題のLambda更新時に存在しないStreamARNを見てしまう問題への対処と、DynamoテーブルのスタックでStreamARNが古い問題が解決できた。

## 最後

改めてamplify pushを実行し、エラーが出なくなることを確認する。


## 感想

参考にしたIssueでもサービス停止してテーブル再作成＆データ移行したとか悲痛な叫びが書いてあった。
テーブル再作成とかに比べれば対処の間データが入ってこないようにすればいいので多少はマシかなと。

自分もエラーメッセージからこの対処方法を見つけるまで時間がかかったので、これが誰かの役に経てば。