cloud-mu 1.9.0.pre.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (618) hide show
  1. checksums.yaml +7 -0
  2. data/Berksfile +56 -0
  3. data/Berksfile.lock +250 -0
  4. data/Jenkinsfile +184 -0
  5. data/LICENSE.md +37 -0
  6. data/README.md +26 -0
  7. data/bin/mu-aws-setup +376 -0
  8. data/bin/mu-cleanup +68 -0
  9. data/bin/mu-configure +1133 -0
  10. data/bin/mu-deploy +166 -0
  11. data/bin/mu-firewall-allow-clients +30 -0
  12. data/bin/mu-gcp-setup +200 -0
  13. data/bin/mu-gen-docs +34 -0
  14. data/bin/mu-gen-env +42 -0
  15. data/bin/mu-load-config.rb +158 -0
  16. data/bin/mu-node-manage +683 -0
  17. data/bin/mu-self-update +228 -0
  18. data/bin/mu-ssh +23 -0
  19. data/bin/mu-tunnel-nagios +144 -0
  20. data/bin/mu-upload-chef-artifacts +757 -0
  21. data/bin/mu-user-manage +275 -0
  22. data/cookbooks/awscli/LICENSE +37 -0
  23. data/cookbooks/awscli/README.md +58 -0
  24. data/cookbooks/awscli/attributes/default.rb +1 -0
  25. data/cookbooks/awscli/libraries/instance_metadata.rb +21 -0
  26. data/cookbooks/awscli/metadata.rb +20 -0
  27. data/cookbooks/awscli/recipes/default.rb +56 -0
  28. data/cookbooks/awscli/templates/default/config.erb +18 -0
  29. data/cookbooks/mu-activedirectory/CHANGELOG.md +13 -0
  30. data/cookbooks/mu-activedirectory/LICENSE +37 -0
  31. data/cookbooks/mu-activedirectory/README.md +6 -0
  32. data/cookbooks/mu-activedirectory/attributes/default.rb +98 -0
  33. data/cookbooks/mu-activedirectory/files/default/password-auth +32 -0
  34. data/cookbooks/mu-activedirectory/files/default/sshd_pol.pp +0 -0
  35. data/cookbooks/mu-activedirectory/files/default/sshd_pol.te +32 -0
  36. data/cookbooks/mu-activedirectory/files/default/syslogd_oddjobd.pp +0 -0
  37. data/cookbooks/mu-activedirectory/files/default/syslogd_oddjobd.te +10 -0
  38. data/cookbooks/mu-activedirectory/files/default/system-auth +34 -0
  39. data/cookbooks/mu-activedirectory/files/default/winbindpol.pp +0 -0
  40. data/cookbooks/mu-activedirectory/files/default/winbindpol.te +37 -0
  41. data/cookbooks/mu-activedirectory/libraries/config.rb +106 -0
  42. data/cookbooks/mu-activedirectory/libraries/helper.rb +86 -0
  43. data/cookbooks/mu-activedirectory/metadata.rb +17 -0
  44. data/cookbooks/mu-activedirectory/providers/domain.rb +152 -0
  45. data/cookbooks/mu-activedirectory/providers/domain_controller.rb +89 -0
  46. data/cookbooks/mu-activedirectory/providers/domain_node.rb +275 -0
  47. data/cookbooks/mu-activedirectory/recipes/default.rb +8 -0
  48. data/cookbooks/mu-activedirectory/recipes/domain-controller.rb +44 -0
  49. data/cookbooks/mu-activedirectory/recipes/domain-node.rb +50 -0
  50. data/cookbooks/mu-activedirectory/recipes/domain.rb +43 -0
  51. data/cookbooks/mu-activedirectory/recipes/sssd.rb +185 -0
  52. data/cookbooks/mu-activedirectory/resources/domain.rb +25 -0
  53. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +25 -0
  54. data/cookbooks/mu-activedirectory/resources/domain_node.rb +20 -0
  55. data/cookbooks/mu-activedirectory/templates/default/dhclient-eth0.conf.erb +4 -0
  56. data/cookbooks/mu-activedirectory/templates/default/interface +0 -0
  57. data/cookbooks/mu-activedirectory/templates/default/krb5.conf.erb +23 -0
  58. data/cookbooks/mu-activedirectory/templates/default/ntp.conf.erb +56 -0
  59. data/cookbooks/mu-activedirectory/templates/default/smb.conf.erb +33 -0
  60. data/cookbooks/mu-activedirectory/templates/default/sssd.conf.erb +60 -0
  61. data/cookbooks/mu-activedirectory/templates/windows/Backup.xml.erb +20 -0
  62. data/cookbooks/mu-activedirectory/templates/windows/bkupInfo.xml.erb +1 -0
  63. data/cookbooks/mu-activedirectory/templates/windows/gpreprt.xml.erb +198 -0
  64. data/cookbooks/mu-activedirectory/templates/windows/gptmpl.inf.erb +12 -0
  65. data/cookbooks/mu-activedirectory/templates/windows/manifest.xml.erb +1 -0
  66. data/cookbooks/mu-firewall/CHANGELOG.md +11 -0
  67. data/cookbooks/mu-firewall/LICENSE +37 -0
  68. data/cookbooks/mu-firewall/README.md +5 -0
  69. data/cookbooks/mu-firewall/attributes/default.rb +3 -0
  70. data/cookbooks/mu-firewall/metadata.rb +16 -0
  71. data/cookbooks/mu-firewall/recipes/default.rb +10 -0
  72. data/cookbooks/mu-glusterfs/CHANGELOG.md +13 -0
  73. data/cookbooks/mu-glusterfs/LICENSE +37 -0
  74. data/cookbooks/mu-glusterfs/README.md +5 -0
  75. data/cookbooks/mu-glusterfs/attributes/default.rb +34 -0
  76. data/cookbooks/mu-glusterfs/metadata.rb +17 -0
  77. data/cookbooks/mu-glusterfs/recipes/client.rb +62 -0
  78. data/cookbooks/mu-glusterfs/recipes/default.rb +16 -0
  79. data/cookbooks/mu-glusterfs/recipes/samba.rb +57 -0
  80. data/cookbooks/mu-glusterfs/recipes/server.rb +200 -0
  81. data/cookbooks/mu-glusterfs/templates/default/mu-gluster-client.erb +71 -0
  82. data/cookbooks/mu-glusterfs/templates/default/smb.conf.erb +14 -0
  83. data/cookbooks/mu-jenkins/CHANGELOG.md +13 -0
  84. data/cookbooks/mu-jenkins/LICENSE +37 -0
  85. data/cookbooks/mu-jenkins/README.md +105 -0
  86. data/cookbooks/mu-jenkins/attributes/default.rb +42 -0
  87. data/cookbooks/mu-jenkins/files/default/cleanup_deploy_config.xml +73 -0
  88. data/cookbooks/mu-jenkins/files/default/deploy_config.xml +44 -0
  89. data/cookbooks/mu-jenkins/metadata.rb +21 -0
  90. data/cookbooks/mu-jenkins/recipes/default.rb +195 -0
  91. data/cookbooks/mu-jenkins/recipes/node-ssh-config.rb +54 -0
  92. data/cookbooks/mu-jenkins/recipes/public_key.rb +24 -0
  93. data/cookbooks/mu-jenkins/templates/default/example_job.config.xml.erb +24 -0
  94. data/cookbooks/mu-jenkins/templates/default/org.jvnet.hudson.plugins.SSHBuildWrapper.xml.erb +14 -0
  95. data/cookbooks/mu-jenkins/templates/default/ssh_config.erb +6 -0
  96. data/cookbooks/mu-master/CHANGELOG.md +13 -0
  97. data/cookbooks/mu-master/LICENSE +37 -0
  98. data/cookbooks/mu-master/README.md +6 -0
  99. data/cookbooks/mu-master/attributes/default.rb +95 -0
  100. data/cookbooks/mu-master/files/default/0-mu-log-server.conf +19 -0
  101. data/cookbooks/mu-master/files/default/addRSA.ldif +8 -0
  102. data/cookbooks/mu-master/files/default/check_mem.pl +197 -0
  103. data/cookbooks/mu-master/files/default/cloudamatic.png +0 -0
  104. data/cookbooks/mu-master/files/default/dirsrv_admin.pp +0 -0
  105. data/cookbooks/mu-master/files/default/dirsrv_admin.te +13 -0
  106. data/cookbooks/mu-master/files/default/nagios_selinux.pp +0 -0
  107. data/cookbooks/mu-master/files/default/nagios_selinux.te +51 -0
  108. data/cookbooks/mu-master/files/default/nagios_selinux_7.pp +0 -0
  109. data/cookbooks/mu-master/files/default/nagios_selinux_7.te +17 -0
  110. data/cookbooks/mu-master/files/default/pam_sshd +18 -0
  111. data/cookbooks/mu-master/files/default/ssl_enable.ldif +18 -0
  112. data/cookbooks/mu-master/files/default/syslogd_oddjobd.pp +0 -0
  113. data/cookbooks/mu-master/files/default/syslogd_oddjobd.te +10 -0
  114. data/cookbooks/mu-master/files/default/vimrc +19 -0
  115. data/cookbooks/mu-master/libraries/mu.rb +29 -0
  116. data/cookbooks/mu-master/metadata.rb +30 -0
  117. data/cookbooks/mu-master/providers/user.rb +41 -0
  118. data/cookbooks/mu-master/recipes/389ds.rb +164 -0
  119. data/cookbooks/mu-master/recipes/basepackages.rb +58 -0
  120. data/cookbooks/mu-master/recipes/caching_nameserver.rb +37 -0
  121. data/cookbooks/mu-master/recipes/default.rb +451 -0
  122. data/cookbooks/mu-master/recipes/eks-kubectl.rb +41 -0
  123. data/cookbooks/mu-master/recipes/firewall-holes.rb +70 -0
  124. data/cookbooks/mu-master/recipes/init.rb +542 -0
  125. data/cookbooks/mu-master/recipes/ssl-certs.rb +109 -0
  126. data/cookbooks/mu-master/recipes/sssd.rb +89 -0
  127. data/cookbooks/mu-master/recipes/update_nagios_only.rb +242 -0
  128. data/cookbooks/mu-master/recipes/vault.rb +111 -0
  129. data/cookbooks/mu-master/resources/user.rb +19 -0
  130. data/cookbooks/mu-master/templates/default/389-directory-setup.inf.erb +28 -0
  131. data/cookbooks/mu-master/templates/default/chef-server.rb.erb +18 -0
  132. data/cookbooks/mu-master/templates/default/dhclient-eth0.conf.erb +9 -0
  133. data/cookbooks/mu-master/templates/default/mu-momma-cat.erb +149 -0
  134. data/cookbooks/mu-master/templates/default/mu.rc.erb +9 -0
  135. data/cookbooks/mu-master/templates/default/openssl.cnf.erb +354 -0
  136. data/cookbooks/mu-master/templates/default/sssd.conf.erb +44 -0
  137. data/cookbooks/mu-master/templates/default/web_app.conf.erb +90 -0
  138. data/cookbooks/mu-mongo/CHANGELOG.md +13 -0
  139. data/cookbooks/mu-mongo/LICENSE +37 -0
  140. data/cookbooks/mu-mongo/README.md +5 -0
  141. data/cookbooks/mu-mongo/attributes/default.rb +22 -0
  142. data/cookbooks/mu-mongo/files/default/keyfile +16 -0
  143. data/cookbooks/mu-mongo/files/default/remove_nodes.js +5 -0
  144. data/cookbooks/mu-mongo/metadata.rb +17 -0
  145. data/cookbooks/mu-mongo/recipes/default.rb +149 -0
  146. data/cookbooks/mu-mongo/recipes/yum-update-rule.rb +18 -0
  147. data/cookbooks/mu-mongo/templates/default/mongo_create_openfema_db.js.erb +2 -0
  148. data/cookbooks/mu-mongo/templates/default/mongo_init.js.erb +1 -0
  149. data/cookbooks/mu-mongo/templates/default/mongo_logrotate.erb +14 -0
  150. data/cookbooks/mu-mongo/templates/default/mongo_replset_addnodes.js.erb +6 -0
  151. data/cookbooks/mu-mongo/templates/default/replset_init.js.erb +2 -0
  152. data/cookbooks/mu-openvpn/CHANGELOG.md +13 -0
  153. data/cookbooks/mu-openvpn/LICENSE +37 -0
  154. data/cookbooks/mu-openvpn/README.md +6 -0
  155. data/cookbooks/mu-openvpn/attributes/default.rb +119 -0
  156. data/cookbooks/mu-openvpn/metadata.rb +18 -0
  157. data/cookbooks/mu-openvpn/recipes/default.rb +108 -0
  158. data/cookbooks/mu-openvpn/templates/default/users.json.erb +42 -0
  159. data/cookbooks/mu-php54/CHANGELOG.md +12 -0
  160. data/cookbooks/mu-php54/LICENSE +37 -0
  161. data/cookbooks/mu-php54/README.md +0 -0
  162. data/cookbooks/mu-php54/files/centos/php.ini +1802 -0
  163. data/cookbooks/mu-php54/files/ubuntu/php.ini +1870 -0
  164. data/cookbooks/mu-php54/metadata.rb +21 -0
  165. data/cookbooks/mu-php54/recipes/default.rb +97 -0
  166. data/cookbooks/mu-splunk/CHANGELOG.md +37 -0
  167. data/cookbooks/mu-splunk/LICENSE +37 -0
  168. data/cookbooks/mu-splunk/README.md +451 -0
  169. data/cookbooks/mu-splunk/attributes/default.rb +95 -0
  170. data/cookbooks/mu-splunk/attributes/upgrade.rb +49 -0
  171. data/cookbooks/mu-splunk/definitions/splunk_installer.rb +103 -0
  172. data/cookbooks/mu-splunk/files/default/splunk-nocheck +10 -0
  173. data/cookbooks/mu-splunk/libraries/helpers.rb +72 -0
  174. data/cookbooks/mu-splunk/libraries/splunk_app_provider.rb +156 -0
  175. data/cookbooks/mu-splunk/libraries/splunk_app_resource.rb +43 -0
  176. data/cookbooks/mu-splunk/metadata.json +30 -0
  177. data/cookbooks/mu-splunk/metadata.rb +17 -0
  178. data/cookbooks/mu-splunk/recipes/client.rb +143 -0
  179. data/cookbooks/mu-splunk/recipes/default.rb +31 -0
  180. data/cookbooks/mu-splunk/recipes/disabled.rb +41 -0
  181. data/cookbooks/mu-splunk/recipes/install_forwarder.rb +23 -0
  182. data/cookbooks/mu-splunk/recipes/install_server.rb +23 -0
  183. data/cookbooks/mu-splunk/recipes/server.rb +53 -0
  184. data/cookbooks/mu-splunk/recipes/service.rb +95 -0
  185. data/cookbooks/mu-splunk/recipes/setup_auth.rb +49 -0
  186. data/cookbooks/mu-splunk/recipes/setup_ssl.rb +63 -0
  187. data/cookbooks/mu-splunk/recipes/upgrade.rb +94 -0
  188. data/cookbooks/mu-splunk/recipes/user.rb +34 -0
  189. data/cookbooks/mu-splunk/templates/default/base_logs_unix_inputs.conf.erb +26 -0
  190. data/cookbooks/mu-splunk/templates/default/inputs.conf.erb +13 -0
  191. data/cookbooks/mu-splunk/templates/default/outputs.conf.erb +9 -0
  192. data/cookbooks/mu-splunk/templates/default/splunk-init.erb +74 -0
  193. data/cookbooks/mu-splunk/templates/default/system-web.conf.erb +7 -0
  194. data/cookbooks/mu-tools/CHANGELOG.md +12 -0
  195. data/cookbooks/mu-tools/LICENSE +37 -0
  196. data/cookbooks/mu-tools/README.md +188 -0
  197. data/cookbooks/mu-tools/attributes/default.rb +142 -0
  198. data/cookbooks/mu-tools/attributes/ebs_rolling_snapshots.rb +3 -0
  199. data/cookbooks/mu-tools/files/amazon/etc/freshclam.conf +235 -0
  200. data/cookbooks/mu-tools/files/centos/CentOS-Base.repo +52 -0
  201. data/cookbooks/mu-tools/files/centos/etc/bashrc +93 -0
  202. data/cookbooks/mu-tools/files/centos/etc/freshclam.conf +235 -0
  203. data/cookbooks/mu-tools/files/centos/etc/login.defs +72 -0
  204. data/cookbooks/mu-tools/files/centos/etc/profile +77 -0
  205. data/cookbooks/mu-tools/files/centos/etc/security/limits.conf +57 -0
  206. data/cookbooks/mu-tools/files/centos/etc/sysconfig/init +19 -0
  207. data/cookbooks/mu-tools/files/centos/etc/sysctl.conf +82 -0
  208. data/cookbooks/mu-tools/files/centos-6/README_MU +0 -0
  209. data/cookbooks/mu-tools/files/centos-6/etc/audit/stig.rules +173 -0
  210. data/cookbooks/mu-tools/files/centos-6/etc/bashrc +90 -0
  211. data/cookbooks/mu-tools/files/centos-6/etc/login.defs +70 -0
  212. data/cookbooks/mu-tools/files/centos-6/etc/pam.d/su +12 -0
  213. data/cookbooks/mu-tools/files/centos-6/etc/profile +83 -0
  214. data/cookbooks/mu-tools/files/centos-6/etc/securetty +12 -0
  215. data/cookbooks/mu-tools/files/centos-6/etc/sysconfig/init +30 -0
  216. data/cookbooks/mu-tools/files/centos-6/etc/sysctl.conf +40 -0
  217. data/cookbooks/mu-tools/files/default/Mu_CA.pem +34 -0
  218. data/cookbooks/mu-tools/files/default/PSWindowsUpdate.zip +0 -0
  219. data/cookbooks/mu-tools/files/default/ebs_snapshots.py +123 -0
  220. data/cookbooks/mu-tools/files/default/etc/BANNER +0 -0
  221. data/cookbooks/mu-tools/files/default/etc/BANNER-FEDERAL +19 -0
  222. data/cookbooks/mu-tools/files/default/gpo_no_uac.zip +0 -0
  223. data/cookbooks/mu-tools/files/default/mypol.pp +0 -0
  224. data/cookbooks/mu-tools/files/default/mypol.te +37 -0
  225. data/cookbooks/mu-tools/files/default/nrpe_c7.pp +0 -0
  226. data/cookbooks/mu-tools/files/default/nrpe_c7.te +31 -0
  227. data/cookbooks/mu-tools/files/default/nrpe_check_disk.pp +0 -0
  228. data/cookbooks/mu-tools/files/default/nrpe_check_disk.te +11 -0
  229. data/cookbooks/mu-tools/files/default/nrpe_disk.pp +0 -0
  230. data/cookbooks/mu-tools/files/default/nrpe_disk.te +10 -0
  231. data/cookbooks/mu-tools/files/default/nrpe_file.pp +0 -0
  232. data/cookbooks/mu-tools/files/default/nrpe_file.te +31 -0
  233. data/cookbooks/mu-tools/files/default/ntrights +0 -0
  234. data/cookbooks/mu-tools/files/default/serverclass.conf +18 -0
  235. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_unix/local/app.conf +1 -0
  236. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_unix/local/inputs.conf +13 -0
  237. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_windows/local/app.conf +1 -0
  238. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_windows/local/inputs.conf +8 -0
  239. data/cookbooks/mu-tools/files/default/sshd_pol.pp +0 -0
  240. data/cookbooks/mu-tools/files/default/sshd_pol.te +32 -0
  241. data/cookbooks/mu-tools/files/redhat/etc/bashrc +93 -0
  242. data/cookbooks/mu-tools/files/redhat/etc/freshclam.conf +235 -0
  243. data/cookbooks/mu-tools/files/redhat/etc/login.defs +72 -0
  244. data/cookbooks/mu-tools/files/redhat/etc/profile +77 -0
  245. data/cookbooks/mu-tools/files/redhat/etc/security/limits.conf +57 -0
  246. data/cookbooks/mu-tools/files/redhat/etc/sysconfig/init +19 -0
  247. data/cookbooks/mu-tools/files/redhat/etc/sysctl.conf +82 -0
  248. data/cookbooks/mu-tools/files/redhat-6/README_MU +0 -0
  249. data/cookbooks/mu-tools/files/redhat-6/etc/audit/stig.rules +173 -0
  250. data/cookbooks/mu-tools/files/redhat-6/etc/bashrc +90 -0
  251. data/cookbooks/mu-tools/files/redhat-6/etc/login.defs +70 -0
  252. data/cookbooks/mu-tools/files/redhat-6/etc/pam.d/su +12 -0
  253. data/cookbooks/mu-tools/files/redhat-6/etc/profile +83 -0
  254. data/cookbooks/mu-tools/files/redhat-6/etc/securetty +12 -0
  255. data/cookbooks/mu-tools/files/redhat-6/etc/sysconfig/init +30 -0
  256. data/cookbooks/mu-tools/files/redhat-6/etc/sysctl.conf +40 -0
  257. data/cookbooks/mu-tools/files/redhat-7.1/etc/freshclam.conf +235 -0
  258. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/bash.bashrc +64 -0
  259. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/common-session +30 -0
  260. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/login.defs +338 -0
  261. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/profile +30 -0
  262. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/security/limits.conf +56 -0
  263. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/sysctl.conf +60 -0
  264. data/cookbooks/mu-tools/libraries/helper.rb +292 -0
  265. data/cookbooks/mu-tools/metadata.rb +28 -0
  266. data/cookbooks/mu-tools/recipes/add_admin_ssh_keys.rb +35 -0
  267. data/cookbooks/mu-tools/recipes/apply_security.rb +440 -0
  268. data/cookbooks/mu-tools/recipes/aws_api.rb +23 -0
  269. data/cookbooks/mu-tools/recipes/base_repositories.rb +31 -0
  270. data/cookbooks/mu-tools/recipes/cisbenchmark.rb +59 -0
  271. data/cookbooks/mu-tools/recipes/clamav.rb +53 -0
  272. data/cookbooks/mu-tools/recipes/cloudinit.rb +58 -0
  273. data/cookbooks/mu-tools/recipes/configure_oracle_tools.rb +81 -0
  274. data/cookbooks/mu-tools/recipes/disable-requiretty.rb +22 -0
  275. data/cookbooks/mu-tools/recipes/ebs_rolling_snapshots.rb +75 -0
  276. data/cookbooks/mu-tools/recipes/efs.rb +70 -0
  277. data/cookbooks/mu-tools/recipes/eks.rb +160 -0
  278. data/cookbooks/mu-tools/recipes/gcloud.rb +98 -0
  279. data/cookbooks/mu-tools/recipes/google_api.rb +25 -0
  280. data/cookbooks/mu-tools/recipes/maldet.rb +67 -0
  281. data/cookbooks/mu-tools/recipes/nagios.rb +19 -0
  282. data/cookbooks/mu-tools/recipes/newclient.rb +23 -0
  283. data/cookbooks/mu-tools/recipes/nrpe.rb +115 -0
  284. data/cookbooks/mu-tools/recipes/python_pip.rb +35 -0
  285. data/cookbooks/mu-tools/recipes/retrieve_application.rb +51 -0
  286. data/cookbooks/mu-tools/recipes/rsyslog.rb +65 -0
  287. data/cookbooks/mu-tools/recipes/set_local_fw.rb +57 -0
  288. data/cookbooks/mu-tools/recipes/set_mu_hostname.rb +81 -0
  289. data/cookbooks/mu-tools/recipes/split_var_partitions.rb +86 -0
  290. data/cookbooks/mu-tools/recipes/splunk-client.rb +69 -0
  291. data/cookbooks/mu-tools/recipes/splunk-server.rb +104 -0
  292. data/cookbooks/mu-tools/recipes/store_inspec_attr.rb +8 -0
  293. data/cookbooks/mu-tools/recipes/updates.rb +96 -0
  294. data/cookbooks/mu-tools/recipes/windows-client.rb +202 -0
  295. data/cookbooks/mu-tools/resources/aws_windows.rb +33 -0
  296. data/cookbooks/mu-tools/resources/disk.rb +88 -0
  297. data/cookbooks/mu-tools/resources/mommacat_request.rb +11 -0
  298. data/cookbooks/mu-tools/resources/scheduled_tasks.rb +29 -0
  299. data/cookbooks/mu-tools/resources/sshd_service.rb +45 -0
  300. data/cookbooks/mu-tools/resources/windows_users.rb +242 -0
  301. data/cookbooks/mu-tools/templates/amazon/sshd_config.erb +168 -0
  302. data/cookbooks/mu-tools/templates/centos-6/sshd_config.erb +212 -0
  303. data/cookbooks/mu-tools/templates/centos-7/sshd_config.erb +215 -0
  304. data/cookbooks/mu-tools/templates/default/0-mu-log-client.conf.erb +13 -0
  305. data/cookbooks/mu-tools/templates/default/conf.maldet.erb +137 -0
  306. data/cookbooks/mu-tools/templates/default/etc_hosts.erb +30 -0
  307. data/cookbooks/mu-tools/templates/default/etc_pamd_password-auth.erb +14 -0
  308. data/cookbooks/mu-tools/templates/default/etc_pamd_system-auth.erb +14 -0
  309. data/cookbooks/mu-tools/templates/default/etc_sysconfig_network.erb +12 -0
  310. data/cookbooks/mu-tools/templates/default/kubeconfig.erb +29 -0
  311. data/cookbooks/mu-tools/templates/default/kubelet.service.erb +35 -0
  312. data/cookbooks/mu-tools/templates/default/maldet_scanall.sh.erb +15 -0
  313. data/cookbooks/mu-tools/templates/default/nrpe.cfg.erb +233 -0
  314. data/cookbooks/mu-tools/templates/redhat-6/sshd_config.erb +213 -0
  315. data/cookbooks/mu-tools/templates/redhat-7/sshd_config.erb +215 -0
  316. data/cookbooks/mu-tools/templates/ubuntu-12.04/sshd_config.erb +146 -0
  317. data/cookbooks/mu-tools/templates/ubuntu-14.04/sshd_config.erb +145 -0
  318. data/cookbooks/mu-tools/templates/windows/Backup.xml.erb +20 -0
  319. data/cookbooks/mu-tools/templates/windows/bkupInfo.xml.erb +1 -0
  320. data/cookbooks/mu-tools/templates/windows/gpreprt.xml.erb +214 -0
  321. data/cookbooks/mu-tools/templates/windows/gptmpl.inf.erb +12 -0
  322. data/cookbooks/mu-tools/templates/windows/manifest.xml.erb +1 -0
  323. data/cookbooks/mu-tools/templates/windows/set_ad_dns_scheduled_task.ps1.erb +6 -0
  324. data/cookbooks/mu-tools/templates/windows/sshd_config.erb +136 -0
  325. data/cookbooks/mu-utility/CHANGELOG.md +12 -0
  326. data/cookbooks/mu-utility/LICENSE +37 -0
  327. data/cookbooks/mu-utility/README.md +6 -0
  328. data/cookbooks/mu-utility/attributes/default.rb +1 -0
  329. data/cookbooks/mu-utility/libraries/matchers.rb +21 -0
  330. data/cookbooks/mu-utility/metadata.rb +16 -0
  331. data/cookbooks/mu-utility/recipes/apt.rb +23 -0
  332. data/cookbooks/mu-utility/recipes/cleanup_image_helper.rb +118 -0
  333. data/cookbooks/mu-utility/recipes/iptables.rb +26 -0
  334. data/cookbooks/mu-utility/recipes/luks.rb +18 -0
  335. data/cookbooks/mu-utility/recipes/nat.rb +104 -0
  336. data/cookbooks/mu-utility/recipes/php.rb +33 -0
  337. data/cookbooks/mu-utility/recipes/rdp_gateway.rb +83 -0
  338. data/cookbooks/mu-utility/recipes/remi.rb +44 -0
  339. data/cookbooks/mu-utility/recipes/vim.rb +26 -0
  340. data/cookbooks/mu-utility/recipes/windows_basics.rb +37 -0
  341. data/cookbooks/mu-utility/recipes/zip.rb +26 -0
  342. data/cookbooks/mu-utility/templates/default/BundleConfig.xml.erb +34 -0
  343. data/cookbooks/mu-utility/templates/default/config.xml.erb +60 -0
  344. data/cookbooks/nagios/Berksfile +8 -0
  345. data/cookbooks/nagios/CHANGELOG.md +589 -0
  346. data/cookbooks/nagios/CONTRIBUTING.md +11 -0
  347. data/cookbooks/nagios/LICENSE +37 -0
  348. data/cookbooks/nagios/README.md +328 -0
  349. data/cookbooks/nagios/TESTING.md +2 -0
  350. data/cookbooks/nagios/attributes/config.rb +171 -0
  351. data/cookbooks/nagios/attributes/default.rb +228 -0
  352. data/cookbooks/nagios/chefignore +102 -0
  353. data/cookbooks/nagios/definitions/command.rb +33 -0
  354. data/cookbooks/nagios/definitions/contact.rb +33 -0
  355. data/cookbooks/nagios/definitions/contactgroup.rb +33 -0
  356. data/cookbooks/nagios/definitions/host.rb +33 -0
  357. data/cookbooks/nagios/definitions/hostdependency.rb +33 -0
  358. data/cookbooks/nagios/definitions/hostescalation.rb +34 -0
  359. data/cookbooks/nagios/definitions/hostgroup.rb +33 -0
  360. data/cookbooks/nagios/definitions/nagios_conf.rb +38 -0
  361. data/cookbooks/nagios/definitions/resource.rb +33 -0
  362. data/cookbooks/nagios/definitions/service.rb +33 -0
  363. data/cookbooks/nagios/definitions/servicedependency.rb +33 -0
  364. data/cookbooks/nagios/definitions/serviceescalation.rb +34 -0
  365. data/cookbooks/nagios/definitions/servicegroup.rb +33 -0
  366. data/cookbooks/nagios/definitions/timeperiod.rb +33 -0
  367. data/cookbooks/nagios/libraries/base.rb +314 -0
  368. data/cookbooks/nagios/libraries/command.rb +91 -0
  369. data/cookbooks/nagios/libraries/contact.rb +230 -0
  370. data/cookbooks/nagios/libraries/contactgroup.rb +112 -0
  371. data/cookbooks/nagios/libraries/custom_option.rb +36 -0
  372. data/cookbooks/nagios/libraries/data_bag_helper.rb +23 -0
  373. data/cookbooks/nagios/libraries/default.rb +90 -0
  374. data/cookbooks/nagios/libraries/host.rb +412 -0
  375. data/cookbooks/nagios/libraries/hostdependency.rb +181 -0
  376. data/cookbooks/nagios/libraries/hostescalation.rb +173 -0
  377. data/cookbooks/nagios/libraries/hostgroup.rb +119 -0
  378. data/cookbooks/nagios/libraries/nagios.rb +282 -0
  379. data/cookbooks/nagios/libraries/resource.rb +59 -0
  380. data/cookbooks/nagios/libraries/service.rb +455 -0
  381. data/cookbooks/nagios/libraries/servicedependency.rb +215 -0
  382. data/cookbooks/nagios/libraries/serviceescalation.rb +195 -0
  383. data/cookbooks/nagios/libraries/servicegroup.rb +144 -0
  384. data/cookbooks/nagios/libraries/timeperiod.rb +160 -0
  385. data/cookbooks/nagios/libraries/users_helper.rb +54 -0
  386. data/cookbooks/nagios/metadata.rb +25 -0
  387. data/cookbooks/nagios/recipes/_load_databag_config.rb +153 -0
  388. data/cookbooks/nagios/recipes/_load_default_config.rb +241 -0
  389. data/cookbooks/nagios/recipes/apache.rb +48 -0
  390. data/cookbooks/nagios/recipes/default.rb +204 -0
  391. data/cookbooks/nagios/recipes/nginx.rb +82 -0
  392. data/cookbooks/nagios/recipes/pagerduty.rb +143 -0
  393. data/cookbooks/nagios/recipes/server_package.rb +40 -0
  394. data/cookbooks/nagios/recipes/server_source.rb +164 -0
  395. data/cookbooks/nagios/templates/default/apache2.conf.erb +96 -0
  396. data/cookbooks/nagios/templates/default/cgi.cfg.erb +266 -0
  397. data/cookbooks/nagios/templates/default/commands.cfg.erb +13 -0
  398. data/cookbooks/nagios/templates/default/contacts.cfg.erb +37 -0
  399. data/cookbooks/nagios/templates/default/hostgroups.cfg.erb +25 -0
  400. data/cookbooks/nagios/templates/default/hosts.cfg.erb +15 -0
  401. data/cookbooks/nagios/templates/default/htpasswd.users.erb +6 -0
  402. data/cookbooks/nagios/templates/default/nagios.cfg.erb +22 -0
  403. data/cookbooks/nagios/templates/default/nginx.conf.erb +62 -0
  404. data/cookbooks/nagios/templates/default/pagerduty.cgi.erb +185 -0
  405. data/cookbooks/nagios/templates/default/resource.cfg.erb +27 -0
  406. data/cookbooks/nagios/templates/default/servicedependencies.cfg.erb +15 -0
  407. data/cookbooks/nagios/templates/default/servicegroups.cfg.erb +14 -0
  408. data/cookbooks/nagios/templates/default/services.cfg.erb +14 -0
  409. data/cookbooks/nagios/templates/default/templates.cfg.erb +31 -0
  410. data/cookbooks/nagios/templates/default/timeperiods.cfg.erb +13 -0
  411. data/cookbooks/s3fs/CHANGELOG.md +13 -0
  412. data/cookbooks/s3fs/LICENSE +37 -0
  413. data/cookbooks/s3fs/README.md +6 -0
  414. data/cookbooks/s3fs/attributes/default.rb +15 -0
  415. data/cookbooks/s3fs/files/default/fuse-2.9.3.zip +0 -0
  416. data/cookbooks/s3fs/metadata.rb +16 -0
  417. data/cookbooks/s3fs/recipes/default.rb +91 -0
  418. data/data_bags/demo/app.json +7 -0
  419. data/data_bags/nagios_services/chef.json +6 -0
  420. data/data_bags/nagios_services/linux_diskspace.json +5 -0
  421. data/data_bags/nagios_services/momma_cat.json +6 -0
  422. data/data_bags/nagios_services/mu-master-memory.json +5 -0
  423. data/data_bags/nagios_services/nagios_ui.json +6 -0
  424. data/data_bags/nagios_services/node_ssh.json +6 -0
  425. data/data_bags/nagios_services/ssh.json +6 -0
  426. data/demo/lambda_test.yaml +29 -0
  427. data/environments/DEV.json +8 -0
  428. data/environments/PROD.json +8 -0
  429. data/environments/dev.json +8 -0
  430. data/environments/development.json +8 -0
  431. data/environments/prod.json +8 -0
  432. data/extras/README.md +1 -0
  433. data/extras/admin-role-binding.yaml +16 -0
  434. data/extras/admin-user.yaml +6 -0
  435. data/extras/aws-auth-cm.yaml.erb +12 -0
  436. data/extras/clean-stock-amis +48 -0
  437. data/extras/git-fix-permissions-hook +12 -0
  438. data/extras/gitlab-eks-helper.sh.erb +20 -0
  439. data/extras/image-generators/README.md +2 -0
  440. data/extras/image-generators/aws/centos6.yaml +18 -0
  441. data/extras/image-generators/aws/centos7-govcloud.yaml +24 -0
  442. data/extras/image-generators/aws/centos7.yaml +17 -0
  443. data/extras/image-generators/aws/rhel7.yaml +17 -0
  444. data/extras/image-generators/aws/win2k12.yaml +16 -0
  445. data/extras/image-generators/aws/win2k16.yaml +16 -0
  446. data/extras/image-generators/aws/windows.yaml +18 -0
  447. data/extras/image-generators/gcp/centos6.yaml +17 -0
  448. data/extras/lambda_waf_domain_blacklist.py +103 -0
  449. data/extras/platform_berksfile_base +50 -0
  450. data/extras/ruby_rpm/build.sh +17 -0
  451. data/extras/ruby_rpm/muby.spec +44 -0
  452. data/extras/vault_tools/README.md +6 -0
  453. data/extras/vault_tools/export_vaults.sh +3 -0
  454. data/extras/vault_tools/recreate_vaults.sh +5 -0
  455. data/extras/vault_tools/test_vaults.sh +5 -0
  456. data/install/README.md +8 -0
  457. data/install/cfn_create_mu_master.json +1034 -0
  458. data/install/chef-server.rb.erb +19 -0
  459. data/install/deprecated-bash-library.sh +1891 -0
  460. data/install/images/Usage.png +0 -0
  461. data/install/installer +71 -0
  462. data/install/jenkinskeys.rb +8 -0
  463. data/install/user-dot-murc.erb +14 -0
  464. data/modules/html.erb +19 -0
  465. data/modules/mommacat.ru +426 -0
  466. data/modules/mu/cleanup.rb +339 -0
  467. data/modules/mu/cloud.rb +1446 -0
  468. data/modules/mu/clouds/README.md +201 -0
  469. data/modules/mu/clouds/aws/alarm.rb +319 -0
  470. data/modules/mu/clouds/aws/cache_cluster.rb +1010 -0
  471. data/modules/mu/clouds/aws/collection.rb +373 -0
  472. data/modules/mu/clouds/aws/container_cluster.rb +667 -0
  473. data/modules/mu/clouds/aws/database.rb +1836 -0
  474. data/modules/mu/clouds/aws/dnszone.rb +911 -0
  475. data/modules/mu/clouds/aws/firewall_rule.rb +641 -0
  476. data/modules/mu/clouds/aws/folder.rb +92 -0
  477. data/modules/mu/clouds/aws/function.rb +349 -0
  478. data/modules/mu/clouds/aws/group.rb +251 -0
  479. data/modules/mu/clouds/aws/loadbalancer.rb +888 -0
  480. data/modules/mu/clouds/aws/log.rb +363 -0
  481. data/modules/mu/clouds/aws/msg_queue.rb +480 -0
  482. data/modules/mu/clouds/aws/notification.rb +139 -0
  483. data/modules/mu/clouds/aws/role.rb +656 -0
  484. data/modules/mu/clouds/aws/search_domain.rb +646 -0
  485. data/modules/mu/clouds/aws/server.rb +2294 -0
  486. data/modules/mu/clouds/aws/server_pool.rb +1388 -0
  487. data/modules/mu/clouds/aws/storage_pool.rb +495 -0
  488. data/modules/mu/clouds/aws/user.rb +382 -0
  489. data/modules/mu/clouds/aws/userdata/README.md +4 -0
  490. data/modules/mu/clouds/aws/userdata/linux.erb +179 -0
  491. data/modules/mu/clouds/aws/userdata/windows.erb +278 -0
  492. data/modules/mu/clouds/aws/vpc.rb +1943 -0
  493. data/modules/mu/clouds/aws.rb +1009 -0
  494. data/modules/mu/clouds/cloudformation/alarm.rb +146 -0
  495. data/modules/mu/clouds/cloudformation/cache_cluster.rb +167 -0
  496. data/modules/mu/clouds/cloudformation/collection.rb +117 -0
  497. data/modules/mu/clouds/cloudformation/database.rb +278 -0
  498. data/modules/mu/clouds/cloudformation/dnszone.rb +274 -0
  499. data/modules/mu/clouds/cloudformation/firewall_rule.rb +308 -0
  500. data/modules/mu/clouds/cloudformation/loadbalancer.rb +193 -0
  501. data/modules/mu/clouds/cloudformation/log.rb +170 -0
  502. data/modules/mu/clouds/cloudformation/server.rb +370 -0
  503. data/modules/mu/clouds/cloudformation/server_pool.rb +279 -0
  504. data/modules/mu/clouds/cloudformation/vpc.rb +322 -0
  505. data/modules/mu/clouds/cloudformation.rb +733 -0
  506. data/modules/mu/clouds/docker.rb +30 -0
  507. data/modules/mu/clouds/google/container_cluster.rb +290 -0
  508. data/modules/mu/clouds/google/database.rb +152 -0
  509. data/modules/mu/clouds/google/firewall_rule.rb +267 -0
  510. data/modules/mu/clouds/google/group.rb +164 -0
  511. data/modules/mu/clouds/google/loadbalancer.rb +479 -0
  512. data/modules/mu/clouds/google/server.rb +1510 -0
  513. data/modules/mu/clouds/google/server_pool.rb +274 -0
  514. data/modules/mu/clouds/google/user.rb +266 -0
  515. data/modules/mu/clouds/google/userdata/README.md +4 -0
  516. data/modules/mu/clouds/google/userdata/linux.erb +137 -0
  517. data/modules/mu/clouds/google/userdata/windows.erb +275 -0
  518. data/modules/mu/clouds/google/vpc.rb +890 -0
  519. data/modules/mu/clouds/google.rb +811 -0
  520. data/modules/mu/config/README.md +11 -0
  521. data/modules/mu/config/alarm.rb +271 -0
  522. data/modules/mu/config/cache_cluster.rb +172 -0
  523. data/modules/mu/config/collection.rb +87 -0
  524. data/modules/mu/config/container_cluster.rb +103 -0
  525. data/modules/mu/config/container_cluster.yml +36 -0
  526. data/modules/mu/config/database.rb +458 -0
  527. data/modules/mu/config/database.yml +26 -0
  528. data/modules/mu/config/dnszone.rb +327 -0
  529. data/modules/mu/config/firewall_rule.rb +118 -0
  530. data/modules/mu/config/folder.rb +70 -0
  531. data/modules/mu/config/function.rb +140 -0
  532. data/modules/mu/config/group.rb +64 -0
  533. data/modules/mu/config/loadbalancer.rb +482 -0
  534. data/modules/mu/config/log.rb +47 -0
  535. data/modules/mu/config/log.yml +6 -0
  536. data/modules/mu/config/msg_queue.rb +47 -0
  537. data/modules/mu/config/msg_queue.yml +9 -0
  538. data/modules/mu/config/notification.rb +44 -0
  539. data/modules/mu/config/project.rb +71 -0
  540. data/modules/mu/config/role.rb +102 -0
  541. data/modules/mu/config/search_domain.rb +61 -0
  542. data/modules/mu/config/search_domain.yml +25 -0
  543. data/modules/mu/config/server.rb +587 -0
  544. data/modules/mu/config/server.yml +8 -0
  545. data/modules/mu/config/server_pool.rb +216 -0
  546. data/modules/mu/config/server_pool.yml +71 -0
  547. data/modules/mu/config/storage_pool.rb +145 -0
  548. data/modules/mu/config/user.rb +78 -0
  549. data/modules/mu/config/vpc.rb +743 -0
  550. data/modules/mu/config/vpc.yml +6 -0
  551. data/modules/mu/config.rb +2000 -0
  552. data/modules/mu/defaults/README.md +2 -0
  553. data/modules/mu/defaults/amazon_images.yaml +121 -0
  554. data/modules/mu/defaults/google_images.yaml +16 -0
  555. data/modules/mu/deploy.rb +686 -0
  556. data/modules/mu/groomer.rb +123 -0
  557. data/modules/mu/groomers/README.md +58 -0
  558. data/modules/mu/groomers/chef.rb +1024 -0
  559. data/modules/mu/kittens.rb +11319 -0
  560. data/modules/mu/logger.rb +208 -0
  561. data/modules/mu/master/README.md +27 -0
  562. data/modules/mu/master/chef.rb +471 -0
  563. data/modules/mu/master/ldap.rb +1005 -0
  564. data/modules/mu/master.rb +415 -0
  565. data/modules/mu/mommacat.rb +2703 -0
  566. data/modules/mu-load-config.rb +1 -0
  567. data/modules/mu.rb +724 -0
  568. data/modules/scratchpad.erb +1 -0
  569. data/modules/tests/super_complex_bok.yml +41 -0
  570. data/modules/tests/super_simple_bok.yml +40 -0
  571. data/mu.gemspec +62 -0
  572. data/roles/demo-dbservice-configure.json +19 -0
  573. data/roles/demo-portal-configure.json +19 -0
  574. data/roles/mu-master-jenkins.json +24 -0
  575. data/roles/mu-master-nagios-only.json +13 -0
  576. data/roles/mu-master.json +12 -0
  577. data/roles/mu-node.json +19 -0
  578. data/roles/mu-splunk-server.json +13 -0
  579. data/roles/mu-splunk.json +13 -0
  580. data/test/clean_up.py +25 -0
  581. data/test/demo-test-profile/README.md +3 -0
  582. data/test/demo-test-profile/controls/flask.rb +84 -0
  583. data/test/demo-test-profile/inspec.lock +7 -0
  584. data/test/demo-test-profile/inspec.yml +11 -0
  585. data/test/etco-test-profile/README.md +3 -0
  586. data/test/etco-test-profile/controls/all-in-one.rb +182 -0
  587. data/test/etco-test-profile/inspec.lock +7 -0
  588. data/test/etco-test-profile/inspec.yml +11 -0
  589. data/test/exec_inspec.py +246 -0
  590. data/test/exec_mu_install.py +241 -0
  591. data/test/exec_retry.py +44 -0
  592. data/test/mu-master-test/README.md +3 -0
  593. data/test/mu-master-test/controls/all_in_one.rb +557 -0
  594. data/test/mu-master-test/inspec.lock +3 -0
  595. data/test/mu-master-test/inspec.yml +11 -0
  596. data/test/mu-tools-test/README.md +3 -0
  597. data/test/mu-tools-test/controls/base.rb +265 -0
  598. data/test/mu-tools-test/inspec.lock +3 -0
  599. data/test/mu-tools-test/inspec.yml +8 -0
  600. data/test/simple-server-php-test/README.md +3 -0
  601. data/test/simple-server-php-test/controls/apachephp.rb +25 -0
  602. data/test/simple-server-php-test/controls/example.rb +19 -0
  603. data/test/simple-server-php-test/inspec.lock +7 -0
  604. data/test/simple-server-php-test/inspec.yml +12 -0
  605. data/test/simple-server-rails-test/README.md +3 -0
  606. data/test/simple-server-rails-test/controls/rails.rb +188 -0
  607. data/test/simple-server-rails-test/inspec.lock +7 -0
  608. data/test/simple-server-rails-test/inspec.yml +11 -0
  609. data/test/simple-windows-test/README.md +3 -0
  610. data/test/simple-windows-test/controls/windows.rb +20 -0
  611. data/test/simple-windows-test/inspec.lock +7 -0
  612. data/test/simple-windows-test/inspec.yml +11 -0
  613. data/test/smoke_test.rb +75 -0
  614. data/test/wordpress-test/README.md +3 -0
  615. data/test/wordpress-test/controls/wordpress.rb +97 -0
  616. data/test/wordpress-test/inspec.lock +7 -0
  617. data/test/wordpress-test/inspec.yml +11 -0
  618. metadata +979 -0
@@ -0,0 +1,1943 @@
1
+ # Copyright:: Copyright (c) 2014 eGlobalTech, Inc., all rights reserved
2
+ #
3
+ # Licensed under the BSD-3 license (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License in the root of the project or at
6
+ #
7
+ # http://egt-labs.com/mu/LICENSE.html
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module MU
16
+ class Cloud
17
+ class AWS
18
+
19
+ # Creation of Virtual Private Clouds and associated artifacts (routes, subnets, etc).
20
+ class VPC < MU::Cloud::VPC
21
+
22
+ @deploy = nil
23
+ @config = nil
24
+ attr_reader :mu_name
25
+ attr_reader :cloud_id
26
+ attr_reader :config
27
+
28
+ # @param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member.
29
+ # @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::vpcs}
30
+ def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
31
+ @deploy = mommacat
32
+ @config = MU::Config.manxify(kitten_cfg)
33
+ @subnets = []
34
+ @subnetcachesemaphore = Mutex.new
35
+ @cloud_id = cloud_id
36
+ if !mu_name.nil?
37
+ @mu_name = mu_name
38
+ loadSubnets if !@cloud_id.nil?
39
+ elsif @config['scrub_mu_isms']
40
+ @mu_name = @config['name']
41
+ else
42
+ @mu_name = @deploy.getResourceName(@config['name'])
43
+ end
44
+ end
45
+
46
+ # Called automatically by {MU::Deploy#createResources}
47
+ def create
48
+ MU.log "Creating VPC #{@mu_name}", details: @config
49
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_vpc(cidr_block: @config['ip_block']).vpc
50
+ vpc_id = @config['vpc_id'] = resp.vpc_id
51
+
52
+ MU::MommaCat.createStandardTags(vpc_id, region: @config['region'])
53
+ MU::MommaCat.createTag(vpc_id, "Name", @mu_name, region: @config['region'])
54
+
55
+ if @config['tags']
56
+ @config['tags'].each { |tag|
57
+ MU::MommaCat.createTag(vpc_id, tag['key'], tag['value'], region: @config['region'])
58
+ }
59
+ end
60
+
61
+ if @config['optional_tags']
62
+ MU::MommaCat.listOptionalTags.each { |key, value|
63
+ MU::MommaCat.createTag(vpc_id, key, value, region: @config['region'])
64
+ }
65
+ end
66
+
67
+ if resp.state != "available"
68
+ begin
69
+ MU.log "Waiting for VPC #{@mu_name} (#{vpc_id}) to be available", MU::NOTICE
70
+ sleep 5
71
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_vpcs(vpc_ids: [vpc_id]).vpcs.first
72
+ end while resp.state != "available"
73
+ # There's a default route table that comes with. Let's tag it.
74
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
75
+ filters: [
76
+ {
77
+ name: "vpc-id",
78
+ values: [vpc_id]
79
+ }
80
+ ]
81
+ )
82
+ resp.route_tables.each { |rtb|
83
+ MU::MommaCat.createTag(rtb.route_table_id, "Name", @mu_name+"-#DEFAULTPRIV", region: @config['region'])
84
+ if @config['tags']
85
+ @config['tags'].each { |tag|
86
+ MU::MommaCat.createTag(rtb.route_table_id, tag['key'], tag['value'], region: @config['region'])
87
+ }
88
+ end
89
+
90
+ MU::MommaCat.createStandardTags(rtb.route_table_id, region: @config['region'])
91
+
92
+ if @config['optional_tags']
93
+ MU::MommaCat.listOptionalTags.each { |key, value|
94
+ MU::MommaCat.createTag(rtb.route_table_id, key, value, region: @config['region'])
95
+ }
96
+ end
97
+ }
98
+ end
99
+ @config['vpc_id'] = vpc_id
100
+ @cloud_id = vpc_id
101
+
102
+ if @config['create_internet_gateway']
103
+ MU.log "Creating Internet Gateway #{@mu_name}"
104
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_internet_gateway
105
+ internet_gateway_id = resp.internet_gateway.internet_gateway_id
106
+ sleep 5
107
+ MU::MommaCat.createStandardTags(internet_gateway_id, region: @config['region'])
108
+ MU::MommaCat.createTag(internet_gateway_id, "Name", @mu_name, region: @config['region'])
109
+ if @config['tags']
110
+ @config['tags'].each { |tag|
111
+ MU::MommaCat.createTag(internet_gateway_id, tag['key'], tag['value'], region: @config['region'])
112
+ }
113
+ end
114
+
115
+ if @config['optional_tags']
116
+ MU::MommaCat.listOptionalTags.each { |key, value|
117
+ MU::MommaCat.createTag(internet_gateway_id, key, value, region: @config['region'])
118
+ }
119
+ end
120
+
121
+ MU::Cloud::AWS.ec2(@config['region']).attach_internet_gateway(vpc_id: vpc_id, internet_gateway_id: internet_gateway_id)
122
+ @config['internet_gateway_id'] = internet_gateway_id
123
+ end
124
+
125
+ route_table_ids = []
126
+ if !@config['route_tables'].nil?
127
+ @config['route_tables'].each { |rtb|
128
+ rtb = createRouteTable(rtb)
129
+ route_table_ids << rtb['route_table_id']
130
+ }
131
+ end
132
+
133
+ if @config['endpoint']
134
+ config = {
135
+ :vpc_id => @cloud_id,
136
+ :service_name => @config['endpoint'],
137
+ :route_table_ids => route_table_ids
138
+ }
139
+
140
+ if @config['endpoint_policy'] && !@config['endpoint_policy'].empty?
141
+ statement = {:Statement => @config['endpoint_policy']}
142
+ config[:policy_document] = statement.to_json
143
+ end
144
+
145
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_vpc_endpoint(config).vpc_endpoint
146
+ endpoint_id = resp.vpc_endpoint_id
147
+ MU.log "Creating VPC endpoint #{endpoint_id}"
148
+ attempts = 0
149
+
150
+ while resp.state == "pending"
151
+ MU.log "Waiting for VPC endpoint #{endpoint_id} to become available" if attempts % 5 == 0
152
+ sleep 10
153
+ begin
154
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_vpc_endpoints(vpc_endpoint_ids: [endpoint_id]).vpc_endpoints.first
155
+ rescue Aws::EmptyStructure, NoMethodError
156
+ sleep 5
157
+ retry
158
+ end
159
+ raise MuError, "Timed out while waiting for VPC endpoint #{endpoint_id}: #{resp}" if attempts > 30
160
+ attempts += 1
161
+ end
162
+
163
+ raise MuError, "VPC endpoint failed #{endpoint_id}: #{resp}" if resp.state == "failed"
164
+ end
165
+
166
+ if @config["enable_traffic_logging"]
167
+ loggroup = @deploy.findLitterMate(name: @config['name']+"loggroup", type: "logs")
168
+ logrole = @deploy.findLitterMate(name: @config['name']+"logrole", type: "roles")
169
+
170
+ MU.log "Enabling traffic logging on VPC #{@mu_name} to log group #{loggroup.mu_name}"
171
+ MU::Cloud::AWS.ec2(@config['region']).create_flow_logs(
172
+ resource_ids: [@cloud_id],
173
+ resource_type: "VPC",
174
+ traffic_type: "ALL",
175
+ log_group_name: loggroup.mu_name,
176
+ deliver_logs_permission_arn: logrole.cloudobj.arn
177
+ )
178
+ end
179
+
180
+ nat_gateways = []
181
+ if !@config['subnets'].nil?
182
+ allocation_ids = []
183
+ subnet_semaphore = Mutex.new
184
+ subnetthreads = Array.new
185
+ parent_thread_id = Thread.current.object_id
186
+ azs = []
187
+ @config['subnets'].each { |subnet|
188
+ subnet_name = @config['name']+"-"+subnet['name']
189
+ MU.log "Creating Subnet #{subnet_name} (#{subnet['ip_block']})", details: subnet
190
+ azs = MU::Cloud::AWS.listAZs if azs.size == 0
191
+ if !subnet['availability_zone'].nil?
192
+ az = subnet['availability_zone']
193
+ else
194
+ az = azs.pop
195
+ end
196
+
197
+ subnetthreads << Thread.new {
198
+ MU.dupGlobals(parent_thread_id)
199
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_subnet(
200
+ vpc_id: vpc_id,
201
+ cidr_block: subnet['ip_block'],
202
+ availability_zone: az
203
+ ).subnet
204
+ subnet_id = subnet['subnet_id'] = resp.subnet_id
205
+ MU::MommaCat.createStandardTags(subnet_id, region: @config['region'])
206
+ MU::MommaCat.createTag(subnet_id, "Name", @mu_name+"-"+subnet['name'], region: @config['region'])
207
+ if @config['tags']
208
+ @config['tags'].each { |tag|
209
+ MU::MommaCat.createTag(subnet_id, tag['key'], tag['value'], region: @config['region'])
210
+ }
211
+ end
212
+
213
+ if @config['optional_tags']
214
+ MU::MommaCat.listOptionalTags.each { |key, value|
215
+ MU::MommaCat.createTag(subnet_id, key, value, region: @config['region'])
216
+ }
217
+ end
218
+
219
+ retries = 0
220
+ begin
221
+ if resp.state != "available"
222
+ begin
223
+ MU.log "Waiting for Subnet #{subnet_name} (#{subnet_id}) to be available", MU::NOTICE
224
+ sleep 5
225
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(subnet_ids: [subnet_id]).subnets.first
226
+ rescue Aws::EC2::Errors::InvalidSubnetIDNotFound => e
227
+ sleep 10
228
+ retry
229
+ end while resp.state != "available"
230
+ end
231
+ rescue NoMethodError => e
232
+ if retries <= 3
233
+ MU.log "Got bogus Aws::EmptyResponse error on #{subnet_id} (retries used: #{retries}/3)", MU::WARN
234
+ retries = retries + 1
235
+ sleep 5
236
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(subnet_ids: [subnet_id]).subnets.first
237
+ retry
238
+ else
239
+ raise e
240
+ end
241
+ end
242
+
243
+ if !subnet['route_table'].nil?
244
+ routes = {}
245
+ @config['route_tables'].each { |tbl|
246
+ routes[tbl['name']] = tbl
247
+ }
248
+ if routes.nil? or routes[subnet['route_table']].nil?
249
+ MU.log "Subnet #{subnet_name} references non-existent route #{subnet['route_table']}", MU::ERR, details: @deploy.deployment['vpcs']
250
+ raise MuError, "deploy failure"
251
+ end
252
+ MU.log "Associating Route Table '#{subnet['route_table']}' (#{routes[subnet['route_table']]['route_table_id']}) with #{subnet_name}"
253
+ retries = 0
254
+ begin
255
+ MU::Cloud::AWS.ec2(@config['region']).associate_route_table(
256
+ route_table_id: routes[subnet['route_table']]['route_table_id'],
257
+ subnet_id: subnet_id
258
+ )
259
+ rescue Aws::EC2::Errors::InvalidRouteTableIDNotFound => e
260
+ retries = retries + 1
261
+ if retries < 10
262
+ sleep 10
263
+ retry
264
+ else
265
+ raise MuError, e.inspect
266
+ end
267
+ end
268
+ end
269
+ retries = 0
270
+ begin
271
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(subnet_ids: [subnet_id]).subnets.first
272
+ rescue Aws::EC2::Errors::InvalidSubnetIDNotFound => e
273
+ if retries < 10
274
+ MU.log "Got #{e.inspect}, waiting and retrying", MU::WARN
275
+ sleep 10
276
+ retries = retries + 1
277
+ retry
278
+ end
279
+ raise MuError, e.inspect, e.backtrace
280
+ end
281
+
282
+ if subnet['is_public'] && subnet['create_nat_gateway']
283
+ MU::MommaCat.lock("nat-gateway-eipalloc")
284
+ filters = [{name: "domain", values: ["vpc"]}]
285
+ eips = MU::Cloud::AWS.ec2(@config['region']).describe_addresses(filters: filters).addresses
286
+ allocation_id = nil
287
+ eips.each { |eip|
288
+ next if !eip.association_id.nil? and !eip.association_id.empty?
289
+ if (eip.private_ip_address.nil? || eip.private_ip_address.empty?) and MU::MommaCat.lock(eip.allocation_id, true, true)
290
+ if !allocation_ids.include?(eip.allocation_id)
291
+ allocation_id = eip.allocation_id
292
+ break
293
+ end
294
+ end
295
+ }
296
+
297
+ if allocation_id.nil?
298
+ allocation_id = MU::Cloud::AWS.ec2(@config['region']).allocate_address(domain: "vpc").allocation_id
299
+ MU::MommaCat.lock(allocation_id, false, true)
300
+ end
301
+
302
+ allocation_ids << allocation_id
303
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_nat_gateway(
304
+ subnet_id: subnet['subnet_id'],
305
+ allocation_id: allocation_id
306
+ ).nat_gateway
307
+
308
+ nat_gateway_id = resp.nat_gateway_id
309
+ attempts = 0
310
+ MU::MommaCat.unlock("nat-gateway-eipalloc")
311
+ while resp.state == "pending"
312
+ MU.log "Waiting for nat gateway #{nat_gateway_id} () to become available (EIP allocation: #{allocation_id})" if attempts % 5 == 0
313
+ sleep 30
314
+ begin
315
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_nat_gateways(nat_gateway_ids: [nat_gateway_id]).nat_gateways.first
316
+ rescue Aws::EmptyStructure, NoMethodError
317
+ sleep 5
318
+ retry
319
+ end
320
+ if attempts > 30
321
+ MU::MommaCat.unlock(allocation_id, true)
322
+ raise MuError, "Timed out while waiting for NAT Gateway #{nat_gateway_id}: #{resp}"
323
+ end
324
+ attempts += 1
325
+ end
326
+ MU::MommaCat.unlock(allocation_id, true)
327
+
328
+ raise MuError, "NAT Gateway failed #{nat_gateway_id}: #{resp}" if resp.state == "failed"
329
+ nat_gateways << {'id' => nat_gateway_id, 'availability_zone' => subnet['availability_zone']}
330
+ end
331
+
332
+ if subnet.has_key?("map_public_ips")
333
+ retries = 0
334
+ begin
335
+ resp = MU::Cloud::AWS.ec2(@config['region']).modify_subnet_attribute(
336
+ subnet_id: subnet_id,
337
+ map_public_ip_on_launch: {
338
+ value: subnet['map_public_ips'],
339
+ }
340
+ )
341
+ rescue Aws::EC2::Errors::InvalidSubnetIDNotFound => e
342
+ if retries < 10
343
+ MU.log "Got #{e.inspect} while trying to enable map_public_ips on subnet, waiting and retrying", MU::WARN
344
+ sleep 10
345
+ retries += 1
346
+ retry
347
+ end
348
+ raise MuError, "Got #{e.inspect}, #{e.backtrace} while trying to enable map_public_ips on subnet"
349
+ end
350
+ end
351
+
352
+ if subnet["enable_traffic_logging"]
353
+ loggroup = @deploy.findLitterMate(name: @config['name']+"loggroup", type: "logs")
354
+ logrole = @deploy.findLitterMate(name: @config['name']+"logrole", type: "roles")
355
+ MU.log "Enabling traffic logging on Subnet #{subnet_name} in VPC #{@mu_name} to log group #{loggroup.mu_name}"
356
+ MU::Cloud::AWS.ec2(@config['region']).create_flow_logs(
357
+ resource_ids: [subnet_id],
358
+ resource_type: "Subnet",
359
+ traffic_type: subnet["traffic_type_to_log"],
360
+ log_group_name: loggroup.mu_name,
361
+ deliver_logs_permission_arn: logrole.cloudobj.arn
362
+ )
363
+ end
364
+ }
365
+ }
366
+
367
+ subnetthreads.each { |t|
368
+ t.join
369
+ }
370
+
371
+ notify
372
+ end
373
+
374
+ if !nat_gateways.empty?
375
+ nat_gateways.each { |gateway|
376
+ @config['subnets'].each { |subnet|
377
+ if subnet['is_public'] == false && subnet['availability_zone'] == gateway['availability_zone']
378
+ @config['route_tables'].each { |rtb|
379
+ if rtb['name'] == subnet['route_table']
380
+ rtb['routes'].each { |route|
381
+ if route['gateway'] == '#NAT'
382
+ route_config = {
383
+ :route_table_id => rtb['route_table_id'],
384
+ :destination_cidr_block => route['destination_network'],
385
+ :nat_gateway_id => gateway['id']
386
+ }
387
+
388
+ MU.log "Creating route for #{route['destination_network']} through NAT gatway #{gateway['id']}", details: route_config
389
+ begin
390
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_route(route_config)
391
+ rescue Aws::EC2::Errors::RouteAlreadyExists => e
392
+ MU.log "Attempt to create duplicate route to #{route['destination_network']} for #{gateway['id']} in #{rtb['route_table_id']}", MU::WARN
393
+ end
394
+ end
395
+ }
396
+ end
397
+ }
398
+ end
399
+ }
400
+ }
401
+ end
402
+
403
+ if @config['enable_dns_support']
404
+ MU.log "Enabling DNS support in #{@mu_name}"
405
+ MU::Cloud::AWS.ec2(@config['region']).modify_vpc_attribute(
406
+ vpc_id: vpc_id,
407
+ enable_dns_support: {value: @config['enable_dns_support']}
408
+ )
409
+ end
410
+ if @config['enable_dns_hostnames']
411
+ MU.log "Enabling DNS hostnames in #{@mu_name}"
412
+ MU::Cloud::AWS.ec2(@config['region']).modify_vpc_attribute(
413
+ vpc_id: vpc_id,
414
+ enable_dns_hostnames: {value: @config['enable_dns_hostnames']}
415
+ )
416
+ end
417
+
418
+ if @config['dhcp']
419
+ MU.log "Setting custom DHCP options in #{@mu_name}", details: @config['dhcp']
420
+ dhcpopts = []
421
+
422
+ if @config['dhcp']['netbios_type']
423
+ dhcpopts << {key: "netbios-node-type", values: [@config['dhcp']['netbios_type'].to_s]}
424
+ end
425
+ if @config['dhcp']['domains']
426
+ dhcpopts << {key: "domain-name", values: @config['dhcp']['domains']}
427
+ end
428
+ if @config['dhcp']['dns_servers']
429
+ dhcpopts << {key: "domain-name-servers", values: @config['dhcp']['dns_servers']}
430
+ end
431
+ if @config['dhcp']['ntp_servers']
432
+ dhcpopts << {key: "ntp-servers", values: @config['dhcp']['ntp_servers']}
433
+ end
434
+ if @config['dhcp']['netbios_servers']
435
+ dhcpopts << {key: "netbios-name-servers", values: @config['dhcp']['netbios_servers']}
436
+ end
437
+
438
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_dhcp_options(
439
+ dhcp_configurations: dhcpopts
440
+ )
441
+ dhcpopt_id = resp.dhcp_options.dhcp_options_id
442
+ MU::MommaCat.createStandardTags(dhcpopt_id, region: @config['region'])
443
+ MU::MommaCat.createTag(dhcpopt_id, "Name", @mu_name, region: @config['region'])
444
+
445
+ if @config['tags']
446
+ @config['tags'].each { |tag|
447
+ MU::MommaCat.createTag(dhcpopt_id, tag['key'], tag['value'], region: @config['region'])
448
+ }
449
+ end
450
+
451
+ if @config['optional_tags']
452
+ MU::MommaCat.listOptionalTags.each { |key, value|
453
+ MU::MommaCat.createTag(dhcpopt_id, key, value, region: @config['region'])
454
+ }
455
+ end
456
+
457
+ MU::Cloud::AWS.ec2(@config['region']).associate_dhcp_options(dhcp_options_id: dhcpopt_id, vpc_id: vpc_id)
458
+ end
459
+ notify
460
+
461
+ if !MU::Cloud::AWS.isGovCloud?(@config['region'])
462
+ mu_zone = MU::Cloud::DNSZone.find(cloud_id: "platform-mu").values.first
463
+ if !mu_zone.nil?
464
+ MU::Cloud::AWS::DNSZone.toggleVPCAccess(id: mu_zone.id, vpc_id: vpc_id, region: @config['region'])
465
+ end
466
+ end
467
+ loadSubnets
468
+
469
+ MU.log "VPC #{@mu_name} created", details: @config
470
+ end
471
+
472
+ # Canonical Amazon Resource Number for this resource
473
+ # @return [String]
474
+ def arn
475
+ "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":ec2:"+@config['region']+":"+MU.account_number+":vpc/"+@cloud_id
476
+ end
477
+
478
+ # Describe this VPC
479
+ # @return [Hash]
480
+ def notify
481
+ @config
482
+ end
483
+
484
+ # Called automatically by {MU::Deploy#createResources}
485
+ def groom
486
+ vpc_name = @deploy.getResourceName(@config['name'])
487
+
488
+ # Generate peering connections
489
+ if !@config['peers'].nil? and @config['peers'].size > 0
490
+ @config['peers'].each { |peer|
491
+ peer_obj = nil
492
+ begin
493
+ if peer['account'].nil? or peer['account'] == MU.account_number
494
+ tag_key, tag_value = peer['vpc']['tag'].split(/=/, 2) if !peer['vpc']['tag'].nil?
495
+ if peer['vpc']['deploy_id'].nil? and peer['vpc']['vpc_id'].nil? and tag_key.nil?
496
+ peer['vpc']['deploy_id'] = @deploy.deploy_id
497
+ end
498
+ peer_obj = MU::MommaCat.findStray(
499
+ "AWS",
500
+ "vpcs",
501
+ deploy_id: peer['vpc']['deploy_id'],
502
+ cloud_id: peer['vpc']['vpc_id'],
503
+ name: peer['vpc']['vpc_name'],
504
+ tag_key: tag_key,
505
+ tag_value: tag_value,
506
+ dummy_ok: true,
507
+ region: peer['vpc']['region']
508
+ )
509
+ raise MuError, "No result looking for #{@mu_name}'s peer VPCs (#{peer['vpc']})" if peer_obj.nil? or peer_obj.first.nil?
510
+ peer_obj = peer_obj.first
511
+ peer_id = peer_obj.cloud_id
512
+
513
+ MU.log "Setting peering connection from VPC #{@config['name']} (#{@cloud_id}) to #{peer_id}"
514
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_vpc_peering_connection(
515
+ vpc_id: @cloud_id,
516
+ peer_vpc_id: peer_id
517
+ )
518
+ else
519
+ peer_id = peer['vpc']['vpc_id']
520
+ MU.log "Setting peering connection from VPC #{@config['name']} (#{@cloud_id}) to #{peer_id} in account #{peer['account']}", MU::INFO, details: peer
521
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_vpc_peering_connection(
522
+ vpc_id: @cloud_id,
523
+ peer_vpc_id: peer_id,
524
+ peer_owner_id: peer['account']
525
+ )
526
+ end
527
+ rescue Aws::EC2::Errors::VpcPeeringConnectionAlreadyExists => e
528
+ MU.log "Attempt to create duplicate peering connection to #{peer_id} from VPC #{@config['name']}", MU::WARN
529
+ end
530
+ peering_name = @deploy.getResourceName(@config['name']+"-PEER-"+peer_id)
531
+
532
+ peering_id = resp.vpc_peering_connection.vpc_peering_connection_id
533
+ MU::MommaCat.createStandardTags(peering_id, region: @config['region'])
534
+ MU::MommaCat.createTag(peering_id, "Name", peering_name, region: @config['region'])
535
+
536
+ if @config['optional_tags']
537
+ MU::MommaCat.listOptionalTags.each { |key, value|
538
+ MU::MommaCat.createTag(peering_id, key, value, region: @config['region'])
539
+ }
540
+ end
541
+
542
+ if @config['tags']
543
+ @config['tags'].each { |tag|
544
+ MU::MommaCat.createTag(peering_id, tag['key'], tag['value'], region: @config['region'])
545
+ }
546
+ end
547
+
548
+ # Create routes to our new friend.
549
+ MU::Cloud::AWS::VPC.listAllSubnetRouteTables(@cloud_id, region: @config['region']).each { |rtb_id|
550
+ my_route_config = {
551
+ :route_table_id => rtb_id,
552
+ :destination_cidr_block => peer_obj.cloud_desc.cidr_block,
553
+ :vpc_peering_connection_id => peering_id
554
+ }
555
+ begin
556
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_route(my_route_config)
557
+ rescue Aws::EC2::Errors::RouteAlreadyExists => e
558
+ rtbdesc = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
559
+ route_table_ids: [rtb_id]
560
+ ).route_tables.first
561
+ rtbdesc.routes.each { |r|
562
+ if r.destination_cidr_block == peer_obj.cloud_desc.cidr_block
563
+ if r.vpc_peering_connection_id != peering_id
564
+ MU.log "Attempt to create duplicate route to #{peer_obj.cloud_desc.cidr_block} from VPC #{@config['name']}", MU::ERR, details: r
565
+ raise MuError, "Can't create route via #{peering_id}, a route to #{peer_obj.cloud_desc.cidr_block} already exists"
566
+ else
567
+ break # this is fine, the route simply already exists
568
+ end
569
+ end
570
+ }
571
+ end
572
+ }
573
+
574
+ begin
575
+ cnxn = MU::Cloud::AWS.ec2(@config['region']).describe_vpc_peering_connections(
576
+ vpc_peering_connection_ids: [peering_id]
577
+ ).vpc_peering_connections.first
578
+
579
+ if cnxn.status.code == "pending-acceptance"
580
+ if ((!peer_obj.nil? and !peer_obj.deploydata.nil? and peer_obj.deploydata['auto_accept_peers']) or $MU_CFG['allow_invade_foreign_vpcs'])
581
+ MU.log "Auto-accepting peering connection from VPC #{@config['name']} (#{@cloud_id}) to #{peer_id}", MU::NOTICE
582
+ begin
583
+ MU::Cloud::AWS.ec2(@config['region']).accept_vpc_peering_connection(
584
+ vpc_peering_connection_id: peering_id
585
+ )
586
+ rescue Aws::EC2::Errors::VpcPeeringConnectionAlreadyExists => e
587
+ MU.log "Attempt to create duplicate peering connection to #{peer_id} from VPC #{@config['name']}", MU::WARN
588
+ end
589
+
590
+ # Create routes back from our new friend to us.
591
+ MU::Cloud::AWS::VPC.listAllSubnetRouteTables(peer_id, region: peer['vpc']['region']).each { |rtb_id|
592
+ peer_route_config = {
593
+ :route_table_id => rtb_id,
594
+ :destination_cidr_block => @config['ip_block'],
595
+ :vpc_peering_connection_id => peering_id
596
+ }
597
+ begin
598
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_route(peer_route_config)
599
+ rescue Aws::EC2::Errors::RouteAlreadyExists => e
600
+ MU.log "Attempt to create duplicate route to #{@config['ip_block']} from VPC #{peer_id}", MU::WARN
601
+ end
602
+ }
603
+ else
604
+ MU.log "VPC #{peer_id} is not managed by this Mu server or is not configured to auto-accept peering requests. You must accept the peering request for '#{@config['name']}' (#{@cloud_id}) by hand.", MU::WARN, details: "In the AWS Console, go to VPC => Peering Connections and look in the Actions drop-down. You can also set 'Invade Foreign VPCs' to 'true' using mu-configure to auto-accept all peering connections within this account, regardless of whether this Mu server owns the VPCs. This setting is per-user."
605
+ end
606
+ end
607
+
608
+ if cnxn.status.code == "failed" or cnxn.status.code == "rejected" or cnxn.status.code == "expired" or cnxn.status.code == "deleted"
609
+ MU.log "VPC peering connection from VPC #{@config['name']} (#{@cloud_id}) to #{peer_id} #{cnxn.status.code}: #{cnxn.status.message}", MU::ERR
610
+ begin
611
+ MU::Cloud::AWS.ec2(@config['region']).delete_vpc_peering_connection(
612
+ vpc_peering_connection_id: peering_id
613
+ )
614
+ rescue Aws::EC2::Errors::InvalidStateTransition => e
615
+ # XXX apparently this is normal?
616
+ end
617
+ raise MuError, "VPC peering connection from VPC #{@config['name']} (#{@cloud_id}) to #{peer_id} #{cnxn.status.code}: #{cnxn.status.message}"
618
+ end
619
+ end while cnxn.status.code != "active" and !(cnxn.status.code == "pending-acceptance" and (peer_obj.nil? or peer_obj.deploydata.nil? or !peer_obj.deploydata['auto_accept_peers']))
620
+
621
+ }
622
+ end
623
+
624
+ # Add any routes that reference instances, which would've been created
625
+ # in Server objects' create phases.
626
+ if !@config['route_tables'].nil?
627
+ @config['route_tables'].each { |rtb|
628
+ route_table_id = rtb['route_table_id']
629
+
630
+ rtb['routes'].each { |route|
631
+ if !route['nat_host_id'].nil? or !route['nat_host_name'].nil?
632
+ route_config = {
633
+ :route_table_id => route_table_id,
634
+ :destination_cidr_block => route['destination_network']
635
+ }
636
+
637
+ nat_instance = findBastion(
638
+ nat_name: route["nat_host_name"],
639
+ nat_cloud_id: route["nat_host_id"]
640
+ )
641
+ if nat_instance.nil?
642
+ raise MuError, "VPC #{vpc_name} is configured to use #{route} as a route, but I can't find a matching bastion host!"
643
+ end
644
+ route_config[:instance_id] = nat_instance.cloud_id
645
+
646
+ MU.log "Creating route for #{route['destination_network']} through NAT host #{nat_instance.cloud_id}", details: route_config
647
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_route(route_config)
648
+ end
649
+ }
650
+
651
+ }
652
+ end
653
+
654
+ end
655
+
656
+ # Locate an existing VPC or VPCs and return an array containing matching AWS resource descriptors for those that match.
657
+ # @param cloud_id [String]: The cloud provider's identifier for this resource.
658
+ # @param region [String]: The cloud provider region
659
+ # @param tag_key [String]: A tag key to search.
660
+ # @param tag_value [String]: The value of the tag specified by tag_key to match when searching by tag.
661
+ # @return [Array<Hash<String,OpenStruct>>]: The cloud provider's complete descriptions of matching VPCs
662
+ def self.find(cloud_id: nil, region: MU.curRegion, tag_key: "Name", tag_value: nil, flags: {})
663
+
664
+ retries = 0
665
+ map = {}
666
+ begin
667
+ sleep 5 if retries < 0
668
+
669
+ if tag_value
670
+ MU.log "Searching for VPC by tag:#{tag_key}=#{tag_value}", MU::DEBUG
671
+ resp = MU::Cloud::AWS.ec2(region).describe_vpcs(
672
+ filters: [
673
+ {name: "tag:#{tag_key}", values: [tag_value]}
674
+ ]
675
+ )
676
+ if resp.data.vpcs.nil? or resp.data.vpcs.size == 0
677
+ return nil
678
+ elsif resp.data.vpcs.size >= 1
679
+ resp.data.vpcs.each { |vpc|
680
+ map[vpc.vpc_id] = vpc
681
+ }
682
+ return map
683
+ end
684
+ end
685
+
686
+ if !cloud_id.nil?
687
+ MU.log "Searching for VPC id '#{cloud_id}' in #{region}", MU::DEBUG
688
+ begin
689
+ resp = MU::Cloud::AWS.ec2(region).describe_vpcs(vpc_ids: [cloud_id.to_s])
690
+ resp.vpcs.each { |vpc|
691
+ map[vpc.vpc_id] = vpc
692
+ }
693
+ return map
694
+ rescue Aws::EC2::Errors::InvalidVpcIDNotFound => e
695
+ end
696
+ end
697
+
698
+ retries = retries + 1
699
+ end while retries < 5
700
+
701
+ return map
702
+ end
703
+
704
+ # Return an array of MU::Cloud::AWS::VPC::Subnet objects describe the
705
+ # member subnets of this VPC.
706
+ #
707
+ # @return [Array<MU::Cloud::AWS::VPC::Subnet>]
708
+ def subnets
709
+ if @subnets.nil? or @subnets.size == 0
710
+ return loadSubnets
711
+ end
712
+ return @subnets
713
+ end
714
+
715
+ # Describe subnets associated with this VPC. We'll compose identifying
716
+ # information similar to what MU::Cloud.describe builds for first-class
717
+ # resources.
718
+ # XXX this is weaksauce. Subnets should be objects with their own methods
719
+ # that work like first-class objects. How would we enforce that?
720
+ # @return [Array<Hash>]: A list of cloud provider identifiers of subnets associated with this VPC.
721
+ def loadSubnets
722
+ if @cloud_id
723
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(
724
+ filters: [
725
+ { name: "vpc-id", values: [@cloud_id] }
726
+ ]
727
+ )
728
+ if resp.nil? or resp.subnets.nil? or resp.subnets.size == 0
729
+ MU.log "Got empty results when trying to list subnets in #{@cloud_id}", MU::WARN
730
+ return []
731
+ end
732
+ end
733
+
734
+ @subnetcachesemaphore.synchronize {
735
+ @subnets ||= []
736
+ ext_ids = @subnets.each.collect { |s| s.cloud_id }
737
+
738
+ # If we're a plain old Mu resource, load our config and deployment
739
+ # metadata. Like ya do.
740
+ if !@config.nil? and @config.has_key?("subnets")
741
+ @config['subnets'].each { |subnet|
742
+ subnet['mu_name'] = @mu_name+"-"+subnet['name'] if !subnet.has_key?("mu_name")
743
+ subnet['region'] = @config['region']
744
+ if !resp.nil? and !resp.data.nil? and !resp.data.subnets.nil?
745
+ resp.data.subnets.each { |desc|
746
+ if desc.cidr_block == subnet["ip_block"]
747
+ subnet["tags"] = MU.structToHash(desc.tags)
748
+ subnet["cloud_id"] = desc.subnet_id
749
+ break
750
+ end
751
+ }
752
+ end
753
+
754
+ if subnet["cloud_id"] and !ext_ids.include?(subnet["cloud_id"])
755
+ @subnets << MU::Cloud::AWS::VPC::Subnet.new(self, subnet)
756
+ elsif !subnet["cloud_id"]
757
+ resp.data.subnets.each { |desc|
758
+ if desc.cidr_block == subnet["ip_block"]
759
+ subnet['cloud_id'] = desc.subnet_id
760
+ @subnets << MU::Cloud::AWS::VPC::Subnet.new(self, subnet)
761
+ end
762
+ }
763
+ end
764
+
765
+ }
766
+ end
767
+
768
+ # Of course we might be loading up a dummy subnet object from a
769
+ # foreign or non-Mu-created VPC and subnet. So make something up.
770
+ if !resp.nil? and @subnets.empty?
771
+ resp.data.subnets.each { |desc|
772
+ subnet = {}
773
+ subnet["ip_block"] = desc.cidr_block
774
+ subnet["name"] = subnet["ip_block"].gsub(/[\.\/]/, "_")
775
+ subnet['mu_name'] = @mu_name+"-"+subnet['name']
776
+ subnet["tags"] = MU.structToHash(desc.tags)
777
+ subnet["cloud_id"] = desc.subnet_id
778
+ subnet['region'] = @config['region']
779
+ if !ext_ids.include?(desc.subnet_id)
780
+ @subnets << MU::Cloud::AWS::VPC::Subnet.new(self, subnet)
781
+ end
782
+ }
783
+ end
784
+ return @subnets
785
+ }
786
+ end
787
+
788
+ # Given some search criteria try locating a NAT Gaateway in this VPC.
789
+ # @param nat_cloud_id [String]: The cloud provider's identifier for this NAT.
790
+ # @param nat_filter_key [String]: A cloud provider filter to help identify the resource, used in conjunction with nat_filter_value.
791
+ # @param nat_filter_value [String]: A cloud provider filter to help identify the resource, used in conjunction with nat_filter_key.
792
+ # @param region [String]: The cloud provider region of the target instance.
793
+ def findNat(nat_cloud_id: nil, nat_filter_key: nil, nat_filter_value: nil, region: MU.curRegion)
794
+ # Discard the nat_cloud_id if it's an AWS instance ID
795
+ nat_cloud_id = nil if nat_cloud_id && nat_cloud_id.start_with?("i-")
796
+
797
+ if @gateways.nil?
798
+ @gateways =
799
+ if nat_cloud_id
800
+ MU::Cloud::AWS.ec2(region).describe_nat_gateways(nat_gateway_ids: [nat_cloud_id])
801
+ elsif nat_filter_key && nat_filter_value
802
+ MU::Cloud::AWS.ec2(region).describe_nat_gateways(
803
+ filter: [
804
+ {
805
+ name: nat_filter_key,
806
+ values: [nat_filter_value]
807
+ }
808
+ ]
809
+ ).nat_gateways
810
+ end
811
+ end
812
+
813
+ @gateways ? @gateways.first : nil
814
+ end
815
+
816
+ # Given some search criteria for a {MU::Cloud::Server}, see if we can
817
+ # locate a NAT host in this VPC.
818
+ # @param nat_name [String]: The name of the resource as defined in its 'name' Basket of Kittens field, typically used in conjunction with deploy_id.
819
+ # @param nat_cloud_id [String]: The cloud provider's identifier for this NAT.
820
+ # @param nat_tag_key [String]: A cloud provider tag to help identify the resource, used in conjunction with tag_value.
821
+ # @param nat_tag_value [String]: A cloud provider tag to help identify the resource, used in conjunction with tag_key.
822
+ # @param nat_ip [String]: An IP address associated with the NAT instance.
823
+ def findBastion(nat_name: nil, nat_cloud_id: nil, nat_tag_key: nil, nat_tag_value: nil, nat_ip: nil)
824
+ nat = nil
825
+ deploy_id = nil
826
+ nat_name = nat_name.to_s if !nat_name.nil? and nat_name.class.to_s == "MU::Config::Tail"
827
+ nat_ip = nat_ip.to_s if !nat_ip.nil? and nat_ip.class.to_s == "MU::Config::Tail"
828
+ nat_cloud_id = nat_cloud_id.to_s if !nat_cloud_id.nil? and nat_cloud_id.class.to_s == "MU::Config::Tail"
829
+ nat_tag_key = nat_tag_key.to_s if !nat_tag_key.nil? and nat_tag_key.class.to_s == "MU::Config::Tail"
830
+ nat_tag_value = nat_tag_value.to_s if !nat_tag_value.nil? and nat_tag_value.class.to_s == "MU::Config::Tail"
831
+
832
+ # If we're searching by name, assume it's part of this here deploy.
833
+ if nat_cloud_id.nil? and !@deploy.nil?
834
+ deploy_id = @deploy.deploy_id
835
+ end
836
+ found = MU::MommaCat.findStray(
837
+ @config['cloud'],
838
+ "server",
839
+ name: nat_name,
840
+ region: @config['region'],
841
+ cloud_id: nat_cloud_id,
842
+ deploy_id: deploy_id,
843
+ tag_key: nat_tag_key,
844
+ tag_value: nat_tag_value,
845
+ allow_multi: true,
846
+ dummy_ok: true,
847
+ calling_deploy: @deploy
848
+ )
849
+
850
+ return nil if found.nil? || found.empty?
851
+ if found.size > 1
852
+ found.each { |nat|
853
+ # Try some AWS-specific criteria
854
+ cloud_desc = nat.cloud_desc
855
+ if !nat_host_ip.nil? and
856
+ (cloud_desc.private_ip_address == nat_host_ip or cloud_desc.public_ip_address == nat_host_ip)
857
+ return nat
858
+ elsif cloud_desc.vpc_id == @cloud_id
859
+ # XXX Strictly speaking we could have different NATs in different
860
+ # subnets, so this can be wrong in corner cases. Why you'd
861
+ # architect something that obnoxiously, I have no idea.
862
+ return nat
863
+ end
864
+ }
865
+ elsif found.size == 1
866
+ return found.first
867
+ end
868
+ return nil
869
+ end
870
+
871
+ # Check for a subnet in this VPC matching one or more of the specified
872
+ # criteria, and return it if found.
873
+ def getSubnet(cloud_id: nil, name: nil, tag_key: nil, tag_value: nil, ip_block: nil)
874
+ if !cloud_id and !name and !tag_key and !tag_value and !ip_block
875
+ raise MuError, "getSubnet called with no non-nil arguments"
876
+ end
877
+ subnets
878
+
879
+ @subnets.each { |subnet|
880
+ if !cloud_id.nil? and !subnet.cloud_id.nil? and subnet.cloud_id.to_s == cloud_id.to_s
881
+ return subnet
882
+ elsif !name.nil? and !subnet.name.nil? and subnet.name.to_s == name.to_s
883
+ return subnet
884
+ elsif !ip_block.nil? and !subnet.ip_block.nil? and subnet.ip_block.to_s == ip_block.to_s
885
+ return subnet
886
+ end
887
+ }
888
+ return nil
889
+ end
890
+
891
+ # Get the subnets associated with an instance.
892
+ # @param instance_id [String]: The cloud identifier of the instance
893
+ # @param instance [String]: A cloud descriptor for the instance, to save us an API call if we already have it
894
+ # @param region [String]: The cloud provider region of the target instance
895
+ # @return [Array<String>]
896
+ def self.getInstanceSubnets(instance_id: nil, instance: nil, region: MU.curRegion)
897
+ return [] if instance_id.nil? and instance.nil?
898
+ my_subnets = []
899
+
900
+ if instance.nil?
901
+ begin
902
+ instance = MU::Cloud::AWS.ec2(region).describe_instances(instance_ids: [instance_id]).reservations.first.instances.first
903
+ rescue NoMethodError, Aws::EC2::Errors::InvalidInstanceIDNotFound => e
904
+ MU.log "Failed to identify instance #{instance_id} in MU::Cloud::AWS::VPC.getInstanceSubnets", MU::WARN
905
+ return []
906
+ end
907
+ end
908
+ my_subnets << instance.subnet_id if !instance.subnet_id.nil?
909
+ if !instance.network_interfaces.nil?
910
+ instance.network_interfaces.each { |iface|
911
+ my_subnets << iface.subnet_id if !iface.subnet_id.nil?
912
+ }
913
+ end
914
+ return my_subnets.uniq.sort
915
+ end
916
+
917
+ @route_cache = {}
918
+ @rtb_cache = {}
919
+ @rtb_cache_semaphore = Mutex.new
920
+ # Check whether we (the Mu Master) have a direct route to a particular
921
+ # subnet. Useful for skipping hops through bastion hosts to get directly
922
+ # at child nodes in peered VPCs and the like.
923
+ # @param target_instance [OpenStruct]: The cloud descriptor of the instance to check.
924
+ # @param region [String]: The cloud provider region of the target subnet.
925
+ # @return [Boolean]
926
+ def self.haveRouteToInstance?(target_instance, region: MU.curRegion)
927
+ return false if target_instance.nil?
928
+ return false if MU.myCloud != "AWS"
929
+ instance_id = target_instance.instance_id
930
+ # XXX check if I'm even in AWS before all this bullshit
931
+ target_vpc_id = target_instance.vpc_id
932
+ my_vpc_id = MU.myCloudDescriptor.vpc_id
933
+ if (target_vpc_id && !target_vpc_id.empty?) && (my_vpc_id && !my_vpc_id.empty?)
934
+ # If the master and the node are in the same vpc then more likely than not there is a route...
935
+ if target_vpc_id == my_vpc_id
936
+ MU.log "I share a VPC with #{instance_id}, I can route to it directly", MU::DEBUG
937
+ @route_cache[instance_id] = true
938
+ return true
939
+ end
940
+ end
941
+
942
+ return @route_cache[instance_id] if @route_cache.has_key?(instance_id) && @route_cache[instance_id]
943
+ my_subnets = MU::Cloud::AWS::VPC.getInstanceSubnets(instance: MU.myCloudDescriptor)
944
+ target_subnets = MU::Cloud::AWS::VPC.getInstanceSubnets(instance: target_instance, region: region)
945
+ # XXX make sure accounts for being in different regions
946
+
947
+ resp = nil
948
+ my_subnets_key = my_subnets.join(",")
949
+ target_subnets_key = target_subnets.join(",")
950
+ MU::Cloud::AWS::VPC.update_route_tables_cache(my_subnets_key, region: MU.myRegion)
951
+ MU::Cloud::AWS::VPC.update_route_tables_cache(target_subnets_key, region: region)
952
+
953
+ if MU::Cloud::AWS::VPC.have_route_peered_vpc?(my_subnets_key, target_subnets_key, instance_id)
954
+ return true
955
+ else
956
+ # The cache can be out of date at times, check again without it
957
+ MU::Cloud::AWS::VPC.update_route_tables_cache(my_subnets_key, use_cache: false, region: MU.myRegion)
958
+ MU::Cloud::AWS::VPC.update_route_tables_cache(target_subnets_key, use_cache: false, region: region)
959
+
960
+ return MU::Cloud::AWS::VPC.have_route_peered_vpc?(my_subnets_key, target_subnets_key, instance_id)
961
+ end
962
+
963
+ @route_cache[instance_id] = false
964
+ return false
965
+ end
966
+
967
+ # updates the route table cache (@rtb_cache).
968
+ # @param subnet_key [String]: The subnet/subnets route tables will be extracted from.
969
+ # @param use_cache [Boolean]: If to use the existing cache and add records to cache only if missing, or to also replace exising records in cache.
970
+ # @param region [String]: The cloud provider region of the target subnet.
971
+ def self.update_route_tables_cache(subnet_key, use_cache: true, region: MU.curRegion)
972
+ @rtb_cache_semaphore.synchronize {
973
+ update =
974
+ if !use_cache
975
+ true
976
+ elsif use_cache && !@rtb_cache.has_key?(subnet_key)
977
+ true
978
+ else
979
+ false
980
+ end
981
+
982
+ if update
983
+ route_tables = MU::Cloud::AWS::VPC.get_route_tables(subnet_ids: subnet_key.split(","), region: region)
984
+
985
+ if route_tables.empty? && !subnet_key.empty?
986
+ vpc_id = MU::Cloud::AWS.ec2(region).describe_subnets(subnet_ids: subnet_key.split(",")).subnets.first.vpc_id
987
+ MU.log "No route table associations found for #{subnet_key}, falling back to the default table for #{vpc_id}", MU::NOTICE
988
+ route_tables = MU::Cloud::AWS::VPC.get_route_tables(vpc_ids: [vpc_id], region: region)
989
+ end
990
+
991
+ @rtb_cache[subnet_key] = route_tables
992
+ end
993
+ }
994
+ end
995
+
996
+ # Checks if the MU master has a route to a subnet in a peered VPC. Can be used on any subnets
997
+ # @param source_subnets_key [String]: The subnet/subnets on one side of the peered VPC.
998
+ # @param target_subnets_key [String]: The subnet/subnets on the other side of the peered VPC.
999
+ # @param instance_id [String]: The instance ID in the target subnet/subnets.
1000
+ # @return [Boolean]
1001
+ def self.have_route_peered_vpc?(source_subnets_key, target_subnets_key, instance_id)
1002
+ my_routes = []
1003
+ vpc_peer_mapping = {}
1004
+
1005
+ @rtb_cache[source_subnets_key].each { |route_table|
1006
+ route_table.routes.each { |route|
1007
+ if route.destination_cidr_block != "0.0.0.0/0" and route.state == "active" and !route.destination_cidr_block.nil?
1008
+ my_routes << NetAddr::IPv4Net.parse(route.destination_cidr_block)
1009
+ if !route.vpc_peering_connection_id.nil?
1010
+ vpc_peer_mapping[route.vpc_peering_connection_id] = route.destination_cidr_block
1011
+ end
1012
+ end
1013
+ }
1014
+ }
1015
+ my_routes.uniq!
1016
+
1017
+ target_routes = []
1018
+ @rtb_cache[target_subnets_key].each { |route_table|
1019
+ route_table.routes.each { |route|
1020
+ next if route.destination_cidr_block == "0.0.0.0/0" or route.state != "active" or route.destination_cidr_block.nil?
1021
+ cidr = NetAddr::IPv4Net.parse(route.destination_cidr_block)
1022
+ shared_ip_space = false
1023
+ my_routes.each { |my_cidr|
1024
+ if my_cidr.contains(NetAddr::IPv4Net.parse(route.destination_cidr_block).nth(2)) or my_cidr.cmp(cidr)
1025
+ shared_ip_space = true
1026
+ break
1027
+ end
1028
+ }
1029
+
1030
+ if shared_ip_space && !route.vpc_peering_connection_id.nil? && vpc_peer_mapping.has_key?(route.vpc_peering_connection_id)
1031
+ MU.log "I share a VPC peering connection (#{route.vpc_peering_connection_id}) with #{instance_id} for #{route.destination_cidr_block}, I can route to it directly", MU::DEBUG
1032
+ @route_cache[instance_id] = true
1033
+ return true
1034
+ end
1035
+ }
1036
+ }
1037
+
1038
+ return false
1039
+ end
1040
+
1041
+ # Retrieves the route tables of used by subnets
1042
+ # @param subnet_ids [Array]: The cloud identifier of the subnets to retrieve the route tables for.
1043
+ # @param vpc_ids [Array]: The cloud identifier of the VPCs to retrieve route tables for.
1044
+ # @param region [String]: The cloud provider region of the target subnet.
1045
+ # @return [Array<OpenStruct>]: The cloud provider's complete descriptions of the route tables
1046
+ def self.get_route_tables(subnet_ids: [], vpc_ids: [], region: MU.curRegion)
1047
+ resp = []
1048
+ if !subnet_ids.empty?
1049
+ resp = MU::Cloud::AWS.ec2(region).describe_route_tables(
1050
+ filters: [
1051
+ {
1052
+ name: "association.subnet-id",
1053
+ values: subnet_ids
1054
+ }
1055
+ ]
1056
+ ).route_tables
1057
+ elsif !vpc_ids.empty?
1058
+ resp = MU::Cloud::AWS.ec2(region).describe_route_tables(
1059
+ filters: [
1060
+ {
1061
+ name: "vpc-id",
1062
+ values: vpc_ids
1063
+ },
1064
+ {
1065
+ name: "association.main",
1066
+ values: ["true"]
1067
+ },
1068
+ ]
1069
+ ).route_tables
1070
+ else
1071
+ resp = MU::Cloud::AWS.ec2(region).describe_route_tables.route_tables
1072
+ end
1073
+
1074
+ return resp
1075
+ end
1076
+
1077
+ # Remove all VPC resources associated with the currently loaded deployment.
1078
+ # @param noop [Boolean]: If true, will only print what would be done
1079
+ # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
1080
+ # @param region [String]: The cloud provider region
1081
+ # @return [void]
1082
+ def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, flags: {})
1083
+
1084
+ tagfilters = [
1085
+ {name: "tag:MU-ID", values: [MU.deploy_id]}
1086
+ ]
1087
+ if !ignoremaster
1088
+ tagfilters << {name: "tag:MU-MASTER-IP", values: [MU.mu_public_ip]}
1089
+ end
1090
+
1091
+ vpcs = []
1092
+ retries = 0
1093
+ begin
1094
+ resp = MU::Cloud::AWS.ec2(region).describe_vpcs(filters: tagfilters).vpcs
1095
+ vpcs = resp if !resp.empty?
1096
+ rescue Aws::EC2::Errors::InvalidVpcIDNotFound => e
1097
+ if retries < 5
1098
+ sleep 5
1099
+ retries += 1
1100
+ retry
1101
+ end
1102
+ end
1103
+
1104
+ if !vpcs.empty?
1105
+ gwthreads = []
1106
+ vpcs.each { |vpc|
1107
+ # NAT gateways don't have any tags, and we can't assign them a name. Lets find them based on a VPC ID
1108
+ gwthreads << Thread.new {
1109
+ purge_nat_gateways(noop, vpc_id: vpc.vpc_id, region: region)
1110
+ purge_endpoints(noop, vpc_id: vpc.vpc_id, region: region)
1111
+ }
1112
+ }
1113
+ gwthreads.each { |t|
1114
+ t.join
1115
+ }
1116
+ end
1117
+
1118
+ purge_gateways(noop, tagfilters, region: region)
1119
+ purge_routetables(noop, tagfilters, region: region)
1120
+ purge_interfaces(noop, tagfilters, region: region)
1121
+ purge_subnets(noop, tagfilters, region: region)
1122
+ purge_vpcs(noop, tagfilters, region: region)
1123
+ purge_dhcpopts(noop, tagfilters, region: region)
1124
+
1125
+ unless noop
1126
+ MU::Cloud::AWS.iam.list_roles.roles.each{ |role|
1127
+ match_string = "#{MU.deploy_id}.*TRAFFIC-LOG"
1128
+ }
1129
+ end
1130
+ end
1131
+
1132
+ # Cloud-specific configuration properties.
1133
+ # @param config [MU::Config]: The calling MU::Config object
1134
+ # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
1135
+ def self.schema(config)
1136
+ toplevel_required = []
1137
+ # Flow Logs can be declared at the VPC level or the subnet level
1138
+ flowlogs = {
1139
+ "traffic_type_to_log" => {
1140
+ "type" => "string",
1141
+ "description" => "The class of traffic to log - accepted traffic, rejected traffic or all traffic.",
1142
+ "enum" => ["accept", "reject", "all"],
1143
+ "default" => "all"
1144
+ },
1145
+ "log_group_name" => {
1146
+ "type" => "string",
1147
+ "description" => "An existing CloudWachLogs log group the traffic will be logged to. If not provided, a new one will be created"
1148
+ },
1149
+ "enable_traffic_logging" => {
1150
+ "type" => "boolean",
1151
+ "description" => "If traffic logging is enabled or disabled. Will be enabled on all subnets and network interfaces if set to true on a VPC",
1152
+ "default" => false
1153
+ }
1154
+ }
1155
+
1156
+ schema = {
1157
+ "subnets" => {
1158
+ "items" => {
1159
+ "properties" => flowlogs
1160
+ }
1161
+ }
1162
+ }
1163
+ schema.merge!(flowlogs)
1164
+ [toplevel_required, schema]
1165
+ end
1166
+
1167
+ # Cloud-specific pre-processing of {MU::Config::BasketofKittens::vpcs}, bare and unvalidated.
1168
+ # @param vpc [Hash]: The resource to process and validate
1169
+ # @param configurator [MU::Config]: The overall deployment config of which this resource is a member
1170
+ # @return [Boolean]: True if validation succeeded, False otherwise
1171
+ def self.validateConfig(vpc, configurator)
1172
+ ok = true
1173
+
1174
+ if (!vpc['route_tables'] or vpc['route_tables'].size == 0) and vpc['create_standard_subnets']
1175
+ vpc['route_tables'] = [
1176
+ {
1177
+ "name" => "internet",
1178
+ "routes" => [ { "destination_network" => "0.0.0.0/0", "gateway" => "#INTERNET" } ]
1179
+ },
1180
+ {
1181
+ "name" => "private",
1182
+ "routes" => [ { "destination_network" => "0.0.0.0/0", "gateway" => "#NAT" } ]
1183
+ }
1184
+ ]
1185
+ end
1186
+
1187
+ if vpc["enable_traffic_logging"]
1188
+ logdesc = {
1189
+ "name" => vpc['name']+"loggroup",
1190
+ }
1191
+ logdesc["tags"] = vpc["tags"] if !vpc["tags"].nil?
1192
+ # logdesc["optional_tags"] = vpc["optional_tags"] if !vpc["optional_tags"].nil?
1193
+ configurator.insertKitten(logdesc, "logs")
1194
+ vpc['dependencies'] ||= []
1195
+ vpc['dependencies'] << {
1196
+ "type" => "log",
1197
+ "name" => vpc['name']+"loggroup"
1198
+ }
1199
+
1200
+ roledesc = {
1201
+ "name" => vpc['name']+"logrole",
1202
+ "can_assume" => [
1203
+ {
1204
+ "entity_id" => "vpc-flow-logs.amazonaws.com",
1205
+ "entity_type" => "service"
1206
+ }
1207
+ ],
1208
+ "policies" => [
1209
+ {
1210
+ "name" => "FlowLogPerms",
1211
+ "permissions" => [
1212
+ "logs:CreateLogGroup",
1213
+ "logs:CreateLogStream",
1214
+ "logs:DescribeLogGroups",
1215
+ "logs:DescribeLogStreams",
1216
+ "logs:PutLogEvents"
1217
+ ],
1218
+ "targets" => [
1219
+ {
1220
+ "type" => "log",
1221
+ "identifier" => vpc['name']+"loggroup"
1222
+ }
1223
+ ]
1224
+ }
1225
+ ],
1226
+ "dependencies" => [
1227
+ {
1228
+ "type" => "log",
1229
+ "name" => vpc['name']+"loggroup"
1230
+ }
1231
+ ]
1232
+ }
1233
+ roledesc["tags"] = vpc["tags"] if !vpc["tags"].nil?
1234
+ roledesc["optional_tags"] = vpc["optional_tags"] if !vpc["optional_tags"].nil?
1235
+ configurator.insertKitten(roledesc, "roles")
1236
+ vpc['dependencies'] ||= []
1237
+ vpc['dependencies'] << {
1238
+ "type" => "role",
1239
+ "name" => vpc['name']+"logrole"
1240
+ }
1241
+ end
1242
+
1243
+ subnet_routes = Hash.new
1244
+ public_routes = Array.new
1245
+
1246
+ if vpc['subnets']
1247
+ vpc['subnets'].each { |subnet|
1248
+ subnet_routes[subnet['route_table']] = Array.new if subnet_routes[subnet['route_table']].nil?
1249
+ subnet_routes[subnet['route_table']] << subnet['name']
1250
+ }
1251
+ end
1252
+ if vpc['endpoint_policy'] && !vpc['endpoint_policy'].empty?
1253
+ if !vpc['endpoint']
1254
+ MU.log "'endpoint_policy' is declared however endpoint is not set", MU::ERR
1255
+ ok = false
1256
+ end
1257
+
1258
+ attributes = %w{Effect Action Resource Principal Sid}
1259
+ vpc['endpoint_policy'].each { |rule|
1260
+ rule.keys.each { |key|
1261
+ if !attributes.include?(key)
1262
+ MU.log "'Attribute #{key} can't be used in 'endpoint_policy'", MU::ERR
1263
+ ok = false
1264
+ end
1265
+ }
1266
+ }
1267
+ end
1268
+
1269
+ nat_gateway_route_tables = []
1270
+ nat_gateway_added = false
1271
+ public_rtbs = []
1272
+ private_rtbs = []
1273
+ nat_routes = {}
1274
+ vpc['route_tables'].each { |table|
1275
+ routes = []
1276
+ table['routes'].each { |route|
1277
+ if routes.include?(route['destination_network'])
1278
+ MU.log "Duplicate routes to #{route['destination_network']} in route table #{table['name']}", MU::ERR
1279
+ ok = false
1280
+ else
1281
+ routes << route['destination_network']
1282
+ end
1283
+
1284
+ if (route['nat_host_name'] or route['nat_host_id'])
1285
+ private_rtbs << table['name']
1286
+ route.delete("gateway") if route['gateway'] == '#INTERNET'
1287
+ end
1288
+ if !route['nat_host_name'].nil? and configurator.haveLitterMate?(route['nat_host_name'], "server") and !subnet_routes.nil? and !subnet_routes.empty?
1289
+ subnet_routes[table['name']].each { |subnet|
1290
+ nat_routes[subnet] = route['nat_host_name']
1291
+ }
1292
+ vpc['dependencies'] << {
1293
+ "type" => "server",
1294
+ "name" => route['nat_host_name']
1295
+ }
1296
+ elsif route['gateway'] == '#NAT'
1297
+ private_rtbs << table['name']
1298
+ elsif route['gateway'] == '#INTERNET'
1299
+ public_rtbs << table['name']
1300
+ end
1301
+ next if !vpc['subnets']
1302
+
1303
+ vpc['subnets'].each { |subnet|
1304
+ if route['gateway'] == '#INTERNET'
1305
+ if table['name'] == subnet['route_table']
1306
+ subnet['is_public'] = true
1307
+ if vpc['create_nat_gateway']
1308
+ if vpc['nat_gateway_multi_az']
1309
+ subnet['create_nat_gateway'] = true
1310
+ else
1311
+ if nat_gateway_added
1312
+ subnet['create_nat_gateway'] = false
1313
+ else
1314
+ subnet['create_nat_gateway'] = true
1315
+ nat_gateway_added = true
1316
+ end
1317
+ end
1318
+ end
1319
+ else
1320
+ subnet['is_public'] = false
1321
+ end
1322
+ if !nat_routes[subnet['name']].nil?
1323
+ subnet['nat_host_name'] = nat_routes[subnet['name']]
1324
+ end
1325
+ elsif route['gateway'] == '#NAT'
1326
+ if table['name'] == subnet['route_table']
1327
+ if route['nat_host_name'] or route['nat_host_id']
1328
+ MU.log "You can either use a NAT gateway or a NAT server, not both.", MU::ERR
1329
+ ok = false
1330
+ end
1331
+
1332
+ subnet['is_public'] = false
1333
+ nat_gateway_route_tables << table
1334
+ end
1335
+ end
1336
+ }
1337
+ }
1338
+ }
1339
+
1340
+ if (!vpc['subnets'] or vpc['subnets'].empty?) and vpc['create_standard_subnets']
1341
+ if vpc['availability_zones'].nil? or vpc['availability_zones'].empty?
1342
+ vpc['availability_zones'] = MU::Cloud::AWS.listAZs(vpc['region'])
1343
+ else
1344
+ # turn into a hash so we can use list parameters easily
1345
+ vpc['availability_zones'] = vpc['availability_zones'].map { |val| val['zone'] }
1346
+ end
1347
+ subnets = configurator.divideNetwork(vpc['ip_block'], vpc['availability_zones'].size*vpc['route_tables'].size, 28)
1348
+
1349
+ ok = false if subnets.nil?
1350
+ vpc['subnets'] = []
1351
+ count = 0
1352
+ vpc['availability_zones'].each { |az|
1353
+ addnat = false
1354
+ if vpc['create_nat_gateway'] and (vpc['nat_gateway_multi_az'] or !nat_gateway_added) and public_rtbs.size > 0
1355
+ addnat = true
1356
+ nat_gateway_added = true
1357
+ end
1358
+ vpc['route_tables'].each { |rtb|
1359
+ vpc['subnets'] << {
1360
+ "name" => "Subnet#{count}#{rtb['name'].capitalize}",
1361
+ "availability_zone" => az,
1362
+ "ip_block" => subnets.shift,
1363
+ "route_table" => rtb['name'],
1364
+
1365
+ "map_public_ips" => (public_rtbs and public_rtbs.include?(rtb['name'])),
1366
+ "is_public" => (public_rtbs and public_rtbs.include?(rtb['name'])),
1367
+ "create_nat_gateway" => (addnat and public_rtbs and public_rtbs.include?(rtb['name']))
1368
+ }
1369
+ }
1370
+ count = count + 1
1371
+ }
1372
+ end
1373
+
1374
+ nat_gateway_route_tables.uniq!
1375
+ if nat_gateway_route_tables.size < 2 && vpc['nat_gateway_multi_az']
1376
+ MU.log "'nat_gateway_multi_az' is enabled but only one route table exists. For multi-az support create one private route table per AZ", MU::ERR
1377
+ ok = false
1378
+ end
1379
+
1380
+ if nat_gateway_route_tables.size > 0 && !vpc['create_nat_gateway']
1381
+ MU.log "There are route tables with a NAT gateway route, but create_nat_gateway is set to false. Setting to true", MU::NOTICE
1382
+ vpc['create_nat_gateway'] = true
1383
+ end
1384
+
1385
+ ok
1386
+ end
1387
+
1388
+
1389
+ private
1390
+
1391
+
1392
+ # List the route tables for each subnet in the given VPC
1393
+ def self.listAllSubnetRouteTables(vpc_id, region: MU.curRegion)
1394
+ resp = MU::Cloud::AWS.ec2(region).describe_subnets(
1395
+ filters: [
1396
+ {
1397
+ name: "vpc-id",
1398
+ values: [vpc_id]
1399
+ }
1400
+ ]
1401
+ )
1402
+
1403
+ subnets = resp.subnets.map { |subnet| subnet.subnet_id }
1404
+
1405
+ tables = MU::Cloud::AWS.ec2(region).describe_route_tables(
1406
+ filters: [
1407
+ {
1408
+ name: "vpc-id",
1409
+ values: [vpc_id]
1410
+ },
1411
+ {
1412
+ name: "association.subnet-id",
1413
+ values: subnets
1414
+ }
1415
+ ]
1416
+ )
1417
+
1418
+ if tables.nil? or tables.route_tables.size == 0
1419
+ MU.log "No route table associations found for #{subnets}, falling back to the default table for #{vpc_id}", MU::NOTICE
1420
+ tables = MU::Cloud::AWS.ec2(MU.myRegion).describe_route_tables(
1421
+ filters: [
1422
+ {name: "vpc-id", values: [vpc_id]},
1423
+ {name: "association.main", values: ["true"]},
1424
+ ]
1425
+ )
1426
+ end
1427
+
1428
+ table_ids = []
1429
+ tables.route_tables.each { |rtb|
1430
+ table_ids << rtb.route_table_id
1431
+ }
1432
+ return table_ids.uniq
1433
+ end
1434
+
1435
+ # Helper method for manufacturing route tables. Expect to be called from
1436
+ # {MU::Cloud::AWS::VPC#create} or {MU::Cloud::AWS::VPC#groom}.
1437
+ # @param rtb [Hash]: A route table description parsed through {MU::Config::BasketofKittens::vpcs::route_tables}.
1438
+ # @return [Hash]: The modified configuration that was originally passed in.
1439
+ def createRouteTable(rtb)
1440
+ vpc_id = @cloud_id
1441
+ vpc_name = @config['name']
1442
+ MU.setVar("curRegion", @config['region']) if !@config['region'].nil?
1443
+ resp = MU::Cloud::AWS.ec2.create_route_table(vpc_id: vpc_id).route_table
1444
+ route_table_id = rtb['route_table_id'] = resp.route_table_id
1445
+ sleep 5
1446
+ MU::MommaCat.createTag(route_table_id, "Name", vpc_name+"-"+rtb['name'].upcase)
1447
+
1448
+ if @config['tags']
1449
+ @config['tags'].each { |tag|
1450
+ MU::MommaCat.createTag(route_table_id, tag['key'], tag['value'])
1451
+ }
1452
+ end
1453
+
1454
+ if @config['optional_tags']
1455
+ MU::MommaCat.listOptionalTags.each { |key, value|
1456
+ MU::MommaCat.createTag(route_table_id, key, value, region: @config['region'])
1457
+ }
1458
+ end
1459
+
1460
+ MU::MommaCat.createStandardTags(route_table_id)
1461
+ rtb['routes'].each { |route|
1462
+ if route['nat_host_id'].nil? and route['nat_host_name'].nil?
1463
+ route_config = {
1464
+ :route_table_id => route_table_id,
1465
+ :destination_cidr_block => route['destination_network']
1466
+ }
1467
+ if !route['peer_id'].nil?
1468
+ route_config[:vpc_peering_connection_id] = route['peer_id']
1469
+ else
1470
+ route_config[:gateway_id] = @config['internet_gateway_id']
1471
+ end
1472
+ # XXX how do the network interfaces work with this?
1473
+ unless route['gateway'] == '#NAT'
1474
+ # Need to change the order of how things are created to create the route here
1475
+ MU.log "Creating route for #{route['destination_network']}", details: route_config
1476
+ resp = MU::Cloud::AWS.ec2.create_route(route_config)
1477
+ end
1478
+ end
1479
+ }
1480
+ return rtb
1481
+ end
1482
+
1483
+
1484
+ # Remove all network gateways associated with the currently loaded deployment.
1485
+ # @param noop [Boolean]: If true, will only print what would be done
1486
+ # @param region [String]: The cloud provider region
1487
+ # @return [void]
1488
+ def self.purge_gateways(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1489
+ resp = MU::Cloud::AWS.ec2(region).describe_internet_gateways(
1490
+ filters: tagfilters
1491
+ )
1492
+ gateways = resp.data.internet_gateways
1493
+
1494
+ gateways.each { |gateway|
1495
+ gateway.attachments.each { |attachment|
1496
+ MU.log "Detaching Internet Gateway #{gateway.internet_gateway_id} from #{attachment.vpc_id}"
1497
+ begin
1498
+ MU::Cloud::AWS.ec2(region).detach_internet_gateway(
1499
+ internet_gateway_id: gateway.internet_gateway_id,
1500
+ vpc_id: attachment.vpc_id
1501
+ ) if !noop
1502
+ rescue Aws::EC2::Errors::GatewayNotAttached => e
1503
+ MU.log "Gateway #{gateway.internet_gateway_id} was already detached", MU::WARN
1504
+ end
1505
+ }
1506
+ MU.log "Deleting Internet Gateway #{gateway.internet_gateway_id}"
1507
+ begin
1508
+ MU::Cloud::AWS.ec2(region).delete_internet_gateway(internet_gateway_id: gateway.internet_gateway_id) if !noop
1509
+ rescue Aws::EC2::Errors::InvalidInternetGatewayIDNotFound
1510
+ MU.log "Gateway #{gateway.internet_gateway_id} was already destroyed by the time I got to it", MU::WARN
1511
+ end
1512
+ }
1513
+ return nil
1514
+ end
1515
+
1516
+ # Remove all NAT gateways associated with the VPC of the currently loaded deployment.
1517
+ # @param noop [Boolean]: If true, will only print what would be done
1518
+ # @param vpc_id [String]: The cloud provider's unique VPC identifier
1519
+ # @param region [String]: The cloud provider region
1520
+ # @return [void]
1521
+ def self.purge_nat_gateways(noop = false, vpc_id: nil, region: MU.curRegion)
1522
+ gateways = MU::Cloud::AWS.ec2(region).describe_nat_gateways(
1523
+ filter: [
1524
+ {
1525
+ name: "vpc-id",
1526
+ values: [vpc_id],
1527
+ }
1528
+ ]
1529
+ ).nat_gateways
1530
+
1531
+ threads = []
1532
+ parent_thread_id = Thread.current.object_id
1533
+ if !gateways.empty?
1534
+ gateways.each { |gateway|
1535
+ threads << Thread.new {
1536
+ MU.dupGlobals(parent_thread_id)
1537
+ MU.log "Deleting NAT Gateway #{gateway.nat_gateway_id}"
1538
+ if !noop
1539
+ begin
1540
+ MU::Cloud::AWS.ec2(region).delete_nat_gateway(nat_gateway_id: gateway.nat_gateway_id)
1541
+ resp = MU::Cloud::AWS.ec2(region).describe_nat_gateways(nat_gateway_ids: [gateway.nat_gateway_id]).nat_gateways.first
1542
+
1543
+ attempts = 0
1544
+ while resp.state != "deleted" and resp.state != "failed"
1545
+ MU.log "Waiting for nat gateway #{gateway.nat_gateway_id} to delete" if attempts % 2 == 0
1546
+ sleep 30
1547
+ begin
1548
+ resp = MU::Cloud::AWS.ec2(region).describe_nat_gateways(nat_gateway_ids: [gateway.nat_gateway_id]).nat_gateways.first
1549
+ rescue Aws::EmptyStructure, NoMethodError
1550
+ sleep 5
1551
+ retry
1552
+ rescue Aws::EC2::Errors::NatGatewayNotFound
1553
+ MU.log "NAT gateway #{gateway.nat_gateway_id} already deleted", MU::NOTICE
1554
+ end
1555
+ MU.log "Timed out while waiting for NAT Gateway to delete #{gateway.nat_gateway_id}: #{resp}", MU::WARN if attempts > 50
1556
+ attempts += 1
1557
+ end
1558
+ rescue Aws::EC2::Errors::NatGatewayMalformed
1559
+ MU.log "NAT Gateway #{gateway.nat_gateway_id} was already deleted", MU::NOTICE
1560
+ end
1561
+ end
1562
+ }
1563
+ }
1564
+ end
1565
+
1566
+ threads.each { |t|
1567
+ t.join
1568
+ }
1569
+
1570
+ return nil
1571
+ end
1572
+
1573
+ # Remove all VPC endpoints associated with the VPC of the currently loaded deployment.
1574
+ # @param noop [Boolean]: If true, will only print what would be done
1575
+ # @param vpc_id [String]: The cloud provider's unique VPC identifier
1576
+ # @param region [String]: The cloud provider region
1577
+ # @return [void]
1578
+ def self.purge_endpoints(noop = false, vpc_id: nil, region: MU.curRegion)
1579
+ vpc_endpoints = MU::Cloud::AWS.ec2(region).describe_vpc_endpoints(
1580
+ filters: [
1581
+ {
1582
+ name:"vpc-id",
1583
+ values: [vpc_id],
1584
+ }
1585
+ ]
1586
+ ).vpc_endpoints
1587
+
1588
+ threads = []
1589
+ parent_thread_id = Thread.current.object_id
1590
+ if !vpc_endpoints.empty?
1591
+ vpc_endpoints.each { |endpoint|
1592
+ threads << Thread.new {
1593
+ MU.dupGlobals(parent_thread_id)
1594
+ MU.log "Deleting VPC endpoint #{endpoint.vpc_endpoint_id}"
1595
+ if !noop
1596
+ begin
1597
+ MU::Cloud::AWS.ec2(region).delete_vpc_endpoints(vpc_endpoint_ids: [endpoint.vpc_endpoint_id])
1598
+ resp = MU::Cloud::AWS.ec2(region).describe_vpc_endpoints(vpc_endpoint_ids: [endpoint.vpc_endpoint_id]).vpc_endpoints.first
1599
+
1600
+ attempts = 0
1601
+ while resp.state != "deleted"
1602
+ MU.log "Waiting for VPC endpoint #{endpoint.vpc_endpoint_id} to delete" if attempts % 5 == 0
1603
+ sleep 30
1604
+ begin
1605
+ resp = MU::Cloud::AWS.ec2(region).describe_vpc_endpoints(vpc_endpoint_ids: [endpoint.vpc_endpoint_id]).vpc_endpoints.first
1606
+ rescue Aws::EmptyStructure, NoMethodError
1607
+ sleep 5
1608
+ retry
1609
+ rescue Aws::EC2::Errors::InvalidVpcEndpointIdNotFound
1610
+ MU.log "VPC endpoint #{endpoint.vpc_endpoint_id} already deleted", MU::NOTICE
1611
+ end
1612
+ MU.log "Timed out while waiting for VPC endpoint to delete #{endpoint.vpc_endpoint_id}: #{resp}", MU::WARN if attempts > 50
1613
+ attempts += 1
1614
+ end
1615
+ rescue Aws::EC2::Errors::VpcEndpointIdMalformed
1616
+ MU.log "VPC endpoint #{endpoint.vpc_endpoint_id} was already deleted", MU::NOTICE
1617
+ rescue Aws::EC2::Errors::InvalidVpcEndpointIdNotFound
1618
+ MU.log "VPC endpoint #{endpoint.vpc_endpoint_id} already deleted", MU::NOTICE
1619
+ end
1620
+ end
1621
+ }
1622
+ }
1623
+ end
1624
+
1625
+ threads.each { |t|
1626
+ t.join
1627
+ }
1628
+
1629
+ return nil
1630
+ end
1631
+
1632
+ # Remove all route tables associated with the currently loaded deployment.
1633
+ # @param noop [Boolean]: If true, will only print what would be done
1634
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
1635
+ # @param region [String]: The cloud provider region
1636
+ # @return [void]
1637
+ def self.purge_routetables(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1638
+ resp = MU::Cloud::AWS.ec2(region).describe_route_tables(
1639
+ filters: tagfilters
1640
+ )
1641
+ route_tables = resp.data.route_tables
1642
+
1643
+ return if route_tables.nil? or route_tables.size == 0
1644
+
1645
+ route_tables.each { |table|
1646
+ table.routes.each { |route|
1647
+ if !route.network_interface_id.nil?
1648
+ MU.log "Deleting Network Interface #{route.network_interface_id}"
1649
+ begin
1650
+ MU::Cloud::AWS.ec2(region).delete_network_interface(network_interface_id: route.network_interface_id) if !noop
1651
+ rescue Aws::EC2::Errors::InvalidNetworkInterfaceIDNotFound => e
1652
+ MU.log "Network Interface #{route.network_interface_id} has already been deleted", MU::WARN
1653
+ end
1654
+ end
1655
+ if route.gateway_id != "local"
1656
+ MU.log "Deleting #{table.route_table_id}'s route for #{route.destination_cidr_block}"
1657
+ begin
1658
+ MU::Cloud::AWS.ec2(region).delete_route(
1659
+ route_table_id: table.route_table_id,
1660
+ destination_cidr_block: route.destination_cidr_block
1661
+ ) if !noop
1662
+ rescue Aws::EC2::Errors::InvalidRouteNotFound
1663
+ MU.log "Route #{table.route_table_id} has already been deleted", MU::WARN
1664
+ end
1665
+ end
1666
+ }
1667
+ can_delete = true
1668
+ table.associations.each { |assoc|
1669
+ begin
1670
+ MU::Cloud::AWS.ec2(region).disassociate_route_table(association_id: assoc.route_table_association_id) if !noop
1671
+ rescue Aws::EC2::Errors::InvalidAssociationIDNotFound => e
1672
+ MU.log "Route table association #{assoc.route_table_association_id} already removed", MU::WARN
1673
+ rescue Aws::EC2::Errors::InvalidParameterValue => e
1674
+ # normal and ignorable with the default route table
1675
+ can_delete = false
1676
+ next
1677
+ end
1678
+ }
1679
+ next if !can_delete
1680
+ MU.log "Deleting Route Table #{table.route_table_id}"
1681
+ begin
1682
+ MU::Cloud::AWS.ec2(region).delete_route_table(route_table_id: table.route_table_id) if !noop
1683
+ rescue Aws::EC2::Errors::InvalidRouteTableIDNotFound
1684
+ MU.log "Route table #{table.route_table_id} already removed", MU::WARN
1685
+ end
1686
+ }
1687
+ return nil
1688
+ end
1689
+
1690
+
1691
+ # Remove all network interfaces associated with the currently loaded deployment.
1692
+ # @param noop [Boolean]: If true, will only print what would be done
1693
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
1694
+ # @param region [String]: The cloud provider region
1695
+ # @return [void]
1696
+ def self.purge_interfaces(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1697
+ resp = MU::Cloud::AWS.ec2(region).describe_network_interfaces(
1698
+ filters: tagfilters
1699
+ )
1700
+ ifaces = resp.data.network_interfaces
1701
+
1702
+ return if ifaces.nil? or ifaces.size == 0
1703
+
1704
+ ifaces.each { |iface|
1705
+ MU.log "Deleting Network Interface #{iface.network_interface_id}"
1706
+ MU::Cloud::AWS.ec2(region).delete_network_interface(network_interface_id: iface.network_interface_id)
1707
+ }
1708
+ end
1709
+
1710
+ # Remove all subnets associated with the currently loaded deployment.
1711
+ # @param noop [Boolean]: If true, will only print what would be done
1712
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
1713
+ # @param region [String]: The cloud provider region
1714
+ # @return [void]
1715
+ def self.purge_subnets(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1716
+ resp = MU::Cloud::AWS.ec2(region).describe_subnets(
1717
+ filters: tagfilters
1718
+ )
1719
+ subnets = resp.data.subnets
1720
+
1721
+ return if subnets.nil? or subnets.size == 0
1722
+
1723
+ retries = 0
1724
+ subnets.each { |subnet|
1725
+ begin
1726
+ if subnet.state != "available"
1727
+ MU.log "Waiting for #{subnet.subnet_id} to be in a removable state...", MU::NOTICE
1728
+ sleep 30
1729
+ else
1730
+ MU.log "Deleting Subnet #{subnet.subnet_id}"
1731
+ MU::Cloud::AWS.ec2(region).delete_subnet(subnet_id: subnet.subnet_id) if !noop
1732
+ end
1733
+ rescue Aws::EC2::Errors::DependencyViolation => e
1734
+ if retries < 7
1735
+ MU.log "#{e.inspect}, retrying in 10s", MU::WARN
1736
+ sleep 10
1737
+ retries = retries + 1
1738
+ retry
1739
+ else
1740
+ raise e
1741
+ end
1742
+ rescue Aws::EC2::Errors::InvalidSubnetIDNotFound
1743
+ MU.log "Subnet #{subnet.subnet_id} disappeared before I could remove it", MU::WARN
1744
+ next
1745
+ end while subnet.state != "available"
1746
+ }
1747
+ end
1748
+
1749
+ # Remove all DHCP options sets associated with the currently loaded
1750
+ # deployment.
1751
+ # @param noop [Boolean]: If true, will only print what would be done
1752
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
1753
+ # @param region [String]: The cloud provider region
1754
+ # @return [void]
1755
+ def self.purge_dhcpopts(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1756
+ resp = MU::Cloud::AWS.ec2(region).describe_dhcp_options(
1757
+ filters: tagfilters
1758
+ )
1759
+ sets = resp.data.dhcp_options
1760
+
1761
+ return if sets.nil? or sets.size == 0
1762
+
1763
+ sets.each { |optset|
1764
+ begin
1765
+ MU.log "Deleting DHCP Option Set #{optset.dhcp_options_id}"
1766
+ MU::Cloud::AWS.ec2(region).delete_dhcp_options(dhcp_options_id: optset.dhcp_options_id)
1767
+ rescue Aws::EC2::Errors::DependencyViolation => e
1768
+ MU.log e.inspect, MU::ERR
1769
+ # rescue Aws::EC2::Errors::InvalidSubnetIDNotFound
1770
+ # MU.log "Subnet #{subnet.subnet_id} disappeared before I could remove it", MU::WARN
1771
+ # next
1772
+ end
1773
+ }
1774
+ end
1775
+
1776
+ # Remove all VPCs associated with the currently loaded deployment.
1777
+ # @param noop [Boolean]: If true, will only print what would be done
1778
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
1779
+ # @param region [String]: The cloud provider region
1780
+ # @return [void]
1781
+ def self.purge_vpcs(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1782
+ resp = MU::Cloud::AWS.ec2(region).describe_vpcs(
1783
+ filters: tagfilters
1784
+ )
1785
+
1786
+ vpcs = resp.data.vpcs
1787
+ return if vpcs.nil? or vpcs.size == 0
1788
+
1789
+ vpcs.each { |vpc|
1790
+ my_peer_conns = MU::Cloud::AWS.ec2(region).describe_vpc_peering_connections(
1791
+ filters: [
1792
+ {
1793
+ name: "requester-vpc-info.vpc-id",
1794
+ values: [vpc.vpc_id]
1795
+ }
1796
+ ]
1797
+ ).vpc_peering_connections
1798
+ my_peer_conns.concat(MU::Cloud::AWS.ec2(region).describe_vpc_peering_connections(
1799
+ filters: [
1800
+ {
1801
+ name: "accepter-vpc-info.vpc-id",
1802
+ values: [vpc.vpc_id]
1803
+ }
1804
+ ]
1805
+ ).vpc_peering_connections)
1806
+ my_peer_conns.each { |cnxn|
1807
+
1808
+ [cnxn.accepter_vpc_info.vpc_id, cnxn.requester_vpc_info.vpc_id].each { |peer_vpc|
1809
+ MU::Cloud::AWS::VPC.listAllSubnetRouteTables(peer_vpc, region: region).each { |rtb_id|
1810
+ resp = MU::Cloud::AWS.ec2(region).describe_route_tables(
1811
+ route_table_ids: [rtb_id]
1812
+ )
1813
+ resp.route_tables.each { |rtb|
1814
+ rtb.routes.each { |route|
1815
+ if route.vpc_peering_connection_id == cnxn.vpc_peering_connection_id
1816
+ MU.log "Removing route #{route.destination_cidr_block} from route table #{rtb_id} in VPC #{peer_vpc}"
1817
+ MU::Cloud::AWS.ec2(region).delete_route(
1818
+ route_table_id: rtb_id,
1819
+ destination_cidr_block: route.destination_cidr_block
1820
+ ) if !noop
1821
+ end
1822
+ }
1823
+ }
1824
+ }
1825
+ }
1826
+ MU.log "Deleting VPC peering connection #{cnxn.vpc_peering_connection_id}"
1827
+ begin
1828
+ MU::Cloud::AWS.ec2(region).delete_vpc_peering_connection(
1829
+ vpc_peering_connection_id: cnxn.vpc_peering_connection_id
1830
+ ) if !noop
1831
+ rescue Aws::EC2::Errors::InvalidStateTransition => e
1832
+ MU.log "VPC peering connection #{cnxn.vpc_peering_connection_id} not in removable (state #{cnxn.status.code})", MU::WARN
1833
+ end
1834
+ }
1835
+
1836
+ MU.log "Deleting VPC #{vpc.vpc_id}"
1837
+ retries = 0
1838
+ begin
1839
+ MU::Cloud::AWS.ec2(region).delete_vpc(vpc_id: vpc.vpc_id) if !noop
1840
+ rescue Aws::EC2::Errors::InvalidVpcIDNotFound
1841
+ MU.log "VPC #{vpc.vpc_id} has already been deleted", MU::WARN
1842
+ rescue Aws::EC2::Errors::DependencyViolation => e
1843
+ MU.log "Couldn't delete VPC #{vpc.vpc_id} from #{region}: #{e.inspect}", MU::ERR#, details: caller
1844
+ if retries < 5
1845
+ retries += 1
1846
+ sleep 10
1847
+ retry
1848
+ else
1849
+ next
1850
+ end
1851
+ end
1852
+
1853
+ if !MU::Cloud::AWS.isGovCloud?(region)
1854
+ mu_zone = MU::Cloud::DNSZone.find(cloud_id: "platform-mu", region: region).values.first
1855
+ if !mu_zone.nil?
1856
+ MU::Cloud::AWS::DNSZone.toggleVPCAccess(id: mu_zone.id, vpc_id: vpc.vpc_id, remove: true)
1857
+ end
1858
+ end
1859
+ }
1860
+ end
1861
+
1862
+ protected
1863
+
1864
+ # Subnets are almost a first-class resource. So let's kinda sorta treat
1865
+ # them like one. This should only be invoked on objects that already
1866
+ # exists in the cloud layer.
1867
+ class Subnet < MU::Cloud::AWS::VPC
1868
+
1869
+ attr_reader :cloud_id
1870
+ attr_reader :ip_block
1871
+ attr_reader :mu_name
1872
+ attr_reader :name
1873
+ attr_reader :az
1874
+ attr_reader :cloud_desc
1875
+
1876
+ # @param parent [MU::Cloud::AWS::VPC]: The parent VPC of this subnet.
1877
+ # @param config [Hash<String>]:
1878
+ def initialize(parent, config)
1879
+ @parent = parent
1880
+ @config = MU::Config.manxify(config)
1881
+ @cloud_id = config['cloud_id']
1882
+ @mu_name = config['mu_name']
1883
+ @name = config['name']
1884
+ @deploydata = config # This is a dummy for the sake of describe()
1885
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(subnet_ids: [@cloud_id]).subnets.first
1886
+ @az = resp.availability_zone
1887
+ @ip_block = resp.cidr_block
1888
+ @cloud_desc = resp
1889
+
1890
+ end
1891
+
1892
+ # Return the cloud identifier for the default route of this subnet.
1893
+ def defaultRoute
1894
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
1895
+ filters: [{name: "association.subnet-id", values: [@cloud_id]}]
1896
+ )
1897
+ if resp.route_tables.size == 0 # use default route table for the VPC
1898
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
1899
+ filters: [{name: "vpc-id", values: [@parent.cloud_id]}]
1900
+ )
1901
+ end
1902
+ resp.route_tables.each { |route_table|
1903
+ route_table.routes.each { |route|
1904
+ if route.destination_cidr_block =="0.0.0.0/0" and route.state != "blackhole"
1905
+ return route.instance_id if !route.instance_id.nil?
1906
+ return route.gateway_id if !route.gateway_id.nil?
1907
+ return route.vpc_peering_connection_id if !route.vpc_peering_connection_id.nil?
1908
+ return route.network_interface_id if !route.network_interface_id.nil?
1909
+ end
1910
+ }
1911
+ }
1912
+ return nil
1913
+ end
1914
+
1915
+ # Is this subnet privately-routable only, or public?
1916
+ # @return [Boolean]
1917
+ def private?
1918
+ return false if @cloud_id.nil?
1919
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
1920
+ filters: [{name: "association.subnet-id", values: [@cloud_id]}]
1921
+ )
1922
+ if resp.route_tables.size == 0 # use default route table for the VPC
1923
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
1924
+ filters: [{name: "vpc-id", values: [@parent.cloud_id]}]
1925
+ )
1926
+ end
1927
+ resp.route_tables.each { |route_table|
1928
+ route_table.routes.each { |route|
1929
+ return false if !route.gateway_id.nil? and route.gateway_id != "local" # you can have an IgW and route it to a subset of IPs instead of 0.0.0.0/0
1930
+ if route.destination_cidr_block == "0.0.0.0/0"
1931
+ return true if !route.instance_id.nil?
1932
+ return true if route.nat_gateway_id
1933
+ end
1934
+ }
1935
+ }
1936
+ return true
1937
+ end
1938
+ end
1939
+
1940
+ end #class
1941
+ end #class
1942
+ end
1943
+ end #module