├── .gitignore ├── LICENSE ├── README.md ├── step1-inital ├── README.md └── test.tf ├── step2-basic ├── README.md ├── main.tf ├── output.tf ├── terraform.tfvars ├── variables.tf └── versions.tf ├── step3-loop ├── README.md ├── main.tf ├── output.tf ├── terraform.tfvars ├── variables.tf └── versions.tf ├── step4-if ├── README.md ├── main.tf ├── output.tf ├── terraform.tfvars ├── variables.tf └── versions.tf ├── step5-module ├── README.md ├── compute │ ├── main.tf │ └── variables.tf ├── main.tf ├── network │ ├── main.tf │ ├── output.tf │ └── variables.tf ├── output.tf ├── terraform.tfvars ├── variables.tf └── versions.tf ├── step6-tips ├── README.md ├── muti-region │ ├── .terraform.lock.hcl │ ├── main.tf │ └── versions.tf └── remote-backend │ ├── .terraform.lock.hcl │ ├── README.md │ ├── terraform.tfvars │ ├── tfbackend.tf │ ├── variables.tf │ └── versions.tf └── step7-test ├── README.md ├── compute ├── main.tf └── variables.tf ├── main.tf ├── network ├── main.tf ├── output.tf └── variables.tf ├── terraform.tfvars ├── variables.tf └── versions.tf /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | 11 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 12 | # .tfvars files are managed as part of configuration and so should be included in 13 | # version control. 14 | # 15 | # example.tfvars 16 | 17 | # Ignore override files as they are usually used to override resources locally and so 18 | # are not checked in 19 | override.tf 20 | override.tf.json 21 | *_override.tf 22 | *_override.tf.json 23 | 24 | # Include override files you do wish to add to version control using negated pattern 25 | # 26 | # !example_override.tf 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2023 container 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform Practice 2 | 3 | Terraform入門者を対象とした練習問題です。練習問題を通じてTerraformの使い方、書き方を学びます。 4 | 5 | > **注意** 6 | > 書き方を学ぶことに重点を置いています。様々なリソースの記述方法を解説したものではありません。 7 | 8 | **プラクティス**の後に書かれた部分が問題です。問題文を読み自分で考えてコマンドやコードを書いてください。 9 | 10 | Terraform Practiceはいくつかのステップに分けて構成されています。ステップの順番通り進んでいくことを想定していますが各ステップは独立しています。学習したいステップだけ実施しても構いません。各ステップごとにディレクトリを作成し、そのディレクトリ内にファイルを作成してください。ステップ内は続けて実施してください。答えの例はステップ名のディレクトリ配下にあります。 11 | 12 | なお、本プラクティスはAWSを前提にしています。 13 | 14 | 各ステップは以下内容です。 15 | 16 | - ステップ1はTerraformを使う準備、基本的なコマンドの使い方です。 17 | - ステップ2は基礎的なTerraformの使い方・知識です。この内容さえ押さえておけば最低限Terraformを使えるようになります。 18 | - ステップ3~5をTerraformさらに高度に使う方法です。ループや分岐、モジュール化です。 19 | - ステップ6はTipsです。読み飛ばしても構いません。 20 | - ステップ7は最終問題です。プラクティスで学んだことを思い出してコード作成に挑戦します。 21 | 22 | ## 目次 23 | 24 | ### [ステップ1 Terraformを使う](./step1-inital/README.md) 25 | - [実行準備](./step1-inital/README.md#実行準備) 26 | - [Terraform CLIのインストール](./step1-inital/README.md#terraform-cliのインストール) 27 | - [AWSプロファイルの準備](./step1-inital/README.md#awsプロファイルの準備) 28 | - [基礎的なterraformの使い方](./step1-inital/README.md#基礎的なterraformの使い方) 29 | ### [ステップ2 基本的なTerraformの書き方・知識](./step2-basic/README.md) 30 | - [2-1. Terraformの基本的な設定](./step2-basic/README.md#2-1-terraformの基本的な設定) 31 | - [プロバイダーの設定をする](./step2-basic/README.md#プロバイダーの設定をする) 32 | - [Terraformとプロバイダーのバージョン要件を設定する](./step2-basic/README.md#terraformとプロバイダーのバージョン要件を設定する) 33 | - [2-2. コードを書いてリソースをデプロイする](./step2-basic/README.md#2-2-コードを書いてリソースをデプロイする) 34 | - [2-3. リソース間で値を渡す](./step2-basic/README.md#2-3-リソース間で値を渡す) 35 | - [2-4. Valiablesで変数にする](./step2-basic/README.md#2-4-valiablesで変数にする) 36 | - [2-5. default\_tags](./step2-basic/README.md#2-5-default_tags) 37 | - [2-6. デプロイしたリソースの情報を確認にする](./step2-basic/README.md#2-6-デプロイしたリソースの情報を確認にする) 38 | - [2-7. tfstateについて](./step2-basic/README.md#2-7-tfstateについて) 39 | - [2-8. .terraformについて](./step2-basic/README.md#2-8-terraformについて) 40 | - [2-9. コメントアウトでリソースを削除する](./step2-basic/README.md#2-9-コメントアウトでリソースを削除する) 41 | ### [ステップ3 繰り返し処理](./step3-loop/README.md) 42 | - [3-1. countによるリソースの繰り返し](./step3-loop/README.md#3-1-countによるリソースの繰り返し) 43 | - [3-2. for\_eachによるリソースの繰り返し](./step3-loop/README.md#3-2-for_eachによるリソースの繰り返し) 44 | - [3-3. dynamicによるブロックの繰り返し](./step3-loop/README.md#3-3-dynamicによるブロックの繰り返し) 45 | - [3-4. for\_eachで複数の値を指定](./step3-loop/README.md#3-4-for_eachで複数の値を指定) 46 | - [3-5. 繰り返しで作成したリソースのoutput](./step3-loop/README.md#3-5-繰り返しで作成したリソースのoutput) 47 | ### [ステップ4 分岐](./step4-if/README.md) 48 | - [4-1. 三項演算子によるパラメータの分岐](./step4-if/README.md#4-1-三項演算子によるパラメータの分岐) 49 | - [4-2. 三項演算子とループによるリソースの分岐](./step4-if/README.md#4-2-三項演算子とループによるリソースの分岐) 50 | - [ただ分岐させたい場合](./step4-if/README.md#ただ分岐させたい場合) 51 | - [分岐させてループも回したい場合](./step4-if/README.md#分岐させてループも回したい場合) 52 | ### [ステップ5 モジュール化](./step5-module/README.md) 53 | - [5-1. リソース群をモジュールにする](./step5-module/README.md#5-1-リソース群をモジュールにする) 54 | - [5-2. モジュールを使いまわして複数デプロイする](./step5-module/README.md#5-2-モジュールを使いまわして複数デプロイする) 55 | - [5-3. モジュールの値を参照する](./step5-module/README.md#5-3-モジュールの値を参照する) 56 | ### [ステップ6 Tips](./step6-tips/README.md) 57 | ### [ステップ7 総合問題](./step7-test/README.md) -------------------------------------------------------------------------------- /step1-inital/README.md: -------------------------------------------------------------------------------- 1 | - [ステップ1 Terraformを使う](#ステップ1-terraformを使う) 2 | - [実行準備](#実行準備) 3 | - [Terraform CLIのインストール](#terraform-cliのインストール) 4 | - [AWSプロファイルの準備](#awsプロファイルの準備) 5 | - [基礎的なterraformの使い方](#基礎的なterraformの使い方) 6 | - [コードの準備](#コードの準備) 7 | - [Terraformでリソースをデプロイ](#terraformでリソースをデプロイ) 8 | - [Terraformでデプロイしたリソースを削除](#terraformでデプロイしたリソースを削除) 9 | 10 | # ステップ1 Terraformを使う 11 | 12 | まずはTerraformを使って簡単なリソースをデプロイしてみましょう。Terraform CLIを端末にインストールし、サンプルのコードを使ってリソースをデプロイしてみます。 13 | 14 | ## 実行準備 15 | 16 | Terraformを使えるように準備します。前提として作業端末はインターネットに繋がっている必要があります。(HashicorpおよびAWSへアクセスします。) 17 | 18 | ### Terraform CLIのインストール 19 | 20 | Terraformコマンドはシングルバイナリで実行できます。[こちらの公式ページ](https://developer.hashicorp.com/terraform/downloads)にアクセスし、端末にあった方法でインストールしてください。 21 | 22 | たとえば、v1.3.7(2023/1時点の最新)をUbuntuにインストールする場合は以下のようにします。 23 | 24 | ``` sh 25 | $ wget https://releases.hashicorp.com/terraform/1.3.7/terraform_1.3.7_linux_amd64.zip 26 | $ unzip terraform_1.3.7_linux_amd64.zip 27 | $ chmod +x terraform 28 | $ mv terraform /usr/local/bin/ 29 | ``` 30 | 31 | Terraform CLIをインストールしたら以下でコマンドが使用できることを確認します。 32 | 33 | ``` sh 34 | $ terraform version 35 | ``` 36 | 37 | ### AWSプロファイルの準備 38 | 39 | 本プラクティスではAWSを使用します。AWS CLIの設定をしておいてください。使用するIAMユーザーにはAdministratorAccess等の強い権限を与えておくと楽ですが、組織のポリシーに従い権限を付与してください。 40 | 41 | AWS CLIのインストールおよび設定は公式のドキュメントを参照してください。 42 | 43 | - [Installing or updating the latest version of the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 44 | - [Configuration basics](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html) 45 | 46 | ## 基礎的なterraformの使い方 47 | 48 | ### コードの準備 49 | 50 | 以下コマンドで作業用に`step1`ディレクトリを作成し、そのディレクトリ下に`test.tf`を作成します。このコードは東京リージョンに10.1.0.0/16のCIDRブロックを持つtf-testという名前のVPCを作成します。(リージョンは変えても構いません) 51 | 52 | ``` sh 53 | $ mkdir step1 54 | $ cd step1 55 | $ cat < test.tf 56 | terraform { 57 | required_providers { 58 | aws = { 59 | version = ">= 4.49.0" 60 | } 61 | } 62 | } 63 | 64 | provider "aws" { 65 | region = "ap-northeast-1" 66 | } 67 | 68 | resource "aws_vpc" "tf_test" { 69 | cidr_block = "10.1.0.0/16" 70 | 71 | tags = { 72 | Name = "tf-test" 73 | } 74 | } 75 | EOF 76 | ``` 77 | 78 | ### Terraformでリソースをデプロイ 79 | 80 | Terraformを使ってリソースをデプロイするには`init`、`plan`、`apply`を行います。 81 | 82 | まずは`terraform init`コマンドでTerraformを実行するディレクトリの初期化を行います。`Terraform has been successfully initialized!`が出れば成功です。initは実行ディレクトリで1回行えばあとは実行しなくてもよいです。(プロバイダーやモジュールを変更した場合はinitし直しが必要です。) 83 | 84 | ``` sh 85 | $ terraform init 86 | ... 87 | Terraform has been successfully initialized! 88 | ... 89 | ``` 90 | 91 | 続いて`terraform plan`でコードによりデプロイされるリソースの内容を確認します。planは省略可能ですがplanで確認するクセをつけた方がいいです。 92 | 93 | ``` sh 94 | $ terraform plan 95 | ... 96 | ``` 97 | 98 | 最後に`terraform apply`をするとリソースがデプロイされます。applyするとplanの結果も表示されます。確認メッセージで`yes`を入力するとデプロイされます。問題を見つけた場合は`no`など入力すればいいです。最終的に`Apply complete!`のメッセージが出ればデプロイ完了です。 99 | 100 | ``` sh 101 | $ terraform apply 102 | ... 103 | Do you want to perform these actions? 104 | Terraform will perform the actions described above. 105 | Only 'yes' will be accepted to approve. 106 | 107 | Enter a value: # yesを入力 108 | ... 109 | Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 110 | ``` 111 | 112 | マネージメントコンソールや以下コマンドでTerraformコードのリソースがデプロイされていることを確認できます。 113 | 114 | ``` sh 115 | $ aws ec2 describe-vpcs --filters "Name=tag-value,Values=tf-test" 116 | ``` 117 | 118 | ### Terraformでデプロイしたリソースを削除 119 | 120 | `terraform destroy`でデプロイしたリソースを削除できます。destroyを実行するとapplyと同様に最終確認メッセージの入力が求められます。`yes`を入力するとリソースを削除します。destroyは危険なコマンドなため注意して扱いましょう。`Destroy complete!`のメッセージが出れば削除完了です。 121 | 122 | ``` sh 123 | $ pwd # step1ディレクトリにいることを確認 124 | $ terraform destroy 125 | ... 126 | Do you really want to destroy all resources? 127 | Terraform will destroy all your managed infrastructure, as shown above. 128 | There is no undo. Only 'yes' will be accepted to confirm. 129 | 130 | Enter a value: # yesを入力 131 | ... 132 | Destroy complete! Resources: 1 destroyed. 133 | ``` 134 | 135 | applyの時と同様、マネージメントコンソールやAWSコマンドでTerraformコードのリソースが削除されていることを確認できます。 136 | -------------------------------------------------------------------------------- /step1-inital/test.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | version = ">= 4.49.0" 5 | } 6 | } 7 | } 8 | 9 | provider "aws" { 10 | region = "ap-south-1" 11 | } 12 | 13 | resource "aws_vpc" "tf_test" { 14 | cidr_block = "10.1.0.0/16" 15 | 16 | tags = { 17 | Name = "tf-test" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /step2-basic/README.md: -------------------------------------------------------------------------------- 1 | - [ステップ2 基本的なTerraformの書き方・知識](#ステップ2基本的なterraformの書き方知識) 2 | - [2-1. Terraformの基本的な設定](#2-1-terraformの基本的な設定) 3 | - [プロバイダーの設定をする](#プロバイダーの設定をする) 4 | - [Terraformとプロバイダーのバージョン要件を設定する](#terraformとプロバイダーのバージョン要件を設定する) 5 | - [2-2. コードを書いてリソースをデプロイする](#2-2-コードを書いてリソースをデプロイする) 6 | - [2-3. リソース間で値を渡す](#2-3-リソース間で値を渡す) 7 | - [2-4. Valiablesで変数にする](#2-4-valiablesで変数にする) 8 | - [2-5. default\_tags](#2-5-default_tags) 9 | - [2-6. デプロイしたリソースの情報を確認にする](#2-6-デプロイしたリソースの情報を確認にする) 10 | - [2-7. tfstateについて](#2-7-tfstateについて) 11 | - [2-8. .terraformについて](#2-8-terraformについて) 12 | - [2-9. コメントアウトでリソースを削除する](#2-9-コメントアウトでリソースを削除する) 13 | - [後片付け](#後片付け) 14 | 15 | 16 | # ステップ2 基本的なTerraformの書き方・知識 17 | 18 | Terraformを使う上で最低限知っていた方がいいと思うことを記載しています。 19 | 20 | `step2`ディレクトリを作成、移動して実施ください。以降のプラクティスはすべて`step2`ディレクトリ内で行う想定です。ファイルはステップ内で同じものを続けて使ったください。 21 | 22 | ## 2-1. Terraformの基本的な設定 23 | 24 | ### プロバイダーの設定をする 25 | 26 | Terraformはプロバイダーを使ってリソースのデプロイをします。対応したプロバイダーは[こちら](https://registry.terraform.io/browse/providers)にあります。AWSプロバイダーの場合はリソースをデプロイするリージョンを設定できます。設定は[providerブロック](https://developer.hashicorp.com/terraform/language/providers/configuration)に書きます。providerブロックもどこに書いても良いですが、`versions.tf`ファイルに書くのがよいでしょう。 27 | 28 | **プラクティス** 29 | 30 | - [Provider Configuration](https://developer.hashicorp.com/terraform/language/providers/configuration)を参考に以下内容を`versions.tf`に追加してください。 31 | - `aws`プロバイダーの設定ブロックで東京リージョン(ap-northeast-1)を指定します(任意の別リージョンでも良いです) 32 | 33 | > ヒント: ステップ1の設定をまねてください。 34 | 35 | ### Terraformとプロバイダーのバージョン要件を設定する 36 | 37 | Terraform自体とクラウドプロバイダーのバージョンに要件を設定できます。これは[terraformブロック](https://developer.hashicorp.com/terraform/language/settings)で設定します。例えばTerraformとAWSプロバイダーのバージョンを2023/1時点の最新であるTerraform:1.3.7、AWSプロバイダー:4.49.0以上といった指定ができます。 38 | 39 | Terraformおよびクラウドプロバイダーは活発に開発が行われており、バージョン違いで意図した動作をしないことが懸念されます。そのため、terraformブロックで使用するバージョン要件を設定した方がいいです。 40 | 41 | terraformブロックはどのファイルに書いてもいいですが`versions.tf`ファイルに書くのがよいでしょう。 42 | 43 | **プラクティス** 44 | 45 | - [Terraform Settings](https://developer.hashicorp.com/terraform/language/settings)を参考に以下内容の`versions.tf`を作成してください。 46 | - AWSプロバイダーのバージョンを`4.49.0`以上を要件に指定します。 47 | 48 | > ヒント: ステップ1の設定をまねてください。 49 | 50 | ## 2-2. コードを書いてリソースをデプロイする 51 | 52 | Terraformはクラウドプロバイダーを使用してリソースをデプロイします。たとえばAWSの場合、[AWSプロバイダー](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)にTerraformでデプロイできるリソースが載っています。デプロイしたいリソースを[resourceブロック](https://developer.hashicorp.com/terraform/language/resources/syntax)に記述してデプロイします。各リソースには設定可能なパラメーターが数多く用意されています。リソースごとに必須のパラメーターとオプションのパラメーターがあります。指定しなかったオプションパラメーターはデフォルト値でデプロイされます。すべてのパラメーターを記述してもいいですが大変です。明示的にデフォルトから変えたいパラメーターのみ書くのが良いと思います。本プラクティスでは明示的に指定しているパラメーター以外は指定なし(デフォルト値)で良いです。 53 | 54 | **プラクティス** 55 | 56 | - [Resource: aws_vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc)を参考に以下内容の`main.tf`ファイルを作成します。 57 | - リソースタイプ:aws_vpc リソース名:tf_test 58 | - CIDRブロック:10.1.0.0/16 59 | - Name:tf-test、Env:terraform-practice、Owner:自分の名前 のタグを設定 60 | - initしてplanで内容を確認しapplyします。 61 | - マネージメントコンソールまたは以下コマンドでリソースが作成されたことを確認します。 62 | 63 | ``` sh 64 | $ aws ec2 describe-vpcs --filters "Name=tag-value,Values=tf-test" 65 | ``` 66 | 67 | ## 2-3. リソース間で値を渡す 68 | 69 | つづいて作成したVPCにサブネットを追加します。サブネットは[Resource: aws_subnet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet)で宣言します。サブネットを作成する際、[vpc_id](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet#vpc_id)の指定が必須です。VPCのIDを自分で確認して入力するのは手間です。Terraformでは作成したリソースの情報(属性)を別リソースから参照できます。VPCの場合、[Resource: aws_vpc の Attributes Reference](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc#attributes-reference)に書かれている属性を別リソースから参照できます。別リソースからは`リソースタイプ.リソース名.属性名`で参照できます。 70 | 71 | **プラクティス** 72 | 73 | - [Resource: aws_subnet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet)を参考に以下内容を`main.tf`に追加します。 74 | - リソースタイプ:aws_subnet リソース名:tf_test 75 | - vpc_id:aws_vpc.tf_testのIDを参照 76 | - CIDRブロック:10.1.10.0/24 77 | - Name:tf-test、Env:terraform-practice、Owner:自分の名前 のタグを設定 78 | - planしてapplyします。 79 | - マネージメントコンソールまたは以下コマンドでリソースが作成されたことを確認します。 80 | 81 | ``` sh 82 | $ aws ec2 describe-subnets --filters "Name=tag-value,Values=tf-test" 83 | ``` 84 | 85 | ## 2-4. Valiablesで変数にする 86 | 87 | 値を変数にして外だしすることもできます。後のStepであつかうモジュール化をする場合によく使います。変数化したい値ごとに[valiablesブロック](https://developer.hashicorp.com/terraform/language/values/variables)で記述します。valiablesブロックもどこに書いてもいいですが、`valiables.tf`ファイルにまとめて書くのがよいでしょう。valiablesブロックで宣言した変数はリソース内で`var.名前`と指定すれば参照できます。文字列の中に変数を埋め込みたい場合は`${var.名前}`と書くと文字列に変数を入れられます。valiabelsブロックには[type](https://developer.hashicorp.com/terraform/language/values/variables#type-constraints)が指定でき値の制限ができます。基本的にtypeは指定するようにしましょう。また、[description](https://developer.hashicorp.com/terraform/language/values/variables#input-variable-documentation)で値の説明を書けます。基本的にdescriptionも記述するようにしましょう。 88 | 89 | variablesブロックで宣言した変数に値を設定するには[いくつかの方法](https://developer.hashicorp.com/terraform/language/values/variables#assigning-values-to-root-module-variables)があります。`terraform.tfvars`ファイルに`変数名=値`の形式で指定することが多いと思います。 90 | 91 | **プラクティス** 92 | 93 | - `variables.tf`ファイルを作成し以下内容のvariablesブロックを記述します。 94 | - 変数名: subnet_cidr 95 | - タイプ: 文字列 96 | - 説明: サブネットのCIDRです 97 | - `terraform.tfvars`ファイルを作成し、subnet_cidrに`10.1.10.0/24`を設定します。 98 | - `main.tf`のaws_subnet.tf_testのcidr_blockを変数`subnet_cidr`から読みこむようにします。 99 | - planします。`No changes.`となり変更がないことを確認します。 100 | - `terraform.tfvars`ファイルのsubnet_cidrに`10.1.20.0/24`に変更します。 101 | - planしてサブネットが`replaced`されることを確認し、applyします。 102 | 103 | ## 2-5. default_tags 104 | 105 | 作成したVPCやサブネットにはEnvやOwnerなど、同じタグが設定されています。こういったすべてのリソースに設定したいタグは[default_tags](https://www.hashicorp.com/blog/default-tags-in-the-terraform-aws-provider)で設定すると楽です。default_tagsはproviderブロックで設定します。 106 | 107 | **プラクティス** 108 | 109 | - `varsions.tf`内のAWSプロバイダー設定にて、Env:terraform-practice、Owner:自分の名前 のデフォルトタグを設定します 110 | - `main.tf`のvpcおよびsubentからEnvとOwnerのタグを消します 111 | - planします。このとき、tagsの部分のみに差分が出ることを確認します 112 | - 確認後applyします 113 | - マネージメントコンソールまたは以下コマンドで"2-3. リソース間で値を渡す"で表示されたリソースがそのまま表示されること確認します。 114 | 115 | ``` sh 116 | $ aws ec2 describe-subnets --filters "Name=tag-value,Values=tf-test" 117 | ``` 118 | 119 | ## 2-6. デプロイしたリソースの情報を確認にする 120 | 121 | Terraformで作成したリソースの情報(ARNやIDなど)を確認するには[outputブロック](https://developer.hashicorp.com/terraform/language/values/outputs)に記述します。outputブロックもどこに書いてもいいですが、`ouput.tf`ファイルにまとめて書くのがいいでしょう。outputする値の指定はリソースの属性参照と同じく`リソースタイプ.リソース名.属性名`です。また、属性名を省略し`リソースタイプ.リソース名`と指定すると、そのリソースのすべての属性を表示できます。 122 | 123 | **プラクティス** 124 | 125 | - `output.tf`ファイルを作成し以下内容のoutputブロックを記述します。 126 | - vpc_id: vpcのid 127 | - vpc_arn: vpcのarn 128 | - subnet: subnetのすべて属性 129 | - planしてapplyします。applyしたあとoutputが表示されます。 130 | - `terraform output`するとoutputした値を確認できます。 131 | 132 | ## 2-7. tfstateについて 133 | 134 | Terraformを実行したディレクトリを見ると`terraform.tfstate`と`terraform.tfstate.backup`というファイルがあります。これらのファイルはTerraformがデプロイしたリソースを記録するものです。中身を確認するとJSON形式でデプロイしたリソースの情報が書かれています。このファイルはとても大事です。**絶対に消さないようにしましょう。**もし消してしまった場合、今までデプロイしたリソースはTerraformの管理外となってしまいます。 135 | 136 | **プラクティス** 137 | 138 | 試しに`terraform.tfstate`を消してみます。この操作は学習目的のものです。実運用では絶対にしないでください。 139 | 140 | - `terraform.tfstate`を削除します。 141 | - `terraform.tfstate.backup`を別ディレクトリ等に退避します。 142 | - planします。VPCおよびサブネットが`作成`されることを確認します。 143 | 144 | planすると作成になりました。つまり、Terraform的には今まで作成したVPCやサブネットはなかったことになっています。このままapplyします。 145 | 146 | - applyします。 147 | - マネージメントコンソールまたは以下コマンドでVPCおよびサブネットを確認します。それぞれ2つずつ表示されます。 148 | 149 | ``` sh 150 | $ aws ec2 describe-vpcs --filters "Name=tag-value,Values=tf-test" 151 | $ aws ec2 describe-subnets --filters "Name=tag-value,Values=tf-test" 152 | ``` 153 | 154 | VPCはCIDRが同じでもデプロイできるためVPCおよびサブネットがもう1セット作成されました。このように、tfstateファイルがなくなると以前のリソースをTerraformで管理できなくなります。そのため、tfstateファイルは絶対なくさないようにします。 155 | 156 | また、複数人でTerraformを使って環境を管理する場合、このtfstateファイルを共有した方がよいです。その場合、tfstateファイルを外部のバックエンドに保存して共有します。バックエンドにはS3が使えるため、S3に保管しつつバージョニングを有効にしてtfstateファイルを保護します。さらにtfstateが同時に更新されることを防ぐために排他制御もした方がいいです。これらについては別のステップにてあつかいます。 157 | 158 | - `terraform destroy`で新しいリソースを削除します。 159 | - 退避させた`terraform.tfstate.backup`を`terraform.tfstate`として戻します。 160 | 161 | ## 2-8. .terraformについて 162 | 163 | Terraformを実行したディレクトリを見ると隠しディレクトリとして`.terraform`が作成されています。これはterraform initした時に作成されたもので、この中にはプロバイダーを管理するための情報やtfstateのバックエンド情報が格納されています。このディレクトリはそれなりに容量があります。TerraformをGitで共有する場合、この.terraformディレクトリをGitに含めないように`.gitignore`を設定した方がいいです。GitHubやGitLabには.gitignoreのテンプレートでterraofromがあり、そのテンプレートを使えば.terraformが除外されるように設定さますので活用しましょう。 164 | 165 | **プラクティス** 166 | 167 | - ディレクトリ内に`.terraform`があることを確認します。 168 | 169 | ## 2-9. コメントアウトでリソースを削除する 170 | 171 | Terraformで作成したリソースはdestroy以外にも削除する方法があります。リソースをコメントアウトして再度applyするとコメントアウトしたリソースを消すことができます。これだとコメントアウトしたリソースだけ削除できます。destroyだと誤ってすべてのリソースを削除してしまうかもしれないため、複数のリソースが含まれる場合はこの方法の方がいいかもしれません。コメントアウトの仕方はこちらの[Comments](https://developer.hashicorp.com/terraform/language/syntax/configuration#comments)にあります。 172 | 173 | **プラクティス** 174 | 175 | - `main.tf`内のvpcおよびsubnetをコメントアウトします。 176 | - `output.tf`内のvpcおよびsubnetの出力する部分をコメントアウトします。 177 | - planしてapplyします。リソースが削除されます。 178 | 179 | > 今回はvpcとsubentを同時に削除しましたが、subnet部分だけコメントアウトしてapplyするとsubnetだけ削除できます。 180 | 181 | # 後片付け 182 | 183 | - destroyします。またはmain.tfをすべてコメントアウトしてapplyします。 -------------------------------------------------------------------------------- /step2-basic/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "tf_test" { 2 | cidr_block = "10.1.0.0/16" 3 | 4 | tags = { 5 | Name = "tf-test" 6 | } 7 | } 8 | 9 | resource "aws_subnet" "tf_test" { 10 | vpc_id = aws_vpc.tf_test.id 11 | cidr_block = var.subnet_cidr 12 | 13 | tags = { 14 | Name = "tf-test" 15 | } 16 | } -------------------------------------------------------------------------------- /step2-basic/output.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | value = aws_vpc.tf_test.id 3 | } 4 | 5 | output "vpc_arn" { 6 | value = aws_vpc.tf_test.arn 7 | } 8 | 9 | output "subnet" { 10 | value = aws_subnet.tf_test 11 | } 12 | -------------------------------------------------------------------------------- /step2-basic/terraform.tfvars: -------------------------------------------------------------------------------- 1 | subnet_cidr = "10.1.20.0/24" -------------------------------------------------------------------------------- /step2-basic/variables.tf: -------------------------------------------------------------------------------- 1 | variable "subnet_cidr" { 2 | type = string 3 | description = "サブネットのCIDRです" 4 | } -------------------------------------------------------------------------------- /step2-basic/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | version = ">= 4.49.0" 5 | } 6 | } 7 | } 8 | 9 | provider "aws" { 10 | region = "ap-south-1" 11 | default_tags { 12 | tags = { 13 | Env = "terraform-practice" 14 | Owner = "mori" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /step3-loop/README.md: -------------------------------------------------------------------------------- 1 | - [ステップ3 繰り返し処理](#ステップ3-繰り返し処理) 2 | - [3-1. countによるリソースの繰り返し](#3-1-countによるリソースの繰り返し) 3 | - [3-2. for\_eachによるリソースの繰り返し](#3-2-for_eachによるリソースの繰り返し) 4 | - [3-3. dynamicによるブロックの繰り返し](#3-3-dynamicによるブロックの繰り返し) 5 | - [3-4. for\_eachで複数の値を指定](#3-4-for_eachで複数の値を指定) 6 | - [3-5. 繰り返しで作成したリソースのoutput](#3-5-繰り返しで作成したリソースのoutput) 7 | - [後片付け](#後片付け) 8 | 9 | 10 | # ステップ3 繰り返し処理 11 | 12 | Terraformは基本的に1つのリソースブロックにつき1つのリソースがデプロイされます。しかし、同じ設定のリソースを複数作りたいこともあります。そのような時、ループを使えば記述量を減らせます。Terraformにはいくつかループの書き方があります。 13 | 14 | `step3`ディレクトリを作成、移動して実施ください。以降のプラクティスはすべて`step3`ディレクトリ内で行う想定です。`versions.tf`を作成しておいてください。必要に応じて`main.tf`、`varibales.tf`、`output.tf`、`terraform.tfvars`も作成してください。すべてのリソースにはStep=step3のタグを設定します。ファイルはステップ内で同じものを続けて使ったください。 15 | 16 | ## 3-1. countによるリソースの繰り返し 17 | 18 | countは指定した回数だけ繰り返します。使い方は[count](https://developer.hashicorp.com/terraform/language/meta-arguments/count)を参照ください。 19 | 20 | **プラクティス** 21 | 22 | - 以下の変数を設定します。 23 | - vpc-cidr: "10.1.0.0/16" 24 | - subnet-cidrs: ["10.1.10.0/24","10.1.20.0/24"] (リスト型です) 25 | - [vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc)を作成します。 26 | - cidrは変数vpc-cidrで指定します。 27 | - [subnet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet)を作成します。 28 | - 作成するsubnetの数はリスト変数subnet-cirdsの長さから取得します。リストの長さは[length](https://developer.hashicorp.com/terraform/language/functions/length)関数を使います。 29 | - subnetは上記作成したvpcに作ります。 30 | - 各subnetのcidrは変数subnet-cidrsから取得します。 31 | - initしてplanで内容を確認しapplyします。 32 | - マネージメントコンソールまたは以下コマンドでリソースが作成されたことを確認します。 33 | 34 | ``` sh 35 | $ aws ec2 describe-subnets --filters "Name=tag-value,Values=step3" 36 | ``` 37 | 38 | countの場合、planの結果でもわかる通り各リソースは`リソースタイプ.リソース名[インデック番号]`で作成されます。インデックス番号は0からの連番です。countで注意するのはインデックス番号が変わるとリソースも作り直しになる点です。たとえば、subnet-cidrsを["10.1.20.0/24"]だけにしてplanしてみてください。planしたら値を戻してください。感覚的にはインデックス0のsubnet(10.1.10.0/24)だけ削除してほしいところです。しかし、実際にはインデックス0のリソースが`replaced`になり、インデックス1のリソースが`destroyed`になります。これをapplyするとインデックス0、1のリソースを一度削除し、インデックス0を新しいcidr(10.1.20.0/24)で作り直します。VPCやサブネットなどは一度作ってから途中で消すことはあまりないかもしれませんが、作成するリソースの個数が変わるようなリソースの場合countはやめた方がいいです。なので、ループを書く時は次に解説する`for_each`を使うようにしましょう。 39 | 40 | > planした後subnet-cidrsの値を["10.1.10.0/24","10.1.20.0/24"]に戻すのを忘れないでください。 41 | 42 | ## 3-2. for_eachによるリソースの繰り返し 43 | 44 | for_eachもループですがcountとは違い回数指定ではなく、[map/object型](https://developer.hashicorp.com/terraform/language/expressions/types#maps-objects)を指定します。使い方は[for_each](https://developer.hashicorp.com/terraform/language/meta-arguments/for_each)を参照ください。 45 | 46 | **プラクティス** 47 | 48 | - countで書いたリソースブロックをコメントアウトします。 49 | - planしてapplyしてsubnetを削除します。(これをしないと同じCIDRでサブネットを作成しようとしてエラーになる) 50 | - countで書いたループをfor_eachに書き直します。 51 | - リスト型をmap型にするには[toset](https://developer.hashicorp.com/terraform/language/functions/toset)関数を使います。tosetで変換するとmapのkeyにリストの値が設定されます。 52 | - planしてapplyします。 53 | - マネージメントコンソールまたは以下コマンドでリソースが作成されたことを確認します。 54 | 55 | ``` sh 56 | $ aws ec2 describe-subnets --filters "Name=tag-value,Values=step3" 57 | ``` 58 | 59 | for_eachの場合、planの結果でもわかる通り各リソースは`リソースタイプ.リソース名[キー名]`で作成されます。countで試したように、subnet-cidrsを"10.1.20.0/24"だけにしてplanしてみてください。planしたら値を戻してください。今度は10.1.10.0/24のサブネットのみ`destroyed`されることが確認できます。このようにfor_eachの場合は順番が変わっても作り直しにならないです。なのでループを書く時は極力`for_each`を使うようにしましょう。 60 | 61 | > planした後subnet-cidrsの値を"10.1.10.0/24","10.1.20.0/24"に戻すのを忘れないでください。 62 | 63 | ## 3-3. dynamicによるブロックの繰り返し 64 | 65 | まずはSecurityGroupのリソースを作ります。 66 | 67 | **プラクティス1** 68 | 69 | - [SecurityGroup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group)を作成します。 70 | - 名前と説明は適当につけてください。 71 | - vpcは上記作成したvpcを指定してください。 72 | - ingressブロックで10.1.10.10/32からのtcp:443を許可するルールを追加してください 73 | - さらに別のingressブロックを追加し10.1.10.11/32からのtcp:443を許可するルールを追加してください 74 | - 作成するSecurityGroupのidを表示するようにしてください。 75 | - planしてapplyします。 76 | - マネージメントコンソールまたは以下コマンドでリソース・ルールが作成されたことを確認します。 77 | 78 | ``` sh 79 | aws ec2 describe-security-group-rules --filter Name="group-id",Values="" 80 | ``` 81 | 82 | さて、上記のSGにさらに3つのingressルールを追加したいとします。3つならingressブロックを手で追加して書けなくもないですが面倒です。そこで[dynamic](https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks)を使います。dynamicを使うとリソール内のブロックに対して繰り返しが使えます。書き方はfor_eachに似ています。 83 | 84 | **プラクティス2** 85 | 86 | - 以下の変数を追加で設定します。 87 | - sg-allow-cidrs: ["10.1.10.10/32","10.1.10.11/32","10.1.10.12/32","10.1.10.13/32","10.1.10.14/32"] 88 | - プラクティス1で作成したSecurityGroupを改良します。 89 | - ingressをsg-allow-cidrsでループさせます。 (contentを忘れないように) 90 | - 許可するcidrはsg-allow-cidrsを設定します。 91 | - planすると"10.1.10.12/32","10.1.10.13/32","10.1.10.14/32"が追加されることをが確認できます。 92 | - applyします。 93 | - マネージメントコンソールまたは以下コマンドでリソース・ルールが作成されたことを確認します。 94 | 95 | ``` sh 96 | aws ec2 describe-security-group-rules --filter Name="group-id",Values="" 97 | ``` 98 | 99 | dynamicは数が多いときにも使えますが、数を動的に変えたときにも使えます。たとえば今回のプラクティスで扱ったインバウンドルールなどは後から追加・削除が起こり得る設定なためdynamicと相性がいいです。 100 | 101 | ## 3-4. for_eachで複数の値を指定 102 | 103 | for_eachはmap/object型をループさせます。そのため、複数の値をセットでループできます。 104 | 105 | **プラクティス** 106 | 107 | - 以下の変数を追加で設定します。 108 | - sg-allow-cidrsを削除します。 109 | - sg-ingress-rulusを以下のように設定します。 110 | ``` 111 | sg-ingress-rulus = { 112 | "10.1.10.10/32" = { 113 | protocol = "TCP" 114 | } 115 | "10.1.10.11/32" = { 116 | protocol = "TCP" 117 | } 118 | "10.1.10.12/32" = { 119 | protocol = "UDP" 120 | } 121 | "10.1.10.13/32" = { 122 | protocol = "UDP" 123 | } 124 | "10.1.10.14/32" = { 125 | protocol = "UDP" 126 | } 127 | } 128 | ``` 129 | - 上記のtypeは以下の通りです。 130 | ``` 131 | type = map(object({ 132 | protocol = string 133 | })) 134 | ``` 135 | - SecurityGroupを改良します。 136 | - ingressをsg-ingress-rulusでループさせます。(mapの変数を指定する場合はtosetで変換不要です) 137 | - 許可するプロトコルはループのvalue.protocolを設定します。 138 | - 許可するcidrはループのkeyを設定します。 139 | - planすると"10.1.10.12/32","10.1.10.13/32","10.1.10.14/32"のルールが再作成されることを確認できます。 140 | - applyします。 141 | - マネージメントコンソールまたは以下コマンドでリソース・ルールが作成されたことを確認します。 142 | 143 | ``` sh 144 | aws ec2 describe-security-group-rules --filter Name="group-id",Values="" 145 | ``` 146 | 147 | 今回はkeyをcidr、valueをprotocolにしましたが、keyを適当な文字列にし、cidrをvalueに移してもいいです。また、valueにdescriptionなどを追加するのもいいです。 148 | 149 | ## 3-5. 繰り返しで作成したリソースのoutput 150 | 151 | まずはfor_eachで作成したsubnetをoutputします。 152 | 153 | **プラクティス1** 154 | 155 | - subnetのリソース情報をすべて表示します。リソースの情報をすべて表示するには`value = <リソースタイプ>.<リソース名>`です。 156 | - planしてapplyします。 157 | 158 | 以下のようなoutputが得られたはずです。さまざまな情報の中に各サブネットのIDが含まれています。 159 | 160 | ``` 161 | subnets = { 162 | "10.1.10.0/24" = { 163 | ... 164 | "id" = "subnet-031c458bb2c6ed4d3" 165 | ... 166 | } 167 | "10.1.20.0/24" = { 168 | ... 169 | "id" = "subnet-00d3d5e31362ff5f7" 170 | ... 171 | } 172 | ``` 173 | 174 | さて、リソースすべての情報を表示してもいいですが必要な情報のみ、今回だとサブネットのIDだけを表示したいとします。for_eachで作成したリソースのoutputはobject型のため、たとえば`aws_subnet.リソース名[*].id`と指定してもエラーになります。この場合、[for](https://developer.hashicorp.com/terraform/language/expressions/for)を使って元のmapを加工します。 175 | 176 | - outputを修正しsubentのidだけを表示するようにします。[ヒント](https://zenn.dev/machamp/articles/a8df5c66ee2eb0#%E4%BD%9C%E6%88%90%E3%81%97%E3%81%9F%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%92output%E3%81%97%E3%81%9F%E3%81%84%E6%99%82) 177 | 178 | # 後片付け 179 | 180 | - destroyします。またはmain.tfをすべてコメントアウトしてapplyします。 -------------------------------------------------------------------------------- /step3-loop/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "main" { 2 | cidr_block = var.vpc-cidr 3 | } 4 | 5 | # resource "aws_subnet" "count" { 6 | # count = length(var.subnet-cidrs) 7 | 8 | # vpc_id = aws_vpc.main.id 9 | # cidr_block = var.subnet-cidrs[count.index] 10 | # } 11 | 12 | resource "aws_subnet" "foreach" { 13 | for_each = toset(var.subnet-cidrs) 14 | 15 | vpc_id = aws_vpc.main.id 16 | cidr_block = each.key 17 | } 18 | 19 | resource "aws_security_group" "dynamic" { 20 | name = "dynamic-test" 21 | description = "Dynamic Test" 22 | vpc_id = aws_vpc.main.id 23 | 24 | # dynamic "ingress" { 25 | # for_each = toset(var.sg-allow-cidrs) 26 | # content{ 27 | # description = "TLS from VPC" 28 | # from_port = 443 29 | # to_port = 443 30 | # protocol = "tcp" 31 | # cidr_blocks = [ingress.key] 32 | # } 33 | # } 34 | 35 | dynamic "ingress" { 36 | for_each = var.sg-ingress-rulus 37 | content { 38 | description = "TLS from VPC" 39 | from_port = 443 40 | to_port = 443 41 | protocol = ingress.value.protocol 42 | cidr_blocks = [ingress.key] 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /step3-loop/output.tf: -------------------------------------------------------------------------------- 1 | output "dynamic-sgid" { 2 | value = aws_security_group.dynamic.id 3 | } 4 | 5 | output "subnets" { 6 | value = [for key, value in aws_subnet.foreach : value.id] 7 | } -------------------------------------------------------------------------------- /step3-loop/terraform.tfvars: -------------------------------------------------------------------------------- 1 | vpc-cidr = "10.1.0.0/16" 2 | subnet-cidrs = ["10.1.10.0/24", "10.1.20.0/24"] 3 | # sg-allow-cidrs = ["10.1.10.10/32","10.1.10.11/32","10.1.10.12/32","10.1.10.13/32","10.1.10.14/32"] 4 | sg-ingress-rulus = { 5 | "10.1.10.10/32" = { 6 | protocol = "TCP" 7 | } 8 | "10.1.10.11/32" = { 9 | protocol = "TCP" 10 | } 11 | "10.1.10.12/32" = { 12 | protocol = "UDP" 13 | } 14 | "10.1.10.13/32" = { 15 | protocol = "UDP" 16 | } 17 | "10.1.10.14/32" = { 18 | protocol = "UDP" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /step3-loop/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc-cidr" { 2 | type = string 3 | description = "VPCのCIDR" 4 | } 5 | 6 | variable "subnet-cidrs" { 7 | type = list(string) 8 | description = "サブネットのCIDRのリスト" 9 | } 10 | 11 | # variable "sg-allow-cidrs" { 12 | # type = list(string) 13 | # description = "SGで許可するCIDRのリスト" 14 | # } 15 | 16 | variable "sg-ingress-rulus" { 17 | type = map(object({ 18 | protocol = string 19 | })) 20 | description = "SGで許可するルールのオブジェクト" 21 | } 22 | -------------------------------------------------------------------------------- /step3-loop/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | version = ">= 4.49.0" 5 | } 6 | } 7 | } 8 | 9 | provider "aws" { 10 | region = "ap-south-1" 11 | default_tags { 12 | tags = { 13 | Env = "terraform-practice" 14 | Owner = "mori" 15 | Steps = "step3" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /step4-if/README.md: -------------------------------------------------------------------------------- 1 | - [ステップ4 分岐](#ステップ4-分岐) 2 | - [4-1. 三項演算子によるパラメータの分岐](#4-1-三項演算子によるパラメータの分岐) 3 | - [4-2. 三項演算子とループによるリソースの分岐](#4-2-三項演算子とループによるリソースの分岐) 4 | - [ただ分岐させたい場合](#ただ分岐させたい場合) 5 | - [分岐させてループも回したい場合](#分岐させてループも回したい場合) 6 | - [後片付け](#後片付け) 7 | 8 | 9 | # ステップ4 分岐 10 | 11 | Terraformにif文はありませんが、条件式を使えば似たようなことができます。 12 | 13 | `step4`ディレクトリを作成、移動して実施ください。以降のプラクティスはすべて`step4`ディレクトリ内で行う想定です。`versions.tf`を作成しておいてください。必要に応じて`main.tf`、`varibales.tf`、`output.tf`、`terraform.tfvars`も作成してください。すべてのリソースにはStep=step4のタグを設定します。ファイルはステップ内で同じものを続けて使ったください。 14 | 15 | ## 4-1. 三項演算子によるパラメータの分岐 16 | 17 | [Conditional式](https://developer.hashicorp.com/terraform/language/expressions/conditionals)(三項演算子)で条件分岐を書けます。 18 | 19 | **プラクティス** 20 | 21 | - 以下の変数を設定します。 22 | - vpc-cidr: "10.1.0.0/16" 23 | - dev: true (bool型です。) 24 | - [vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc)を作成します。 25 | - cidrは変数vpc-cidrで指定します。 26 | - `enable_dns_support`の値を変数devがtrueの場合は`true`を、そうでない場合は`false`を設定するようにします。 27 | - `enable_dns_hostnames`の値も変数devがtrueの場合は`true`を、そうでない場合は`false`を設定するようにします。 28 | - vpcのidを表示するようにします。 29 | - initしてplanで内容を確認しapplyします。 30 | - マネージメントコンソールまたは以下コマンドでリソースが作成されたことを確認します。 31 | 32 | ``` sh 33 | $ aws ec2 describe-vpc-attribute --attribute enableDnsSupport --vpc-id 34 | $ aws ec2 describe-vpc-attribute --attribute enableDnsHostnames --vpc-id 35 | ``` 36 | 37 | - 変数devの値をfalseにします。 38 | - planしてapplyします。in placeで値が変更になります。 39 | - マネージメントコンソールまたは以下コマンドでリソースが作成されたことを確認します。 40 | 41 | ``` sh 42 | $ aws ec2 describe-vpc-attribute --attribute enableDnsSupport --vpc-id 43 | $ aws ec2 describe-vpc-attribute --attribute enableDnsHostnames --vpc-id 44 | ``` 45 | 46 | 今回は条件判定に使った変数devをbool型にしましたがstring型などでも判定できます。その場合、[演算子](Arithmetic and Logical Operators)を使います。たとえば変数の値がある文字列と一致するか判定するなら` 変数 == "文字列" `と書きます。 47 | 48 | ## 4-2. 三項演算子とループによるリソースの分岐 49 | 50 | ループ(count/for_each)と組み合わせるばリソースレベルでの分岐もできます。 51 | 52 | ### ただ分岐させたい場合 53 | 54 | countの場合、条件に一致する時はcountの値を`1`にし、一致しない場合は`0`を指定します。count=0だとループしないのでリソースが作られません。 55 | 56 | for_eachの場合、条件に一致する時は`{ dummy = "dummy" }`等のダミーマップにし、一致しない場合は空のマップ`{}`を指定します。空マップだとループしないのでリソースが作られません。 57 | 58 | **プラクティス** 59 | 60 | - 以下の変数を追加で設定します。 61 | - subnet-cidr: "10.1.10.0/24" (stringです) 62 | - [subnet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet)を作成します。 63 | - 変数devがtrueの場合はサブネットを作成します。そうでない場合はサブネットを作成しません。(count/for_eachどちらを使ってもいいです) 64 | - cidrはsubnet-cidrで指定します。 65 | - planしてapplyします。 66 | - マネージメントコンソールまたは以下コマンドでリソースが作成されたことを確認します。 67 | 68 | ``` sh 69 | $ aws ec2 describe-subnets --filters "Name=tag-value,Values=step4" 70 | ``` 71 | 72 | - 変数devをfalseにします。 73 | - planしてapplyします。VPCの設定も変わりますが、サブネットがdestroyedになればOKです。 74 | 75 | ### 分岐させてループも回したい場合 76 | 77 | countの場合、条件に一致する時はcountの値をリストの長さでループを回し、一致しない場合はcountの値で`0`を指定します。 78 | 79 | for_eachの場合、条件に一致する時はmap/objyectでループを回し、一致しない場合は空のマップ`{}`を指定します。ただし、tosetでリストを変換している場合は空マップではなく`toset([])`と指定しないと左右の型が不一致でエラーになります。 80 | 81 | **プラクティス** 82 | 83 | - 変数subnet-cidrをsubnet-cidrsに変更し、リスト型で ["10.1.10.0/24","10.1.20.0/24"] を設定します。 84 | - [subnet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet)を作成します。 85 | - 変数devがtrueの場合はサブネットを作成します。サブネットはsubnet-cidrsに設定されている数だけ作成します。変数devがfalseの場合はサブネットを作成しません。(count/for_eachどちらを使ってもいいです) 86 | - cidrはsubnet-cidrsの値で指定します。 87 | - planしてapplyします。 88 | - マネージメントコンソールまたは以下コマンドでリソースが作成されたことを確認します。 89 | 90 | ``` sh 91 | $ aws ec2 describe-subnets --filters "Name=tag-value,Values=step4" 92 | ``` 93 | 94 | - 変数devをfalseにします。 95 | - planしてapplyします。VPCの設定も変わりますが、サブネットがdestroyedになればOKです。 96 | 97 | # 後片付け 98 | 99 | - destroyします。またはmain.tfをすべてコメントアウトしてapplyします。 -------------------------------------------------------------------------------- /step4-if/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "main" { 2 | cidr_block = var.vpc-cidr 3 | enable_dns_support = var.dev ? true : false 4 | enable_dns_hostnames = var.dev ? true : false 5 | } 6 | 7 | ## countの場合 8 | # resource "aws_subnet" "conditional_count" { 9 | # count = var.dev ? 1 : 0 10 | 11 | # vpc_id = aws_vpc.main.id 12 | # cidr_block = var.subnet-cidr 13 | # } 14 | 15 | # resource "aws_subnet" "conditional_count_loop" { 16 | # count = var.dev ? length(var.subnet-cidrs) : 0 17 | 18 | # vpc_id = aws_vpc.main.id 19 | # cidr_block = var.subnet-cidrs[count.index] 20 | # } 21 | 22 | ## for_eachの場合 23 | # resource "aws_subnet" "conditional_foreach" { 24 | # for_each = var.dev ? { dummy = "dummy" } : {} 25 | 26 | # vpc_id = aws_vpc.main.id 27 | # cidr_block = var.subnet-cidr 28 | # } 29 | 30 | resource "aws_subnet" "conditional_foreach_loop" { 31 | for_each = var.dev ? toset(var.subnet-cidrs) : toset([]) 32 | 33 | vpc_id = aws_vpc.main.id 34 | cidr_block = each.key 35 | } -------------------------------------------------------------------------------- /step4-if/output.tf: -------------------------------------------------------------------------------- 1 | output "vpc-id" { 2 | value = aws_vpc.main.id 3 | } 4 | -------------------------------------------------------------------------------- /step4-if/terraform.tfvars: -------------------------------------------------------------------------------- 1 | vpc-cidr = "10.1.0.0/16" 2 | dev = true 3 | # subnet-cidr = "10.1.10.0/24" 4 | subnet-cidrs = ["10.1.10.0/24", "10.1.20.0/24"] 5 | -------------------------------------------------------------------------------- /step4-if/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc-cidr" { 2 | type = string 3 | description = "VPCのCIDR" 4 | } 5 | 6 | variable "dev" { 7 | type = bool 8 | description = "dev環境かどうか" 9 | } 10 | 11 | # variable "subnet-cidr" { 12 | # type = string 13 | # description = "サブネットのCIDR" 14 | # } 15 | 16 | variable "subnet-cidrs" { 17 | type = list(string) 18 | description = "サブネットのCIDRのリスト" 19 | } 20 | -------------------------------------------------------------------------------- /step4-if/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | version = ">= 4.49.0" 5 | } 6 | } 7 | } 8 | 9 | provider "aws" { 10 | region = "ap-south-1" 11 | default_tags { 12 | tags = { 13 | Env = "terraform-practice" 14 | Owner = "mori" 15 | Steps = "step4" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /step5-module/README.md: -------------------------------------------------------------------------------- 1 | - [ステップ5 モジュール化](#ステップ5-モジュール化) 2 | - [5-1. リソース群をモジュールにする](#5-1-リソース群をモジュールにする) 3 | - [5-2. モジュールを使いまわして複数デプロイする](#5-2-モジュールを使いまわして複数デプロイする) 4 | - [5-3. モジュールの値を参照する](#5-3-モジュールの値を参照する) 5 | - [後片付け](#後片付け) 6 | 7 | 8 | # ステップ5 モジュール化 9 | 10 | モジュールはいくつかのTerraformコードをまとめたものです。 11 | 12 | `step5`ディレクトリを作成、移動して実施ください。以降のプラクティスはすべて`step5`ディレクトリ内で行う想定です。`versions.tf`を作成しておいてください。必要に応じて`main.tf`、`varibales.tf`、`output.tf`、`terraform.tfvars`も作成してください。すべてのリソースにはStep=step5のタグを設定します。ファイルはステップ内で同じものを続けて使ったください。 13 | 14 | ## 5-1. リソース群をモジュールにする 15 | 16 | [モジュール](https://developer.hashicorp.com/terraform/language/modules)にすると複数のリソースをまとめてデプロイできます。Terraformコードをモジュール化する方法は、モジュール化したいコード群を1つのディレクトリにまとめるだけです。モジュールは[moduleブロック](https://developer.hashicorp.com/terraform/language/modules/syntax)を使って呼び出します。呼び出し元をルートモジュール、呼び出されるモジュールを子モジュールと言います。 17 | 18 | 子モジュールは呼び出されるモジュールであるため、`versions.tf`や`terraform.tfvars`などは不要です。ルートモジュールから値を設定したり、モジュールで作成したリソースの値をルートモジュールに返すには`variables.tf`や`output.tf`が必要です。 19 | 20 | **プラクティス** 21 | 22 | - `step5`ディレクトリ配下に`network`ディレクトリを作成し、以下のコードを作成してください。 23 | - [vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc)を作成します。 24 | - cidrは変数で設定します。 25 | - [subnet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet)を作成します。 26 | - cidrは変数で設定します。変数はリスト型にし、変数の値の数だけサブネットを作ります。 27 | - vpcのidとsubentのidをルートモジュールに返します。 28 | - `step5`ディレクトリ直下に以下のコードを作成します。 29 | - networkモジュールをデプロイ。ソースのパスは相対パス(./network)で指定します。 30 | - networkに必要な変数を設定します。(値は変数で指定してもいいし、直接指定してもいいです。) 31 | - networkで作成したvpcとsubnetのidを表示するようにします。(モジュールの値はmodule.<モジュール名>.<変数名>で参照できます。) 32 | - initしてplanしてapplyします。 33 | 34 | ## 5-2. モジュールを使いまわして複数デプロイする 35 | 36 | モジュールは何度も呼び出せます。 37 | 38 | **プラクティス** 39 | 40 | - `step5`ディレクトリ直下に以下のコードを作成します。 41 | - 追加でもう1つnetworkモジュールをデプロイします。ソースのパスは相対パス(./network)で指定します。 42 | - networkに必要な変数を設定します。(値は変数で指定してもいいし、直接指定してもいいです。1つ目と値を変えても変えなくてもいいです) 43 | - 追加のnetworkで作成したvpcとsubnetのidも表示するようにします。 44 | - initしてplanしてapplyします。 45 | 46 | ## 5-3. モジュールの値を参照する 47 | 48 | モジュールで作成した値を別モジュールで参照もできます。なお、ルートモジュールから呼び出す子モジュールを追加する場合、再度initが必要です。 49 | 50 | **プラクティス** 51 | 52 | - `step5`ディレクトリ配下に`compute`ディレクトリを作成し、以下のコードを作成してください。 53 | - [ec2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance)を作成します。 54 | - amiはそのリージョンの最新のAmazonLinux2を指定します。 55 | - amiのidは変数にして入力してもいいし、dataで引っ張ってきてもいいです。dataで読み込む場合、[こちら](https://aws.amazon.com/jp/blogs/news/query-for-the-latest-amazon-linux-ami-ids-using-aws-systems-manager-parameter-store/)にある通り、SSMパラメーターで取得できます。データの取得は[aws_ssm_parameter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter)を使います。 56 | - インスタンスタイプは変数で指定します。 57 | - subnetは変数で指定します。 58 | - `step5`ディレクトリ直下に以下のコードを作成します。 59 | - computeモジュールをデプロイします。 60 | - インスタンスタイプは`t3.micro`を設定します。 61 | - サブネットのIDは1つ目のnetworkモジュールで作成した0番目のサブネットIDを指定します。 62 | - initしてplanしてapplyします。 63 | 64 | # 後片付け 65 | 66 | - destroyします。またはmain.tfをすべてコメントアウトしてapplyします。 67 | -------------------------------------------------------------------------------- /step5-module/compute/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_ssm_parameter" "al2" { 2 | name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" 3 | } 4 | 5 | resource "aws_instance" "this" { 6 | ami = data.aws_ssm_parameter.al2.value 7 | instance_type = var.type 8 | subnet_id = var.subnet-id 9 | } -------------------------------------------------------------------------------- /step5-module/compute/variables.tf: -------------------------------------------------------------------------------- 1 | variable "type" { 2 | type = string 3 | description = "EC2のインスタンスタイプ" 4 | } 5 | 6 | variable "subnet-id" { 7 | type = string 8 | description = "EC2をデプロイするサブネットのID" 9 | } 10 | -------------------------------------------------------------------------------- /step5-module/main.tf: -------------------------------------------------------------------------------- 1 | module "network1" { 2 | source = "./network" 3 | 4 | vpc-cidr = var.vpc-cidr 5 | subnet-cidrs = var.subnet-cidrs 6 | } 7 | 8 | module "network2" { 9 | source = "./network" 10 | 11 | vpc-cidr = var.vpc-cidr 12 | subnet-cidrs = var.subnet-cidrs 13 | } 14 | 15 | module "compute" { 16 | source = "./compute" 17 | 18 | type = var.type 19 | subnet-id = module.network1.subnet-ids[0] 20 | } -------------------------------------------------------------------------------- /step5-module/network/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "main" { 2 | cidr_block = var.vpc-cidr 3 | } 4 | 5 | # countの場合 6 | # resource "aws_subnet" "count" { 7 | # count = length(var.subnet-cidrs) 8 | 9 | # vpc_id = aws_vpc.main.id 10 | # cidr_block = var.subnet-cidrs[count.index] 11 | # } 12 | 13 | # for_eachの場合 14 | resource "aws_subnet" "foreach" { 15 | for_each = toset(var.subnet-cidrs) 16 | 17 | vpc_id = aws_vpc.main.id 18 | cidr_block = each.key 19 | } 20 | -------------------------------------------------------------------------------- /step5-module/network/output.tf: -------------------------------------------------------------------------------- 1 | output "vpc-id" { 2 | value = aws_vpc.main.id 3 | } 4 | 5 | output "subnet-ids" { 6 | value = [for key, value in aws_subnet.foreach : value.id] 7 | } -------------------------------------------------------------------------------- /step5-module/network/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc-cidr" { 2 | type = string 3 | description = "VPCのCIDR" 4 | } 5 | 6 | variable "subnet-cidrs" { 7 | type = list(string) 8 | description = "サブネットのCIDRのリスト" 9 | } 10 | -------------------------------------------------------------------------------- /step5-module/output.tf: -------------------------------------------------------------------------------- 1 | output "vpc-id-1" { 2 | value = module.network1.vpc-id 3 | } 4 | 5 | output "subnet-ids-1" { 6 | value = module.network1.subnet-ids 7 | } 8 | 9 | output "vpc-id-2" { 10 | value = module.network2.vpc-id 11 | } 12 | 13 | output "subnet-ids-2" { 14 | value = module.network2.subnet-ids 15 | } -------------------------------------------------------------------------------- /step5-module/terraform.tfvars: -------------------------------------------------------------------------------- 1 | vpc-cidr = "10.1.0.0/16" 2 | subnet-cidrs = ["10.1.10.0/24", "10.1.20.0/24"] 3 | type = "t3.micro" -------------------------------------------------------------------------------- /step5-module/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc-cidr" { 2 | type = string 3 | description = "VPCのCIDR" 4 | } 5 | 6 | variable "subnet-cidrs" { 7 | type = list(string) 8 | description = "サブネットのCIDRのリスト" 9 | } 10 | 11 | variable "type" { 12 | type = string 13 | description = "EC2インスタンスのタイプ" 14 | } -------------------------------------------------------------------------------- /step5-module/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | version = ">= 4.49.0" 5 | } 6 | } 7 | } 8 | 9 | provider "aws" { 10 | region = "ap-south-1" 11 | default_tags { 12 | tags = { 13 | Env = "terraform-practice" 14 | Owner = "mori" 15 | Steps = "step5" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /step6-tips/README.md: -------------------------------------------------------------------------------- 1 | - [ステップ6 Tips](#ステップ6-tips) 2 | - [tfstateのリモートバックエンド化](#tfstateのリモートバックエンド化) 3 | - [リモートのtfstateから値を参照する](#リモートのtfstateから値を参照する) 4 | - [1つのモジュールで複数のリージョンを使う](#1つのモジュールで複数のリージョンを使う) 5 | - [特定のリソース、モジュールだけ操作](#特定のリソースモジュールだけ操作) 6 | - [リソースの依存関係は自動でやってくれるが明示的にdepends\_onさせることもできる](#リソースの依存関係は自動でやってくれるが明示的にdepends_onさせることもできる) 7 | - [ignore\_changesで変更を無視できる](#ignore_changesで変更を無視できる) 8 | - [リソースファイルは分けてもいい](#リソースファイルは分けてもいい) 9 | - [Terraformのベストプラクティス](#terraformのベストプラクティス) 10 | - [その他便利なterraformコマンド](#その他便利なterraformコマンド) 11 | - [fmt](#fmt) 12 | - [show](#show) 13 | - [validate](#validate) 14 | 15 | # ステップ6 Tips 16 | 17 | 知っておくと便利なことをまとめます。とくにプラクティスはなく読むものです。読み飛ばしても構いません。 18 | 19 | ## tfstateのリモートバックエンド化 20 | 21 | tfstateはTerraformでデプロイしたリソース情報が書き込まれる大事なファイルです。消さないのはもちろんのこと、万が一消してしまっても復元できるようにするのが望ましいです。また、チームでコードを共有する場合、tfstateも共有します。 22 | 23 | tfstateを配置する場所をバックエンドと言います。デフォルトのバックエンドはローカル(カレントディレクトリ)ですが、外部ストレージサービス(S3等)の指定もできます。また、同時に複数人がtfstateを更新しないようにDynamoDBを使った排他制御もできます。[こちら](./remote-backend/)にstateを格納するS3バケットと排他制御用のDynamoDBを作るサンプルのTerraformコードを用意しましたのでお使いください。一点、README.mdに書きましたがこのコード自体のstateはローカルに作成されます。リソース作成後にコマンドでtfstateをS3にアップロードし、versions.tfを書き換えればこのモジュールのtfstateもリモートバックエンドで管理できます。 24 | 25 | 別のモジュールで上記作成したリモートバックエンドを使うには以下のようにterraformブロックを設定します。``は任意のモジュール名に書き換えてください。 26 | 27 | ``` 28 | terraform { 29 | ... 30 | backend "s3" { 31 | bucket = "-tfstate" 32 | key = "/terraform.tfstate" 33 | encrypt = true 34 | dynamodb_table = "-tfstate-lock" 35 | region = "ap-northeast-1" 36 | } 37 | } 38 | ``` 39 | 40 | **参考** 41 | 42 | - [Terraform公式:バックエンドS3](https://developer.hashicorp.com/terraform/language/settings/backends/s3) 43 | 44 | ## リモートのtfstateから値を参照する 45 | 46 | リモートバックエンドに保管したtfstateからoutputで定義した値を参照できます。例えばstep5のモジュールをリモートバックエンド化し、別のモジュールから参照するには以下のように書きます。 47 | 48 | ``` 49 | data "terraform_remote_state" "step5" { 50 | backend = "s3" 51 | 52 | config = { 53 | bucket = "-tfstate" 54 | key = "step5/terraform.tfstate" 55 | region = "ap-northeast-1" 56 | } 57 | } 58 | 59 | module "test" { 60 | source = "./test" 61 | 62 | base_name = var.base_name 63 | vpc_id = data.terraform_remote_state.step5.outputs.vpc-id-1.vpc-id 64 | } 65 | ``` 66 | 67 | データを参照する際、上記例では`data.terraform_remote_state.step5.outputs.vpc-id-1.vpc-id`で指定しています。最後の`vpc-id-1.vpc-id`はstep5のoutput.tfに定義したoutput名とvalue名です。以下、step5のoutputです。 68 | 69 | ``` 70 | output "vpc-id-1" { 71 | value = module.network1.vpc-id 72 | } 73 | ``` 74 | 75 | ## 1つのモジュールで複数のリージョンを使う 76 | 77 | 1モジュールで複数のリージョンにデプロイするには[alias](https://developer.hashicorp.com/terraform/language/providers/configuration#alias-multiple-provider-configurations)を使います。例えば以下のように書けば`us-east-1`と`us-east-2`にデプロイできます。複数リージョンへのデプロイはCloudFront等us-east-1でしかデプロイ出来ないリソースと組み合わせる時に使います。 78 | 79 | **versions.tf** 80 | 81 | ``` 82 | terraform { 83 | required_version = ">= 1.3.7" 84 | } 85 | 86 | provider "aws" { 87 | region = "us-east-1" 88 | } 89 | 90 | provider "aws" { 91 | alias = "us-east-2" 92 | region = "us-east-2" 93 | } 94 | ``` 95 | 96 | **main.tf** 97 | 98 | ``` 99 | resource "aws_vpc" "us-east-1" { 100 | cidr_block = "10.1.0.0/16" 101 | } 102 | 103 | resource "aws_vpc" "us-east-2" { 104 | provider = aws.us-east-2 105 | cidr_block = "10.1.0.0/16" 106 | } 107 | ``` 108 | 109 | ## 特定のリソース、モジュールだけ操作 110 | 111 | モジュール内の特定のリソースあるいはモジュールのみ操作(plan,apply等)できます。コマンドに[-traget](https://developer.hashicorp.com/terraform/tutorials/cli/resource-targeting)をつければいいです。 112 | 113 | ``` sh 114 | terraform plan -traget=<リソースタイプ.リソース名> 115 | terraform plan -traget=module.<モジュール名> 116 | ``` 117 | 118 | ## リソースの依存関係は自動でやってくれるが明示的にdepends_onさせることもできる 119 | 120 | 基本的にTerraformは依存関係を自動で推測してデプロイしてくれます。例えば、ステップ2で作成したVPCとサブネットだと、サブネットからVPC IDを参照しているため、VPC→サブネットの順番でデプロイしてくれます。しかし、依存関係を明示的に指定したいこともあります。[depends_on](https://developer.hashicorp.com/terraform/language/meta-arguments/depends_on)を使えば明示的な依存関係を設定できます。たとえば以下のように書きます。 121 | 122 | ``` 123 | resource "aws_vpc" "est" { 124 | cidr_block = "10.1.0.0/16" 125 | } 126 | 127 | resource "aws_subnet" "test" { 128 | vpc_id = aws_vpc.test.id 129 | 130 | depends_on = [ 131 | aws_vpc.test 132 | ] 133 | } 134 | ``` 135 | 136 | ## ignore_changesで変更を無視できる 137 | 138 | 初期デプロイ以降は変更を無視したい場合もあります。例えば、AutoScallingGroupの希望数は自動調整で値が変更されていたります。Terraformコードと実環境の差異を無視したいときは[lifecycle.ignore_changes](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes)を使います。例えば以下のように書くと`cidr_block`の値を変更しても無視されます。 139 | 140 | ``` 141 | resource "aws_vpc" "est" { 142 | cidr_block = "10.1.0.0/16" 143 | 144 | lifecycle { 145 | ignore_changes = [ 146 | cidr_block, 147 | ] 148 | } 149 | } 150 | ``` 151 | 152 | ## リソースファイルは分けてもいい 153 | 154 | 本プラクティスでは`main.tf`にリソース定義をまとめて書いていましたがmain.tfでなくても構いません。例えば、`vpc.tf`と`subnet.tf`にファイルを分けてもいいです。main.tfがあまりにも長くなる場合はリソースごとにファイルを分けても良いでしょう。 155 | 156 | ## Terraformのベストプラクティス 157 | 158 | 色々ありますがGCPのドキュメントが日本語だし情報量も多くて良いかと思います。[Terraform を使用するためのベスト プラクティス](https://cloud.google.com/docs/terraform/best-practices-for-terraform?hl=ja) 159 | 160 | ## その他便利なterraformコマンド 161 | 162 | ### [fmt](https://developer.hashicorp.com/terraform/cli/commands/fmt) 163 | 164 | `terraform fmt`でカレントディレクトリのterraformコードを見やすくインデントを修正してくれます。`terraform fmt -recursive`にするとカレントディレクトリ配下も再帰的にfmtしてくれます。 165 | 166 | ### [show](https://developer.hashicorp.com/terraform/cli/commands/show) 167 | 168 | `terraform show`でカレントディレクトリのモジュールでデプロイしたリソース情報を見れます。(outputしていない値も確認できます。) 169 | 170 | ### [validate](https://developer.hashicorp.com/terraform/cli/commands/validate) 171 | 172 | `terraform validate`でカレントディレクトリのterraformコードの構文チェックができます。 173 | -------------------------------------------------------------------------------- /step6-tips/muti-region/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "4.51.0" 6 | hashes = [ 7 | "h1:v30kL4QQPD/huHQ3qj1M4nzdkx81Li1dGJ0ORehfZBY=", 8 | "zh:10aebfd8f22f7a69a2fcfcf35cb17ebbc0966ac8e822a9a0e1c843e429389de7", 9 | "zh:26661203ab083ec35a5ae2d9b516793d98f4380655bcd304724af7495aaa7c09", 10 | "zh:27ad57b820666a252e64959a4369fe9e40df5bf5d37a6ee272cc9131a501448f", 11 | "zh:5d7b1acfae1a7835509d8f501e4e731cac2246b9f5b6674b643790d6eaca8037", 12 | "zh:62cb21d9c90fd6b7af5c4b4d47f3e2908c3c7087809f3faeb561c4a12b14cbf5", 13 | "zh:731490aa24ffe958e23f67619b897c96178a1c628453b02727c69f15dc90ff7b", 14 | "zh:7b85f3513da571db05ad43f6a1ba195ab33ce8284f03537b636b561c1ad43075", 15 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 16 | "zh:9d8f3b176046f438768d8b1ba25d2bb8d234499a2c2c8fa0e1336c84ae708c41", 17 | "zh:bec27993d9501fb84df58d7f0b3eed2c8243e61f3676e66de17a9e2a8ff2f5be", 18 | "zh:cd27798991a86b44adab3969db96666dde12309caf55e40bffe90bd895a6edd3", 19 | "zh:dba49fdf4339a941357944616b1bf79483c2bed31c235e4cd59698802c8d2fdb", 20 | "zh:ed1ccf97ec02191e0840ac4fdbd2da21eea661b9d7e11c0f98b71ab67a3d3718", 21 | "zh:edeb801e3c84e653dc3449a0b73b64d1ba167cef674863ae5106d9a063548c70", 22 | "zh:f680647b4fce3d7603a24ae69c32cf6664fb0182844d00f18ddae3d5d878441c", 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /step6-tips/muti-region/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "us-east-1" { 2 | cidr_block = "10.1.0.0/16" 3 | } 4 | 5 | resource "aws_vpc" "us-east-2" { 6 | provider = aws.us-east-2 7 | cidr_block = "10.1.0.0/16" 8 | } -------------------------------------------------------------------------------- /step6-tips/muti-region/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.3.7" 3 | } 4 | 5 | provider "aws" { 6 | region = "us-east-1" 7 | } 8 | 9 | provider "aws" { 10 | alias = "us-east-2" 11 | region = "us-east-2" 12 | } -------------------------------------------------------------------------------- /step6-tips/remote-backend/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "4.49.0" 6 | constraints = "4.49.0" 7 | hashes = [ 8 | "h1:oOwWQpvQWd1uVP1axBz/TO6xzzLWoL982AY/MQfeF7I=", 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /step6-tips/remote-backend/README.md: -------------------------------------------------------------------------------- 1 | # remote-backend 2 | 3 | tfstateを格納するリモートバックエンドを構築します。このモジュールでリソースを作成した後、このモジュールのtfstateを以下の手順でリモートバックエンドに格納します。 4 | 5 | - ローカルのtfstateをリモートバックエンドにアップロードします。 6 | 7 | ``` sh 8 | $ aws s3 cp ./terraform.tfstate s3://-tfstate/tf-backend/ 9 | ``` 10 | 11 | - versions.tfを修正しbackendブロックを追加します。 12 | 13 | ``` 14 | terraform { 15 | required_version = ">= 1.3.7" 16 | 17 | required_providers { 18 | aws = { 19 | version = "= 4.49.0" 20 | source = "hashicorp/aws" 21 | } 22 | } 23 | 24 |  # tf-backendモジュール実行後に以下を追加 25 | backend "s3" { 26 | bucket = "-tfstate" 27 | key = "tf-backend/terraform.tfstate" 28 | encrypt = true 29 | dynamodb_table = "-tfstate-lock" 30 | region = "ap-northeast-1" 31 | } 32 | } 33 | ... 34 | ``` 35 | 36 | - ローカルのtfstateを削除します。 37 | 38 | ``` sh 39 | $ rm terraform.tfstate terraform.tfstate.backup 40 | ``` 41 | 42 | - initしてplanして差分がないことを確認します。 43 | 44 | ``` sh 45 | $ terraform init 46 | $ terraform plan 47 | ``` 48 | -------------------------------------------------------------------------------- /step6-tips/remote-backend/terraform.tfvars: -------------------------------------------------------------------------------- 1 | base_name = "terraform-practice-mori" -------------------------------------------------------------------------------- /step6-tips/remote-backend/tfbackend.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_bucket" "tfstate" { 2 | bucket = "${var.base_name}-tfstate" 3 | 4 | tags = { 5 | "Name" = "${var.base_name}-tfstate" 6 | } 7 | } 8 | 9 | resource "aws_s3_bucket_acl" "tfstate" { 10 | bucket = aws_s3_bucket.tfstate.bucket 11 | acl = "private" 12 | } 13 | 14 | resource "aws_s3_bucket_server_side_encryption_configuration" "tfstate" { 15 | bucket = aws_s3_bucket.tfstate.bucket 16 | rule { 17 | apply_server_side_encryption_by_default { 18 | sse_algorithm = "AES256" 19 | } 20 | } 21 | } 22 | 23 | resource "aws_s3_bucket_versioning" "tfstate" { 24 | bucket = aws_s3_bucket.tfstate.bucket 25 | versioning_configuration { 26 | status = "Enabled" 27 | } 28 | } 29 | 30 | resource "aws_s3_bucket_public_access_block" "tfstate" { 31 | bucket = aws_s3_bucket.tfstate.bucket 32 | 33 | block_public_acls = true 34 | block_public_policy = true 35 | ignore_public_acls = true 36 | restrict_public_buckets = true 37 | } 38 | 39 | resource "aws_dynamodb_table" "tfstate_lock" { 40 | name = "${var.base_name}-tfstate-lock" 41 | read_capacity = 1 42 | write_capacity = 1 43 | hash_key = "LockID" 44 | 45 | attribute { 46 | name = "LockID" 47 | type = "S" 48 | } 49 | } -------------------------------------------------------------------------------- /step6-tips/remote-backend/variables.tf: -------------------------------------------------------------------------------- 1 | # common parameter 2 | variable "base_name" { 3 | description = "作成するリソースに付与する接頭語" 4 | type = string 5 | } -------------------------------------------------------------------------------- /step6-tips/remote-backend/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 1.3.7" 4 | 5 | required_providers { 6 | aws = { 7 | version = "= 4.49.0" 8 | source = "hashicorp/aws" 9 | } 10 | } 11 | 12 | # # 初回実行時は以下をコメントアウト。tf-backendモジュール実行後に以下を追加 13 | # backend "s3" { 14 | # bucket = "terraform-practice-mori-tfstate" 15 | # key = "terraform-practice-mori/terraform.tfstate" 16 | # encrypt = true 17 | # dynamodb_table = "terraform-practice-mori-tfstate-lock" 18 | # region = "ap-northeast-1" 19 | # } 20 | } 21 | 22 | provider "aws" { 23 | region = "ap-northeast-1" 24 | default_tags { 25 | tags = { 26 | pj = "terraform-practice-mori" 27 | owner = "mori" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /step7-test/README.md: -------------------------------------------------------------------------------- 1 | # ステップ7 総合問題 2 | 3 | 今まで学んだことの総復習です。前のステップやインターネットを駆使して良いので、以下を満たすTerraformコードを書いてデプロイしてください。 4 | 5 | **プラクティス1** 6 | 7 | - 子モジュールとして以下2つ作成してください。 8 | - network 9 | - [VPC](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc)を1つ作成します。 10 | - CIDRは変数で設定します。 11 | - [サブネット](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet)を1つ作成します。 12 | - VPCのIDは同じモジュールのVPC IDを参照します。 13 | - CIDRは変数で設定します。 14 | - 各リソースにはNameタグをつけます。タグの値は`<プロジェクト名>-リソース`にします。(例、tfpractice-vpc) 15 | - 以下変数を定義します。 16 | - PJ名 17 | - VPCのCIDR 18 | - サブネットのCIDR 19 | - 作成したVPCとサブネットのIDをアウトプットします。 20 | - compute 21 | - [EC2インスタンス](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance)を作成します。 22 | - AMIはAmazonLinux2を使います。リージョンの最新AMI IDを指定してください。 23 | - インスタンスタイプは変数で設定します。 24 | - サブネットIDは変数で設定します。 25 | - セキュリティーグループは同じモジュールのSG IDを参照します。 26 | - 変数appがforntの場合、ルートブロックデバイスの暗号化をしてください。そうでない場合、暗号化しないでください。 27 | - [セキュリティーグループ](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group)を作成します。 28 | - 名前は`<プロジェクト名>--ec2-sg`にします。 29 | - 説明は`<プロジェクト名>- ec2 sg`にします。 30 | - VPC IDは変数で設定します。 31 | - インバウンドの設定をします。 32 | - 変数allow-tcp-portsで指定したポート番号の分だけ設定します。 33 | - プロトコルはtcpを指定します。 34 | - 指定したCIDRからのインバウンドを許可します。 35 | - [S3バケット](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket)を作成します。 36 | - 変数appがbackの場合作成します。それ以外の場合、リソースを作成しません。 37 | - バケット名は`<プロジェクト名>--bucket`にします。 38 | - 各リソースにはNameタグをつけます。`<プロジェクト名>-`にします。(例、tfpractice-front) 39 | - 以下変数を定義します。 40 | - PJ名 41 | - APP名 42 | - インスタンスのタイプ 43 | - VPC ID 44 | - サブネット ID 45 | - セキュリティーグループでインバウンドを許可するCIDR 46 | - セキュリティーグループでインバウンドを許可するポート番号のリスト 47 | - ルートモジュール 48 | - 子モジュールとしてnetworkとcomputeを読み込んでください。 49 | - networkの変数に値を設定してください。 50 | - PJ名、VPCのCIDR、サブネットのCIDRは任意の値を設定してください。 51 | - computeの変数に値を設定してください。 52 | - VPC IDやサブネットIDはnetworkモジュールの値を参照してください。 53 | - 変数appにはfrontを設定してください。 54 | - セキュリティーグループでインバウンドを許可するCIDRは自身の端末のIPアドレスを設定してください。[IPアドレスの確認](https://www.cman.jp/network/support/go_access.cgi) 55 | - セキュリティーグループでインバウンドを許可するポート番号は80と443を設定してください。 56 | 57 | コードが書けたらinitしてplanしてapplyしてください。 58 | 以下リソースが作成されていることを確認してください。 59 | 60 | - VPC 61 | - Nameが指定した通りか確認します。 62 | - CIDRが指定した通りか確認します。 63 | - サブネット 64 | - Nameが指定した通りか確認します。 65 | - CIDRが指定した通りか確認します。 66 | - EC2 67 | - Nameが指定した通りか確認します。 68 | - AMIがAmazonlinux2か確認します。 69 | - 指定したサブネットにあるか確認します。 70 | - 指定したSGがついているか確認します。 71 | - ルートボリュームが暗号化されているか確認します。 72 | - セキュリティーグループ 73 | - Nameが指定した通りか確認します。 74 | - 指定した宛先からの指定ポートのインバウンドが許可されているか確認します。 75 | 76 | 以下リソースが作成されていないことを確認してください。 77 | 78 | - S3バケット 79 | 80 | **プラクティス2** 81 | 82 | - ルートモジュールに以下の修正を加えてください。 83 | - 追加でcomputeのモジュールを読み込みます。 84 | - networkの変数に値を設定してください。 85 | - computeの変数に値を設定してください。 86 | - VPC IDやサブネットIDはnetworkモジュールの値を参照してください。 87 | - 変数appにはbackを設定してください。 88 | - セキュリティーグループでインバウンドを許可するCIDRは自身の端末のIPアドレスを設定してください。[IPアドレスの確認](https://www.cman.jp/network/support/go_access.cgi) 89 | - セキュリティーグループでインバウンドを許可するポート番号は80と443を設定してください。 90 | 91 | コードがかけたらinitしてplanしてapplyしてください。 92 | 以下リソースが追加で作成されていることを確認してください。 93 | 94 | - EC2 95 | - Nameが指定した通りか確認します。 96 | - AMIがAmazonlinux2か確認します。 97 | - 指定したサブネットにあるか確認します。 98 | - 指定したSGがついているか確認します。 99 | - ルートボリュームが暗号化されていないことを確認します。 100 | - セキュリティーグループ 101 | - Nameが指定した通りか確認します。 102 | - 指定した宛先からの指定ポートのインバウンドが許可されているか確認します。 103 | - S3バケット 104 | - Nameが指定した通りか確認します。 105 | 106 | # 後片付け 107 | 108 | - destroyします。またはmain.tfをすべてコメントアウトしてapplyします。 109 | 110 | -------------------------------------------------------------------------------- /step7-test/compute/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_ssm_parameter" "al2" { 2 | name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" 3 | } 4 | 5 | resource "aws_instance" "this" { 6 | ami = data.aws_ssm_parameter.al2.value 7 | instance_type = var.type 8 | subnet_id = var.subnet-id 9 | security_groups = [aws_security_group.this.id] 10 | 11 | root_block_device { 12 | encrypted = var.app == "front" ? true : false 13 | } 14 | 15 | tags = { 16 | "Name" = "${var.pj}-${var.app}" 17 | } 18 | } 19 | 20 | resource "aws_security_group" "this" { 21 | name = "${var.pj}-${var.app}-ec2-sg" 22 | description = "${var.pj}-${var.app} ec2 sg" 23 | vpc_id = var.vpc-id 24 | 25 | dynamic "ingress" { 26 | for_each = toset(var.allow-tcp-ports) 27 | content { 28 | from_port = ingress.key 29 | to_port = ingress.key 30 | protocol = "tcp" 31 | cidr_blocks = [var.allow-access-cidr] 32 | } 33 | } 34 | 35 | tags = { 36 | "Name" = "${var.pj}-${var.app}" 37 | } 38 | } 39 | 40 | resource "aws_s3_bucket" "this" { 41 | for_each = var.app == "back" ? { app = "back" } : {} 42 | bucket = "${var.pj}-${var.app}-bucket" 43 | 44 | tags = { 45 | "Name" = "${var.pj}-${var.app}" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /step7-test/compute/variables.tf: -------------------------------------------------------------------------------- 1 | variable "pj" { 2 | type = string 3 | description = "PJ名" 4 | } 5 | 6 | variable "app" { 7 | type = string 8 | description = "アプリケーション名" 9 | } 10 | 11 | variable "type" { 12 | type = string 13 | description = "EC2のインスタンスタイプ" 14 | } 15 | 16 | variable "vpc-id" { 17 | type = string 18 | description = "EC2をデプロイするVPCのID" 19 | } 20 | 21 | variable "subnet-id" { 22 | type = string 23 | description = "EC2をデプロイするサブネットのID" 24 | } 25 | 26 | variable "allow-access-cidr" { 27 | type = string 28 | description = "EC2にアクセスを許可するCIDR" 29 | } 30 | 31 | variable "allow-tcp-ports" { 32 | type = list(string) 33 | description = "EC2にアクセスを許可するTCP PORT" 34 | } -------------------------------------------------------------------------------- /step7-test/main.tf: -------------------------------------------------------------------------------- 1 | module "network" { 2 | source = "./network" 3 | 4 | pj = var.pj 5 | vpc-cidr = var.vpc-cidr 6 | subnet-cidr = var.subnet-cidr 7 | } 8 | 9 | module "compute" { 10 | source = "./compute" 11 | 12 | pj = var.pj 13 | app = "front" 14 | type = var.type 15 | vpc-id = module.network.vpc-id 16 | subnet-id = module.network.subnet-id 17 | allow-access-cidr = var.allow-access-cidr 18 | allow-tcp-ports = var.allow-tcp-ports 19 | } 20 | 21 | module "backend" { 22 | source = "./compute" 23 | 24 | pj = var.pj 25 | app = "back" 26 | type = var.type 27 | vpc-id = module.network.vpc-id 28 | subnet-id = module.network.subnet-id 29 | allow-access-cidr = var.allow-access-cidr 30 | allow-tcp-ports = var.allow-tcp-ports 31 | } 32 | -------------------------------------------------------------------------------- /step7-test/network/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "main" { 2 | cidr_block = var.vpc-cidr 3 | 4 | tags = { 5 | "Name" = "${var.pj}-vpc" 6 | } 7 | } 8 | 9 | resource "aws_subnet" "private" { 10 | vpc_id = aws_vpc.main.id 11 | cidr_block = var.subnet-cidr 12 | 13 | tags = { 14 | "Name" = "${var.pj}-private" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /step7-test/network/output.tf: -------------------------------------------------------------------------------- 1 | output "vpc-id" { 2 | value = aws_vpc.main.id 3 | } 4 | 5 | output "subnet-id" { 6 | value = aws_subnet.private.id 7 | } 8 | -------------------------------------------------------------------------------- /step7-test/network/variables.tf: -------------------------------------------------------------------------------- 1 | variable "pj" { 2 | type = string 3 | description = "PJ名" 4 | } 5 | 6 | variable "vpc-cidr" { 7 | type = string 8 | description = "VPCのCIDR" 9 | } 10 | 11 | variable "subnet-cidr" { 12 | type = string 13 | description = "サブネットのCIDRのリスト" 14 | } 15 | -------------------------------------------------------------------------------- /step7-test/terraform.tfvars: -------------------------------------------------------------------------------- 1 | pj = "tfpractice" 2 | vpc-cidr = "10.1.0.0/16" 3 | subnet-cidr = "10.1.10.0/24" 4 | type = "t3.micro" 5 | allow-access-cidr = "192.168.0.1/32" 6 | allow-tcp-ports = ["80", "443"] 7 | -------------------------------------------------------------------------------- /step7-test/variables.tf: -------------------------------------------------------------------------------- 1 | variable "pj" { 2 | type = string 3 | description = "PJ名" 4 | } 5 | 6 | variable "vpc-cidr" { 7 | type = string 8 | description = "VPCのCIDR" 9 | } 10 | 11 | variable "type" { 12 | type = string 13 | description = "EC2インスタンスのタイプ" 14 | } 15 | 16 | variable "subnet-cidr" { 17 | type = string 18 | description = "サブネットのCIDRのリスト" 19 | } 20 | 21 | variable "allow-access-cidr" { 22 | type = string 23 | description = "EC2にアクセスを許可するCIDR" 24 | } 25 | 26 | variable "allow-tcp-ports" { 27 | type = list(string) 28 | description = "EC2にアクセスを許可するTCP PORT" 29 | } 30 | -------------------------------------------------------------------------------- /step7-test/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | version = ">= 4.49.0" 5 | } 6 | } 7 | } 8 | 9 | provider "aws" { 10 | region = "ap-south-1" 11 | default_tags { 12 | tags = { 13 | Pj = "tfpractice" 14 | Owner = "mori" 15 | Steps = "step7" 16 | } 17 | } 18 | } 19 | --------------------------------------------------------------------------------