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