@k2works/claude-code-booster 6.3.0 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +61 -61
  3. package/bin/claude-code-booster +113 -113
  4. package/lib/assets/.claude/README.md +287 -287
  5. package/lib/assets/.claude/agents/xp-architect.md +250 -250
  6. package/lib/assets/.claude/agents/xp-executive.md +207 -207
  7. package/lib/assets/.claude/agents/xp-interaction-designer.md +239 -239
  8. package/lib/assets/.claude/agents/xp-product-manager.md +245 -245
  9. package/lib/assets/.claude/agents/xp-programmer.md +268 -268
  10. package/lib/assets/.claude/agents/xp-project-manager.md +229 -229
  11. package/lib/assets/.claude/agents/xp-technical-writer.md +224 -224
  12. package/lib/assets/.claude/agents/xp-tester.md +265 -265
  13. package/lib/assets/.claude/agents/xp-user-representative.md +204 -204
  14. package/lib/assets/.claude/scripts/generate-inception-deck.mjs +911 -911
  15. package/lib/assets/.claude/settings.json +11 -11
  16. package/lib/assets/.claude/skills/ai-agent-guidelines/SKILL.md +111 -111
  17. package/lib/assets/.claude/skills/analyzing-architecture/SKILL.md +83 -83
  18. package/lib/assets/.claude/skills/analyzing-business-architecture/SKILL.md +122 -122
  19. package/lib/assets/.claude/skills/analyzing-business-case/SKILL.md +148 -148
  20. package/lib/assets/.claude/skills/analyzing-business-strategy/SKILL.md +277 -277
  21. package/lib/assets/.claude/skills/analyzing-data-model/SKILL.md +77 -77
  22. package/lib/assets/.claude/skills/analyzing-domain-model/SKILL.md +117 -117
  23. package/lib/assets/.claude/skills/analyzing-inception-deck/SKILL.md +84 -84
  24. package/lib/assets/.claude/skills/analyzing-non-functional/SKILL.md +95 -95
  25. package/lib/assets/.claude/skills/analyzing-operation/SKILL.md +95 -95
  26. package/lib/assets/.claude/skills/analyzing-requirements/SKILL.md +91 -91
  27. package/lib/assets/.claude/skills/analyzing-review/SKILL.md +174 -174
  28. package/lib/assets/.claude/skills/analyzing-tech-stack/SKILL.md +101 -101
  29. package/lib/assets/.claude/skills/analyzing-test-strategy/SKILL.md +89 -89
  30. package/lib/assets/.claude/skills/analyzing-ui-design/SKILL.md +80 -80
  31. package/lib/assets/.claude/skills/analyzing-usecases/SKILL.md +72 -72
  32. package/lib/assets/.claude/skills/closing-iteration/SKILL.md +176 -176
  33. package/lib/assets/.claude/skills/creating-adr/SKILL.md +117 -117
  34. package/lib/assets/.claude/skills/creating-development-strategy/SKILL.md +151 -127
  35. package/lib/assets/.claude/skills/creating-iteration-report/SKILL.md +210 -210
  36. package/lib/assets/.claude/skills/creating-journal/SKILL.md +140 -140
  37. package/lib/assets/.claude/skills/creating-manual/SKILL.md +199 -199
  38. package/lib/assets/.claude/skills/creating-release-report/SKILL.md +161 -161
  39. package/lib/assets/.claude/skills/creating-slides/SKILL.md +86 -86
  40. package/lib/assets/.claude/skills/creating-slides/agents/openai.yaml +4 -4
  41. package/lib/assets/.claude/skills/creating-slides/references/manga-css.md +45 -45
  42. package/lib/assets/.claude/skills/creating-slides/scripts/slide_tools.py +239 -239
  43. package/lib/assets/.claude/skills/developing-backend/SKILL.md +100 -100
  44. package/lib/assets/.claude/skills/developing-frontend/SKILL.md +93 -93
  45. package/lib/assets/.claude/skills/developing-release/SKILL.md +120 -120
  46. package/lib/assets/.claude/skills/developing-review/SKILL.md +175 -175
  47. package/lib/assets/.claude/skills/developing-uiux-review/SKILL.md +207 -207
  48. package/lib/assets/.claude/skills/generating-bmc/SKILL.md +123 -123
  49. package/lib/assets/.claude/skills/generating-slides/SKILL.md +94 -94
  50. package/lib/assets/.claude/skills/git-commit/SKILL.md +81 -81
  51. package/lib/assets/.claude/skills/killing-processes/SKILL.md +44 -44
  52. package/lib/assets/.claude/skills/migrating-ai-dlc/evals/evals.json +59 -59
  53. package/lib/assets/.claude/skills/opening-iteration/SKILL.md +124 -124
  54. package/lib/assets/.claude/skills/operating-backup/SKILL.md +59 -59
  55. package/lib/assets/.claude/skills/operating-cicd/SKILL.md +54 -54
  56. package/lib/assets/.claude/skills/operating-deploy/SKILL.md +67 -67
  57. package/lib/assets/.claude/skills/operating-docs/SKILL.md +219 -219
  58. package/lib/assets/.claude/skills/operating-provision/SKILL.md +77 -77
  59. package/lib/assets/.claude/skills/operating-qt/SKILL.md +147 -147
  60. package/lib/assets/.claude/skills/operating-review/SKILL.md +171 -171
  61. package/lib/assets/.claude/skills/operating-script/SKILL.md +145 -145
  62. package/lib/assets/.claude/skills/operating-setup/SKILL.md +63 -63
  63. package/lib/assets/.claude/skills/orchestrating-analysis/SKILL.md +110 -110
  64. package/lib/assets/.claude/skills/orchestrating-development/SKILL.md +172 -172
  65. package/lib/assets/.claude/skills/orchestrating-operation/SKILL.md +161 -161
  66. package/lib/assets/.claude/skills/orchestrating-project/SKILL.md +149 -149
  67. package/lib/assets/.claude/skills/planning-releases/SKILL.md +121 -121
  68. package/lib/assets/.claude/skills/practicing-getting-start-tdd/SKILL.md +262 -262
  69. package/lib/assets/.claude/skills/syncing-github-project/SKILL.md +151 -151
  70. package/lib/assets/.claude/skills/tracking-progress/SKILL.md +91 -91
  71. package/lib/assets/.claude/skills/validating-design/SKILL.md +130 -130
  72. package/lib/assets/.claude/skills/validating-iteration-plan/SKILL.md +238 -238
  73. package/lib/assets/.claude/skills/validating-operating-setup/SKILL.md +118 -118
  74. package/lib/assets/.devcontainer/devcontainer.json +37 -37
  75. package/lib/assets/.env.example +32 -32
  76. package/lib/assets/.gitattributes +4 -4
  77. package/lib/assets/.github/workflows/docker-publish.yml +77 -77
  78. package/lib/assets/.github/workflows/mkdocs.yml +39 -39
  79. package/lib/assets/AGENTS.md +102 -102
  80. package/lib/assets/README.md +304 -304
  81. package/lib/assets/docker-compose.yml +33 -33
  82. package/lib/assets/docs/adr/index.md +10 -10
  83. package/lib/assets/docs/article/index.md +2 -1
  84. package/lib/assets/docs/assets/css/extra.css +29 -29
  85. package/lib/assets/docs/assets/js/extra.js +48 -48
  86. package/lib/assets/docs/assets/js/mathjax.js +27 -27
  87. package/lib/assets/docs/design/index.md +44 -44
  88. package/lib/assets/docs/development/index.md +39 -39
  89. package/lib/assets/docs/index.md +1 -1
  90. package/lib/assets/docs/log.md +3 -0
  91. package/lib/assets/docs/operation/index.md +41 -41
  92. package/lib/assets/docs/reference/BDD/345/260/216/345/205/245/343/202/254/343/202/244/343/203/211.md +766 -0
  93. package/lib/assets/docs/reference/CodexCLIMCP/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/343/203/225/343/203/255/343/203/274.md +555 -555
  94. package/lib/assets/docs/reference/CodexCLIMCP/343/202/265/343/203/274/343/203/220/343/203/274/350/250/255/345/256/232/346/211/213/351/240/206.md +351 -351
  95. package/lib/assets/docs/reference/Java/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +592 -592
  96. package/lib/assets/docs/reference/OKF/345/260/216/345/205/245/343/202/254/343/202/244/343/203/211_V0.2.md +965 -965
  97. package/lib/assets/docs/reference/SonarQube/343/203/255/343/203/274/343/202/253/343/203/253/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +652 -652
  98. package/lib/assets/docs/reference/TypeScript/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +476 -476
  99. package/lib/assets/docs/reference/UI/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +460 -460
  100. package/lib/assets/docs/reference/Vim/346/223/215/344/275/234/343/203/236/343/203/213/343/203/245/343/202/242/343/203/253.md +177 -177
  101. package/lib/assets/docs/reference/images/Ansoff.drawio.svg +3 -3
  102. package/lib/assets/docs/reference/images/BMC.drawio.svg +3 -3
  103. package/lib/assets/docs/reference/images/BrandBasicStrategy.drawio.svg +3 -3
  104. package/lib/assets/docs/reference/images/BrandCategorization.drawio.svg +3 -3
  105. package/lib/assets/docs/reference/images/BrandRecurutementStrategy.drawio.svg +3 -3
  106. package/lib/assets/docs/reference/images/BrandValue.drawio.svg +3 -3
  107. package/lib/assets/docs/reference/images/BusinessActivitiy.svg +3 -3
  108. package/lib/assets/docs/reference/images/HRM.drawio.svg +3 -3
  109. package/lib/assets/docs/reference/images/MarketingStructure.drawio.svg +3 -3
  110. package/lib/assets/docs/reference/images/OrganizationElemnts.svg +3 -3
  111. package/lib/assets/docs/reference/images/PPM.drawio.svg +3 -3
  112. package/lib/assets/docs/reference/images/PositioningMap.drawio.svg +3 -3
  113. package/lib/assets/docs/reference/images/ProductLayer.drawio.svg +3 -3
  114. package/lib/assets/docs/reference/images/ProductMix.drawio.svg +3 -3
  115. package/lib/assets/docs/reference/images/SWOT.drawio.svg +3 -3
  116. package/lib/assets/docs/reference/images/TargetMarket.drawio.svg +3 -3
  117. package/lib/assets/docs/reference/images/ThreeGenericStrategies.drawio.svg +3 -3
  118. package/lib/assets/docs/reference/images/VRIO.drawio.svg +3 -3
  119. package/lib/assets/docs/reference/images/ValueChain.drawio.svg +3 -3
  120. package/lib/assets/docs/reference/index.md +1 -0
  121. package/lib/assets/docs/reference//343/202/210/343/201/204/343/202/275/343/203/225/343/203/210/343/202/246/343/202/247/343/202/242/343/201/250/343/201/257.md +250 -250
  122. package/lib/assets/docs/reference//343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +2226 -2226
  123. package/lib/assets/docs/reference//343/202/244/343/203/263/343/203/225/343/203/251/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +1888 -1888
  124. package/lib/assets/docs/reference//343/202/250/343/202/257/343/202/271/343/203/210/343/203/252/343/203/274/343/203/240/343/203/227/343/203/255/343/202/260/343/203/251/343/203/237/343/203/263/343/202/260.md +549 -549
  125. package/lib/assets/docs/reference//343/202/263/343/203/274/343/203/207/343/202/243/343/203/263/343/202/260/343/201/250/343/203/206/343/202/271/343/203/210/343/202/254/343/202/244/343/203/211.md +715 -715
  126. package/lib/assets/docs/reference//343/203/206/343/202/271/343/203/210/346/210/246/347/225/245/343/202/254/343/202/244/343/203/211.md +1323 -1323
  127. package/lib/assets/docs/reference//343/203/207/343/203/274/343/202/277/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +321 -321
  128. package/lib/assets/docs/reference//343/203/211/343/203/241/343/202/244/343/203/263/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +609 -609
  129. package/lib/assets/docs/reference//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +537 -537
  130. package/lib/assets/docs/reference//343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +699 -699
  131. package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/202/254/343/202/244/343/203/211.md +470 -470
  132. package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/203/273/343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273/343/202/254/343/202/244/343/203/211.md +580 -580
  133. package/lib/assets/docs/reference//343/203/255/343/202/270/343/202/253/343/203/253/343/202/267/343/203/263/343/202/255/343/203/263/343/202/260.md +1376 -1376
  134. package/lib/assets/docs/reference//344/274/201/346/245/255/347/265/214/345/226/266/350/253/226.md +2647 -2647
  135. package/lib/assets/docs/reference//347/222/260/345/242/203/345/244/211/346/225/260/347/256/241/347/220/206/343/202/254/343/202/244/343/203/211.md +675 -675
  136. package/lib/assets/docs/reference//347/265/214/345/226/266/346/210/246/347/225/245/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +575 -575
  137. package/lib/assets/docs/reference//350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1258 -1258
  138. package/lib/assets/docs/reference//350/250/200/350/252/236/345/210/245/351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +527 -527
  139. package/lib/assets/docs/reference//351/201/213/345/226/266/347/256/241/347/220/206.md +1492 -1492
  140. package/lib/assets/docs/reference//351/201/213/347/224/250/343/202/271/343/202/257/343/203/252/343/203/227/343/203/210/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +430 -430
  141. package/lib/assets/docs/reference//351/201/213/347/224/250/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +402 -402
  142. package/lib/assets/docs/reference//351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +314 -314
  143. package/lib/assets/docs/reference//351/235/236/346/251/237/350/203/275/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1246 -1246
  144. package/lib/assets/docs/requirements/index.md +17 -17
  145. package/lib/assets/docs/review/index.md +5 -5
  146. package/lib/assets/docs/strategy/index.md +17 -17
  147. package/lib/assets/docs/template/ADR.md +39 -39
  148. package/lib/assets/docs/template/AWS/343/202/271/343/203/206/343/203/274/343/202/270/343/203/263/343/202/260/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +1375 -1375
  149. package/lib/assets/docs/template/AWS/343/203/227/343/203/255/343/203/200/343/202/257/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +643 -643
  150. package/lib/assets/docs/template/README.md +59 -59
  151. package/lib/assets/docs/template/index.md +24 -24
  152. package/lib/assets/docs/template//343/201/276/343/201/232/343/201/223/343/202/214/343/202/222/350/252/255/343/202/202/343/201/206/343/203/252/343/202/271/343/203/210.md +21 -21
  153. package/lib/assets/docs/template//343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +556 -556
  154. package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +68 -68
  155. package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273.md +336 -336
  156. package/lib/assets/docs/template//343/202/244/343/203/263/343/202/273/343/203/227/343/202/267/343/203/247/343/203/263/343/203/207/343/203/203/343/202/255.md +22 -22
  157. package/lib/assets/docs/template//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243.md +388 -388
  158. package/lib/assets/docs/template//343/203/252/343/203/252/343/203/274/343/202/271/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +284 -284
  159. package/lib/assets/docs/template//343/203/252/343/203/252/343/203/274/343/202/271/350/250/210/347/224/273.md +289 -289
  160. package/lib/assets/docs/template//344/272/213/344/276/213/345/210/206/346/236/220.md +522 -522
  161. package/lib/assets/docs/template//344/274/201/346/245/255/345/210/206/346/236/220.md +582 -582
  162. package/lib/assets/docs/template//345/256/214/345/205/250/345/275/242/345/274/217/343/201/256/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271.md +69 -69
  163. package/lib/assets/docs/template//350/246/201/344/273/266/345/256/232/347/276/251.md +669 -669
  164. package/lib/assets/docs/template//350/250/255/350/250/210.md +183 -183
  165. package/lib/assets/docs/template//351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +697 -697
  166. package/lib/assets/gulpfile.js +29 -29
  167. package/lib/assets/mkdocs.yml +147 -138
  168. package/lib/assets/ops/docker/sonarqube-local/docker-compose.yml +57 -57
  169. package/lib/assets/ops/scripts/journal.js +180 -180
  170. package/lib/assets/ops/scripts/mkdocs.js +82 -82
  171. package/lib/assets/ops/scripts/release.js +431 -431
  172. package/lib/assets/ops/scripts/sonar_local.js +726 -726
  173. package/lib/assets/ops/scripts/ssh.js +190 -190
  174. package/lib/assets/ops/scripts/vault.js +299 -299
  175. package/lib/assets/package-lock.json +1673 -1673
  176. package/lib/assets/package.json +45 -45
  177. package/lib/gulpfile.js +37 -37
  178. package/package.json +41 -41
  179. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/benchmark.json +0 -20
  180. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/benchmark.md +0 -13
  181. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/eval_metadata.json +0 -14
  182. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/grading.json +0 -39
  183. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/adr/001-db.md +0 -12
  184. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/adr/002-cache-strategy.md +0 -20
  185. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/adr/index.md +0 -4
  186. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/design/data-model.md +0 -12
  187. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/design/index.md +0 -3
  188. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/index.md +0 -10
  189. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/log.md +0 -7
  190. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/operation/deploy-old.md +0 -12
  191. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/operation/deploy.md +0 -12
  192. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/operation/index.md +0 -4
  193. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/docs_after/reference/OKF/345/260/216/345/205/245/343/202/254/343/202/244/343/203/211_V0.2.md +0 -965
  194. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/with_skill/outputs/report.md +0 -76
  195. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/grading.json +0 -39
  196. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/adr/001-db.md +0 -12
  197. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/adr/002-cache-strategy.md +0 -20
  198. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/adr/index.md +0 -4
  199. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/design/data-model.md +0 -12
  200. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/design/index.md +0 -3
  201. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/index.md +0 -10
  202. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/log.md +0 -7
  203. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/operation/deploy-old.md +0 -12
  204. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/operation/deploy.md +0 -12
  205. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/operation/index.md +0 -4
  206. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/docs_after/reference/OKF/345/260/216/345/205/245/343/202/254/343/202/244/343/203/211_V0.2.md +0 -965
  207. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-1-register-new-adr/without_skill/outputs/report.md +0 -22
  208. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/eval_metadata.json +0 -13
  209. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/grading.json +0 -34
  210. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/adr/001-db.md +0 -12
  211. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/adr/002-cache-strategy.md +0 -11
  212. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/adr/index.md +0 -3
  213. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/design/data-model.md +0 -14
  214. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/design/index.md +0 -3
  215. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/index.md +0 -10
  216. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/log.md +0 -7
  217. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/operation/deploy-old.md +0 -12
  218. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/operation/deploy.md +0 -12
  219. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/operation/index.md +0 -4
  220. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/docs_after/reference/OKF/345/260/216/345/205/245/343/202/254/343/202/244/343/203/211_V0.2.md +0 -965
  221. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/with_skill/outputs/report.md +0 -61
  222. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/grading.json +0 -34
  223. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/adr/001-db.md +0 -12
  224. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/adr/002-cache-strategy.md +0 -11
  225. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/adr/index.md +0 -3
  226. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/design/data-model.md +0 -13
  227. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/design/index.md +0 -3
  228. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/index.md +0 -10
  229. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/log.md +0 -7
  230. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/operation/deploy-old.md +0 -12
  231. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/operation/deploy.md +0 -12
  232. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/operation/index.md +0 -4
  233. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/docs_after/reference/OKF/345/260/216/345/205/245/343/202/254/343/202/244/343/203/211_V0.2.md +0 -965
  234. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-2-verify-reviewed-doc/without_skill/outputs/report.md +0 -18
  235. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/eval_metadata.json +0 -13
  236. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/grading.json +0 -34
  237. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/adr/001-db.md +0 -12
  238. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/adr/002-cache-strategy.md +0 -11
  239. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/adr/index.md +0 -3
  240. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/design/data-model.md +0 -12
  241. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/design/index.md +0 -3
  242. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/index.md +0 -10
  243. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/log.md +0 -7
  244. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/operation/deploy-old.md +0 -13
  245. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/operation/deploy.md +0 -12
  246. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/operation/index.md +0 -7
  247. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/docs_after/reference/OKF/345/260/216/345/205/245/343/202/254/343/202/244/343/203/211_V0.2.md +0 -965
  248. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/with_skill/outputs/report.md +0 -83
  249. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/grading.json +0 -34
  250. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/adr/001-db.md +0 -12
  251. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/adr/002-cache-strategy.md +0 -11
  252. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/adr/index.md +0 -3
  253. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/design/data-model.md +0 -12
  254. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/design/index.md +0 -3
  255. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/index.md +0 -10
  256. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/log.md +0 -7
  257. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/operation/deploy-old.md +0 -14
  258. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/operation/deploy.md +0 -12
  259. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/operation/index.md +0 -4
  260. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/docs_after/reference/OKF/345/260/216/345/205/245/343/202/254/343/202/244/343/203/211_V0.2.md +0 -965
  261. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/eval-3-deprecate-replaced-doc/without_skill/outputs/report.md +0 -15
  262. package/lib/assets/.claude/skills/apply-okf-workspace/iteration-1/review.html +0 -1325
  263. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/benchmark.json +0 -20
  264. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/benchmark.md +0 -13
  265. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/eval_metadata.json +0 -15
  266. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/with_skill/grading.json +0 -44
  267. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/with_skill/outputs/docs_after/adr/index.md +0 -10
  268. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/with_skill/outputs/docs_after/index.md +0 -15
  269. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/with_skill/outputs/docs_after/log.md +0 -5
  270. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/with_skill/outputs/docs_after/reference/OKF/345/260/216/345/205/245/343/202/254/343/202/244/343/203/211_V0.2.md +0 -966
  271. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/with_skill/outputs/docs_after/reference/index.md +0 -6
  272. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/with_skill/outputs/docs_after/reference//343/202/210/343/201/204/343/202/275/343/203/225/343/203/210/343/202/246/343/202/247/343/202/242/343/201/250/343/201/257.md +0 -261
  273. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/with_skill/outputs/docs_after/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/202/254/343/202/244/343/203/211.md +0 -480
  274. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/with_skill/outputs/docs_after/reference//347/222/260/345/242/203/345/244/211/346/225/260/347/256/241/347/220/206/343/202/254/343/202/244/343/203/211.md +0 -681
  275. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/with_skill/outputs/report.md +0 -85
  276. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/without_skill/grading.json +0 -44
  277. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/without_skill/outputs/docs_after/adr/index.md +0 -8
  278. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/without_skill/outputs/docs_after/index.md +0 -11
  279. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/without_skill/outputs/docs_after/log.md +0 -7
  280. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/without_skill/outputs/docs_after/reference/OKF/345/260/216/345/205/245/343/202/254/343/202/244/343/203/211_V0.2.md +0 -964
  281. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/without_skill/outputs/docs_after/reference/index.md +0 -9
  282. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/without_skill/outputs/docs_after/reference//343/202/210/343/201/204/343/202/275/343/203/225/343/203/210/343/202/246/343/202/247/343/202/242/343/201/250/343/201/257.md +0 -250
  283. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/without_skill/outputs/docs_after/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/202/254/343/202/244/343/203/211.md +0 -469
  284. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/without_skill/outputs/docs_after/reference//347/222/260/345/242/203/345/244/211/346/225/260/347/256/241/347/220/206/343/202/254/343/202/244/343/203/211.md +0 -677
  285. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-1-migrate-docs/without_skill/outputs/report.md +0 -49
  286. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-2-upgrade-v01-to-v02/eval_metadata.json +0 -13
  287. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-2-upgrade-v01-to-v02/with_skill/grading.json +0 -34
  288. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-2-upgrade-v01-to-v02/with_skill/outputs/knowledge_after/index.md +0 -7
  289. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-2-upgrade-v01-to-v02/with_skill/outputs/knowledge_after/log.md +0 -7
  290. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-2-upgrade-v01-to-v02/with_skill/outputs/knowledge_after/metrics/churn.md +0 -9
  291. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-2-upgrade-v01-to-v02/with_skill/outputs/knowledge_after/metrics/income-statement.md +0 -15
  292. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/eval-2-upgrade-v01-to-v02/with_skill/outputs/report.md +0 -147
  293. package/lib/assets/.claude/skills/migrating-okf-workspace/iteration-1/review.html +0 -1325
@@ -1,1888 +1,1888 @@
1
- ---
2
- type: Reference
3
- title: "インフラ設計ガイド"
4
- description: "Infrastructure as Code(IaC)によるインフラの設計・構築・管理の指針と、環境構成・ネットワーク・セキュリティの設計方針。"
5
- tags: [reference]
6
- status: stable
7
- generated: { by: human:kakimomokuri, at: 2025-09-11T10:30:55Z }
8
- ---
9
-
10
- # インフラ設計ガイド
11
-
12
- ## 1. 概要
13
-
14
- ### 1.1 目的
15
-
16
- Infrastructure as Code (IaC) によるインフラストラクチャの設計・構築・管理における規律とベストプラクティスを定義する。
17
-
18
- ### 1.2 対象システム
19
-
20
- - システム名: 会議室予約システム
21
- - クラウド基盤: AWS
22
- - IaC ツール: Terraform
23
- - アーキテクチャ: ヘキサゴナルアーキテクチャ
24
-
25
- ### 1.3 基本原則
26
-
27
- ```plantuml
28
- @startuml "IaC原則"
29
- title Infrastructure as Code 基本原則
30
-
31
- rectangle "コード化" as code
32
- rectangle "バージョン管理" as version
33
- rectangle "自動化" as automation
34
- rectangle "再現性" as reproducible
35
- rectangle "モジュール性" as modularity
36
- rectangle "テスト" as testing
37
-
38
- code --> version : すべてのインフラを\nコードで定義
39
- version --> automation : 変更履歴を\n追跡・管理
40
- automation --> reproducible : 手動作業を\n排除
41
- reproducible --> modularity : 同じ環境を\n何度でも構築
42
- modularity --> testing : 再利用可能な\nコンポーネント化
43
- testing --> code : 品質保証と\n継続的改善
44
-
45
- @enduml
46
- ```
47
-
48
- ## 2. プロジェクト構造
49
-
50
- ### 2.1 ディレクトリ構造
51
-
52
- ```plantuml
53
- @startuml "プロジェクト構造"
54
- folder "terraform" as terraform {
55
- folder "modules" as modules {
56
- folder "networking" as networking
57
- folder "compute" as compute
58
- folder "database" as database
59
- folder "security" as security
60
- folder "monitoring" as monitoring
61
- }
62
- folder "environments" as environments {
63
- folder "global" as global
64
- folder "dev" as dev
65
- folder "stage" as stage
66
- folder "prod" as prod
67
- }
68
- folder "examples" as examples
69
- folder "test" as test
70
- }
71
-
72
- modules --> networking : VPC、Subnet、\nRoute Table
73
- modules --> compute : EC2、ASG、ALB
74
- modules --> database : RDS、DynamoDB
75
- modules --> security : IAM、Security Groups
76
- modules --> monitoring : CloudWatch、SNS
77
-
78
- environments --> global : 共通変数・設定
79
- environments --> dev : 開発環境
80
- environments --> stage : ステージング環境
81
- environments --> prod : 本番環境
82
-
83
- @enduml
84
- ```
85
-
86
- ### 2.2 命名規則
87
-
88
- #### 2.2.1 リソース命名
89
-
90
- ```yaml
91
- 命名パターン: "{project}-{environment}-{service}-{resource}"
92
-
93
- 例:
94
- - VPC: "meeting-room-prod-vpc"
95
- - Subnet: "meeting-room-prod-public-subnet-1a"
96
- - Security Group: "meeting-room-prod-web-sg"
97
- - RDS: "meeting-room-prod-postgres-primary"
98
- - ALB: "meeting-room-prod-app-alb"
99
- ```
100
-
101
- #### 2.2.2 Terraform ファイル命名
102
-
103
- ```yaml
104
- 標準ファイル構成:
105
- - main.tf: メインの構成定義
106
- - variables.tf: 入力変数定義
107
- - outputs.tf: 出力値定義
108
- - versions.tf: プロバイダーバージョン指定
109
- - locals.tf: ローカル変数定義 (必要に応じて)
110
- - data.tf: データソース定義 (必要に応じて)
111
- ```
112
-
113
- ## 3. モジュール設計
114
-
115
- ### 3.1 モジュール化の原則
116
-
117
- ```plantuml
118
- @startuml "モジュール設計原則"
119
- rectangle "単一責任" as single_responsibility {
120
- note right : 1つのモジュールは\n1つの責任を持つ
121
- }
122
-
123
- rectangle "再利用性" as reusability {
124
- note right : 複数の環境・プロジェクトで\n利用可能
125
- }
126
-
127
- rectangle "構成可能性" as configurability {
128
- note right : 変数により動作を\nカスタマイズ可能
129
- }
130
-
131
- rectangle "テスト容易性" as testability {
132
- note right : 独立してテスト\nできる設計
133
- }
134
-
135
- single_responsibility --> reusability
136
- reusability --> configurability
137
- configurability --> testability
138
- testability --> single_responsibility
139
-
140
- @enduml
141
- ```
142
-
143
- ### 3.2 会議室予約システム モジュール構成
144
-
145
- #### 3.2.1 ネットワークモジュール
146
-
147
- ```terraform
148
- # modules/networking/vpc/main.tf
149
- resource "aws_vpc" "main" {
150
- cidr_block = var.vpc_cidr
151
- enable_dns_hostnames = true
152
- enable_dns_support = true
153
-
154
- tags = merge(var.tags, {
155
- Name = "${var.name_prefix}-vpc"
156
- })
157
- }
158
-
159
- resource "aws_subnet" "public" {
160
- count = length(var.public_subnet_cidrs)
161
-
162
- vpc_id = aws_vpc.main.id
163
- cidr_block = var.public_subnet_cidrs[count.index]
164
- availability_zone = var.availability_zones[count.index]
165
- map_public_ip_on_launch = true
166
-
167
- tags = merge(var.tags, {
168
- Name = "${var.name_prefix}-public-subnet-${count.index + 1}"
169
- Type = "Public"
170
- })
171
- }
172
-
173
- resource "aws_subnet" "private" {
174
- count = length(var.private_subnet_cidrs)
175
-
176
- vpc_id = aws_vpc.main.id
177
- cidr_block = var.private_subnet_cidrs[count.index]
178
- availability_zone = var.availability_zones[count.index]
179
-
180
- tags = merge(var.tags, {
181
- Name = "${var.name_prefix}-private-subnet-${count.index + 1}"
182
- Type = "Private"
183
- })
184
- }
185
-
186
- resource "aws_internet_gateway" "main" {
187
- vpc_id = aws_vpc.main.id
188
-
189
- tags = merge(var.tags, {
190
- Name = "${var.name_prefix}-igw"
191
- })
192
- }
193
-
194
- resource "aws_nat_gateway" "main" {
195
- count = var.enable_nat_gateway ? length(aws_subnet.public) : 0
196
-
197
- allocation_id = aws_eip.nat[count.index].id
198
- subnet_id = aws_subnet.public[count.index].id
199
-
200
- tags = merge(var.tags, {
201
- Name = "${var.name_prefix}-nat-gw-${count.index + 1}"
202
- })
203
-
204
- depends_on = [aws_internet_gateway.main]
205
- }
206
-
207
- resource "aws_eip" "nat" {
208
- count = var.enable_nat_gateway ? length(aws_subnet.public) : 0
209
-
210
- domain = "vpc"
211
-
212
- tags = merge(var.tags, {
213
- Name = "${var.name_prefix}-nat-eip-${count.index + 1}"
214
- })
215
-
216
- depends_on = [aws_internet_gateway.main]
217
- }
218
- ```
219
-
220
- #### 3.2.2 コンピュートモジュール
221
-
222
- ```terraform
223
- # modules/compute/web-app/main.tf
224
- resource "aws_launch_template" "main" {
225
- name_prefix = "${var.name_prefix}-lt"
226
- image_id = var.ami_id
227
- instance_type = var.instance_type
228
- key_name = var.key_name
229
-
230
- vpc_security_group_ids = [aws_security_group.web.id]
231
-
232
- user_data = base64encode(templatefile("${path.module}/user_data.sh", {
233
- database_url = var.database_url
234
- app_version = var.app_version
235
- environment = var.environment
236
- }))
237
-
238
- tag_specifications {
239
- resource_type = "instance"
240
- tags = merge(var.tags, {
241
- Name = "${var.name_prefix}-instance"
242
- })
243
- }
244
-
245
- lifecycle {
246
- create_before_destroy = true
247
- precondition {
248
- condition = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
249
- error_message = "Instance type must be t3.micro, t3.small, or t3.medium for cost optimization."
250
- }
251
- }
252
- }
253
-
254
- resource "aws_autoscaling_group" "main" {
255
- name = "${var.name_prefix}-asg"
256
- vpc_zone_identifier = var.subnet_ids
257
- target_group_arns = [aws_lb_target_group.main.arn]
258
- health_check_type = "ELB"
259
- health_check_grace_period = 300
260
-
261
- min_size = var.min_size
262
- max_size = var.max_size
263
- desired_capacity = var.desired_capacity
264
-
265
- launch_template {
266
- id = aws_launch_template.main.id
267
- version = "$Latest"
268
- }
269
-
270
- instance_refresh {
271
- strategy = "Rolling"
272
- preferences {
273
- min_healthy_percentage = 50
274
- instance_warmup = 300
275
- }
276
- }
277
-
278
- tag {
279
- key = "Name"
280
- value = "${var.name_prefix}-asg"
281
- propagate_at_launch = false
282
- }
283
-
284
- dynamic "tag" {
285
- for_each = var.tags
286
- content {
287
- key = tag.key
288
- value = tag.value
289
- propagate_at_launch = true
290
- }
291
- }
292
-
293
- lifecycle {
294
- create_before_destroy = true
295
- postcondition {
296
- condition = length(self.availability_zones) > 1
297
- error_message = "Auto Scaling Group must span multiple AZs for high availability."
298
- }
299
- }
300
- }
301
-
302
- resource "aws_lb" "main" {
303
- name = "${var.name_prefix}-alb"
304
- internal = false
305
- load_balancer_type = "application"
306
- security_groups = [aws_security_group.alb.id]
307
- subnets = var.subnet_ids
308
-
309
- enable_deletion_protection = var.environment == "prod" ? true : false
310
-
311
- tags = merge(var.tags, {
312
- Name = "${var.name_prefix}-alb"
313
- })
314
- }
315
-
316
- resource "aws_lb_target_group" "main" {
317
- name = "${var.name_prefix}-tg"
318
- port = 8080
319
- protocol = "HTTP"
320
- vpc_id = var.vpc_id
321
-
322
- health_check {
323
- enabled = true
324
- healthy_threshold = 2
325
- unhealthy_threshold = 2
326
- timeout = 5
327
- interval = 30
328
- path = "/health"
329
- matcher = "200"
330
- port = "traffic-port"
331
- protocol = "HTTP"
332
- }
333
-
334
- tags = merge(var.tags, {
335
- Name = "${var.name_prefix}-tg"
336
- })
337
- }
338
-
339
- resource "aws_lb_listener" "main" {
340
- load_balancer_arn = aws_lb.main.arn
341
- port = "80"
342
- protocol = "HTTP"
343
-
344
- default_action {
345
- type = "forward"
346
- target_group_arn = aws_lb_target_group.main.arn
347
- }
348
- }
349
- ```
350
-
351
- #### 3.2.3 データベースモジュール
352
-
353
- ```terraform
354
- # modules/database/postgresql/main.tf
355
- resource "aws_db_subnet_group" "main" {
356
- name = "${var.name_prefix}-db-subnet-group"
357
- subnet_ids = var.subnet_ids
358
-
359
- tags = merge(var.tags, {
360
- Name = "${var.name_prefix}-db-subnet-group"
361
- })
362
- }
363
-
364
- resource "aws_db_parameter_group" "main" {
365
- family = "postgres15"
366
- name = "${var.name_prefix}-db-params"
367
-
368
- parameter {
369
- name = "log_statement"
370
- value = "all"
371
- }
372
-
373
- parameter {
374
- name = "log_min_duration_statement"
375
- value = "1000"
376
- }
377
-
378
- tags = merge(var.tags, {
379
- Name = "${var.name_prefix}-db-params"
380
- })
381
- }
382
-
383
- resource "aws_db_instance" "main" {
384
- identifier = "${var.name_prefix}-db"
385
-
386
- allocated_storage = var.allocated_storage
387
- max_allocated_storage = var.max_allocated_storage
388
- storage_type = "gp3"
389
- storage_encrypted = true
390
-
391
- engine = "postgres"
392
- engine_version = "15.4"
393
- instance_class = var.instance_class
394
-
395
- db_name = var.database_name
396
- username = var.database_username
397
- password = var.database_password
398
-
399
- db_subnet_group_name = aws_db_subnet_group.main.name
400
- vpc_security_group_ids = [aws_security_group.database.id]
401
- parameter_group_name = aws_db_parameter_group.main.name
402
-
403
- backup_retention_period = var.backup_retention_period
404
- backup_window = var.backup_window
405
- maintenance_window = var.maintenance_window
406
-
407
- skip_final_snapshot = var.environment != "prod"
408
- final_snapshot_identifier = var.environment == "prod" ? "${var.name_prefix}-db-final-snapshot" : null
409
-
410
- deletion_protection = var.environment == "prod" ? true : false
411
-
412
- performance_insights_enabled = var.environment == "prod" ? true : false
413
- monitoring_interval = var.environment == "prod" ? 60 : 0
414
-
415
- tags = merge(var.tags, {
416
- Name = "${var.name_prefix}-db"
417
- })
418
-
419
- lifecycle {
420
- prevent_destroy = false
421
- precondition {
422
- condition = var.allocated_storage >= 20
423
- error_message = "Database must have at least 20GB of storage."
424
- }
425
- }
426
- }
427
-
428
- resource "aws_db_instance" "replica" {
429
- count = var.create_replica ? 1 : 0
430
-
431
- identifier = "${var.name_prefix}-db-replica"
432
-
433
- replicate_source_db = aws_db_instance.main.identifier
434
- instance_class = var.replica_instance_class
435
-
436
- skip_final_snapshot = true
437
- deletion_protection = false
438
-
439
- tags = merge(var.tags, {
440
- Name = "${var.name_prefix}-db-replica"
441
- })
442
- }
443
- ```
444
-
445
- ### 3.3 入力検証とセキュリティ
446
-
447
- ```terraform
448
- # modules/compute/web-app/variables.tf
449
- variable "instance_type" {
450
- description = "EC2 instance type"
451
- type = string
452
-
453
- validation {
454
- condition = contains([
455
- "t3.micro", "t3.small", "t3.medium", "t3.large",
456
- "m5.large", "m5.xlarge", "m5.2xlarge"
457
- ], var.instance_type)
458
- error_message = "Instance type must be a supported type for this application."
459
- }
460
- }
461
-
462
- variable "min_size" {
463
- description = "Minimum number of instances in ASG"
464
- type = number
465
-
466
- validation {
467
- condition = var.min_size > 0
468
- error_message = "Minimum size must be greater than 0 to avoid outages."
469
- }
470
-
471
- validation {
472
- condition = var.min_size <= 10
473
- error_message = "Minimum size must be 10 or fewer for cost control."
474
- }
475
- }
476
-
477
- variable "environment" {
478
- description = "Environment name"
479
- type = string
480
-
481
- validation {
482
- condition = contains(["dev", "stage", "prod"], var.environment)
483
- error_message = "Environment must be one of: dev, stage, prod."
484
- }
485
- }
486
-
487
- variable "vpc_cidr" {
488
- description = "CIDR block for VPC"
489
- type = string
490
-
491
- validation {
492
- condition = can(cidrhost(var.vpc_cidr, 0))
493
- error_message = "VPC CIDR must be a valid CIDR block."
494
- }
495
- }
496
- ```
497
-
498
- ## 4. 環境管理
499
-
500
- ### 4.1 環境分離戦略
501
-
502
- ```plantuml
503
- @startuml "環境分離"
504
- rectangle "Global" as global {
505
- rectangle "共通変数" as common_vars
506
- rectangle "グローバルリソース" as global_resources
507
- rectangle "IAM ポリシー" as iam_policies
508
- }
509
-
510
- rectangle "Development" as dev {
511
- rectangle "単一AZ" as dev_az
512
- rectangle "小規模インスタンス" as dev_instance
513
- rectangle "開発用DB" as dev_db
514
- }
515
-
516
- rectangle "Staging" as stage {
517
- rectangle "マルチAZ" as stage_az
518
- rectangle "本番相当" as stage_instance
519
- rectangle "本番データのコピー" as stage_db
520
- }
521
-
522
- rectangle "Production" as prod {
523
- rectangle "マルチAZ + 冗長化" as prod_az
524
- rectangle "高性能インスタンス" as prod_instance
525
- rectangle "レプリカ + バックアップ" as prod_db
526
- }
527
-
528
- global --> dev : 共通設定継承
529
- global --> stage : 共通設定継承
530
- global --> prod : 共通設定継承
531
-
532
- @enduml
533
- ```
534
-
535
- ### 4.2 グローバル変数管理
536
-
537
- ```terraform
538
- # environments/global/variables.tf
539
- locals {
540
- project_name = "meeting-room"
541
-
542
- common_tags = {
543
- Project = local.project_name
544
- ManagedBy = "terraform"
545
- Owner = "platform-team"
546
- Repository = "meeting-room-infrastructure"
547
- }
548
-
549
- environments = {
550
- dev = {
551
- name = "dev"
552
- instance_type = "t3.micro"
553
- min_size = 1
554
- max_size = 2
555
- desired_capacity = 1
556
- db_instance_class = "db.t3.micro"
557
- backup_retention = 7
558
- multi_az = false
559
- create_replica = false
560
- }
561
-
562
- stage = {
563
- name = "stage"
564
- instance_type = "t3.small"
565
- min_size = 2
566
- max_size = 4
567
- desired_capacity = 2
568
- db_instance_class = "db.t3.small"
569
- backup_retention = 7
570
- multi_az = true
571
- create_replica = false
572
- }
573
-
574
- prod = {
575
- name = "prod"
576
- instance_type = "t3.medium"
577
- min_size = 2
578
- max_size = 8
579
- desired_capacity = 4
580
- db_instance_class = "db.r6g.large"
581
- backup_retention = 30
582
- multi_az = true
583
- create_replica = true
584
- }
585
- }
586
-
587
- vpc_configs = {
588
- dev = {
589
- vpc_cidr = "10.0.0.0/16"
590
- public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"]
591
- private_subnet_cidrs = ["10.0.3.0/24", "10.0.4.0/24"]
592
- availability_zones = ["ap-northeast-1a", "ap-northeast-1c"]
593
- }
594
-
595
- stage = {
596
- vpc_cidr = "10.1.0.0/16"
597
- public_subnet_cidrs = ["10.1.1.0/24", "10.1.2.0/24"]
598
- private_subnet_cidrs = ["10.1.3.0/24", "10.1.4.0/24"]
599
- availability_zones = ["ap-northeast-1a", "ap-northeast-1c"]
600
- }
601
-
602
- prod = {
603
- vpc_cidr = "10.2.0.0/16"
604
- public_subnet_cidrs = ["10.2.1.0/24", "10.2.2.0/24"]
605
- private_subnet_cidrs = ["10.2.3.0/24", "10.2.4.0/24"]
606
- availability_zones = ["ap-northeast-1a", "ap-northeast-1c"]
607
- }
608
- }
609
- }
610
-
611
- output "project_config" {
612
- value = {
613
- name = local.project_name
614
- tags = local.common_tags
615
- environments = local.environments
616
- vpc_configs = local.vpc_configs
617
- }
618
- }
619
- ```
620
-
621
- ### 4.3 環境固有の構成
622
-
623
- ```terraform
624
- # environments/prod/main.tf
625
- terraform {
626
- required_version = ">= 1.0"
627
-
628
- backend "s3" {
629
- bucket = "meeting-room-terraform-state"
630
- key = "prod/terraform.tfstate"
631
- region = "ap-northeast-1"
632
- dynamodb_table = "meeting-room-terraform-locks"
633
- encrypt = true
634
- }
635
-
636
- required_providers {
637
- aws = {
638
- source = "hashicorp/aws"
639
- version = "~> 5.0"
640
- }
641
- }
642
- }
643
-
644
- provider "aws" {
645
- region = "ap-northeast-1"
646
-
647
- default_tags {
648
- tags = local.common_tags
649
- }
650
- }
651
-
652
- # グローバル設定の読み込み
653
- data "terraform_remote_state" "global" {
654
- backend = "s3"
655
-
656
- config = {
657
- bucket = "meeting-room-terraform-state"
658
- key = "global/terraform.tfstate"
659
- region = "ap-northeast-1"
660
- }
661
- }
662
-
663
- locals {
664
- environment = "prod"
665
- global_config = data.terraform_remote_state.global.outputs.project_config
666
- env_config = local.global_config.environments[local.environment]
667
- vpc_config = local.global_config.vpc_configs[local.environment]
668
- common_tags = merge(local.global_config.tags, {
669
- Environment = local.environment
670
- })
671
- name_prefix = "${local.global_config.name}-${local.environment}"
672
- }
673
-
674
- # ネットワークモジュール
675
- module "vpc" {
676
- source = "../../modules/networking/vpc"
677
-
678
- name_prefix = local.name_prefix
679
-
680
- vpc_cidr = local.vpc_config.vpc_cidr
681
- public_subnet_cidrs = local.vpc_config.public_subnet_cidrs
682
- private_subnet_cidrs = local.vpc_config.private_subnet_cidrs
683
- availability_zones = local.vpc_config.availability_zones
684
-
685
- enable_nat_gateway = true
686
-
687
- tags = local.common_tags
688
- }
689
-
690
- # データベースモジュール
691
- module "database" {
692
- source = "../../modules/database/postgresql"
693
-
694
- name_prefix = local.name_prefix
695
-
696
- subnet_ids = module.vpc.private_subnet_ids
697
- vpc_id = module.vpc.vpc_id
698
-
699
- instance_class = local.env_config.db_instance_class
700
- allocated_storage = 100
701
- max_allocated_storage = 1000
702
-
703
- database_name = "meetingroom"
704
- database_username = local.db_credentials.username
705
- database_password = local.db_credentials.password
706
-
707
- backup_retention_period = local.env_config.backup_retention
708
- multi_az = local.env_config.multi_az
709
- create_replica = local.env_config.create_replica
710
-
711
- environment = local.environment
712
- tags = local.common_tags
713
- }
714
-
715
- # Webアプリケーションモジュール
716
- module "web_app" {
717
- source = "../../modules/compute/web-app"
718
-
719
- name_prefix = local.name_prefix
720
-
721
- vpc_id = module.vpc.vpc_id
722
- subnet_ids = module.vpc.public_subnet_ids
723
-
724
- instance_type = local.env_config.instance_type
725
- min_size = local.env_config.min_size
726
- max_size = local.env_config.max_size
727
- desired_capacity = local.env_config.desired_capacity
728
-
729
- database_url = "postgresql://${local.db_credentials.username}:${local.db_credentials.password}@${module.database.endpoint}/meetingroom"
730
- app_version = var.app_version
731
-
732
- environment = local.environment
733
- tags = local.common_tags
734
-
735
- depends_on = [module.database]
736
- }
737
-
738
- # Secrets Managerからのデータベース認証情報取得
739
- data "aws_secretsmanager_secret_version" "db_credentials" {
740
- secret_id = "${local.name_prefix}-db-credentials"
741
- }
742
-
743
- locals {
744
- db_credentials = jsondecode(data.aws_secretsmanager_secret_version.db_credentials.secret_string)
745
- }
746
- ```
747
-
748
- ## 5. セキュリティ設計
749
-
750
- ### 5.1 シークレット管理
751
-
752
- ```terraform
753
- # modules/security/secrets-manager/main.tf
754
- resource "aws_secretsmanager_secret" "db_credentials" {
755
- name = "${var.name_prefix}-db-credentials"
756
- description = "Database credentials for ${var.name_prefix}"
757
-
758
- recovery_window_in_days = var.environment == "prod" ? 30 : 0
759
-
760
- tags = merge(var.tags, {
761
- Name = "${var.name_prefix}-db-credentials"
762
- Type = "Database"
763
- })
764
- }
765
-
766
- resource "aws_secretsmanager_secret_version" "db_credentials" {
767
- secret_id = aws_secretsmanager_secret.db_credentials.id
768
-
769
- secret_string = jsonencode({
770
- username = var.database_username
771
- password = var.database_password
772
- })
773
-
774
- lifecycle {
775
- ignore_changes = [secret_string]
776
- }
777
- }
778
-
779
- # 自動ローテーション(本番環境のみ)
780
- resource "aws_secretsmanager_secret_rotation" "db_credentials" {
781
- count = var.environment == "prod" ? 1 : 0
782
-
783
- secret_id = aws_secretsmanager_secret.db_credentials.id
784
- rotation_lambda_arn = var.rotation_lambda_arn
785
-
786
- rotation_rules {
787
- automatically_after_days = 30
788
- }
789
- }
790
- ```
791
-
792
- ### 5.2 IAM ロール設計
793
-
794
- ```terraform
795
- # modules/security/iam/main.tf
796
- # EC2インスタンス用のIAMロール
797
- resource "aws_iam_role" "ec2_role" {
798
- name = "${var.name_prefix}-ec2-role"
799
-
800
- assume_role_policy = jsonencode({
801
- Version = "2012-10-17"
802
- Statement = [
803
- {
804
- Action = "sts:AssumeRole"
805
- Effect = "Allow"
806
- Principal = {
807
- Service = "ec2.amazonaws.com"
808
- }
809
- }
810
- ]
811
- })
812
-
813
- tags = var.tags
814
- }
815
-
816
- # Secrets Manager読み取り権限
817
- resource "aws_iam_policy" "secrets_manager_policy" {
818
- name = "${var.name_prefix}-secrets-manager-policy"
819
-
820
- policy = jsonencode({
821
- Version = "2012-10-17"
822
- Statement = [
823
- {
824
- Effect = "Allow"
825
- Action = [
826
- "secretsmanager:GetSecretValue",
827
- "secretsmanager:DescribeSecret"
828
- ]
829
- Resource = [
830
- "arn:aws:secretsmanager:${var.region}:${var.account_id}:secret:${var.name_prefix}-db-credentials*"
831
- ]
832
- }
833
- ]
834
- })
835
- }
836
-
837
- resource "aws_iam_role_policy_attachment" "secrets_manager_attachment" {
838
- policy_arn = aws_iam_policy.secrets_manager_policy.arn
839
- role = aws_iam_role.ec2_role.name
840
- }
841
-
842
- # CloudWatch Logs権限
843
- resource "aws_iam_policy" "cloudwatch_logs_policy" {
844
- name = "${var.name_prefix}-cloudwatch-logs-policy"
845
-
846
- policy = jsonencode({
847
- Version = "2012-10-17"
848
- Statement = [
849
- {
850
- Effect = "Allow"
851
- Action = [
852
- "logs:CreateLogGroup",
853
- "logs:CreateLogStream",
854
- "logs:PutLogEvents",
855
- "logs:DescribeLogStreams"
856
- ]
857
- Resource = [
858
- "arn:aws:logs:${var.region}:${var.account_id}:log-group:/aws/ec2/${var.name_prefix}*"
859
- ]
860
- }
861
- ]
862
- })
863
- }
864
-
865
- resource "aws_iam_role_policy_attachment" "cloudwatch_logs_attachment" {
866
- policy_arn = aws_iam_policy.cloudwatch_logs_policy.arn
867
- role = aws_iam_role.ec2_role.name
868
- }
869
-
870
- resource "aws_iam_instance_profile" "ec2_profile" {
871
- name = "${var.name_prefix}-ec2-profile"
872
- role = aws_iam_role.ec2_role.name
873
-
874
- tags = var.tags
875
- }
876
- ```
877
-
878
- ### 5.3 セキュリティグループ設計
879
-
880
- ```terraform
881
- # modules/security/security-groups/main.tf
882
- # ALB用セキュリティグループ
883
- resource "aws_security_group" "alb" {
884
- name_prefix = "${var.name_prefix}-alb-"
885
- vpc_id = var.vpc_id
886
-
887
- ingress {
888
- description = "HTTP"
889
- from_port = 80
890
- to_port = 80
891
- protocol = "tcp"
892
- cidr_blocks = ["0.0.0.0/0"]
893
- }
894
-
895
- ingress {
896
- description = "HTTPS"
897
- from_port = 443
898
- to_port = 443
899
- protocol = "tcp"
900
- cidr_blocks = ["0.0.0.0/0"]
901
- }
902
-
903
- egress {
904
- description = "All outbound"
905
- from_port = 0
906
- to_port = 0
907
- protocol = "-1"
908
- cidr_blocks = ["0.0.0.0/0"]
909
- }
910
-
911
- tags = merge(var.tags, {
912
- Name = "${var.name_prefix}-alb-sg"
913
- Type = "LoadBalancer"
914
- })
915
-
916
- lifecycle {
917
- create_before_destroy = true
918
- }
919
- }
920
-
921
- # Web層用セキュリティグループ
922
- resource "aws_security_group" "web" {
923
- name_prefix = "${var.name_prefix}-web-"
924
- vpc_id = var.vpc_id
925
-
926
- ingress {
927
- description = "HTTP from ALB"
928
- from_port = 8080
929
- to_port = 8080
930
- protocol = "tcp"
931
- security_groups = [aws_security_group.alb.id]
932
- }
933
-
934
- ingress {
935
- description = "SSH"
936
- from_port = 22
937
- to_port = 22
938
- protocol = "tcp"
939
- cidr_blocks = [var.vpc_cidr]
940
- }
941
-
942
- egress {
943
- description = "All outbound"
944
- from_port = 0
945
- to_port = 0
946
- protocol = "-1"
947
- cidr_blocks = ["0.0.0.0/0"]
948
- }
949
-
950
- tags = merge(var.tags, {
951
- Name = "${var.name_prefix}-web-sg"
952
- Type = "WebServer"
953
- })
954
-
955
- lifecycle {
956
- create_before_destroy = true
957
- }
958
- }
959
-
960
- # データベース用セキュリティグループ
961
- resource "aws_security_group" "database" {
962
- name_prefix = "${var.name_prefix}-db-"
963
- vpc_id = var.vpc_id
964
-
965
- ingress {
966
- description = "PostgreSQL from Web"
967
- from_port = 5432
968
- to_port = 5432
969
- protocol = "tcp"
970
- security_groups = [aws_security_group.web.id]
971
- }
972
-
973
- tags = merge(var.tags, {
974
- Name = "${var.name_prefix}-db-sg"
975
- Type = "Database"
976
- })
977
-
978
- lifecycle {
979
- create_before_destroy = true
980
- }
981
- }
982
- ```
983
-
984
- ## 6. 監視・ロギング
985
-
986
- ### 6.1 CloudWatch 監視
987
-
988
- ```terraform
989
- # modules/monitoring/cloudwatch/main.tf
990
- # ALB監視
991
- resource "aws_cloudwatch_metric_alarm" "alb_response_time" {
992
- alarm_name = "${var.name_prefix}-alb-response-time"
993
- comparison_operator = "GreaterThanThreshold"
994
- evaluation_periods = "2"
995
- metric_name = "TargetResponseTime"
996
- namespace = "AWS/ApplicationELB"
997
- period = "300"
998
- statistic = "Average"
999
- threshold = "2.0"
1000
- alarm_description = "This metric monitors ALB response time"
1001
- alarm_actions = [aws_sns_topic.alerts.arn]
1002
-
1003
- dimensions = {
1004
- LoadBalancer = var.alb_arn_suffix
1005
- }
1006
-
1007
- tags = var.tags
1008
- }
1009
-
1010
- resource "aws_cloudwatch_metric_alarm" "alb_healthy_hosts" {
1011
- alarm_name = "${var.name_prefix}-alb-healthy-hosts"
1012
- comparison_operator = "LessThanThreshold"
1013
- evaluation_periods = "2"
1014
- metric_name = "HealthyHostCount"
1015
- namespace = "AWS/ApplicationELB"
1016
- period = "300"
1017
- statistic = "Average"
1018
- threshold = "1"
1019
- alarm_description = "This metric monitors healthy host count"
1020
- alarm_actions = [aws_sns_topic.alerts.arn]
1021
-
1022
- dimensions = {
1023
- TargetGroup = var.target_group_arn_suffix
1024
- LoadBalancer = var.alb_arn_suffix
1025
- }
1026
-
1027
- tags = var.tags
1028
- }
1029
-
1030
- # データベース監視
1031
- resource "aws_cloudwatch_metric_alarm" "database_cpu" {
1032
- alarm_name = "${var.name_prefix}-db-cpu"
1033
- comparison_operator = "GreaterThanThreshold"
1034
- evaluation_periods = "2"
1035
- metric_name = "CPUUtilization"
1036
- namespace = "AWS/RDS"
1037
- period = "300"
1038
- statistic = "Average"
1039
- threshold = "80"
1040
- alarm_description = "This metric monitors database CPU utilization"
1041
- alarm_actions = [aws_sns_topic.alerts.arn]
1042
-
1043
- dimensions = {
1044
- DBInstanceIdentifier = var.db_instance_id
1045
- }
1046
-
1047
- tags = var.tags
1048
- }
1049
-
1050
- resource "aws_cloudwatch_metric_alarm" "database_connections" {
1051
- alarm_name = "${var.name_prefix}-db-connections"
1052
- comparison_operator = "GreaterThanThreshold"
1053
- evaluation_periods = "2"
1054
- metric_name = "DatabaseConnections"
1055
- namespace = "AWS/RDS"
1056
- period = "300"
1057
- statistic = "Average"
1058
- threshold = "80"
1059
- alarm_description = "This metric monitors database connection count"
1060
- alarm_actions = [aws_sns_topic.alerts.arn]
1061
-
1062
- dimensions = {
1063
- DBInstanceIdentifier = var.db_instance_id
1064
- }
1065
-
1066
- tags = var.tags
1067
- }
1068
-
1069
- # SNS通知設定
1070
- resource "aws_sns_topic" "alerts" {
1071
- name = "${var.name_prefix}-alerts"
1072
-
1073
- tags = var.tags
1074
- }
1075
-
1076
- resource "aws_sns_topic_subscription" "email_alerts" {
1077
- count = length(var.alert_email_addresses)
1078
-
1079
- topic_arn = aws_sns_topic.alerts.arn
1080
- protocol = "email"
1081
- endpoint = var.alert_email_addresses[count.index]
1082
- }
1083
-
1084
- # カスタムメトリクス
1085
- resource "aws_cloudwatch_log_group" "app_logs" {
1086
- name = "/aws/ec2/${var.name_prefix}/application"
1087
- retention_in_days = var.log_retention_days
1088
-
1089
- tags = var.tags
1090
- }
1091
-
1092
- resource "aws_cloudwatch_log_group" "access_logs" {
1093
- name = "/aws/ec2/${var.name_prefix}/access"
1094
- retention_in_days = var.log_retention_days
1095
-
1096
- tags = var.tags
1097
- }
1098
- ```
1099
-
1100
- ## 7. テスト戦略
1101
-
1102
- ### 7.1 単体テスト
1103
-
1104
- ```go
1105
- // test/unit/vpc_test.go
1106
- package test
1107
-
1108
- import (
1109
- "testing"
1110
-
1111
- "github.com/gruntwork-io/terratest/modules/terraform"
1112
- "github.com/stretchr/testify/assert"
1113
- )
1114
-
1115
- func TestVPCModule(t *testing.T) {
1116
- t.Parallel()
1117
-
1118
- terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
1119
- TerraformDir: "../modules/networking/vpc",
1120
- Vars: map[string]interface{}{
1121
- "name_prefix": "test-vpc",
1122
- "vpc_cidr": "10.0.0.0/16",
1123
- "public_subnet_cidrs": []string{
1124
- "10.0.1.0/24",
1125
- "10.0.2.0/24",
1126
- },
1127
- "private_subnet_cidrs": []string{
1128
- "10.0.3.0/24",
1129
- "10.0.4.0/24",
1130
- },
1131
- "availability_zones": []string{
1132
- "ap-northeast-1a",
1133
- "ap-northeast-1c",
1134
- },
1135
- "enable_nat_gateway": true,
1136
- "tags": map[string]string{
1137
- "Test": "true",
1138
- "Environment": "test",
1139
- },
1140
- },
1141
- })
1142
-
1143
- defer terraform.Destroy(t, terraformOptions)
1144
- terraform.InitAndApply(t, terraformOptions)
1145
-
1146
- // 出力値の検証
1147
- vpcId := terraform.Output(t, terraformOptions, "vpc_id")
1148
- assert.NotEmpty(t, vpcId, "VPC ID should not be empty")
1149
-
1150
- publicSubnetIds := terraform.OutputList(t, terraformOptions, "public_subnet_ids")
1151
- assert.Len(t, publicSubnetIds, 2, "Should create 2 public subnets")
1152
-
1153
- privateSubnetIds := terraform.OutputList(t, terraformOptions, "private_subnet_ids")
1154
- assert.Len(t, privateSubnetIds, 2, "Should create 2 private subnets")
1155
- }
1156
- ```
1157
-
1158
- ### 7.2 統合テスト
1159
-
1160
- ```go
1161
- // test/integration/full_stack_test.go
1162
- package test
1163
-
1164
- import (
1165
- "fmt"
1166
- "testing"
1167
- "time"
1168
-
1169
- http_helper "github.com/gruntwork-io/terratest/modules/http-helper"
1170
- "github.com/gruntwork-io/terratest/modules/random"
1171
- "github.com/gruntwork-io/terratest/modules/terraform"
1172
- test_structure "github.com/gruntwork-io/terratest/modules/test-structure"
1173
- )
1174
-
1175
- const envDir = "../environments/stage"
1176
-
1177
- func TestFullStackDeployment(t *testing.T) {
1178
- t.Parallel()
1179
-
1180
- // ステージベースのテスト実行
1181
- defer test_structure.RunTestStage(t, "cleanup", func() {
1182
- terraformOptions := test_structure.LoadTerraformOptions(t, envDir)
1183
- terraform.Destroy(t, terraformOptions)
1184
- })
1185
-
1186
- test_structure.RunTestStage(t, "deploy", func() {
1187
- uniqueId := random.UniqueId()
1188
- terraformOptions := &terraform.Options{
1189
- TerraformDir: envDir,
1190
- Vars: map[string]interface{}{
1191
- "app_version": "test-" + uniqueId,
1192
- "integration_test": true,
1193
- "environment_suffix": uniqueId,
1194
- },
1195
- }
1196
-
1197
- test_structure.SaveTerraformOptions(t, envDir, terraformOptions)
1198
- terraform.InitAndApply(t, terraformOptions)
1199
- })
1200
-
1201
- test_structure.RunTestStage(t, "validate", func() {
1202
- terraformOptions := test_structure.LoadTerraformOptions(t, envDir)
1203
-
1204
- // ALB エンドポイントの取得
1205
- albDnsName := terraform.Output(t, terraformOptions, "alb_dns_name")
1206
- url := fmt.Sprintf("http://%s", albDnsName)
1207
-
1208
- // ヘルスチェック
1209
- http_helper.HttpGetWithRetryWithCustomValidation(
1210
- t,
1211
- fmt.Sprintf("%s/health", url),
1212
- nil,
1213
- 30,
1214
- 10*time.Second,
1215
- func(statusCode int, body string) bool {
1216
- return statusCode == 200
1217
- },
1218
- )
1219
-
1220
- // アプリケーション機能テスト
1221
- http_helper.HttpGetWithRetryWithCustomValidation(
1222
- t,
1223
- fmt.Sprintf("%s/api/meeting-rooms", url),
1224
- nil,
1225
- 10,
1226
- 5*time.Second,
1227
- func(statusCode int, body string) bool {
1228
- return statusCode == 200
1229
- },
1230
- )
1231
- })
1232
- }
1233
- ```
1234
-
1235
- ### 7.3 セキュリティテスト
1236
-
1237
- ```go
1238
- // test/security/security_test.go
1239
- package test
1240
-
1241
- import (
1242
- "testing"
1243
-
1244
- "github.com/gruntwork-io/terratest/modules/terraform"
1245
- "github.com/stretchr/testify/assert"
1246
- )
1247
-
1248
- func TestSecurityConfiguration(t *testing.T) {
1249
- t.Parallel()
1250
-
1251
- terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
1252
- TerraformDir: "../environments/prod",
1253
- PlanFilePath: "./tfplan",
1254
- })
1255
-
1256
- // terraform plan の実行
1257
- terraform.InitAndPlan(t, terraformOptions)
1258
- plan := terraform.ShowWithStruct(t, terraformOptions)
1259
-
1260
- // セキュリティ設定の検証
1261
- t.Run("Database encryption", func(t *testing.T) {
1262
- dbInstances := plan.ResourcesByType["aws_db_instance"]
1263
- for _, instance := range dbInstances {
1264
- storageEncrypted := instance.AttributeValues["storage_encrypted"]
1265
- assert.True(t, storageEncrypted.(bool), "Database storage should be encrypted")
1266
- }
1267
- })
1268
-
1269
- t.Run("Security group rules", func(t *testing.T) {
1270
- securityGroups := plan.ResourcesByType["aws_security_group"]
1271
- for _, sg := range securityGroups {
1272
- ingress := sg.AttributeValues["ingress"].([]interface{})
1273
- for _, rule := range ingress {
1274
- ruleMap := rule.(map[string]interface{})
1275
- cidrBlocks := ruleMap["cidr_blocks"].([]interface{})
1276
-
1277
- // 0.0.0.0/0 からのSSH接続を禁止
1278
- if ruleMap["from_port"].(float64) == 22 {
1279
- assert.NotContains(t, cidrBlocks, "0.0.0.0/0",
1280
- "SSH should not be open to the world")
1281
- }
1282
- }
1283
- }
1284
- })
1285
-
1286
- t.Run("Deletion protection", func(t *testing.T) {
1287
- dbInstances := plan.ResourcesByType["aws_db_instance"]
1288
- for _, instance := range dbInstances {
1289
- if instance.AttributeValues["identifier"].(string) == "meeting-room-prod-db" {
1290
- deletionProtection := instance.AttributeValues["deletion_protection"]
1291
- assert.True(t, deletionProtection.(bool),
1292
- "Production database should have deletion protection enabled")
1293
- }
1294
- }
1295
- })
1296
- }
1297
- ```
1298
-
1299
- ## 8. CI/CD 統合
1300
-
1301
- ### 8.1 GitHub Actions ワークフロー
1302
-
1303
- ```yaml
1304
- # .github/workflows/terraform.yml
1305
- name: Terraform Infrastructure
1306
-
1307
- on:
1308
- push:
1309
- branches: [main, develop]
1310
- paths: ['terraform/**']
1311
- pull_request:
1312
- branches: [main]
1313
- paths: ['terraform/**']
1314
-
1315
- env:
1316
- TF_VERSION: 1.5.0
1317
- TF_IN_AUTOMATION: true
1318
-
1319
- permissions:
1320
- id-token: write
1321
- contents: read
1322
- pull-requests: write
1323
-
1324
- jobs:
1325
- validate:
1326
- name: Validate Terraform
1327
- runs-on: ubuntu-latest
1328
-
1329
- steps:
1330
- - name: Checkout
1331
- uses: actions/checkout@v4
1332
-
1333
- - name: Setup Terraform
1334
- uses: hashicorp/setup-terraform@v3
1335
- with:
1336
- terraform_version: ${{ env.TF_VERSION }}
1337
-
1338
- - name: Terraform Format Check
1339
- run: terraform fmt -check -recursive ./terraform/
1340
-
1341
- - name: Terraform Validate
1342
- run: |
1343
- find ./terraform -name "*.tf" -path "*/modules/*" -execdir terraform init -backend=false \;
1344
- find ./terraform -name "*.tf" -path "*/modules/*" -execdir terraform validate \;
1345
-
1346
- plan:
1347
- name: Plan Infrastructure
1348
- runs-on: ubuntu-latest
1349
- if: github.event_name == 'pull_request'
1350
- needs: [validate]
1351
-
1352
- strategy:
1353
- matrix:
1354
- environment: [dev, stage]
1355
-
1356
- steps:
1357
- - name: Checkout
1358
- uses: actions/checkout@v4
1359
-
1360
- - name: Configure AWS credentials
1361
- uses: aws-actions/configure-aws-credentials@v4
1362
- with:
1363
- role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
1364
- role-session-name: terraform-${{ matrix.environment }}
1365
- aws-region: ap-northeast-1
1366
-
1367
- - name: Setup Terraform
1368
- uses: hashicorp/setup-terraform@v3
1369
- with:
1370
- terraform_version: ${{ env.TF_VERSION }}
1371
-
1372
- - name: Terraform Init
1373
- working-directory: ./terraform/environments/${{ matrix.environment }}
1374
- run: terraform init
1375
-
1376
- - name: Terraform Plan
1377
- working-directory: ./terraform/environments/${{ matrix.environment }}
1378
- run: |
1379
- terraform plan -out=tfplan -var="app_version=${{ github.sha }}"
1380
- terraform show -no-color tfplan > plan.txt
1381
-
1382
- - name: Comment PR with Plan
1383
- uses: actions/github-script@v7
1384
- with:
1385
- script: |
1386
- const fs = require('fs');
1387
- const plan = fs.readFileSync('./terraform/environments/${{ matrix.environment }}/plan.txt', 'utf8');
1388
- const truncatedPlan = plan.length > 65000 ? plan.substring(0, 65000) + "\n...truncated" : plan;
1389
-
1390
- github.rest.issues.createComment({
1391
- issue_number: context.issue.number,
1392
- owner: context.repo.owner,
1393
- repo: context.repo.repo,
1394
- body: `## Terraform Plan (${{ matrix.environment }})
1395
-
1396
- \`\`\`
1397
- ${truncatedPlan}
1398
- \`\`\`
1399
- `
1400
- });
1401
-
1402
- deploy-dev:
1403
- name: Deploy to Development
1404
- runs-on: ubuntu-latest
1405
- if: github.ref == 'refs/heads/develop'
1406
- needs: [validate]
1407
- environment: development
1408
-
1409
- steps:
1410
- - name: Checkout
1411
- uses: actions/checkout@v4
1412
-
1413
- - name: Configure AWS credentials
1414
- uses: aws-actions/configure-aws-credentials@v4
1415
- with:
1416
- role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
1417
- role-session-name: terraform-dev
1418
- aws-region: ap-northeast-1
1419
-
1420
- - name: Setup Terraform
1421
- uses: hashicorp/setup-terraform@v3
1422
- with:
1423
- terraform_version: ${{ env.TF_VERSION }}
1424
-
1425
- - name: Terraform Init
1426
- working-directory: ./terraform/environments/dev
1427
- run: terraform init
1428
-
1429
- - name: Terraform Apply
1430
- working-directory: ./terraform/environments/dev
1431
- run: terraform apply -auto-approve -var="app_version=${{ github.sha }}"
1432
-
1433
- deploy-stage:
1434
- name: Deploy to Staging
1435
- runs-on: ubuntu-latest
1436
- if: github.ref == 'refs/heads/main'
1437
- needs: [validate]
1438
- environment: staging
1439
-
1440
- steps:
1441
- - name: Checkout
1442
- uses: actions/checkout@v4
1443
-
1444
- - name: Configure AWS credentials
1445
- uses: aws-actions/configure-aws-credentials@v4
1446
- with:
1447
- role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
1448
- role-session-name: terraform-stage
1449
- aws-region: ap-northeast-1
1450
-
1451
- - name: Setup Terraform
1452
- uses: hashicorp/setup-terraform@v3
1453
- with:
1454
- terraform_version: ${{ env.TF_VERSION }}
1455
-
1456
- - name: Terraform Init
1457
- working-directory: ./terraform/environments/stage
1458
- run: terraform init
1459
-
1460
- - name: Terraform Apply
1461
- working-directory: ./terraform/environments/stage
1462
- run: terraform apply -auto-approve -var="app_version=${{ github.sha }}"
1463
-
1464
- deploy-prod:
1465
- name: Deploy to Production
1466
- runs-on: ubuntu-latest
1467
- if: github.ref == 'refs/heads/main'
1468
- needs: [deploy-stage]
1469
- environment: production
1470
-
1471
- steps:
1472
- - name: Checkout
1473
- uses: actions/checkout@v4
1474
-
1475
- - name: Configure AWS credentials
1476
- uses: aws-actions/configure-aws-credentials@v4
1477
- with:
1478
- role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
1479
- role-session-name: terraform-prod
1480
- aws-region: ap-northeast-1
1481
-
1482
- - name: Setup Terraform
1483
- uses: hashicorp/setup-terraform@v3
1484
- with:
1485
- terraform_version: ${{ env.TF_VERSION }}
1486
-
1487
- - name: Terraform Init
1488
- working-directory: ./terraform/environments/prod
1489
- run: terraform init
1490
-
1491
- - name: Terraform Plan
1492
- working-directory: ./terraform/environments/prod
1493
- run: terraform plan -out=tfplan -var="app_version=${{ github.sha }}"
1494
-
1495
- - name: Manual Approval Required
1496
- run: |
1497
- echo "Production deployment requires manual approval"
1498
- echo "Review the plan and approve in GitHub Actions"
1499
-
1500
- - name: Terraform Apply
1501
- working-directory: ./terraform/environments/prod
1502
- run: terraform apply tfplan
1503
- ```
1504
-
1505
- ### 8.2 OIDC 設定
1506
-
1507
- ```terraform
1508
- # terraform/iam-oidc/main.tf
1509
- resource "aws_iam_openid_connect_provider" "github" {
1510
- url = "https://token.actions.githubusercontent.com"
1511
-
1512
- client_id_list = [
1513
- "sts.amazonaws.com",
1514
- ]
1515
-
1516
- thumbprint_list = [
1517
- "6938fd4d98bab03faadb97b34396831e3780aea1",
1518
- "1c58a3a8518e8759bf075b76b750d4f2df264fcd"
1519
- ]
1520
-
1521
- tags = {
1522
- Name = "github-actions-oidc"
1523
- ManagedBy = "terraform"
1524
- }
1525
- }
1526
-
1527
- resource "aws_iam_role" "github_actions" {
1528
- name = "github-actions-terraform"
1529
-
1530
- assume_role_policy = jsonencode({
1531
- Version = "2012-10-17"
1532
- Statement = [
1533
- {
1534
- Action = "sts:AssumeRoleWithWebIdentity"
1535
- Effect = "Allow"
1536
- Principal = {
1537
- Federated = aws_iam_openid_connect_provider.github.arn
1538
- }
1539
- Condition = {
1540
- StringEquals = {
1541
- "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
1542
- }
1543
- StringLike = {
1544
- "token.actions.githubusercontent.com:sub" = "repo:organization/meeting-room-infrastructure:*"
1545
- }
1546
- }
1547
- }
1548
- ]
1549
- })
1550
-
1551
- tags = {
1552
- Name = "github-actions-terraform"
1553
- ManagedBy = "terraform"
1554
- }
1555
- }
1556
-
1557
- resource "aws_iam_role_policy" "github_actions_terraform" {
1558
- name = "terraform-permissions"
1559
- role = aws_iam_role.github_actions.id
1560
-
1561
- policy = jsonencode({
1562
- Version = "2012-10-17"
1563
- Statement = [
1564
- {
1565
- Effect = "Allow"
1566
- Action = [
1567
- "ec2:*",
1568
- "rds:*",
1569
- "elasticloadbalancing:*",
1570
- "autoscaling:*",
1571
- "iam:*",
1572
- "s3:*",
1573
- "dynamodb:*",
1574
- "secretsmanager:*",
1575
- "logs:*",
1576
- "cloudwatch:*",
1577
- "sns:*"
1578
- ]
1579
- Resource = "*"
1580
- }
1581
- ]
1582
- })
1583
- }
1584
- ```
1585
-
1586
- ## 9. コスト最適化
1587
-
1588
- ### 9.1 リソース最適化
1589
-
1590
- ```terraform
1591
- # modules/cost-optimization/spot-instances/main.tf
1592
- resource "aws_launch_template" "spot" {
1593
- name_prefix = "${var.name_prefix}-spot-"
1594
-
1595
- image_id = var.ami_id
1596
- instance_type = var.instance_type
1597
- key_name = var.key_name
1598
-
1599
- vpc_security_group_ids = var.security_group_ids
1600
-
1601
- instance_market_options {
1602
- market_type = "spot"
1603
- spot_options {
1604
- spot_instance_type = "one-time"
1605
- max_price = var.spot_price
1606
- }
1607
- }
1608
-
1609
- user_data = var.user_data
1610
-
1611
- tag_specifications {
1612
- resource_type = "instance"
1613
- tags = merge(var.tags, {
1614
- Name = "${var.name_prefix}-spot-instance"
1615
- })
1616
- }
1617
-
1618
- lifecycle {
1619
- create_before_destroy = true
1620
- }
1621
- }
1622
-
1623
- resource "aws_autoscaling_group" "spot" {
1624
- name = "${var.name_prefix}-spot-asg"
1625
-
1626
- vpc_zone_identifier = var.subnet_ids
1627
- target_group_arns = var.target_group_arns
1628
- health_check_type = "ELB"
1629
-
1630
- min_size = var.min_size
1631
- max_size = var.max_size
1632
- desired_capacity = var.desired_capacity
1633
-
1634
- mixed_instances_policy {
1635
- launch_template {
1636
- launch_template_specification {
1637
- launch_template_id = aws_launch_template.spot.id
1638
- version = "$Latest"
1639
- }
1640
- }
1641
-
1642
- instances_distribution {
1643
- on_demand_base_capacity = var.on_demand_base_capacity
1644
- on_demand_percentage_above_base_capacity = var.on_demand_percentage
1645
- spot_allocation_strategy = "diversified"
1646
- spot_instance_pools = 4
1647
- }
1648
- }
1649
-
1650
- tag {
1651
- key = "Name"
1652
- value = "${var.name_prefix}-spot-asg"
1653
- propagate_at_launch = false
1654
- }
1655
- }
1656
- ```
1657
-
1658
- ### 9.2 スケジューリングと自動化
1659
-
1660
- ```terraform
1661
- # modules/scheduling/auto-shutdown/main.tf
1662
- resource "aws_lambda_function" "auto_shutdown" {
1663
- filename = "auto_shutdown.zip"
1664
- function_name = "${var.name_prefix}-auto-shutdown"
1665
- role = aws_iam_role.lambda_role.arn
1666
- handler = "lambda_function.lambda_handler"
1667
- runtime = "python3.9"
1668
- timeout = 300
1669
-
1670
- source_code_hash = data.archive_file.auto_shutdown_zip.output_base64sha256
1671
-
1672
- environment {
1673
- variables = {
1674
- ENVIRONMENT = var.environment
1675
- TAG_KEY = "AutoShutdown"
1676
- TAG_VALUE = "enabled"
1677
- }
1678
- }
1679
-
1680
- tags = var.tags
1681
- }
1682
-
1683
- resource "aws_cloudwatch_event_rule" "auto_shutdown_schedule" {
1684
- count = var.environment == "dev" ? 1 : 0
1685
-
1686
- name = "${var.name_prefix}-auto-shutdown"
1687
- description = "Schedule for auto shutdown of development resources"
1688
- schedule_expression = "cron(0 18 ? * MON-FRI *)" # 平日18時
1689
-
1690
- tags = var.tags
1691
- }
1692
-
1693
- resource "aws_cloudwatch_event_target" "lambda_target" {
1694
- count = var.environment == "dev" ? 1 : 0
1695
-
1696
- rule = aws_cloudwatch_event_rule.auto_shutdown_schedule[0].name
1697
- target_id = "AutoShutdownTarget"
1698
- arn = aws_lambda_function.auto_shutdown.arn
1699
- }
1700
-
1701
- resource "aws_lambda_permission" "allow_cloudwatch" {
1702
- count = var.environment == "dev" ? 1 : 0
1703
-
1704
- statement_id = "AllowExecutionFromCloudWatch"
1705
- action = "lambda:InvokeFunction"
1706
- function_name = aws_lambda_function.auto_shutdown.function_name
1707
- principal = "events.amazonaws.com"
1708
- source_arn = aws_cloudwatch_event_rule.auto_shutdown_schedule[0].arn
1709
- }
1710
- ```
1711
-
1712
- ## 10. 災害対策・冗長化
1713
-
1714
- ### 10.1 マルチ AZ 構成
1715
-
1716
- ```plantuml
1717
- @startuml "マルチAZ構成"
1718
- cloud "AWS Cloud" {
1719
- rectangle "Region: ap-northeast-1" as region {
1720
- rectangle "Availability Zone 1a" as az1 {
1721
- node "Web Server 1" as web1
1722
- database "DB Primary" as db1
1723
- }
1724
-
1725
- rectangle "Availability Zone 1c" as az2 {
1726
- node "Web Server 2" as web2
1727
- database "DB Standby" as db2
1728
- }
1729
-
1730
- component "Application Load Balancer" as alb {
1731
- component "Health Check" as hc
1732
- }
1733
-
1734
- storage "S3 Bucket" as s3 {
1735
- folder "Static Assets" as assets
1736
- folder "Backup Data" as backup
1737
- }
1738
- }
1739
- }
1740
-
1741
- alb --> web1
1742
- alb --> web2
1743
- web1 --> db1
1744
- web2 --> db1
1745
- db1 --> db2 : Synchronous\nReplication
1746
- web1 --> s3
1747
- web2 --> s3
1748
-
1749
- note right of alb : トラフィック分散\nヘルスチェック
1750
- note right of db2 : 自動フェイルオーバー\n(Multi-AZ)
1751
-
1752
- @enduml
1753
- ```
1754
-
1755
- ### 10.2 バックアップ戦略
1756
-
1757
- ```terraform
1758
- # modules/backup/automated-backup/main.tf
1759
- resource "aws_backup_vault" "main" {
1760
- name = "${var.name_prefix}-backup-vault"
1761
- kms_key_arn = aws_kms_key.backup.arn
1762
-
1763
- tags = var.tags
1764
- }
1765
-
1766
- resource "aws_kms_key" "backup" {
1767
- description = "KMS key for backup vault encryption"
1768
- deletion_window_in_days = 30
1769
-
1770
- tags = merge(var.tags, {
1771
- Name = "${var.name_prefix}-backup-kms"
1772
- })
1773
- }
1774
-
1775
- resource "aws_backup_plan" "main" {
1776
- name = "${var.name_prefix}-backup-plan"
1777
-
1778
- rule {
1779
- rule_name = "daily_backup"
1780
- target_vault_name = aws_backup_vault.main.name
1781
- schedule = "cron(0 2 ? * * *)" # 毎日午前2時
1782
-
1783
- lifecycle {
1784
- cold_storage_after = 30
1785
- delete_after = 120
1786
- }
1787
-
1788
- recovery_point_tags = merge(var.tags, {
1789
- BackupType = "Daily"
1790
- })
1791
- }
1792
-
1793
- rule {
1794
- rule_name = "weekly_backup"
1795
- target_vault_name = aws_backup_vault.main.name
1796
- schedule = "cron(0 2 ? * SUN *)" # 毎週日曜日午前2時
1797
-
1798
- lifecycle {
1799
- cold_storage_after = 30
1800
- delete_after = 365
1801
- }
1802
-
1803
- recovery_point_tags = merge(var.tags, {
1804
- BackupType = "Weekly"
1805
- })
1806
- }
1807
-
1808
- tags = var.tags
1809
- }
1810
-
1811
- resource "aws_backup_selection" "database" {
1812
- iam_role_arn = aws_iam_role.backup.arn
1813
- name = "${var.name_prefix}-db-backup-selection"
1814
- plan_id = aws_backup_plan.main.id
1815
-
1816
- resources = [
1817
- var.rds_db_instance_arn
1818
- ]
1819
-
1820
- condition {
1821
- string_equals {
1822
- key = "aws:ResourceTag/Environment"
1823
- value = var.environment
1824
- }
1825
- }
1826
- }
1827
- ```
1828
-
1829
- ## 11. まとめ
1830
-
1831
- ### 11.1 ベストプラクティス チェックリスト
1832
-
1833
- ```yaml
1834
- インフラコード品質:
1835
- - [ ] すべてのリソースがコードで定義されている
1836
- - [ ] バージョン管理されている
1837
- - [ ] 適切なコメントとドキュメントがある
1838
- - [ ] 命名規則に従っている
1839
-
1840
- モジュール設計:
1841
- - [ ] 単一責任の原則に従っている
1842
- - [ ] 再利用可能な設計になっている
1843
- - [ ] 入力検証が実装されている
1844
- - [ ] 適切な出力値が定義されている
1845
-
1846
- セキュリティ:
1847
- - [ ] シークレット管理が適切に実装されている
1848
- - [ ] IAM権限が最小権限になっている
1849
- - [ ] セキュリティグループが適切に設定されている
1850
- - [ ] 暗号化が有効になっている
1851
-
1852
- 運用:
1853
- - [ ] 監視・アラートが設定されている
1854
- - [ ] ログ収集が実装されている
1855
- - [ ] バックアップ戦略が定義されている
1856
- - [ ] 災害対策が考慮されている
1857
-
1858
- テスト:
1859
- - [ ] 単体テストが実装されている
1860
- - [ ] 統合テストが実装されている
1861
- - [ ] セキュリティテストが実装されている
1862
- - [ ] CI/CDパイプラインが構築されている
1863
- ```
1864
-
1865
- ### 11.2 継続的改善
1866
-
1867
- ```plantuml
1868
- @startuml "継続的改善サイクル"
1869
- circle "Plan" as plan
1870
- circle "Code" as code
1871
- circle "Build" as build
1872
- circle "Test" as test
1873
- circle "Deploy" as deploy
1874
- circle "Monitor" as monitor
1875
- circle "Learn" as learn
1876
-
1877
- plan --> code : 要件定義\nアーキテクチャ設計
1878
- code --> build : Terraform\nコード作成
1879
- build --> test : 静的解析\n単体テスト
1880
- test --> deploy : 統合テスト\n本番デプロイ
1881
- deploy --> monitor : 監視\nログ収集
1882
- monitor --> learn : 分析\n改善点抽出
1883
- learn --> plan : フィードバック\n次期計画
1884
-
1885
- @enduml
1886
- ```
1887
-
1888
- このインフラ設計ガイドに従うことで、会議室予約システムの安全で効率的なインフラストラクチャを構築・運用できます。
1
+ ---
2
+ type: Reference
3
+ title: "インフラ設計ガイド"
4
+ description: "Infrastructure as Code(IaC)によるインフラの設計・構築・管理の指針と、環境構成・ネットワーク・セキュリティの設計方針。"
5
+ tags: [reference]
6
+ status: stable
7
+ generated: { by: human:kakimomokuri, at: 2025-09-11T10:30:55Z }
8
+ ---
9
+
10
+ # インフラ設計ガイド
11
+
12
+ ## 1. 概要
13
+
14
+ ### 1.1 目的
15
+
16
+ Infrastructure as Code (IaC) によるインフラストラクチャの設計・構築・管理における規律とベストプラクティスを定義する。
17
+
18
+ ### 1.2 対象システム
19
+
20
+ - システム名: 会議室予約システム
21
+ - クラウド基盤: AWS
22
+ - IaC ツール: Terraform
23
+ - アーキテクチャ: ヘキサゴナルアーキテクチャ
24
+
25
+ ### 1.3 基本原則
26
+
27
+ ```plantuml
28
+ @startuml "IaC原則"
29
+ title Infrastructure as Code 基本原則
30
+
31
+ rectangle "コード化" as code
32
+ rectangle "バージョン管理" as version
33
+ rectangle "自動化" as automation
34
+ rectangle "再現性" as reproducible
35
+ rectangle "モジュール性" as modularity
36
+ rectangle "テスト" as testing
37
+
38
+ code --> version : すべてのインフラを\nコードで定義
39
+ version --> automation : 変更履歴を\n追跡・管理
40
+ automation --> reproducible : 手動作業を\n排除
41
+ reproducible --> modularity : 同じ環境を\n何度でも構築
42
+ modularity --> testing : 再利用可能な\nコンポーネント化
43
+ testing --> code : 品質保証と\n継続的改善
44
+
45
+ @enduml
46
+ ```
47
+
48
+ ## 2. プロジェクト構造
49
+
50
+ ### 2.1 ディレクトリ構造
51
+
52
+ ```plantuml
53
+ @startuml "プロジェクト構造"
54
+ folder "terraform" as terraform {
55
+ folder "modules" as modules {
56
+ folder "networking" as networking
57
+ folder "compute" as compute
58
+ folder "database" as database
59
+ folder "security" as security
60
+ folder "monitoring" as monitoring
61
+ }
62
+ folder "environments" as environments {
63
+ folder "global" as global
64
+ folder "dev" as dev
65
+ folder "stage" as stage
66
+ folder "prod" as prod
67
+ }
68
+ folder "examples" as examples
69
+ folder "test" as test
70
+ }
71
+
72
+ modules --> networking : VPC、Subnet、\nRoute Table
73
+ modules --> compute : EC2、ASG、ALB
74
+ modules --> database : RDS、DynamoDB
75
+ modules --> security : IAM、Security Groups
76
+ modules --> monitoring : CloudWatch、SNS
77
+
78
+ environments --> global : 共通変数・設定
79
+ environments --> dev : 開発環境
80
+ environments --> stage : ステージング環境
81
+ environments --> prod : 本番環境
82
+
83
+ @enduml
84
+ ```
85
+
86
+ ### 2.2 命名規則
87
+
88
+ #### 2.2.1 リソース命名
89
+
90
+ ```yaml
91
+ 命名パターン: "{project}-{environment}-{service}-{resource}"
92
+
93
+ 例:
94
+ - VPC: "meeting-room-prod-vpc"
95
+ - Subnet: "meeting-room-prod-public-subnet-1a"
96
+ - Security Group: "meeting-room-prod-web-sg"
97
+ - RDS: "meeting-room-prod-postgres-primary"
98
+ - ALB: "meeting-room-prod-app-alb"
99
+ ```
100
+
101
+ #### 2.2.2 Terraform ファイル命名
102
+
103
+ ```yaml
104
+ 標準ファイル構成:
105
+ - main.tf: メインの構成定義
106
+ - variables.tf: 入力変数定義
107
+ - outputs.tf: 出力値定義
108
+ - versions.tf: プロバイダーバージョン指定
109
+ - locals.tf: ローカル変数定義 (必要に応じて)
110
+ - data.tf: データソース定義 (必要に応じて)
111
+ ```
112
+
113
+ ## 3. モジュール設計
114
+
115
+ ### 3.1 モジュール化の原則
116
+
117
+ ```plantuml
118
+ @startuml "モジュール設計原則"
119
+ rectangle "単一責任" as single_responsibility {
120
+ note right : 1つのモジュールは\n1つの責任を持つ
121
+ }
122
+
123
+ rectangle "再利用性" as reusability {
124
+ note right : 複数の環境・プロジェクトで\n利用可能
125
+ }
126
+
127
+ rectangle "構成可能性" as configurability {
128
+ note right : 変数により動作を\nカスタマイズ可能
129
+ }
130
+
131
+ rectangle "テスト容易性" as testability {
132
+ note right : 独立してテスト\nできる設計
133
+ }
134
+
135
+ single_responsibility --> reusability
136
+ reusability --> configurability
137
+ configurability --> testability
138
+ testability --> single_responsibility
139
+
140
+ @enduml
141
+ ```
142
+
143
+ ### 3.2 会議室予約システム モジュール構成
144
+
145
+ #### 3.2.1 ネットワークモジュール
146
+
147
+ ```terraform
148
+ # modules/networking/vpc/main.tf
149
+ resource "aws_vpc" "main" {
150
+ cidr_block = var.vpc_cidr
151
+ enable_dns_hostnames = true
152
+ enable_dns_support = true
153
+
154
+ tags = merge(var.tags, {
155
+ Name = "${var.name_prefix}-vpc"
156
+ })
157
+ }
158
+
159
+ resource "aws_subnet" "public" {
160
+ count = length(var.public_subnet_cidrs)
161
+
162
+ vpc_id = aws_vpc.main.id
163
+ cidr_block = var.public_subnet_cidrs[count.index]
164
+ availability_zone = var.availability_zones[count.index]
165
+ map_public_ip_on_launch = true
166
+
167
+ tags = merge(var.tags, {
168
+ Name = "${var.name_prefix}-public-subnet-${count.index + 1}"
169
+ Type = "Public"
170
+ })
171
+ }
172
+
173
+ resource "aws_subnet" "private" {
174
+ count = length(var.private_subnet_cidrs)
175
+
176
+ vpc_id = aws_vpc.main.id
177
+ cidr_block = var.private_subnet_cidrs[count.index]
178
+ availability_zone = var.availability_zones[count.index]
179
+
180
+ tags = merge(var.tags, {
181
+ Name = "${var.name_prefix}-private-subnet-${count.index + 1}"
182
+ Type = "Private"
183
+ })
184
+ }
185
+
186
+ resource "aws_internet_gateway" "main" {
187
+ vpc_id = aws_vpc.main.id
188
+
189
+ tags = merge(var.tags, {
190
+ Name = "${var.name_prefix}-igw"
191
+ })
192
+ }
193
+
194
+ resource "aws_nat_gateway" "main" {
195
+ count = var.enable_nat_gateway ? length(aws_subnet.public) : 0
196
+
197
+ allocation_id = aws_eip.nat[count.index].id
198
+ subnet_id = aws_subnet.public[count.index].id
199
+
200
+ tags = merge(var.tags, {
201
+ Name = "${var.name_prefix}-nat-gw-${count.index + 1}"
202
+ })
203
+
204
+ depends_on = [aws_internet_gateway.main]
205
+ }
206
+
207
+ resource "aws_eip" "nat" {
208
+ count = var.enable_nat_gateway ? length(aws_subnet.public) : 0
209
+
210
+ domain = "vpc"
211
+
212
+ tags = merge(var.tags, {
213
+ Name = "${var.name_prefix}-nat-eip-${count.index + 1}"
214
+ })
215
+
216
+ depends_on = [aws_internet_gateway.main]
217
+ }
218
+ ```
219
+
220
+ #### 3.2.2 コンピュートモジュール
221
+
222
+ ```terraform
223
+ # modules/compute/web-app/main.tf
224
+ resource "aws_launch_template" "main" {
225
+ name_prefix = "${var.name_prefix}-lt"
226
+ image_id = var.ami_id
227
+ instance_type = var.instance_type
228
+ key_name = var.key_name
229
+
230
+ vpc_security_group_ids = [aws_security_group.web.id]
231
+
232
+ user_data = base64encode(templatefile("${path.module}/user_data.sh", {
233
+ database_url = var.database_url
234
+ app_version = var.app_version
235
+ environment = var.environment
236
+ }))
237
+
238
+ tag_specifications {
239
+ resource_type = "instance"
240
+ tags = merge(var.tags, {
241
+ Name = "${var.name_prefix}-instance"
242
+ })
243
+ }
244
+
245
+ lifecycle {
246
+ create_before_destroy = true
247
+ precondition {
248
+ condition = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
249
+ error_message = "Instance type must be t3.micro, t3.small, or t3.medium for cost optimization."
250
+ }
251
+ }
252
+ }
253
+
254
+ resource "aws_autoscaling_group" "main" {
255
+ name = "${var.name_prefix}-asg"
256
+ vpc_zone_identifier = var.subnet_ids
257
+ target_group_arns = [aws_lb_target_group.main.arn]
258
+ health_check_type = "ELB"
259
+ health_check_grace_period = 300
260
+
261
+ min_size = var.min_size
262
+ max_size = var.max_size
263
+ desired_capacity = var.desired_capacity
264
+
265
+ launch_template {
266
+ id = aws_launch_template.main.id
267
+ version = "$Latest"
268
+ }
269
+
270
+ instance_refresh {
271
+ strategy = "Rolling"
272
+ preferences {
273
+ min_healthy_percentage = 50
274
+ instance_warmup = 300
275
+ }
276
+ }
277
+
278
+ tag {
279
+ key = "Name"
280
+ value = "${var.name_prefix}-asg"
281
+ propagate_at_launch = false
282
+ }
283
+
284
+ dynamic "tag" {
285
+ for_each = var.tags
286
+ content {
287
+ key = tag.key
288
+ value = tag.value
289
+ propagate_at_launch = true
290
+ }
291
+ }
292
+
293
+ lifecycle {
294
+ create_before_destroy = true
295
+ postcondition {
296
+ condition = length(self.availability_zones) > 1
297
+ error_message = "Auto Scaling Group must span multiple AZs for high availability."
298
+ }
299
+ }
300
+ }
301
+
302
+ resource "aws_lb" "main" {
303
+ name = "${var.name_prefix}-alb"
304
+ internal = false
305
+ load_balancer_type = "application"
306
+ security_groups = [aws_security_group.alb.id]
307
+ subnets = var.subnet_ids
308
+
309
+ enable_deletion_protection = var.environment == "prod" ? true : false
310
+
311
+ tags = merge(var.tags, {
312
+ Name = "${var.name_prefix}-alb"
313
+ })
314
+ }
315
+
316
+ resource "aws_lb_target_group" "main" {
317
+ name = "${var.name_prefix}-tg"
318
+ port = 8080
319
+ protocol = "HTTP"
320
+ vpc_id = var.vpc_id
321
+
322
+ health_check {
323
+ enabled = true
324
+ healthy_threshold = 2
325
+ unhealthy_threshold = 2
326
+ timeout = 5
327
+ interval = 30
328
+ path = "/health"
329
+ matcher = "200"
330
+ port = "traffic-port"
331
+ protocol = "HTTP"
332
+ }
333
+
334
+ tags = merge(var.tags, {
335
+ Name = "${var.name_prefix}-tg"
336
+ })
337
+ }
338
+
339
+ resource "aws_lb_listener" "main" {
340
+ load_balancer_arn = aws_lb.main.arn
341
+ port = "80"
342
+ protocol = "HTTP"
343
+
344
+ default_action {
345
+ type = "forward"
346
+ target_group_arn = aws_lb_target_group.main.arn
347
+ }
348
+ }
349
+ ```
350
+
351
+ #### 3.2.3 データベースモジュール
352
+
353
+ ```terraform
354
+ # modules/database/postgresql/main.tf
355
+ resource "aws_db_subnet_group" "main" {
356
+ name = "${var.name_prefix}-db-subnet-group"
357
+ subnet_ids = var.subnet_ids
358
+
359
+ tags = merge(var.tags, {
360
+ Name = "${var.name_prefix}-db-subnet-group"
361
+ })
362
+ }
363
+
364
+ resource "aws_db_parameter_group" "main" {
365
+ family = "postgres15"
366
+ name = "${var.name_prefix}-db-params"
367
+
368
+ parameter {
369
+ name = "log_statement"
370
+ value = "all"
371
+ }
372
+
373
+ parameter {
374
+ name = "log_min_duration_statement"
375
+ value = "1000"
376
+ }
377
+
378
+ tags = merge(var.tags, {
379
+ Name = "${var.name_prefix}-db-params"
380
+ })
381
+ }
382
+
383
+ resource "aws_db_instance" "main" {
384
+ identifier = "${var.name_prefix}-db"
385
+
386
+ allocated_storage = var.allocated_storage
387
+ max_allocated_storage = var.max_allocated_storage
388
+ storage_type = "gp3"
389
+ storage_encrypted = true
390
+
391
+ engine = "postgres"
392
+ engine_version = "15.4"
393
+ instance_class = var.instance_class
394
+
395
+ db_name = var.database_name
396
+ username = var.database_username
397
+ password = var.database_password
398
+
399
+ db_subnet_group_name = aws_db_subnet_group.main.name
400
+ vpc_security_group_ids = [aws_security_group.database.id]
401
+ parameter_group_name = aws_db_parameter_group.main.name
402
+
403
+ backup_retention_period = var.backup_retention_period
404
+ backup_window = var.backup_window
405
+ maintenance_window = var.maintenance_window
406
+
407
+ skip_final_snapshot = var.environment != "prod"
408
+ final_snapshot_identifier = var.environment == "prod" ? "${var.name_prefix}-db-final-snapshot" : null
409
+
410
+ deletion_protection = var.environment == "prod" ? true : false
411
+
412
+ performance_insights_enabled = var.environment == "prod" ? true : false
413
+ monitoring_interval = var.environment == "prod" ? 60 : 0
414
+
415
+ tags = merge(var.tags, {
416
+ Name = "${var.name_prefix}-db"
417
+ })
418
+
419
+ lifecycle {
420
+ prevent_destroy = false
421
+ precondition {
422
+ condition = var.allocated_storage >= 20
423
+ error_message = "Database must have at least 20GB of storage."
424
+ }
425
+ }
426
+ }
427
+
428
+ resource "aws_db_instance" "replica" {
429
+ count = var.create_replica ? 1 : 0
430
+
431
+ identifier = "${var.name_prefix}-db-replica"
432
+
433
+ replicate_source_db = aws_db_instance.main.identifier
434
+ instance_class = var.replica_instance_class
435
+
436
+ skip_final_snapshot = true
437
+ deletion_protection = false
438
+
439
+ tags = merge(var.tags, {
440
+ Name = "${var.name_prefix}-db-replica"
441
+ })
442
+ }
443
+ ```
444
+
445
+ ### 3.3 入力検証とセキュリティ
446
+
447
+ ```terraform
448
+ # modules/compute/web-app/variables.tf
449
+ variable "instance_type" {
450
+ description = "EC2 instance type"
451
+ type = string
452
+
453
+ validation {
454
+ condition = contains([
455
+ "t3.micro", "t3.small", "t3.medium", "t3.large",
456
+ "m5.large", "m5.xlarge", "m5.2xlarge"
457
+ ], var.instance_type)
458
+ error_message = "Instance type must be a supported type for this application."
459
+ }
460
+ }
461
+
462
+ variable "min_size" {
463
+ description = "Minimum number of instances in ASG"
464
+ type = number
465
+
466
+ validation {
467
+ condition = var.min_size > 0
468
+ error_message = "Minimum size must be greater than 0 to avoid outages."
469
+ }
470
+
471
+ validation {
472
+ condition = var.min_size <= 10
473
+ error_message = "Minimum size must be 10 or fewer for cost control."
474
+ }
475
+ }
476
+
477
+ variable "environment" {
478
+ description = "Environment name"
479
+ type = string
480
+
481
+ validation {
482
+ condition = contains(["dev", "stage", "prod"], var.environment)
483
+ error_message = "Environment must be one of: dev, stage, prod."
484
+ }
485
+ }
486
+
487
+ variable "vpc_cidr" {
488
+ description = "CIDR block for VPC"
489
+ type = string
490
+
491
+ validation {
492
+ condition = can(cidrhost(var.vpc_cidr, 0))
493
+ error_message = "VPC CIDR must be a valid CIDR block."
494
+ }
495
+ }
496
+ ```
497
+
498
+ ## 4. 環境管理
499
+
500
+ ### 4.1 環境分離戦略
501
+
502
+ ```plantuml
503
+ @startuml "環境分離"
504
+ rectangle "Global" as global {
505
+ rectangle "共通変数" as common_vars
506
+ rectangle "グローバルリソース" as global_resources
507
+ rectangle "IAM ポリシー" as iam_policies
508
+ }
509
+
510
+ rectangle "Development" as dev {
511
+ rectangle "単一AZ" as dev_az
512
+ rectangle "小規模インスタンス" as dev_instance
513
+ rectangle "開発用DB" as dev_db
514
+ }
515
+
516
+ rectangle "Staging" as stage {
517
+ rectangle "マルチAZ" as stage_az
518
+ rectangle "本番相当" as stage_instance
519
+ rectangle "本番データのコピー" as stage_db
520
+ }
521
+
522
+ rectangle "Production" as prod {
523
+ rectangle "マルチAZ + 冗長化" as prod_az
524
+ rectangle "高性能インスタンス" as prod_instance
525
+ rectangle "レプリカ + バックアップ" as prod_db
526
+ }
527
+
528
+ global --> dev : 共通設定継承
529
+ global --> stage : 共通設定継承
530
+ global --> prod : 共通設定継承
531
+
532
+ @enduml
533
+ ```
534
+
535
+ ### 4.2 グローバル変数管理
536
+
537
+ ```terraform
538
+ # environments/global/variables.tf
539
+ locals {
540
+ project_name = "meeting-room"
541
+
542
+ common_tags = {
543
+ Project = local.project_name
544
+ ManagedBy = "terraform"
545
+ Owner = "platform-team"
546
+ Repository = "meeting-room-infrastructure"
547
+ }
548
+
549
+ environments = {
550
+ dev = {
551
+ name = "dev"
552
+ instance_type = "t3.micro"
553
+ min_size = 1
554
+ max_size = 2
555
+ desired_capacity = 1
556
+ db_instance_class = "db.t3.micro"
557
+ backup_retention = 7
558
+ multi_az = false
559
+ create_replica = false
560
+ }
561
+
562
+ stage = {
563
+ name = "stage"
564
+ instance_type = "t3.small"
565
+ min_size = 2
566
+ max_size = 4
567
+ desired_capacity = 2
568
+ db_instance_class = "db.t3.small"
569
+ backup_retention = 7
570
+ multi_az = true
571
+ create_replica = false
572
+ }
573
+
574
+ prod = {
575
+ name = "prod"
576
+ instance_type = "t3.medium"
577
+ min_size = 2
578
+ max_size = 8
579
+ desired_capacity = 4
580
+ db_instance_class = "db.r6g.large"
581
+ backup_retention = 30
582
+ multi_az = true
583
+ create_replica = true
584
+ }
585
+ }
586
+
587
+ vpc_configs = {
588
+ dev = {
589
+ vpc_cidr = "10.0.0.0/16"
590
+ public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"]
591
+ private_subnet_cidrs = ["10.0.3.0/24", "10.0.4.0/24"]
592
+ availability_zones = ["ap-northeast-1a", "ap-northeast-1c"]
593
+ }
594
+
595
+ stage = {
596
+ vpc_cidr = "10.1.0.0/16"
597
+ public_subnet_cidrs = ["10.1.1.0/24", "10.1.2.0/24"]
598
+ private_subnet_cidrs = ["10.1.3.0/24", "10.1.4.0/24"]
599
+ availability_zones = ["ap-northeast-1a", "ap-northeast-1c"]
600
+ }
601
+
602
+ prod = {
603
+ vpc_cidr = "10.2.0.0/16"
604
+ public_subnet_cidrs = ["10.2.1.0/24", "10.2.2.0/24"]
605
+ private_subnet_cidrs = ["10.2.3.0/24", "10.2.4.0/24"]
606
+ availability_zones = ["ap-northeast-1a", "ap-northeast-1c"]
607
+ }
608
+ }
609
+ }
610
+
611
+ output "project_config" {
612
+ value = {
613
+ name = local.project_name
614
+ tags = local.common_tags
615
+ environments = local.environments
616
+ vpc_configs = local.vpc_configs
617
+ }
618
+ }
619
+ ```
620
+
621
+ ### 4.3 環境固有の構成
622
+
623
+ ```terraform
624
+ # environments/prod/main.tf
625
+ terraform {
626
+ required_version = ">= 1.0"
627
+
628
+ backend "s3" {
629
+ bucket = "meeting-room-terraform-state"
630
+ key = "prod/terraform.tfstate"
631
+ region = "ap-northeast-1"
632
+ dynamodb_table = "meeting-room-terraform-locks"
633
+ encrypt = true
634
+ }
635
+
636
+ required_providers {
637
+ aws = {
638
+ source = "hashicorp/aws"
639
+ version = "~> 5.0"
640
+ }
641
+ }
642
+ }
643
+
644
+ provider "aws" {
645
+ region = "ap-northeast-1"
646
+
647
+ default_tags {
648
+ tags = local.common_tags
649
+ }
650
+ }
651
+
652
+ # グローバル設定の読み込み
653
+ data "terraform_remote_state" "global" {
654
+ backend = "s3"
655
+
656
+ config = {
657
+ bucket = "meeting-room-terraform-state"
658
+ key = "global/terraform.tfstate"
659
+ region = "ap-northeast-1"
660
+ }
661
+ }
662
+
663
+ locals {
664
+ environment = "prod"
665
+ global_config = data.terraform_remote_state.global.outputs.project_config
666
+ env_config = local.global_config.environments[local.environment]
667
+ vpc_config = local.global_config.vpc_configs[local.environment]
668
+ common_tags = merge(local.global_config.tags, {
669
+ Environment = local.environment
670
+ })
671
+ name_prefix = "${local.global_config.name}-${local.environment}"
672
+ }
673
+
674
+ # ネットワークモジュール
675
+ module "vpc" {
676
+ source = "../../modules/networking/vpc"
677
+
678
+ name_prefix = local.name_prefix
679
+
680
+ vpc_cidr = local.vpc_config.vpc_cidr
681
+ public_subnet_cidrs = local.vpc_config.public_subnet_cidrs
682
+ private_subnet_cidrs = local.vpc_config.private_subnet_cidrs
683
+ availability_zones = local.vpc_config.availability_zones
684
+
685
+ enable_nat_gateway = true
686
+
687
+ tags = local.common_tags
688
+ }
689
+
690
+ # データベースモジュール
691
+ module "database" {
692
+ source = "../../modules/database/postgresql"
693
+
694
+ name_prefix = local.name_prefix
695
+
696
+ subnet_ids = module.vpc.private_subnet_ids
697
+ vpc_id = module.vpc.vpc_id
698
+
699
+ instance_class = local.env_config.db_instance_class
700
+ allocated_storage = 100
701
+ max_allocated_storage = 1000
702
+
703
+ database_name = "meetingroom"
704
+ database_username = local.db_credentials.username
705
+ database_password = local.db_credentials.password
706
+
707
+ backup_retention_period = local.env_config.backup_retention
708
+ multi_az = local.env_config.multi_az
709
+ create_replica = local.env_config.create_replica
710
+
711
+ environment = local.environment
712
+ tags = local.common_tags
713
+ }
714
+
715
+ # Webアプリケーションモジュール
716
+ module "web_app" {
717
+ source = "../../modules/compute/web-app"
718
+
719
+ name_prefix = local.name_prefix
720
+
721
+ vpc_id = module.vpc.vpc_id
722
+ subnet_ids = module.vpc.public_subnet_ids
723
+
724
+ instance_type = local.env_config.instance_type
725
+ min_size = local.env_config.min_size
726
+ max_size = local.env_config.max_size
727
+ desired_capacity = local.env_config.desired_capacity
728
+
729
+ database_url = "postgresql://${local.db_credentials.username}:${local.db_credentials.password}@${module.database.endpoint}/meetingroom"
730
+ app_version = var.app_version
731
+
732
+ environment = local.environment
733
+ tags = local.common_tags
734
+
735
+ depends_on = [module.database]
736
+ }
737
+
738
+ # Secrets Managerからのデータベース認証情報取得
739
+ data "aws_secretsmanager_secret_version" "db_credentials" {
740
+ secret_id = "${local.name_prefix}-db-credentials"
741
+ }
742
+
743
+ locals {
744
+ db_credentials = jsondecode(data.aws_secretsmanager_secret_version.db_credentials.secret_string)
745
+ }
746
+ ```
747
+
748
+ ## 5. セキュリティ設計
749
+
750
+ ### 5.1 シークレット管理
751
+
752
+ ```terraform
753
+ # modules/security/secrets-manager/main.tf
754
+ resource "aws_secretsmanager_secret" "db_credentials" {
755
+ name = "${var.name_prefix}-db-credentials"
756
+ description = "Database credentials for ${var.name_prefix}"
757
+
758
+ recovery_window_in_days = var.environment == "prod" ? 30 : 0
759
+
760
+ tags = merge(var.tags, {
761
+ Name = "${var.name_prefix}-db-credentials"
762
+ Type = "Database"
763
+ })
764
+ }
765
+
766
+ resource "aws_secretsmanager_secret_version" "db_credentials" {
767
+ secret_id = aws_secretsmanager_secret.db_credentials.id
768
+
769
+ secret_string = jsonencode({
770
+ username = var.database_username
771
+ password = var.database_password
772
+ })
773
+
774
+ lifecycle {
775
+ ignore_changes = [secret_string]
776
+ }
777
+ }
778
+
779
+ # 自動ローテーション(本番環境のみ)
780
+ resource "aws_secretsmanager_secret_rotation" "db_credentials" {
781
+ count = var.environment == "prod" ? 1 : 0
782
+
783
+ secret_id = aws_secretsmanager_secret.db_credentials.id
784
+ rotation_lambda_arn = var.rotation_lambda_arn
785
+
786
+ rotation_rules {
787
+ automatically_after_days = 30
788
+ }
789
+ }
790
+ ```
791
+
792
+ ### 5.2 IAM ロール設計
793
+
794
+ ```terraform
795
+ # modules/security/iam/main.tf
796
+ # EC2インスタンス用のIAMロール
797
+ resource "aws_iam_role" "ec2_role" {
798
+ name = "${var.name_prefix}-ec2-role"
799
+
800
+ assume_role_policy = jsonencode({
801
+ Version = "2012-10-17"
802
+ Statement = [
803
+ {
804
+ Action = "sts:AssumeRole"
805
+ Effect = "Allow"
806
+ Principal = {
807
+ Service = "ec2.amazonaws.com"
808
+ }
809
+ }
810
+ ]
811
+ })
812
+
813
+ tags = var.tags
814
+ }
815
+
816
+ # Secrets Manager読み取り権限
817
+ resource "aws_iam_policy" "secrets_manager_policy" {
818
+ name = "${var.name_prefix}-secrets-manager-policy"
819
+
820
+ policy = jsonencode({
821
+ Version = "2012-10-17"
822
+ Statement = [
823
+ {
824
+ Effect = "Allow"
825
+ Action = [
826
+ "secretsmanager:GetSecretValue",
827
+ "secretsmanager:DescribeSecret"
828
+ ]
829
+ Resource = [
830
+ "arn:aws:secretsmanager:${var.region}:${var.account_id}:secret:${var.name_prefix}-db-credentials*"
831
+ ]
832
+ }
833
+ ]
834
+ })
835
+ }
836
+
837
+ resource "aws_iam_role_policy_attachment" "secrets_manager_attachment" {
838
+ policy_arn = aws_iam_policy.secrets_manager_policy.arn
839
+ role = aws_iam_role.ec2_role.name
840
+ }
841
+
842
+ # CloudWatch Logs権限
843
+ resource "aws_iam_policy" "cloudwatch_logs_policy" {
844
+ name = "${var.name_prefix}-cloudwatch-logs-policy"
845
+
846
+ policy = jsonencode({
847
+ Version = "2012-10-17"
848
+ Statement = [
849
+ {
850
+ Effect = "Allow"
851
+ Action = [
852
+ "logs:CreateLogGroup",
853
+ "logs:CreateLogStream",
854
+ "logs:PutLogEvents",
855
+ "logs:DescribeLogStreams"
856
+ ]
857
+ Resource = [
858
+ "arn:aws:logs:${var.region}:${var.account_id}:log-group:/aws/ec2/${var.name_prefix}*"
859
+ ]
860
+ }
861
+ ]
862
+ })
863
+ }
864
+
865
+ resource "aws_iam_role_policy_attachment" "cloudwatch_logs_attachment" {
866
+ policy_arn = aws_iam_policy.cloudwatch_logs_policy.arn
867
+ role = aws_iam_role.ec2_role.name
868
+ }
869
+
870
+ resource "aws_iam_instance_profile" "ec2_profile" {
871
+ name = "${var.name_prefix}-ec2-profile"
872
+ role = aws_iam_role.ec2_role.name
873
+
874
+ tags = var.tags
875
+ }
876
+ ```
877
+
878
+ ### 5.3 セキュリティグループ設計
879
+
880
+ ```terraform
881
+ # modules/security/security-groups/main.tf
882
+ # ALB用セキュリティグループ
883
+ resource "aws_security_group" "alb" {
884
+ name_prefix = "${var.name_prefix}-alb-"
885
+ vpc_id = var.vpc_id
886
+
887
+ ingress {
888
+ description = "HTTP"
889
+ from_port = 80
890
+ to_port = 80
891
+ protocol = "tcp"
892
+ cidr_blocks = ["0.0.0.0/0"]
893
+ }
894
+
895
+ ingress {
896
+ description = "HTTPS"
897
+ from_port = 443
898
+ to_port = 443
899
+ protocol = "tcp"
900
+ cidr_blocks = ["0.0.0.0/0"]
901
+ }
902
+
903
+ egress {
904
+ description = "All outbound"
905
+ from_port = 0
906
+ to_port = 0
907
+ protocol = "-1"
908
+ cidr_blocks = ["0.0.0.0/0"]
909
+ }
910
+
911
+ tags = merge(var.tags, {
912
+ Name = "${var.name_prefix}-alb-sg"
913
+ Type = "LoadBalancer"
914
+ })
915
+
916
+ lifecycle {
917
+ create_before_destroy = true
918
+ }
919
+ }
920
+
921
+ # Web層用セキュリティグループ
922
+ resource "aws_security_group" "web" {
923
+ name_prefix = "${var.name_prefix}-web-"
924
+ vpc_id = var.vpc_id
925
+
926
+ ingress {
927
+ description = "HTTP from ALB"
928
+ from_port = 8080
929
+ to_port = 8080
930
+ protocol = "tcp"
931
+ security_groups = [aws_security_group.alb.id]
932
+ }
933
+
934
+ ingress {
935
+ description = "SSH"
936
+ from_port = 22
937
+ to_port = 22
938
+ protocol = "tcp"
939
+ cidr_blocks = [var.vpc_cidr]
940
+ }
941
+
942
+ egress {
943
+ description = "All outbound"
944
+ from_port = 0
945
+ to_port = 0
946
+ protocol = "-1"
947
+ cidr_blocks = ["0.0.0.0/0"]
948
+ }
949
+
950
+ tags = merge(var.tags, {
951
+ Name = "${var.name_prefix}-web-sg"
952
+ Type = "WebServer"
953
+ })
954
+
955
+ lifecycle {
956
+ create_before_destroy = true
957
+ }
958
+ }
959
+
960
+ # データベース用セキュリティグループ
961
+ resource "aws_security_group" "database" {
962
+ name_prefix = "${var.name_prefix}-db-"
963
+ vpc_id = var.vpc_id
964
+
965
+ ingress {
966
+ description = "PostgreSQL from Web"
967
+ from_port = 5432
968
+ to_port = 5432
969
+ protocol = "tcp"
970
+ security_groups = [aws_security_group.web.id]
971
+ }
972
+
973
+ tags = merge(var.tags, {
974
+ Name = "${var.name_prefix}-db-sg"
975
+ Type = "Database"
976
+ })
977
+
978
+ lifecycle {
979
+ create_before_destroy = true
980
+ }
981
+ }
982
+ ```
983
+
984
+ ## 6. 監視・ロギング
985
+
986
+ ### 6.1 CloudWatch 監視
987
+
988
+ ```terraform
989
+ # modules/monitoring/cloudwatch/main.tf
990
+ # ALB監視
991
+ resource "aws_cloudwatch_metric_alarm" "alb_response_time" {
992
+ alarm_name = "${var.name_prefix}-alb-response-time"
993
+ comparison_operator = "GreaterThanThreshold"
994
+ evaluation_periods = "2"
995
+ metric_name = "TargetResponseTime"
996
+ namespace = "AWS/ApplicationELB"
997
+ period = "300"
998
+ statistic = "Average"
999
+ threshold = "2.0"
1000
+ alarm_description = "This metric monitors ALB response time"
1001
+ alarm_actions = [aws_sns_topic.alerts.arn]
1002
+
1003
+ dimensions = {
1004
+ LoadBalancer = var.alb_arn_suffix
1005
+ }
1006
+
1007
+ tags = var.tags
1008
+ }
1009
+
1010
+ resource "aws_cloudwatch_metric_alarm" "alb_healthy_hosts" {
1011
+ alarm_name = "${var.name_prefix}-alb-healthy-hosts"
1012
+ comparison_operator = "LessThanThreshold"
1013
+ evaluation_periods = "2"
1014
+ metric_name = "HealthyHostCount"
1015
+ namespace = "AWS/ApplicationELB"
1016
+ period = "300"
1017
+ statistic = "Average"
1018
+ threshold = "1"
1019
+ alarm_description = "This metric monitors healthy host count"
1020
+ alarm_actions = [aws_sns_topic.alerts.arn]
1021
+
1022
+ dimensions = {
1023
+ TargetGroup = var.target_group_arn_suffix
1024
+ LoadBalancer = var.alb_arn_suffix
1025
+ }
1026
+
1027
+ tags = var.tags
1028
+ }
1029
+
1030
+ # データベース監視
1031
+ resource "aws_cloudwatch_metric_alarm" "database_cpu" {
1032
+ alarm_name = "${var.name_prefix}-db-cpu"
1033
+ comparison_operator = "GreaterThanThreshold"
1034
+ evaluation_periods = "2"
1035
+ metric_name = "CPUUtilization"
1036
+ namespace = "AWS/RDS"
1037
+ period = "300"
1038
+ statistic = "Average"
1039
+ threshold = "80"
1040
+ alarm_description = "This metric monitors database CPU utilization"
1041
+ alarm_actions = [aws_sns_topic.alerts.arn]
1042
+
1043
+ dimensions = {
1044
+ DBInstanceIdentifier = var.db_instance_id
1045
+ }
1046
+
1047
+ tags = var.tags
1048
+ }
1049
+
1050
+ resource "aws_cloudwatch_metric_alarm" "database_connections" {
1051
+ alarm_name = "${var.name_prefix}-db-connections"
1052
+ comparison_operator = "GreaterThanThreshold"
1053
+ evaluation_periods = "2"
1054
+ metric_name = "DatabaseConnections"
1055
+ namespace = "AWS/RDS"
1056
+ period = "300"
1057
+ statistic = "Average"
1058
+ threshold = "80"
1059
+ alarm_description = "This metric monitors database connection count"
1060
+ alarm_actions = [aws_sns_topic.alerts.arn]
1061
+
1062
+ dimensions = {
1063
+ DBInstanceIdentifier = var.db_instance_id
1064
+ }
1065
+
1066
+ tags = var.tags
1067
+ }
1068
+
1069
+ # SNS通知設定
1070
+ resource "aws_sns_topic" "alerts" {
1071
+ name = "${var.name_prefix}-alerts"
1072
+
1073
+ tags = var.tags
1074
+ }
1075
+
1076
+ resource "aws_sns_topic_subscription" "email_alerts" {
1077
+ count = length(var.alert_email_addresses)
1078
+
1079
+ topic_arn = aws_sns_topic.alerts.arn
1080
+ protocol = "email"
1081
+ endpoint = var.alert_email_addresses[count.index]
1082
+ }
1083
+
1084
+ # カスタムメトリクス
1085
+ resource "aws_cloudwatch_log_group" "app_logs" {
1086
+ name = "/aws/ec2/${var.name_prefix}/application"
1087
+ retention_in_days = var.log_retention_days
1088
+
1089
+ tags = var.tags
1090
+ }
1091
+
1092
+ resource "aws_cloudwatch_log_group" "access_logs" {
1093
+ name = "/aws/ec2/${var.name_prefix}/access"
1094
+ retention_in_days = var.log_retention_days
1095
+
1096
+ tags = var.tags
1097
+ }
1098
+ ```
1099
+
1100
+ ## 7. テスト戦略
1101
+
1102
+ ### 7.1 単体テスト
1103
+
1104
+ ```go
1105
+ // test/unit/vpc_test.go
1106
+ package test
1107
+
1108
+ import (
1109
+ "testing"
1110
+
1111
+ "github.com/gruntwork-io/terratest/modules/terraform"
1112
+ "github.com/stretchr/testify/assert"
1113
+ )
1114
+
1115
+ func TestVPCModule(t *testing.T) {
1116
+ t.Parallel()
1117
+
1118
+ terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
1119
+ TerraformDir: "../modules/networking/vpc",
1120
+ Vars: map[string]interface{}{
1121
+ "name_prefix": "test-vpc",
1122
+ "vpc_cidr": "10.0.0.0/16",
1123
+ "public_subnet_cidrs": []string{
1124
+ "10.0.1.0/24",
1125
+ "10.0.2.0/24",
1126
+ },
1127
+ "private_subnet_cidrs": []string{
1128
+ "10.0.3.0/24",
1129
+ "10.0.4.0/24",
1130
+ },
1131
+ "availability_zones": []string{
1132
+ "ap-northeast-1a",
1133
+ "ap-northeast-1c",
1134
+ },
1135
+ "enable_nat_gateway": true,
1136
+ "tags": map[string]string{
1137
+ "Test": "true",
1138
+ "Environment": "test",
1139
+ },
1140
+ },
1141
+ })
1142
+
1143
+ defer terraform.Destroy(t, terraformOptions)
1144
+ terraform.InitAndApply(t, terraformOptions)
1145
+
1146
+ // 出力値の検証
1147
+ vpcId := terraform.Output(t, terraformOptions, "vpc_id")
1148
+ assert.NotEmpty(t, vpcId, "VPC ID should not be empty")
1149
+
1150
+ publicSubnetIds := terraform.OutputList(t, terraformOptions, "public_subnet_ids")
1151
+ assert.Len(t, publicSubnetIds, 2, "Should create 2 public subnets")
1152
+
1153
+ privateSubnetIds := terraform.OutputList(t, terraformOptions, "private_subnet_ids")
1154
+ assert.Len(t, privateSubnetIds, 2, "Should create 2 private subnets")
1155
+ }
1156
+ ```
1157
+
1158
+ ### 7.2 統合テスト
1159
+
1160
+ ```go
1161
+ // test/integration/full_stack_test.go
1162
+ package test
1163
+
1164
+ import (
1165
+ "fmt"
1166
+ "testing"
1167
+ "time"
1168
+
1169
+ http_helper "github.com/gruntwork-io/terratest/modules/http-helper"
1170
+ "github.com/gruntwork-io/terratest/modules/random"
1171
+ "github.com/gruntwork-io/terratest/modules/terraform"
1172
+ test_structure "github.com/gruntwork-io/terratest/modules/test-structure"
1173
+ )
1174
+
1175
+ const envDir = "../environments/stage"
1176
+
1177
+ func TestFullStackDeployment(t *testing.T) {
1178
+ t.Parallel()
1179
+
1180
+ // ステージベースのテスト実行
1181
+ defer test_structure.RunTestStage(t, "cleanup", func() {
1182
+ terraformOptions := test_structure.LoadTerraformOptions(t, envDir)
1183
+ terraform.Destroy(t, terraformOptions)
1184
+ })
1185
+
1186
+ test_structure.RunTestStage(t, "deploy", func() {
1187
+ uniqueId := random.UniqueId()
1188
+ terraformOptions := &terraform.Options{
1189
+ TerraformDir: envDir,
1190
+ Vars: map[string]interface{}{
1191
+ "app_version": "test-" + uniqueId,
1192
+ "integration_test": true,
1193
+ "environment_suffix": uniqueId,
1194
+ },
1195
+ }
1196
+
1197
+ test_structure.SaveTerraformOptions(t, envDir, terraformOptions)
1198
+ terraform.InitAndApply(t, terraformOptions)
1199
+ })
1200
+
1201
+ test_structure.RunTestStage(t, "validate", func() {
1202
+ terraformOptions := test_structure.LoadTerraformOptions(t, envDir)
1203
+
1204
+ // ALB エンドポイントの取得
1205
+ albDnsName := terraform.Output(t, terraformOptions, "alb_dns_name")
1206
+ url := fmt.Sprintf("http://%s", albDnsName)
1207
+
1208
+ // ヘルスチェック
1209
+ http_helper.HttpGetWithRetryWithCustomValidation(
1210
+ t,
1211
+ fmt.Sprintf("%s/health", url),
1212
+ nil,
1213
+ 30,
1214
+ 10*time.Second,
1215
+ func(statusCode int, body string) bool {
1216
+ return statusCode == 200
1217
+ },
1218
+ )
1219
+
1220
+ // アプリケーション機能テスト
1221
+ http_helper.HttpGetWithRetryWithCustomValidation(
1222
+ t,
1223
+ fmt.Sprintf("%s/api/meeting-rooms", url),
1224
+ nil,
1225
+ 10,
1226
+ 5*time.Second,
1227
+ func(statusCode int, body string) bool {
1228
+ return statusCode == 200
1229
+ },
1230
+ )
1231
+ })
1232
+ }
1233
+ ```
1234
+
1235
+ ### 7.3 セキュリティテスト
1236
+
1237
+ ```go
1238
+ // test/security/security_test.go
1239
+ package test
1240
+
1241
+ import (
1242
+ "testing"
1243
+
1244
+ "github.com/gruntwork-io/terratest/modules/terraform"
1245
+ "github.com/stretchr/testify/assert"
1246
+ )
1247
+
1248
+ func TestSecurityConfiguration(t *testing.T) {
1249
+ t.Parallel()
1250
+
1251
+ terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
1252
+ TerraformDir: "../environments/prod",
1253
+ PlanFilePath: "./tfplan",
1254
+ })
1255
+
1256
+ // terraform plan の実行
1257
+ terraform.InitAndPlan(t, terraformOptions)
1258
+ plan := terraform.ShowWithStruct(t, terraformOptions)
1259
+
1260
+ // セキュリティ設定の検証
1261
+ t.Run("Database encryption", func(t *testing.T) {
1262
+ dbInstances := plan.ResourcesByType["aws_db_instance"]
1263
+ for _, instance := range dbInstances {
1264
+ storageEncrypted := instance.AttributeValues["storage_encrypted"]
1265
+ assert.True(t, storageEncrypted.(bool), "Database storage should be encrypted")
1266
+ }
1267
+ })
1268
+
1269
+ t.Run("Security group rules", func(t *testing.T) {
1270
+ securityGroups := plan.ResourcesByType["aws_security_group"]
1271
+ for _, sg := range securityGroups {
1272
+ ingress := sg.AttributeValues["ingress"].([]interface{})
1273
+ for _, rule := range ingress {
1274
+ ruleMap := rule.(map[string]interface{})
1275
+ cidrBlocks := ruleMap["cidr_blocks"].([]interface{})
1276
+
1277
+ // 0.0.0.0/0 からのSSH接続を禁止
1278
+ if ruleMap["from_port"].(float64) == 22 {
1279
+ assert.NotContains(t, cidrBlocks, "0.0.0.0/0",
1280
+ "SSH should not be open to the world")
1281
+ }
1282
+ }
1283
+ }
1284
+ })
1285
+
1286
+ t.Run("Deletion protection", func(t *testing.T) {
1287
+ dbInstances := plan.ResourcesByType["aws_db_instance"]
1288
+ for _, instance := range dbInstances {
1289
+ if instance.AttributeValues["identifier"].(string) == "meeting-room-prod-db" {
1290
+ deletionProtection := instance.AttributeValues["deletion_protection"]
1291
+ assert.True(t, deletionProtection.(bool),
1292
+ "Production database should have deletion protection enabled")
1293
+ }
1294
+ }
1295
+ })
1296
+ }
1297
+ ```
1298
+
1299
+ ## 8. CI/CD 統合
1300
+
1301
+ ### 8.1 GitHub Actions ワークフロー
1302
+
1303
+ ```yaml
1304
+ # .github/workflows/terraform.yml
1305
+ name: Terraform Infrastructure
1306
+
1307
+ on:
1308
+ push:
1309
+ branches: [main, develop]
1310
+ paths: ['terraform/**']
1311
+ pull_request:
1312
+ branches: [main]
1313
+ paths: ['terraform/**']
1314
+
1315
+ env:
1316
+ TF_VERSION: 1.5.0
1317
+ TF_IN_AUTOMATION: true
1318
+
1319
+ permissions:
1320
+ id-token: write
1321
+ contents: read
1322
+ pull-requests: write
1323
+
1324
+ jobs:
1325
+ validate:
1326
+ name: Validate Terraform
1327
+ runs-on: ubuntu-latest
1328
+
1329
+ steps:
1330
+ - name: Checkout
1331
+ uses: actions/checkout@v4
1332
+
1333
+ - name: Setup Terraform
1334
+ uses: hashicorp/setup-terraform@v3
1335
+ with:
1336
+ terraform_version: ${{ env.TF_VERSION }}
1337
+
1338
+ - name: Terraform Format Check
1339
+ run: terraform fmt -check -recursive ./terraform/
1340
+
1341
+ - name: Terraform Validate
1342
+ run: |
1343
+ find ./terraform -name "*.tf" -path "*/modules/*" -execdir terraform init -backend=false \;
1344
+ find ./terraform -name "*.tf" -path "*/modules/*" -execdir terraform validate \;
1345
+
1346
+ plan:
1347
+ name: Plan Infrastructure
1348
+ runs-on: ubuntu-latest
1349
+ if: github.event_name == 'pull_request'
1350
+ needs: [validate]
1351
+
1352
+ strategy:
1353
+ matrix:
1354
+ environment: [dev, stage]
1355
+
1356
+ steps:
1357
+ - name: Checkout
1358
+ uses: actions/checkout@v4
1359
+
1360
+ - name: Configure AWS credentials
1361
+ uses: aws-actions/configure-aws-credentials@v4
1362
+ with:
1363
+ role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
1364
+ role-session-name: terraform-${{ matrix.environment }}
1365
+ aws-region: ap-northeast-1
1366
+
1367
+ - name: Setup Terraform
1368
+ uses: hashicorp/setup-terraform@v3
1369
+ with:
1370
+ terraform_version: ${{ env.TF_VERSION }}
1371
+
1372
+ - name: Terraform Init
1373
+ working-directory: ./terraform/environments/${{ matrix.environment }}
1374
+ run: terraform init
1375
+
1376
+ - name: Terraform Plan
1377
+ working-directory: ./terraform/environments/${{ matrix.environment }}
1378
+ run: |
1379
+ terraform plan -out=tfplan -var="app_version=${{ github.sha }}"
1380
+ terraform show -no-color tfplan > plan.txt
1381
+
1382
+ - name: Comment PR with Plan
1383
+ uses: actions/github-script@v7
1384
+ with:
1385
+ script: |
1386
+ const fs = require('fs');
1387
+ const plan = fs.readFileSync('./terraform/environments/${{ matrix.environment }}/plan.txt', 'utf8');
1388
+ const truncatedPlan = plan.length > 65000 ? plan.substring(0, 65000) + "\n...truncated" : plan;
1389
+
1390
+ github.rest.issues.createComment({
1391
+ issue_number: context.issue.number,
1392
+ owner: context.repo.owner,
1393
+ repo: context.repo.repo,
1394
+ body: `## Terraform Plan (${{ matrix.environment }})
1395
+
1396
+ \`\`\`
1397
+ ${truncatedPlan}
1398
+ \`\`\`
1399
+ `
1400
+ });
1401
+
1402
+ deploy-dev:
1403
+ name: Deploy to Development
1404
+ runs-on: ubuntu-latest
1405
+ if: github.ref == 'refs/heads/develop'
1406
+ needs: [validate]
1407
+ environment: development
1408
+
1409
+ steps:
1410
+ - name: Checkout
1411
+ uses: actions/checkout@v4
1412
+
1413
+ - name: Configure AWS credentials
1414
+ uses: aws-actions/configure-aws-credentials@v4
1415
+ with:
1416
+ role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
1417
+ role-session-name: terraform-dev
1418
+ aws-region: ap-northeast-1
1419
+
1420
+ - name: Setup Terraform
1421
+ uses: hashicorp/setup-terraform@v3
1422
+ with:
1423
+ terraform_version: ${{ env.TF_VERSION }}
1424
+
1425
+ - name: Terraform Init
1426
+ working-directory: ./terraform/environments/dev
1427
+ run: terraform init
1428
+
1429
+ - name: Terraform Apply
1430
+ working-directory: ./terraform/environments/dev
1431
+ run: terraform apply -auto-approve -var="app_version=${{ github.sha }}"
1432
+
1433
+ deploy-stage:
1434
+ name: Deploy to Staging
1435
+ runs-on: ubuntu-latest
1436
+ if: github.ref == 'refs/heads/main'
1437
+ needs: [validate]
1438
+ environment: staging
1439
+
1440
+ steps:
1441
+ - name: Checkout
1442
+ uses: actions/checkout@v4
1443
+
1444
+ - name: Configure AWS credentials
1445
+ uses: aws-actions/configure-aws-credentials@v4
1446
+ with:
1447
+ role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
1448
+ role-session-name: terraform-stage
1449
+ aws-region: ap-northeast-1
1450
+
1451
+ - name: Setup Terraform
1452
+ uses: hashicorp/setup-terraform@v3
1453
+ with:
1454
+ terraform_version: ${{ env.TF_VERSION }}
1455
+
1456
+ - name: Terraform Init
1457
+ working-directory: ./terraform/environments/stage
1458
+ run: terraform init
1459
+
1460
+ - name: Terraform Apply
1461
+ working-directory: ./terraform/environments/stage
1462
+ run: terraform apply -auto-approve -var="app_version=${{ github.sha }}"
1463
+
1464
+ deploy-prod:
1465
+ name: Deploy to Production
1466
+ runs-on: ubuntu-latest
1467
+ if: github.ref == 'refs/heads/main'
1468
+ needs: [deploy-stage]
1469
+ environment: production
1470
+
1471
+ steps:
1472
+ - name: Checkout
1473
+ uses: actions/checkout@v4
1474
+
1475
+ - name: Configure AWS credentials
1476
+ uses: aws-actions/configure-aws-credentials@v4
1477
+ with:
1478
+ role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
1479
+ role-session-name: terraform-prod
1480
+ aws-region: ap-northeast-1
1481
+
1482
+ - name: Setup Terraform
1483
+ uses: hashicorp/setup-terraform@v3
1484
+ with:
1485
+ terraform_version: ${{ env.TF_VERSION }}
1486
+
1487
+ - name: Terraform Init
1488
+ working-directory: ./terraform/environments/prod
1489
+ run: terraform init
1490
+
1491
+ - name: Terraform Plan
1492
+ working-directory: ./terraform/environments/prod
1493
+ run: terraform plan -out=tfplan -var="app_version=${{ github.sha }}"
1494
+
1495
+ - name: Manual Approval Required
1496
+ run: |
1497
+ echo "Production deployment requires manual approval"
1498
+ echo "Review the plan and approve in GitHub Actions"
1499
+
1500
+ - name: Terraform Apply
1501
+ working-directory: ./terraform/environments/prod
1502
+ run: terraform apply tfplan
1503
+ ```
1504
+
1505
+ ### 8.2 OIDC 設定
1506
+
1507
+ ```terraform
1508
+ # terraform/iam-oidc/main.tf
1509
+ resource "aws_iam_openid_connect_provider" "github" {
1510
+ url = "https://token.actions.githubusercontent.com"
1511
+
1512
+ client_id_list = [
1513
+ "sts.amazonaws.com",
1514
+ ]
1515
+
1516
+ thumbprint_list = [
1517
+ "6938fd4d98bab03faadb97b34396831e3780aea1",
1518
+ "1c58a3a8518e8759bf075b76b750d4f2df264fcd"
1519
+ ]
1520
+
1521
+ tags = {
1522
+ Name = "github-actions-oidc"
1523
+ ManagedBy = "terraform"
1524
+ }
1525
+ }
1526
+
1527
+ resource "aws_iam_role" "github_actions" {
1528
+ name = "github-actions-terraform"
1529
+
1530
+ assume_role_policy = jsonencode({
1531
+ Version = "2012-10-17"
1532
+ Statement = [
1533
+ {
1534
+ Action = "sts:AssumeRoleWithWebIdentity"
1535
+ Effect = "Allow"
1536
+ Principal = {
1537
+ Federated = aws_iam_openid_connect_provider.github.arn
1538
+ }
1539
+ Condition = {
1540
+ StringEquals = {
1541
+ "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
1542
+ }
1543
+ StringLike = {
1544
+ "token.actions.githubusercontent.com:sub" = "repo:organization/meeting-room-infrastructure:*"
1545
+ }
1546
+ }
1547
+ }
1548
+ ]
1549
+ })
1550
+
1551
+ tags = {
1552
+ Name = "github-actions-terraform"
1553
+ ManagedBy = "terraform"
1554
+ }
1555
+ }
1556
+
1557
+ resource "aws_iam_role_policy" "github_actions_terraform" {
1558
+ name = "terraform-permissions"
1559
+ role = aws_iam_role.github_actions.id
1560
+
1561
+ policy = jsonencode({
1562
+ Version = "2012-10-17"
1563
+ Statement = [
1564
+ {
1565
+ Effect = "Allow"
1566
+ Action = [
1567
+ "ec2:*",
1568
+ "rds:*",
1569
+ "elasticloadbalancing:*",
1570
+ "autoscaling:*",
1571
+ "iam:*",
1572
+ "s3:*",
1573
+ "dynamodb:*",
1574
+ "secretsmanager:*",
1575
+ "logs:*",
1576
+ "cloudwatch:*",
1577
+ "sns:*"
1578
+ ]
1579
+ Resource = "*"
1580
+ }
1581
+ ]
1582
+ })
1583
+ }
1584
+ ```
1585
+
1586
+ ## 9. コスト最適化
1587
+
1588
+ ### 9.1 リソース最適化
1589
+
1590
+ ```terraform
1591
+ # modules/cost-optimization/spot-instances/main.tf
1592
+ resource "aws_launch_template" "spot" {
1593
+ name_prefix = "${var.name_prefix}-spot-"
1594
+
1595
+ image_id = var.ami_id
1596
+ instance_type = var.instance_type
1597
+ key_name = var.key_name
1598
+
1599
+ vpc_security_group_ids = var.security_group_ids
1600
+
1601
+ instance_market_options {
1602
+ market_type = "spot"
1603
+ spot_options {
1604
+ spot_instance_type = "one-time"
1605
+ max_price = var.spot_price
1606
+ }
1607
+ }
1608
+
1609
+ user_data = var.user_data
1610
+
1611
+ tag_specifications {
1612
+ resource_type = "instance"
1613
+ tags = merge(var.tags, {
1614
+ Name = "${var.name_prefix}-spot-instance"
1615
+ })
1616
+ }
1617
+
1618
+ lifecycle {
1619
+ create_before_destroy = true
1620
+ }
1621
+ }
1622
+
1623
+ resource "aws_autoscaling_group" "spot" {
1624
+ name = "${var.name_prefix}-spot-asg"
1625
+
1626
+ vpc_zone_identifier = var.subnet_ids
1627
+ target_group_arns = var.target_group_arns
1628
+ health_check_type = "ELB"
1629
+
1630
+ min_size = var.min_size
1631
+ max_size = var.max_size
1632
+ desired_capacity = var.desired_capacity
1633
+
1634
+ mixed_instances_policy {
1635
+ launch_template {
1636
+ launch_template_specification {
1637
+ launch_template_id = aws_launch_template.spot.id
1638
+ version = "$Latest"
1639
+ }
1640
+ }
1641
+
1642
+ instances_distribution {
1643
+ on_demand_base_capacity = var.on_demand_base_capacity
1644
+ on_demand_percentage_above_base_capacity = var.on_demand_percentage
1645
+ spot_allocation_strategy = "diversified"
1646
+ spot_instance_pools = 4
1647
+ }
1648
+ }
1649
+
1650
+ tag {
1651
+ key = "Name"
1652
+ value = "${var.name_prefix}-spot-asg"
1653
+ propagate_at_launch = false
1654
+ }
1655
+ }
1656
+ ```
1657
+
1658
+ ### 9.2 スケジューリングと自動化
1659
+
1660
+ ```terraform
1661
+ # modules/scheduling/auto-shutdown/main.tf
1662
+ resource "aws_lambda_function" "auto_shutdown" {
1663
+ filename = "auto_shutdown.zip"
1664
+ function_name = "${var.name_prefix}-auto-shutdown"
1665
+ role = aws_iam_role.lambda_role.arn
1666
+ handler = "lambda_function.lambda_handler"
1667
+ runtime = "python3.9"
1668
+ timeout = 300
1669
+
1670
+ source_code_hash = data.archive_file.auto_shutdown_zip.output_base64sha256
1671
+
1672
+ environment {
1673
+ variables = {
1674
+ ENVIRONMENT = var.environment
1675
+ TAG_KEY = "AutoShutdown"
1676
+ TAG_VALUE = "enabled"
1677
+ }
1678
+ }
1679
+
1680
+ tags = var.tags
1681
+ }
1682
+
1683
+ resource "aws_cloudwatch_event_rule" "auto_shutdown_schedule" {
1684
+ count = var.environment == "dev" ? 1 : 0
1685
+
1686
+ name = "${var.name_prefix}-auto-shutdown"
1687
+ description = "Schedule for auto shutdown of development resources"
1688
+ schedule_expression = "cron(0 18 ? * MON-FRI *)" # 平日18時
1689
+
1690
+ tags = var.tags
1691
+ }
1692
+
1693
+ resource "aws_cloudwatch_event_target" "lambda_target" {
1694
+ count = var.environment == "dev" ? 1 : 0
1695
+
1696
+ rule = aws_cloudwatch_event_rule.auto_shutdown_schedule[0].name
1697
+ target_id = "AutoShutdownTarget"
1698
+ arn = aws_lambda_function.auto_shutdown.arn
1699
+ }
1700
+
1701
+ resource "aws_lambda_permission" "allow_cloudwatch" {
1702
+ count = var.environment == "dev" ? 1 : 0
1703
+
1704
+ statement_id = "AllowExecutionFromCloudWatch"
1705
+ action = "lambda:InvokeFunction"
1706
+ function_name = aws_lambda_function.auto_shutdown.function_name
1707
+ principal = "events.amazonaws.com"
1708
+ source_arn = aws_cloudwatch_event_rule.auto_shutdown_schedule[0].arn
1709
+ }
1710
+ ```
1711
+
1712
+ ## 10. 災害対策・冗長化
1713
+
1714
+ ### 10.1 マルチ AZ 構成
1715
+
1716
+ ```plantuml
1717
+ @startuml "マルチAZ構成"
1718
+ cloud "AWS Cloud" {
1719
+ rectangle "Region: ap-northeast-1" as region {
1720
+ rectangle "Availability Zone 1a" as az1 {
1721
+ node "Web Server 1" as web1
1722
+ database "DB Primary" as db1
1723
+ }
1724
+
1725
+ rectangle "Availability Zone 1c" as az2 {
1726
+ node "Web Server 2" as web2
1727
+ database "DB Standby" as db2
1728
+ }
1729
+
1730
+ component "Application Load Balancer" as alb {
1731
+ component "Health Check" as hc
1732
+ }
1733
+
1734
+ storage "S3 Bucket" as s3 {
1735
+ folder "Static Assets" as assets
1736
+ folder "Backup Data" as backup
1737
+ }
1738
+ }
1739
+ }
1740
+
1741
+ alb --> web1
1742
+ alb --> web2
1743
+ web1 --> db1
1744
+ web2 --> db1
1745
+ db1 --> db2 : Synchronous\nReplication
1746
+ web1 --> s3
1747
+ web2 --> s3
1748
+
1749
+ note right of alb : トラフィック分散\nヘルスチェック
1750
+ note right of db2 : 自動フェイルオーバー\n(Multi-AZ)
1751
+
1752
+ @enduml
1753
+ ```
1754
+
1755
+ ### 10.2 バックアップ戦略
1756
+
1757
+ ```terraform
1758
+ # modules/backup/automated-backup/main.tf
1759
+ resource "aws_backup_vault" "main" {
1760
+ name = "${var.name_prefix}-backup-vault"
1761
+ kms_key_arn = aws_kms_key.backup.arn
1762
+
1763
+ tags = var.tags
1764
+ }
1765
+
1766
+ resource "aws_kms_key" "backup" {
1767
+ description = "KMS key for backup vault encryption"
1768
+ deletion_window_in_days = 30
1769
+
1770
+ tags = merge(var.tags, {
1771
+ Name = "${var.name_prefix}-backup-kms"
1772
+ })
1773
+ }
1774
+
1775
+ resource "aws_backup_plan" "main" {
1776
+ name = "${var.name_prefix}-backup-plan"
1777
+
1778
+ rule {
1779
+ rule_name = "daily_backup"
1780
+ target_vault_name = aws_backup_vault.main.name
1781
+ schedule = "cron(0 2 ? * * *)" # 毎日午前2時
1782
+
1783
+ lifecycle {
1784
+ cold_storage_after = 30
1785
+ delete_after = 120
1786
+ }
1787
+
1788
+ recovery_point_tags = merge(var.tags, {
1789
+ BackupType = "Daily"
1790
+ })
1791
+ }
1792
+
1793
+ rule {
1794
+ rule_name = "weekly_backup"
1795
+ target_vault_name = aws_backup_vault.main.name
1796
+ schedule = "cron(0 2 ? * SUN *)" # 毎週日曜日午前2時
1797
+
1798
+ lifecycle {
1799
+ cold_storage_after = 30
1800
+ delete_after = 365
1801
+ }
1802
+
1803
+ recovery_point_tags = merge(var.tags, {
1804
+ BackupType = "Weekly"
1805
+ })
1806
+ }
1807
+
1808
+ tags = var.tags
1809
+ }
1810
+
1811
+ resource "aws_backup_selection" "database" {
1812
+ iam_role_arn = aws_iam_role.backup.arn
1813
+ name = "${var.name_prefix}-db-backup-selection"
1814
+ plan_id = aws_backup_plan.main.id
1815
+
1816
+ resources = [
1817
+ var.rds_db_instance_arn
1818
+ ]
1819
+
1820
+ condition {
1821
+ string_equals {
1822
+ key = "aws:ResourceTag/Environment"
1823
+ value = var.environment
1824
+ }
1825
+ }
1826
+ }
1827
+ ```
1828
+
1829
+ ## 11. まとめ
1830
+
1831
+ ### 11.1 ベストプラクティス チェックリスト
1832
+
1833
+ ```yaml
1834
+ インフラコード品質:
1835
+ - [ ] すべてのリソースがコードで定義されている
1836
+ - [ ] バージョン管理されている
1837
+ - [ ] 適切なコメントとドキュメントがある
1838
+ - [ ] 命名規則に従っている
1839
+
1840
+ モジュール設計:
1841
+ - [ ] 単一責任の原則に従っている
1842
+ - [ ] 再利用可能な設計になっている
1843
+ - [ ] 入力検証が実装されている
1844
+ - [ ] 適切な出力値が定義されている
1845
+
1846
+ セキュリティ:
1847
+ - [ ] シークレット管理が適切に実装されている
1848
+ - [ ] IAM権限が最小権限になっている
1849
+ - [ ] セキュリティグループが適切に設定されている
1850
+ - [ ] 暗号化が有効になっている
1851
+
1852
+ 運用:
1853
+ - [ ] 監視・アラートが設定されている
1854
+ - [ ] ログ収集が実装されている
1855
+ - [ ] バックアップ戦略が定義されている
1856
+ - [ ] 災害対策が考慮されている
1857
+
1858
+ テスト:
1859
+ - [ ] 単体テストが実装されている
1860
+ - [ ] 統合テストが実装されている
1861
+ - [ ] セキュリティテストが実装されている
1862
+ - [ ] CI/CDパイプラインが構築されている
1863
+ ```
1864
+
1865
+ ### 11.2 継続的改善
1866
+
1867
+ ```plantuml
1868
+ @startuml "継続的改善サイクル"
1869
+ circle "Plan" as plan
1870
+ circle "Code" as code
1871
+ circle "Build" as build
1872
+ circle "Test" as test
1873
+ circle "Deploy" as deploy
1874
+ circle "Monitor" as monitor
1875
+ circle "Learn" as learn
1876
+
1877
+ plan --> code : 要件定義\nアーキテクチャ設計
1878
+ code --> build : Terraform\nコード作成
1879
+ build --> test : 静的解析\n単体テスト
1880
+ test --> deploy : 統合テスト\n本番デプロイ
1881
+ deploy --> monitor : 監視\nログ収集
1882
+ monitor --> learn : 分析\n改善点抽出
1883
+ learn --> plan : フィードバック\n次期計画
1884
+
1885
+ @enduml
1886
+ ```
1887
+
1888
+ このインフラ設計ガイドに従うことで、会議室予約システムの安全で効率的なインフラストラクチャを構築・運用できます。