[アップデート]AWS SAM CLIのTerraform統合利用時にモジュール等を別フォルダに配置できるようになりました

2023.09.05

初めに

先日しばらく更新が止まっていたAWS SAM CLIのv1.96.0がリリースされました。今回はTerraform周りの方が触られているような印象があります。

SAMでのTerraform統合のビルドの仕様として実行時のディレクトリではなく一時的に別の場所にファイルを複製しそれを元に処理が行われますが、この複製されるファイル群はsam buildを実行したディレクトリ配下のもののみとなるためそれ以外のパスに参照するファイルがあるとビルドができない問題を抱えていたようです

例えば以下の構成の場合大元のmain.tfdevディレクトリにおいてしまってるのでその外側のmodulesやlambdaで利用するアプリコードがビルド時に参照できないものとなります(ハイライト部分のみが対象となる)。

$ tree sam-app-terraform/
.
├── hello_world                 ## lambdaのアプリコード(terraformの構成とは関係ない)
│   ├── __init__.py
│   └── app.py
└── terraform                   ## ここ配下がterraform関連のファイル
    ├── environments
    │   └── dev
    │       ├── main.tf         ## これが大元のtfファイル  
    │       └── samconfig.toml
    └── modules
        └── hello_world
            └── main.tf

今回のバージョンで追加された--terraform-project-root-pathオプションを適切に指定することで今後はこの構成が採用可能となり、Terraformらしいディレクトリ構成の利用やインフラとアプリコードのディレクトリの分離をすることができます。

何が問題だった

例えば先ほどの構造でdev/main.tfのファイル内容を以下のように記載しsam build --hook-name terraformを実行してビルドを施行するとmoduleで指定されたパスの解決ができずに内部的に実行されるterrafromコマンドの実行が失敗します。

terraform/environments/dev/main.tf

provider "aws" {
  profile = "default"
  region  = "ap-northeast-1"
}

module "hello_world_function" {
  source = "../../modules/hello_world"
}
$ sam build --hook-name terraform
...
│ Error: Unreadable module directory
│
│ Unable to evaluate directory symlink: lstat ../../modules: no such file or
│ directory
╵
...

先ほどの構造を考えると本来は問題なく読み込まれるはずですが、SAMではビルド時の処理として一旦別の場所に処理するファイルをコピーしてそこで必要な処理を行うようになっています。

そのコピーされるファイルについてですがsam buildを実行した際ディレクトリ配下のもののみとなるためdev配下外に設置されるものは対象外となります。

実際に--debugオプションをつけて作業用のファイルのコピーフェーズを確認してみるとdev配下のファイルのみがコピーされておりそれ以外のものは処理されていないことが確認できます。

023-09-05 22:16:34,116 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/terraform.tfstate) to destination                                                                              
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/terraform.tfstate)                                                                                                                                                   
2023-09-05 22:16:34,116 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/main.tf) to destination (/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/main.tf)                 
2023-09-05 22:16:34,117 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/z_samcli_backend_override.tf) to destination                                                                   
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/z_samcli_backend_override.tf)                                                                                                                                        
2023-09-05 22:16:34,118 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/9fda4126-74f0-4015-8432-f62f2c671d41.tfstate) to destination                                                   
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/9fda4126-74f0-4015-8432-f62f2c671d41.tfstate)                                                                                                                        
2023-09-05 22:16:34,118 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform                                                                                                       
2023-09-05 22:16:34,118 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform) to destination                                                                  
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform)                                                                                                                                                          
2023-09-05 22:16:34,119 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/terraform.tfstate) to destination                                                                   
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/terraform.tfstate)                                                                                                                                        
2023-09-05 22:16:34,120 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers                                                                                             
2023-09-05 22:16:34,120 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers) to destination                                                        
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers)                                                                                                                                                
2023-09-05 22:16:34,121 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io                                                                       
2023-09-05 22:16:34,121 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers/registry.terraform.io) to destination                                  
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io)                                                                                                                          
2023-09-05 22:16:34,121 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp                                                             
2023-09-05 22:16:34,122 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers/registry.terraform.io/hashicorp) to destination                        
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp)                                                                                                                
2023-09-05 22:16:34,122 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/null                                                        
2023-09-05 22:16:34,123 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers/registry.terraform.io/hashicorp/null) to destination                   
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/null)                                                                                                           
2023-09-05 22:16:34,123 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/null/3.2.1                                                  
2023-09-05 22:16:34,123 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers/registry.terraform.io/hashicorp/null/3.2.1) to destination             
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/null/3.2.1)                                                                                                     
2023-09-05 22:16:34,124 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/null/3.2.1/darwin_arm64                                     
2023-09-05 22:16:34,124 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers/registry.terraform.io/hashicorp/null/3.2.1/darwin_arm64) to destination
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/null/3.2.1/darwin_arm64)                                                                                        
2023-09-05 22:16:34,126 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers/registry.terraform.io/hashicorp/null/3.2.1/darwin_arm64/terraform-provider-null_v3.2.1_x5)
to destination (/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/null/3.2.1/darwin_arm64/terraform-provider-null_v3.2.1_x5)                                       
2023-09-05 22:16:34,131 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/aws                                                         
2023-09-05 22:16:34,131 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers/registry.terraform.io/hashicorp/aws) to destination                    
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/aws)                                                                                                            
2023-09-05 22:16:34,132 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/aws/5.15.0                                                  
2023-09-05 22:16:34,132 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers/registry.terraform.io/hashicorp/aws/5.15.0) to destination             
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/aws/5.15.0)                                                                                                     
2023-09-05 22:16:34,133 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/aws/5.15.0/darwin_arm64                                     
2023-09-05 22:16:34,133 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers/registry.terraform.io/hashicorp/aws/5.15.0/darwin_arm64) to destination
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/aws/5.15.0/darwin_arm64)                                                                                        
2023-09-05 22:16:34,134 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/providers/registry.terraform.io/hashicorp/aws/5.15.0/darwin_arm64/terraform-provider-aws_v5.15.0_x5)
to destination (/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/providers/registry.terraform.io/hashicorp/aws/5.15.0/darwin_arm64/terraform-provider-aws_v5.15.0_x5)                                       
2023-09-05 22:16:34,235 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/modules                                                                                               
2023-09-05 22:16:34,235 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/modules) to destination                                                          
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/modules)                                                                                                                                                  
2023-09-05 22:16:34,240 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform/modules/modules.json) to destination                                                                
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform/modules/modules.json)                                                                                                                                     
2023-09-05 22:16:34,241 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/samconfig.toml) to destination (/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/samconfig.toml)   
2023-09-05 22:16:34,241 | File (.aws-sam) is in ignored set, skipping it                                                                                                                                                           
2023-09-05 22:16:34,242 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs                                                                                                    
2023-09-05 22:16:34,242 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs) to destination                                                               
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs)                                                                                                                                                       
2023-09-05 22:16:34,243 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs/iacs_metadata                                                                                      
2023-09-05 22:16:34,243 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs/iacs_metadata) to destination                                                 
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs/iacs_metadata)                                                                                                                                         
2023-09-05 22:16:34,244 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs/iacs_metadata/template.json) to destination                                                      
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs/iacs_metadata/template.json)                                                                                                                           
2023-09-05 22:16:34,244 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs/iacs_metadata/Makefile) to destination                                                           
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs/iacs_metadata/Makefile)                                                                                                                                
2023-09-05 22:16:34,245 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs/iacs_metadata/copy_terraform_built_artifacts.py) to destination                                  
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs/iacs_metadata/copy_terraform_built_artifacts.py)                                                                                                       
2023-09-05 22:16:34,245 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs/iacs_metadata/__pycache__                                                                          
2023-09-05 22:16:34,246 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs/iacs_metadata/__pycache__) to destination                                     
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs/iacs_metadata/__pycache__)                                                                                                                             
2023-09-05 22:16:34,246 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs/iacs_metadata/__pycache__/zip.cpython-311.pyc) to destination                                    
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs/iacs_metadata/__pycache__/zip.cpython-311.pyc)                                                                                                         
2023-09-05 22:16:34,248 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs/iacs_metadata/zip.py) to destination                                                             
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs/iacs_metadata/zip.py)                                                                                                                                  
2023-09-05 22:16:34,249 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs/iacs_metadata/z_samcli_backend_override) to destination                                          
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.aws-sam-iacs/iacs_metadata/z_samcli_backend_override)                                                                                                               
2023-09-05 22:16:34,250 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/environments/dev/.terraform.lock.hcl) to destination                                                                            
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/.terraform.lock.hcl)

そのため環境を分けるためには別の分割方法とするか、main.tfが置いてあるディレクトリ配下にシンボリックリンクを貼るなどして利用するファイルをすべて実行ディレクトリ配下に収める構成を検討する必要がありました。

追加されたオプション

今回追加された--terraform-project-root-pathをしていることで処理時の大元のパスを変更できるようになり、先に記載したディレクトリ構成が利用可能となりました。

今回の場合は--terraform-project-root-path ../../../を指定してdev直下でで実行すると、sam-app-terraformを起点としてファイルが作業用ディレクトリにコピーされるようになるのでビルドに成功するようになります。

# dev配下で実行
$ sam build --hook-name terraform --terraform-project-root-path ../../../
...
Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke --hook-name terraform
[*] Emulate local Lambda functions: sam local start-lambda --hook-name terraform
## 必要なファイルがビルドアーティファクトの格納先に設置されている
$ tree .aws-sam
.aws-sam
├── build
│   ├── ModuleHelloWorldFunctionAwsLambdaFunctionHelloWorldCD3E0E1F
│   │   ├── main.tf
│   │   └── src
│   │       ├── __init__.py
│   │       ├── app.py
│   │       └── requirements.txt
│   └── template.yaml

--debugをつけて実行してみると先ほどまでなかったmodules配下のファイルが追加されており、コピー先のパスもdevからの相対パスだったのが(ex. /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpxd74p0v5/main.tf))aws-sam-terraform直下からの相対パスになっています(ex. /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpbz_ghgt3/terraform/modules/hello_world/main.tf)。

...
2023-09-05 23:13:40,081 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/modules/hello_world/main.tf) to destination
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpbz_ghgt3/terraform/modules/hello_world/main.tf)
2023-09-05 23:13:40,082 | Creating target folders at /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpbz_ghgt3/terraform/modules/hello_world/src
2023-09-05 23:13:40,082 | Copying directory metadata from source (/Users/xxxx/sam-app-terraform/terraform/modules/hello_world/src) to destination
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpbz_ghgt3/terraform/modules/hello_world/src)
2023-09-05 23:13:40,083 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/modules/hello_world/src/requirements.txt) to destination
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpbz_ghgt3/terraform/modules/hello_world/src/requirements.txt)
2023-09-05 23:13:40,084 | Copying source file (/Users/xxxx/sam-app-terraform/terraform/modules/hello_world/src/__init__.py) to destination
(/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpbz_ghgt3/terraform/modules/hello_world/src/__init__.py)
...

終わりに

今回のアップデートはterrafomr側の制約が大きく緩和れれたのみだけではなく、インフラとアプリのコードのディレクトリの分離もできるようになったので地味ながら人によっては大きなアップデートではないでしょうか。
(見ようによってはバグと捉える方もいるかもしれませんが取り扱いとしては新機能(type/feat)のようです)。

余談ですが今回実はこの件と関係ないところで結構ハマりましてsam_metadata_xxxを指定しないと関数のビルドがされず、かつ明確な記載はないのですがマネージドランタイム系のものを使っても標準のビルドが走らなず(Pythonの場合はpipが走らなかった、ログ等からおそらく仕様?)、なぜか動かない想定の構造でアプリが動く、動くはずの想定でなぜか動かないを繰り返していました。

ビルドは成功するけどアプリの挙動が何か怪しいなーと思ったらでフィルとでは.aws-sam/build配下がビルドアーティファクト出力先となるのでこちらを確認してみてください。