diff --git a/infra/README.md b/infra/README.md index 862ab664339..38ac8cb02ad 100644 --- a/infra/README.md +++ b/infra/README.md @@ -10,4 +10,3 @@ This folder contains different kinds of infrastructure to deploy HASH. - `docker` - Contains production Docker assets. -- `terraform` - A set of Terraform modules to spin up a HASH instance on AWS using ECS Fargate, RDS and Elasticache. diff --git a/infra/terraform/LICENSE.md b/infra/terraform/LICENSE.md deleted file mode 100644 index c7d627721e2..00000000000 --- a/infra/terraform/LICENSE.md +++ /dev/null @@ -1,607 +0,0 @@ -GNU Affero General Public License -================================= - -_Version 3, 19 November 2007_ -_Copyright © 2007 Free Software Foundation, Inc. <>_ - -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -## Preamble - -The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - -The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - -When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - -Developers that use our General Public Licenses protect your rights -with two steps: **(1)** assert copyright on the software, and **(2)** offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - -A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - -The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - -An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - -The precise terms and conditions for copying, distribution and -modification follow. - -## TERMS AND CONDITIONS - -### 0. Definitions - -“This License” refers to version 3 of the GNU Affero General Public License. - -“Copyright” also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - -“The Program” refers to any copyrightable work licensed under this -License. Each licensee is addressed as “you”. “Licensees” and -“recipients” may be individuals or organizations. - -To “modify” a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a “modified version” of the -earlier work or a work “based on” the earlier work. - -A “covered work” means either the unmodified Program or a work based -on the Program. - -To “propagate” a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - -To “convey” a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - -An interactive user interface displays “Appropriate Legal Notices” -to the extent that it includes a convenient and prominently visible -feature that **(1)** displays an appropriate copyright notice, and **(2)** -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - -### 1. Source Code - -The “source code” for a work means the preferred form of the work -for making modifications to it. “Object code” means any non-source -form of a work. - -A “Standard Interface” means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - -The “System Libraries” of an executable work include anything, other -than the work as a whole, that **(a)** is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and **(b)** serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -“Major Component”, in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - -The “Corresponding Source” for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - -The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - -The Corresponding Source for a work in source code form is that -same work. - -### 2. Basic Permissions - -All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - -You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - -Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - -### 3. Protecting Users' Legal Rights From Anti-Circumvention Law - -No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - -When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - -### 4. Conveying Verbatim Copies - -You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - -You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - -### 5. Conveying Modified Source Versions - -You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - -* **a)** The work must carry prominent notices stating that you modified -it, and giving a relevant date. -* **b)** The work must carry prominent notices stating that it is -released under this License and any conditions added under section 7. -This requirement modifies the requirement in section 4 to -“keep intact all notices”. -* **c)** You must license the entire work, as a whole, under this -License to anyone who comes into possession of a copy. This -License will therefore apply, along with any applicable section 7 -additional terms, to the whole of the work, and all its parts, -regardless of how they are packaged. This License gives no -permission to license the work in any other way, but it does not -invalidate such permission if you have separately received it. -* **d)** If the work has interactive user interfaces, each must display -Appropriate Legal Notices; however, if the Program has interactive -interfaces that do not display Appropriate Legal Notices, your -work need not make them do so. - -A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -“aggregate” if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - -### 6. Conveying Non-Source Forms - -You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - -* **a)** Convey the object code in, or embodied in, a physical product -(including a physical distribution medium), accompanied by the -Corresponding Source fixed on a durable physical medium -customarily used for software interchange. -* **b)** Convey the object code in, or embodied in, a physical product -(including a physical distribution medium), accompanied by a -written offer, valid for at least three years and valid for as -long as you offer spare parts or customer support for that product -model, to give anyone who possesses the object code either **(1)** a -copy of the Corresponding Source for all the software in the -product that is covered by this License, on a durable physical -medium customarily used for software interchange, for a price no -more than your reasonable cost of physically performing this -conveying of source, or **(2)** access to copy the -Corresponding Source from a network server at no charge. -* **c)** Convey individual copies of the object code with a copy of the -written offer to provide the Corresponding Source. This -alternative is allowed only occasionally and noncommercially, and -only if you received the object code with such an offer, in accord -with subsection 6b. -* **d)** Convey the object code by offering access from a designated -place (gratis or for a charge), and offer equivalent access to the -Corresponding Source in the same way through the same place at no -further charge. You need not require recipients to copy the -Corresponding Source along with the object code. If the place to -copy the object code is a network server, the Corresponding Source -may be on a different server (operated by you or a third party) -that supports equivalent copying facilities, provided you maintain -clear directions next to the object code saying where to find the -Corresponding Source. Regardless of what server hosts the -Corresponding Source, you remain obligated to ensure that it is -available for as long as needed to satisfy these requirements. -* **e)** Convey the object code using peer-to-peer transmission, provided -you inform other peers where the object code and Corresponding -Source of the work are being offered to the general public at no -charge under subsection 6d. - -A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - -A “User Product” is either **(1)** a “consumer product”, which means any -tangible personal property which is normally used for personal, family, -or household purposes, or **(2)** anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, “normally used” refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - -“Installation Information” for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - -If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - -The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - -Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - -### 7. Additional Terms - -“Additional permissions” are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - -When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - -Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - -* **a)** Disclaiming warranty or limiting liability differently from the -terms of sections 15 and 16 of this License; or -* **b)** Requiring preservation of specified reasonable legal notices or -author attributions in that material or in the Appropriate Legal -Notices displayed by works containing it; or -* **c)** Prohibiting misrepresentation of the origin of that material, or -requiring that modified versions of such material be marked in -reasonable ways as different from the original version; or -* **d)** Limiting the use for publicity purposes of names of licensors or -authors of the material; or -* **e)** Declining to grant rights under trademark law for use of some -trade names, trademarks, or service marks; or -* **f)** Requiring indemnification of licensors and authors of that -material by anyone who conveys the material (or modified versions of -it) with contractual assumptions of liability to the recipient, for -any liability that these contractual assumptions directly impose on -those licensors and authors. - -All other non-permissive additional terms are considered “further -restrictions” within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - -If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - -Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - -### 8. Termination - -You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - -However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated **(a)** -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and **(b)** permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - -Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - -Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - -### 9. Acceptance Not Required for Having Copies - -You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - -### 10. Automatic Licensing of Downstream Recipients - -Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - -An “entity transaction” is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - -### 11. Patents - -A “contributor” is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's “contributor version”. - -A contributor's “essential patent claims” are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, “control” includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - -In the following three paragraphs, a “patent license” is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To “grant” such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - -If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either **(1)** cause the Corresponding Source to be so -available, or **(2)** arrange to deprive yourself of the benefit of the -patent license for this particular work, or **(3)** arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. “Knowingly relying” means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - -If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - -A patent license is “discriminatory” if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license **(a)** in connection with copies of the covered work -conveyed by you (or copies made from those copies), or **(b)** primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - -Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - -### 12. No Surrender of Others' Freedom - -If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - -### 13. Remote Network Interaction; Use with the GNU General Public License - -Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - -Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - -### 14. Revised Versions of this License - -The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License “or any later version” applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - -If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - -Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - -### 15. Disclaimer of Warranty - -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -### 16. Limitation of Liability - -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - -### 17. Interpretation of Sections 15 and 16 - -If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. diff --git a/infra/terraform/README.md b/infra/terraform/README.md deleted file mode 100644 index 5566ea0092a..00000000000 --- a/infra/terraform/README.md +++ /dev/null @@ -1,423 +0,0 @@ -# Terraform infrastructure - -This folder contains Terraform modules to deploy a HASH instance on AWS. The entry-point module is located in [`./hash/`](./hash/). - -## Getting started - -1. Install the [Terraform CLI](https://learn.hashicorp.com/tutorials/terraform/install-cli) -2. Install Docker -3. Install [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) and configure it to use [your credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html) -4. Initialize the Terraform modules by executing the following command in [`./hash/`](./hash/): `terraform init` - -After initializing, you'll be put into the `default` workspace which isn't allowed for the plan. -You can create new workspace names by creating/selecting new workspaces: - -```console -$ terraform workspace new prod # Or to select another workspace -prod -$ terraform workspace select prod -prod -``` - -By default, the selected region is `us-east-1` and can be configured by editing the Terraform variables used for applying the Terraform plan (e.g. the one in [`./hash/prod.auto.tfvars`](./hash/prod.auto.tfvars)). - -# Naming convention - -Resources use the following naming convention: - -```text -{OWNER}-{PROJECT}-{ENV}-{REGION}-{RESOURCE DESCRIPTION} - -OWNER : 'h' for hash -PROJECT : 'hash' -ENV : 'dev' or 'prod' (maybe others too later) -REGION : AWS region shortened -- 'usea1' etc. -RESOURCE DESCRIPTION : a short description of the resource e.g. 'subnetpub1' -``` - -Fields (e.g. Owner, Project) may _not_ include any hyphens. - -Example _valid_ names: `h-hash-prod-usea1-vpc`, `h-hash-dev-usea2-apisvc` -Example _invalid_ name: `h-hash-prod-usea1-api-svc` - -(Inspired by https://stepan.wtf/cloud-naming-convention/) - -The region and environment fields are set by the variables supplied in the `*.tfvars` file. The config automatically shortens AWS region names, for example, `us-east-1` will be converted to `usea1`. - -# Deploying - -Deployment currently relies on a couple of manual steps - but is to be automated in the future. The container registries need to have images pushed in the correct place for the applications to start and the database has to be migrated manually. The order of executions for deployment: - -1. Deploy infrastructure with terraform -2. Migrate databases -3. Build/push docker images - -## Deploy infrastructure with terraform - -Secret environment should be provided in HashiCorp Vault. These are expected in a kvv2 secrets engine path starting with `pipelines/hash/` and ending with the environment name, e.g. for a secrets engine `automation` the path would be `automation/pipelines/hash/prod`. The following secrets are expected: - -```json5 -{ - aws_s3_uploads_access_key_id: "changeme", - aws_s3_uploads_secret_access_key: "changeme", - aws_s3_uploads_bucket: "changeme", - aws_s3_uploads_endpoint: "changeme", // if not using the default S3 endpoint, e.g. if using another S3-compatible service - graph_sentry_dsn: "https://changeme.ingest.sentry.io/changeme", - hash_api_rudderstack_key: "changeme", - hash_block_protocol_api_key: "changeme", - hash_openai_api_key: "changeme", - hash_seed_users: [ - { - email: "changeme@example.com", - isInstanceAdmin: true, - password: "changeme", - preferredName: "Instance Admin", - shortname: "instance-admin", - }, - ], - hydra_secrets_system: "changeme", - hydra_secrets_cookie: "changeme", - kratos_api_key: "changeme", - kratos_secrets_cipher: "32-LONG-SECRET-NOT-SECURE-AT-ALL", - kratos_secrets_cookie: "changeme", - linear_webhook_secret: "changeme", - mailchimp_api_key: "changeme", - mailchimp_list_id: "changeme", - node_api_sentry_dsn: "https://changeme.ingest.sentry.io/changeme", - pg_graph_user_password_hash: "SCRAM-SHA-256$4096:calculateme-see-postgres_roles.tf", - pg_graph_user_password_raw: "changeme", - pg_hydra_user_password_hash: "SCRAM-SHA-256$4096:calculateme-see-postgres_roles.tf", - pg_hydra_user_password_raw: "changeme", - pg_kratos_user_password_hash: "SCRAM-SHA-256$4096:calculateme-see-postgres_roles.tf", - pg_kratos_user_password_raw: "changeme", - pg_superuser_password: "changeme", - pg_temporal_user_password_hash: "SCRAM-SHA-256$4096:calculateme-see-postgres_roles.tf", - pg_temporal_user_password_raw: "changeme", - pg_temporal_visibility_user_password_hash: "SCRAM-SHA-256$4096:calculateme-see-postgres_roles.tf", - pg_temporal_visibility_user_password_raw: "changeme", -} -``` - -
-Generate PG password hash - -**PG Users** - -The database is configured to use `scram-sha-256` for password auth. -To generate a hashed password in this form: - -1. Start a local DB: - `$ docker run --rm -it --name postgres-dummy -d -e POSTGRES_HOST_AUTH_METHOD=trust postgres:14-alpine` -2. Connect to instance - `$ docker exec -it postgres-dummy psql -U postgres` -3. Reset password - `postgres=# \password` - (type in your password twice) -4. Extract password - `select rolpassword from pg_authid where rolname = 'postgres';` -5. Copy the result, repeat from step 3 as needed -6. Quit with `\q` and stop the container - `docker stop postgres-dummy` - -
- -Deployment can then be done by issuing the following command after initializing a terraform workspace with a functioning AWS connection from the [`./hash/`](./hash/) folder: - -```console -$ terraform workspace show -prod -$ terraform apply -.. -``` - -## 2. Migrate databases - -Once the Terraform infrastructure is deployed, you should have an RDS Postgres database accessible from the bastion host with `graph` and `kratos` users/dbs. These need to be migrated locally in preparation for starting the services. - -Before migrating, you must start an SSH tunnel through the bastion host to access the database. This can be done by executing the following command from the [`./hash/`](./hash/) folder: - -```console -$ terraform output -raw rds_hostname -h-hash-prod-usea1-pg.*.us-east-1.rds.amazonaws.com # * = some unique ID for your RDS instance -.. -$ ./ssh_bastion.sh -N -L 5554:h-hash-dev-usea1-pg.*.us-east-1.rds.amazonaws.com:5432 -.. -``` - -> There should be no output from the command after the RSA art. The tunnel should be running, though. -> **Be sure to keep this running while you run all migrations to completion!** - -This will start an SSH tunnel making `localhost:5554` point to the remote RDS instance within the private subnet in AWS. - -To migrate `hash-graph`, you must first build the docker container that contains the graph and run it with the graph credentials you should have in the Vault instance. - -```console -$ docker run --rm \ - --network host \ - -e HASH_GRAPH_PG_USER=graph \ - -e HASH_GRAPH_PG_PASSWORD="changeme" \ - -e HASH_GRAPH_PG_HOST=localhost \ - -e HASH_GRAPH_PG_PORT=5554 \ - -e HASH_GRAPH_PG_DATABASE=graph \ - -e HASH_GRAPH_LOG_LEVEL=debug \ - 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-graphecr:latest \ - migrate -.. -``` - -You can simultaneously run the migrations for Kratos: - -```console -$ DOCKER_BUILDKIT=1 docker build ./apps/hash-external-services/kratos --build-arg ENV=prod -t kratos:latest -.. -$ docker run --network host --rm -e "DSN=postgres://kratos:changeme@localhost:5554/kratos" kratos:latest migrate sql -e --yes -.. -``` - -The Kratos migrations may take a couple of minutes, but while the migrations are running, you can build/push the service container images in the next step. - -## 3. Build/push docker images - -You must build images for `hash-graph`, `hash-api`, `kratos`, and `temporal` setup/migration/worker to push to ECR (`buildkit` env variable may be optional) -Depending on your AWS account ID (`$AWS_ID`), your selected terraform workspace (`$WORKSPACE`) and AWS region (`$REGION`) and region short name (`$REGION_SHORT`) your ECR URL will be different. The general structure is as follows: - -```text -$AWS_ID.dkr.ecr.$REGION.amazonaws.com/h-hash-$WORKSPACE-$REGION_SHORT-graphecr:latest -$AWS_ID.dkr.ecr.$REGION.amazonaws.com/h-hash-$WORKSPACE-$REGION_SHORT-kratosecr:latest -$AWS_ID.dkr.ecr.$REGION.amazonaws.com/h-hash-$WORKSPACE-$REGION_SHORT-apiecr:latest - -$AWS_ID.dkr.ecr.$REGION.amazonaws.com/h-temporal-$WORKSPACE-$REGION_SHORT-setup:$HASH_TEMPORAL_VERSION -$AWS_ID.dkr.ecr.$REGION.amazonaws.com/h-temporal-$WORKSPACE-$REGION_SHORT-migrate:$HASH_TEMPORAL_VERSION - -$AWS_ID.dkr.ecr.$REGION.amazonaws.com/h-hash-$WORKSPACE-$REGION_SHORT-temporalworkeraipy:latest -$AWS_ID.dkr.ecr.$REGION.amazonaws.com/h-hash-$WORKSPACE-$REGION_SHORT-temporalworkeraits:latest -$AWS_ID.dkr.ecr.$REGION.amazonaws.com/h-hash-$WORKSPACE-$REGION_SHORT-temporalworkerintegration:latest -``` - -> If you receive a message about needing to log in, you can run the following AWS CLI command to authorize local push: -> `aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 000000000000.dkr.ecr.$REGION.amazonaws.com` - -The build and push commands ran from the root of this ([`hashintel/hash`](../../)) repo: - -**Building `hash-graph`**: - -```console -$ DOCKER_BUILDKIT=1 docker build ./apps/hash-graph -f ./apps/hash-graph/docker/Dockerfile -t 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-graphecr:latest -.. -$ docker push 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-graphecr:latest -.. -``` - -**Building `hash-api`**: - -```console -$ DOCKER_BUILDKIT=1 docker build . -f ./infra/docker/api/prod/Dockerfile -t 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-apiecr:latest -.. -$ docker push 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-apiecr:latest -.. -``` - -**Building `kratos`** (same as the migration container, with a secret passed in): - -For Kratos, it's required to provide some build-time args to ensure a secure instance running in production mode (and configuring an API secret `$SECRET` which should match the `kratos_api_key` tfvar). - -```console -$ DOCKER_BUILDKIT=1 docker build ./apps/hash-external-services/kratos --build-arg ENV=prod --build-arg API_SECRET=$SECRET -t 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-kratosecr:latest -.. -$ docker push 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-kratosecr:latest -.. -``` - -**Building temporal services**: - -All Temporal services requires the `TEMPORAL_VERSION` build argument to be set to the version of Temporal to use. The current version can be found in [`.env`](../../.env) in the repository root and should be set to the same value as the `HASH_TEMPORAL_VERSION`. The image should be tagged with the same version as the `TEMPORAL_VERSION` build argument. - -```console -$ DOCKER_BUILDKIT=1 docker build ./apps/hash-external-services/temporal/ -f ./apps/hash-external-services/temporal/migrate.Dockerfile --build-arg TEMPORAL_VERSION=$HASH_TEMPORAL_VERSION -t 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-temporal-prod-usea1-migrate:$HASH_TEMPORAL_VERSION -$ docker push 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-temporal-prod-usea1-migrate:$HASH_TEMPORAL_VERSION -.. -$ DOCKER_BUILDKIT=1 docker build ./apps/hash-external-services/temporal/ -f ./apps/hash-external-services/temporal/setup.Dockerfile --build-arg TEMPORAL_VERSION=$HASH_TEMPORAL_VERSION -t 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-temporal-prod-usea1-setup:$HASH_TEMPORAL_VERSION -$ docker push 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-temporal-prod-usea1-migrate:$HASH_TEMPORAL_VERSION -.. -``` - -To build and push the Temporal workers you may use these commands: - -```console -$ # AI Typescript worker -$ DOCKER_BUILDKIT=1 docker build . -f ./apps/hash-ai-worker-ts/docker/Dockerfile -t 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-temporalworkeraits:latest -$ docker push 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-temporalworkeraits:latest -.. -$ # Integration worker -$ DOCKER_BUILDKIT=1 docker build . -f ./apps/hash-integration-worker/docker/Dockerfile -t 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-temporalworkerintegration:latest -$ docker push 000000000000.dkr.ecr.us-east-1.amazonaws.com/h-hash-prod-usea1-temporalworkerintegration:latest -.. -``` - -All of the above steps are also available in the [`HASH backend deployment` GitHub Action workflow](../../.github/workflows/hash-backend-cd.yml). - -# Management - -You've already seen the SSH command located in [`./hash/ssh_bastion.sh`](./hash/ssh_bastion.sh) which is used to SSH into the AWS VPC. This SSH connection is general-purpose and can be used without the tunnel part just as `./ssh_bastion.sh` without any args. - -Alternatively, you should be able to start an AWS Systems Manager session to the bastion host from the AWS Console, https://us-east-1.console.aws.amazon.com/systems-manager/session-manager/sessions?region=us-east-1 (change region to your desired region). - -The ECS Fargate containers are also accessible through the AWS CLI using Fargate Exec. An example command would look like this: - -```console -$ aws ecs list-tasks --cluster h-hash-prod-usea1-ecs --service h-hash-prod-usea1-appsvc -.. "arn:aws:ecs:us-east-1:$AWS_ID:task/h-hash-prod-usea1-ecs/TASKID" -$ aws ecs execute-command --cluster h-hash-prod-usea1-ecs --task TASKID --container h-hash-prod-usea1-apicontainer --interactive --command "/bin/sh" -.. -``` - -where the `TASKID` is the ID of the Fargate service task. Note that this would start an `sh` session directly into your production container, and should be done with caution (if it ever should be done). It's recommended not to run commands against the production environment like this, and we may at some point restrict access to Fargate Exec. - -# Troubleshooting and guides - -## What to do if the HASH DB needs **Disaster Recovery** - -There's a [recovery playbook](./playbooks/db_recovery.md) on what we should do when the DB is on fire, please follow it to recover a snapshot. - -## Help! I need access to the private subnet! - -You can access the private subnet using the Bastion host. For more information, please see [the corresponding readme](./hash/bastion.md). You need an SSH privatekey to access the bastion, which you can find in `1password` if you have the appropriate access level. - -## How do I get the Terraform state from S3? - -Install the AWS CLI and follow instructions to add credentials. Credentials are generated in the IAM User security credentials page. `https://us-east-1.console.aws.amazon.com/iam/home#/users/NAME?section=security_credentials` for a user with the name `NAME`. - -Once the CLI has been configured, you should have a `.aws` folder in your home directory. Terraform will automatically pick this up and connect to the S3 Terraform State backend. - -## How do I log in to AWS ECR container registry? - -If you wish to pull/push container images to ECR manually, you must: - -1. Sign in to the registry -2. Build and tag the image -3. Push the image to ECR - -Please see the [AWS docs](https://docs.aws.amazon.com/AmazonECR/latest/userguide/docker-push-ecr-image.html) for instructions on how to do so. - -## How do I migrate the database after it has been deployed? - -Using the Terraform scripts to deploy an instance of RDS will give you an empty DB (with appropriate DBs/users), which will need to be migrated. Note that we have a GitHub workflow to do this automatically at [`hash-backend-cd.yml`](/.github/workflows/hash-backend-cd.yml). - -To migrate the database locally, you'll need to ensure you've built the dockerfile for `hash-graph` and prepare your environment with some of the values in the Vault key-value entries for the environment you're migrating, see [Deploy infrastructure with terraform](#deploy-infrastructure-with-terraform) for where to look in the Vault. - -Build the dockerfile for `hash-graph`: - -```console -$ turbo --filter @apps/hash-graph build:docker:prod -.. -``` - -Make sure your Vault CLI is logged in correctly. You can export the following environment variables (adjust the vault path to match your environment): - -```shell -export HASH_PG_HOST=$(terraform output -raw rds_hostname) -export HASH_PG_PORT=5554 # or the port you specify in your SSH tunnel -export HASH_GRAPH_PG_DATABASE=graph -export HASH_GRAPH_PG_USER=graph -export HASH_GRAPH_PG_PASSWORD=$(vault kv get -field=pg_graph_user_password_raw automation/pipelines/hash/prod) -export HASH_KRATOS_PG_DATABASE=kratos -export HASH_KRATOS_PG_USER=kratos -export HASH_KRATOS_PG_PASSWORD=$(vault kv get -field=pg_kratos_user_password_raw automation/pipelines/hash/prod) -export HASH_KRATOS_API_SECRET=$(vault kv get -field=kratos_api_key automation/pipelines/hash/prod) -``` - -The migration requires an SSH tunnel to the VPC, see [this README](./hash/bastion.md) for more information on initiate the tunnel. - -Now you can run the `hash-graph` migration: - -```console -$ docker run --rm \ - --network host \ - -e HASH_GRAPH_PG_USER \ - -e HASH_GRAPH_PG_PASSWORD \ - -e HASH_GRAPH_PG_HOST=$HASH_PG_HOST \ - -e HASH_GRAPH_PG_PORT=$HASH_PG_PORT \ - -e HASH_GRAPH_PG_DATABASE \ - -e HASH_GRAPH_LOG_LEVEL=info \ - hash-graph \ - migrate -.. -``` - -And to migrate `kratos`: - -```console -$ pwd -/../hash/ # at root of repo -$ docker build ./apps/hash-external-services/kratos --build-arg ENV=prod --build-arg API_SECRET=$HASH_KRATOS_API_SECRET -t kratos:latest -.. -$ docker run --rm \ - --network host \ - -e LOG_LEVEL=info \ - -e "DSN=postgres://${HASH_KRATOS_PG_USER}:${HASH_KRATOS_PG_PASSWORD}@${HASH_PG_HOST}:${HASH_PG_PORT}/${HASH_KRATOS_PG_DATABASE}" \ - kratos:latest \ - migrate sql -e --yes -``` - -## How do I redeploy a running Fargate task? - -The current Terraform modules define Fargate task definitions, which are parameterized over ECR container tags. This means that one can push a new Docker image with the same task, and trigger a new Fargate deployment to refresh the image of a service. Assuming an image has been pushed to ECR with the same tag defined in the task definition (e.g. `latest`), it is possible to trigger a redeploy with - -```console -$ aws ecs update-service --cluster h-hash-prod-usea1-ecscluster --service h-hash-prod-usea1-apisvc --force-new-deployment -.. -``` - -Where `prod` is the Terraform workspace name, `usea1` is the region, and `apisvc` is the name of the service and `ecscluster` is the name of the ECS cluster. - -# Structure of the modules - -Under the [`./modules/`](./modules/) folder we define the HASH application through an ECS Fargate service and various external services. - -## Shared modules - -- [`base_network`](./modules/base_network/) - contains global VPC (Virtual Private Cloud) definitions and networking setup defining private and public subnets. -- [`bastion`](./modules/bastion/) - a [Bastion host](https://en.wikipedia.org/wiki/Bastion_host) that resides in the public subnet with access to the private subnet. Accessible through SSH. -- [`container_cluster`](./modules/container_cluster/) - is a thin wrapper around the `aws_ecs_cluster` resource with container capacity providers. -- [`container_registry`](./modules/container_registry/) - is a thin wrapper around the `aws_ecr_repository` resource with container evicition policies. -- [`redis`](./modules/redis/) - [external service] a multi Availability Zone Redis cluster with encryption enabled. -- [`tunnel`](./modules/tunnel/) - a custom SSH tunnel using an external data source to allow terraform to connect to services on the private subnet of the VPC. -- [`variables`](./modules/variables/) - contains global variable validation/definitions generally useful for our Terraform infrastructure. -- [`vault_aws_auth`](./modules/vault_aws_auth/) - contains the configuration for authenticating the AWS provider through the Vault AWS authentication backend. - -## HASH-specific modules - -- [`networking`](./modules/hash/networking/) - contains PrivateLink definitions for the various required AWS resources. -- [`postgres`](./modules/postgres/) - [external service] a multi Availability Zone Postgres RDS cluster with encryption enabled. -- [`postgres_roles`](./modules/postgres_roles/) - SQL configurations for the HASH application defining grants, roles and databases (requires an SSH tunnel to connect to the RDS instance). -- [`hash_application`](./modules/hash_application/) - the ECS Fargate container definition using the previous ECR and ECS cluster definitions to start the Graph layer, the HASH API and Kratos. - -**HASH infrastructure diagram**: - -```mermaid -graph TD; - networking - variables - hash - - hash-->container_cluster - hash-->hash_application - hash-->container_registry - hash-->postgres_roles - hash-->tunnel - hash-->redis - hash-->postgres - - tunnel-->postgres_roles - postgres-->postgres_roles - - - container_cluster-->hash_application - container_registry-->hash_application -``` - -## Archived HASH modules - -This list of modules are to be considered obsolete/out of date, and are kept here for reference in case of future re-use. - -- [`citus`](./modules/citus/) - Module for deploying a Citus cluster on AWS using EC2. - Along with [playbooks/citus.md](./playbooks/citus.md) - Instructions for administering the Citus cluster. diff --git a/infra/terraform/modules/base_network/main.tf b/infra/terraform/modules/base_network/main.tf deleted file mode 100644 index 0c77c6bad36..00000000000 --- a/infra/terraform/modules/base_network/main.tf +++ /dev/null @@ -1,177 +0,0 @@ -/** - * # Terraform AWS module: Base network - * - * Module responsible for creating the base network infrastructure. - * - * This includes: - * - VPC - * - Subnets (public and private) - * - Internet gateway - * - Route tables - * - Basic IAM - * - Flow logs (disabled for now) - */ - -resource "aws_vpc" "main" { - # IP address range 10.0.0.0 - 10.0.255.255 (65536 addresses) - # We will have 10.0.0.0 - 10.0.127.0 contain the private subnet - # and have 10.0.128.0 - 10.0.255.0 addresses contain the public subnet - cidr_block = "10.0.0.0/16" - enable_dns_support = true - enable_dns_hostnames = true - tags = { Name = "${var.prefix}-vpc" } -} - -# Flow logs in VPC -resource "aws_flow_log" "flow_log" { - count = var.enable_flow_logs ? 1 : 0 - tags = { Name = "${var.prefix}-flowvpc" } - - iam_role_arn = aws_iam_role.flow_log[0].arn - log_destination = aws_cloudwatch_log_group.flow_log[0].arn - traffic_type = "ALL" - vpc_id = aws_vpc.main.id -} - -resource "aws_cloudwatch_log_group" "flow_log" { - count = var.enable_flow_logs ? 1 : 0 - name = "${var.prefix}-flowlogvpc" -} - -resource "aws_iam_role" "flow_log" { - count = var.enable_flow_logs ? 1 : 0 - name = "${var.prefix}-flowlogrole" - - assume_role_policy = <> /etc/fstab; - -runcmd: - # Install Docker - - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - - - add-apt-repository "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" - - apt-get update -y - - apt-get install -y docker-ce docker-ce-cli containerd.io - - systemctl enable docker - - systemctl start docker - # Install the AWS CLI - - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" - - unzip -q awscliv2.zip - - ./aws/install - # Install psql - - sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' - - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - - - apt-get update - - apt-get -y install postgresql-client-13 - # Mount the Postgres data EBS volume to /data - - ["bash", "/mount_ebs_volume.sh"] - -final_message: "The system is finally up, after $UPTIME seconds" diff --git a/infra/terraform/modules/citus/common.tf b/infra/terraform/modules/citus/common.tf deleted file mode 120000 index a0251abd3ba..00000000000 --- a/infra/terraform/modules/citus/common.tf +++ /dev/null @@ -1 +0,0 @@ -../common.tf \ No newline at end of file diff --git a/infra/terraform/modules/citus/ecr.tf b/infra/terraform/modules/citus/ecr.tf deleted file mode 100644 index 31ae2e5703b..00000000000 --- a/infra/terraform/modules/citus/ecr.tf +++ /dev/null @@ -1,4 +0,0 @@ -resource "aws_ecr_repository" "citus" { - name = "${local.prefix}-citusecr" - tags = {} -} diff --git a/infra/terraform/modules/citus/instance.tf b/infra/terraform/modules/citus/instance.tf deleted file mode 100644 index f2d10a4c041..00000000000 --- a/infra/terraform/modules/citus/instance.tf +++ /dev/null @@ -1,163 +0,0 @@ -data "aws_ami" "ubuntu" { - most_recent = true - - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] - } - - filter { - name = "virtualization-type" - values = ["hvm"] - } - - owners = ["099720109477"] # Canonical -} - -data "template_file" "cloudinit" { - template = file("./cloud-init.yml") - vars = { - ecr_repo_url = aws_ecr_repository.citus.repository_url - ecr_registry_url = split("/", aws_ecr_repository.citus.repository_url)[0] - aws_region = var.region - mount_volume_script_base64 = filebase64("./mount_volume.sh") - data_ebs_volume_id = replace(aws_ebs_volume.ebs1.id, "-", "") - } -} - -resource "aws_iam_role" "instance" { - name = "${local.prefix}-citusinstancerole" - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "ec2.amazonaws.com" - } - }, - ] - }) - inline_policy { - name = "policy" - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "ecr:BatchCheckLayerAvailability", - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - ] - Resource = [aws_ecr_repository.citus.arn] - }, - { - Effect = "Allow" - Action = [ - "ecr:GetAuthorizationToken", - ] - Resource = ["*"] - }, - { - Effect = "Allow" - Action = [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents", - ] - Resource = ["*"] - }, - ] - }) - } -} - -resource "aws_iam_instance_profile" "instance" { - name = "${local.prefix}-citusprofile" - role = aws_iam_role.instance.name - tags = {} -} - -resource "aws_security_group" "ssh" { - name = "${local.prefix}-citussg" - vpc_id = data.terraform_remote_state.base.outputs.vpc_id - - ingress = [ - // @todo: should restrict this to the IP of a bastion host - { - description = "SSH" - from_port = 22 - to_port = 22 - cidr_blocks = ["0.0.0.0/0"] - ipv6_cidr_blocks = ["::/0"] - protocol = "TCP" - prefix_list_ids = [] - security_groups = [] - self = true - }, - // @todo: should restrict this to the IP of a bastion host - { - description = "Postgres connections" - from_port = 5432 - to_port = 5432 - cidr_blocks = ["0.0.0.0/0"] - ipv6_cidr_blocks = ["::/0"] - protocol = "TCP" - prefix_list_ids = [] - security_groups = [] - self = true - }, - ] - - egress = [ - { - description = "All outbound traffic" - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - ipv6_cidr_blocks = ["::/0"] - prefix_list_ids = [] - security_groups = [] - self = true - } - ] - - lifecycle { - // Required to prevent the terraform from getting stuck trying to destroy the - // resouce - create_before_destroy = true - } -} - -resource "aws_instance" "citus1" { - ami = data.aws_ami.ubuntu.id - instance_type = var.instance_type - subnet_id = data.terraform_remote_state.base.outputs.subnet_pub1_id - associate_public_ip_address = true - user_data = data.template_file.cloudinit.rendered - vpc_security_group_ids = [aws_security_group.ssh.id] - iam_instance_profile = aws_iam_instance_profile.instance.name - availability_zone = var.az[var.region][0] - tags = { - Name = "${local.prefix}-citusec2" - } -} - -resource "aws_ebs_volume" "ebs1" { - availability_zone = var.az[var.region][0] - size = 20 # GB - type = "gp3" - tags = { - Name = "${local.prefix}-citusebs1" - } -} - -resource "aws_volume_attachment" "ebs_att1" { - device_name = "/dev/sda2" - volume_id = aws_ebs_volume.ebs1.id - instance_id = aws_instance.citus1.id -} - diff --git a/infra/terraform/modules/citus/main.tf b/infra/terraform/modules/citus/main.tf deleted file mode 100644 index 7a5a530f60c..00000000000 --- a/infra/terraform/modules/citus/main.tf +++ /dev/null @@ -1,28 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 3.53" - } - } - - required_version = ">= 0.14.9" - - backend "s3" { - bucket = "hash-terraform-state-s3-backend" - workspace_key_prefix = "hash" - key = "citus_single_config" - region = "us-east-1" - encrypt = true - } -} - -# For reading outputs from the "base" terraform config -data "terraform_remote_state" "base" { - backend = "s3" - config = { - bucket = "hash-terraform-state-s3-backend" - key = "hash/${terraform.workspace}/base_config" - region = "us-east-1" - } -} diff --git a/infra/terraform/modules/citus/mount_volume.sh b/infra/terraform/modules/citus/mount_volume.sh deleted file mode 100644 index be9df52b0ce..00000000000 --- a/infra/terraform/modules/citus/mount_volume.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -# Mounts an EBS volume with ID set by DATA_EBS_VOLUME_ID to /data -# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-using-volumes.html - -if [ -d /data ] -then - echo "A volume is already mounted to /data"; - exit 0; -fi; - -# Load environment variables -export "$(< /etc/environment xargs)"; - -# Find the name of the device matching the volume ID. The volume may not be available -# immediately, so we retry this several times -device=$(nvme list -o json | jq -r --arg id "$DATA_EBS_VOLUME_ID" '.Devices|.[] | select(.SerialNumber == $id) | .DevicePath'); -while [ -z "$device" ] -do - echo "Volume $DATA_EBS_VOLUME_ID not found. Sleeping for 10 seconds ..."; - sleep 10; - device=$(nvme list -o json | jq -r --arg id "$DATA_EBS_VOLUME_ID" '.Devices|.[] | select(.SerialNumber == $id) | .DevicePath'); - tries=$(( tries + 1 )); - if [ $tries -gt 10 ] - then - echo "Could not find device for volume $DATA_EBS_VOLUME_ID"; - exit 1; - fi; -done; - -echo "Device name = $device"; - -# Make the filesystem if there is not already one on the device -status=$(file -s "$device"); -if [ "$status" == "$device: data" ] -then - mkfs -t xfs "$device"; - echo "Created an XFS filesystem on device $device"; -else - echo "Filesystem already exists on device $device"; -fi; - -mkdir /data; -mount "$device" /data; -echo "Mounted device $device to /data"; - -# Reference the volume in /etc/fstab so it will be remounted when the instance reboots -device_uuid=$(blkid | grep "$device" | sed -E 's/.*UUID="([a-zA-Z0-9\-]+)".*$/\1/'); -echo "Device UUID = $device_uuid"; -echo "UUID=$device_uuid /data xfs defaults,nofail 0 2" > /fstab; diff --git a/infra/terraform/modules/citus/outputs.tf b/infra/terraform/modules/citus/outputs.tf deleted file mode 100644 index d1069156c44..00000000000 --- a/infra/terraform/modules/citus/outputs.tf +++ /dev/null @@ -1,14 +0,0 @@ -output "instance_ip" { - description = "Public IP address of the EC2 instance" - value = aws_instance.citus1.public_ip -} - -output "ecr_repo_url" { - description = "URL of the ECR repository for the Citus image" - value = aws_ecr_repository.citus.repository_url -} - -output "ecr_repo_name" { - description = "Name of the ECR repository for the Citus image" - value = regex("^.*/(.*)$", aws_ecr_repository.citus.repository_url)[0] -} diff --git a/infra/terraform/modules/citus/prod-usea1.tfvars b/infra/terraform/modules/citus/prod-usea1.tfvars deleted file mode 100644 index d39d20ce7d2..00000000000 --- a/infra/terraform/modules/citus/prod-usea1.tfvars +++ /dev/null @@ -1,3 +0,0 @@ -env = "prod" -region = "us-east-1" -instance_type = "t3.small" diff --git a/infra/terraform/modules/citus/variables.tf b/infra/terraform/modules/citus/variables.tf deleted file mode 100644 index 1797062c3a8..00000000000 --- a/infra/terraform/modules/citus/variables.tf +++ /dev/null @@ -1,25 +0,0 @@ -variable "region" { - type = string - description = "The AWS region" - - # TODO: add more AWS regions to the condition - validation { - condition = contains(["us-east-1"], var.region) - error_message = "Invalid AWS region." - } -} - -variable "env" { - type = string - description = "The environment: prod or staging" - - validation { - condition = contains(["prod", "staging"], var.env) - error_message = "Environment must be 'prod' or 'staging'." - } -} - -variable "instance_type" { - type = string - description = "The EC2 instance type to use for the database" -} diff --git a/infra/terraform/modules/common.tf b/infra/terraform/modules/common.tf deleted file mode 100644 index ce3cb0e13c7..00000000000 --- a/infra/terraform/modules/common.tf +++ /dev/null @@ -1,40 +0,0 @@ - -provider "aws" { - profile = "default" - region = var.region - - default_tags { - tags = { - project = "hash" - environment = "${var.env}" - region = "${var.region}" - terraform_workspace = "${terraform.workspace}" - } - } -} - -locals { - prefix = "h-hash-${var.env}-${var.region_short[var.region]}" -} - - -######################################################################################### -# Internal variables -- not intended to be specified in *.tfvars file. - -# Shorter region prefixes used for naming resources -variable "region_short" { - type = map(string) - default = { - us-east-1 = "usea1" - } -} - -# Availability zones in AWS regions -variable "az" { - type = map(list(string)) - default = { - "us-east-1" = ["us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d"] - } -} - -######################################################################################### diff --git a/infra/terraform/modules/container_cluster/main.tf b/infra/terraform/modules/container_cluster/main.tf deleted file mode 100644 index f8bacf8e456..00000000000 --- a/infra/terraform/modules/container_cluster/main.tf +++ /dev/null @@ -1,15 +0,0 @@ -resource "aws_ecs_cluster" "ecs" { - name = "${var.prefix}-${var.ecs_name}" - - setting { - name = "containerInsights" - value = "enhanced" - } - - tags = {} -} - -resource "aws_ecs_cluster_capacity_providers" "ecs" { - cluster_name = aws_ecs_cluster.ecs.name - capacity_providers = var.capacity_providers -} diff --git a/infra/terraform/modules/container_cluster/outputs.tf b/infra/terraform/modules/container_cluster/outputs.tf deleted file mode 100644 index 76722e4d990..00000000000 --- a/infra/terraform/modules/container_cluster/outputs.tf +++ /dev/null @@ -1,4 +0,0 @@ -output "ecs_cluster_arn" { - description = "The ARN of the main ECS cluster" - value = aws_ecs_cluster.ecs.arn -} diff --git a/infra/terraform/modules/container_cluster/variables.tf b/infra/terraform/modules/container_cluster/variables.tf deleted file mode 100644 index 92f79e2e8c6..00000000000 --- a/infra/terraform/modules/container_cluster/variables.tf +++ /dev/null @@ -1,20 +0,0 @@ -variable "capacity_providers" { - type = list(string) - description = "The capacity providers to use for the ECS cluster. Beware that FARGATE_SPOT can introduce instability." - default = ["FARGATE"] -} - -variable "prefix" { - type = string - description = "The prefix to use for resource names, includes region and env" -} - -variable "ecs_name" { - type = string - description = "The name to give the ECS cluster" - - validation { - condition = can(regex("^[a-z]+$", var.ecs_name)) - error_message = "ECS cluster name must only contain lowercase letters" - } -} diff --git a/infra/terraform/modules/container_registry/main.tf b/infra/terraform/modules/container_registry/main.tf deleted file mode 100644 index 5ea5af6b204..00000000000 --- a/infra/terraform/modules/container_registry/main.tf +++ /dev/null @@ -1,48 +0,0 @@ -/** - * # Terraform AWS module: Container Registry - * - * Module responsible for creating container registries. - * We use ECR for our container registries, and this module can create - * ECR repository with our desired lifecycle policy. - */ - -resource "aws_ecr_repository" "ecr" { - name = "${var.prefix}-${var.ecr_name}" - tags = {} -} - -resource "aws_ecr_lifecycle_policy" "ecr_policy" { - repository = aws_ecr_repository.ecr.name - # Lifetime policy always keeps 'latest' image - # It also ensures only one 'latest' image - # Evicts any other images when count > 10 in total (including latest) - policy = jsonencode({ - rules = [ - { - rulePriority = 1, - description = "Keep Latest Image", - selection = { - tagStatus = "tagged", - tagPrefixList = ["latest"], - countType = "imageCountMoreThan", - countNumber = 1 - }, - action = { - type = "expire" - } - }, - { - rulePriority = 2, - description = "Prune Old Images", - selection = { - tagStatus = "any", - countType = "imageCountMoreThan", - countNumber = 10 - }, - action = { - type = "expire" - } - } - ] - }) -} diff --git a/infra/terraform/modules/container_registry/outputs.tf b/infra/terraform/modules/container_registry/outputs.tf deleted file mode 100644 index 708bc2202a2..00000000000 --- a/infra/terraform/modules/container_registry/outputs.tf +++ /dev/null @@ -1,9 +0,0 @@ -output "ecr_arn" { - description = "The ARN of the ECR repository" - value = aws_ecr_repository.ecr.arn -} - -output "url" { - description = "The URL of the ECR repository" - value = aws_ecr_repository.ecr.repository_url -} diff --git a/infra/terraform/modules/container_registry/terraform.tf b/infra/terraform/modules/container_registry/terraform.tf deleted file mode 100644 index bbfdeeefb2d..00000000000 --- a/infra/terraform/modules/container_registry/terraform.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 4.67" - } - } -} - diff --git a/infra/terraform/modules/container_registry/variables.tf b/infra/terraform/modules/container_registry/variables.tf deleted file mode 100644 index 66708f74eb9..00000000000 --- a/infra/terraform/modules/container_registry/variables.tf +++ /dev/null @@ -1,14 +0,0 @@ -variable "prefix" { - type = string - description = "The prefix to use for resource names, includes region and env" -} - -variable "ecr_name" { - type = string - description = "The name to give the ECR repository" - - validation { - condition = can(regex("^[a-z]+$", var.ecr_name)) - error_message = "ECR repository name must only contain lowercase letters" - } -} diff --git a/infra/terraform/modules/privatelink_endpoints/main.tf b/infra/terraform/modules/privatelink_endpoints/main.tf deleted file mode 100644 index b94adac9d15..00000000000 --- a/infra/terraform/modules/privatelink_endpoints/main.tf +++ /dev/null @@ -1,74 +0,0 @@ -variable "region" { - type = string - description = "The AWS region" -} - -output "endpoints" { - value = { - # Allow secrets to be fetched from the private subnet - # SSM contain the Parameter Store, which stores secrets. - ssm = { - name = "com.amazonaws.${var.region}.ssm" - type = "Interface" - private_dns = false - phz_name = "ssm.${var.region}.amazonaws.com" - alias = [""] - } - # Allow Fargate Exec control messages - ssmmessages = { - name = "com.amazonaws.${var.region}.ssmmessages" - type = "Interface" - private_dns = false - phz_name = "ssmmessages.${var.region}.amazonaws.com" - alias = [""] - } - # Used for various Fargate related operations and Fargate Exec - ec2messages = { - name = "com.amazonaws.${var.region}.ec2messages" - type = "Interface" - private_dns = false - phz_name = "ec2messages.${var.region}.amazonaws.com" - alias = [""] - } - # Used for logging to CloudWatch - ec2logs = { - name = "com.amazonaws.${var.region}.logs" - type = "Interface" - private_dns = false - phz_name = "logs.${var.region}.amazonaws.com" - alias = [""] - } - # Allow fetching ECR data within the private subnet from ECS - # Used to pull OCI containers - # See https://docs.aws.amazon.com/AmazonECR/latest/userguide/vpc-endpoints.html#ecr-vpc-endpoint-considerations - ecrdkr = { - name = "com.amazonaws.${var.region}.ecr.dkr" - type = "Interface" - private_dns = false - phz_name = "dkr.ecr.${var.region}.amazonaws.com" - # The wildcard alias "*" allows us to request by - # entries in the AWS account namespace. - alias = ["", "*"] - } - # Used to pull OCI containers - ecrapi = { - name = "com.amazonaws.${var.region}.ecr.api" - type = "Interface" - private_dns = false - phz_name = "api.ecr.${var.region}.amazonaws.com" - alias = [""] - } - # Allows access to S3 buckets - # Also required to pull OCI containers from ECS - s3 = { - name = "com.amazonaws.${var.region}.s3" - type = "Interface" - private_dns = false - phz_name = "s3.${var.region}.amazonaws.com" - # The wildcard alias "*", allows us to request by - # the bucket name without listing them all. - # IAM rules would ensure only the correct buckets are accessible. - alias = ["", "*"] - } - } -} diff --git a/infra/terraform/modules/redis/main.tf b/infra/terraform/modules/redis/main.tf deleted file mode 100644 index 67b5c334193..00000000000 --- a/infra/terraform/modules/redis/main.tf +++ /dev/null @@ -1,52 +0,0 @@ -locals { - prefix = "${var.prefix}-redis" -} - -resource "aws_elasticache_subnet_group" "default" { - name = "${local.prefix}subnet" - subnet_ids = var.subnet_ids -} - -resource "aws_security_group" "redis" { - name = "${local.prefix}sg" - description = "Security group for Elasticache Redis cluster" - vpc_id = var.vpc_id - - ingress { - from_port = 6379 - to_port = 6379 - protocol = "tcp" - cidr_blocks = [var.vpc_cidr_block] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = [var.vpc_cidr_block] - } - - tags = { - Name = "${local.prefix}sg" - } -} - -resource "aws_elasticache_replication_group" "redis" { - replication_group_id = "${local.prefix}replica" - description = "Redis multi-AZ replication group for HASH" - engine = "redis" - automatic_failover_enabled = true - preferred_cache_cluster_azs = var.region_az_names - node_type = var.node_type - parameter_group_name = "default.redis7" - num_cache_clusters = 2 - port = 6379 - subnet_group_name = aws_elasticache_subnet_group.default.name - security_group_ids = [aws_security_group.redis.id] - at_rest_encryption_enabled = true - transit_encryption_enabled = true - - tags = { - Name = "${local.prefix}replica" - } -} diff --git a/infra/terraform/modules/redis/outputs.tf b/infra/terraform/modules/redis/outputs.tf deleted file mode 100644 index e6c481f854a..00000000000 --- a/infra/terraform/modules/redis/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output "node" { - description = "Redis node connection information" - value = { - address = aws_elasticache_replication_group.redis.primary_endpoint_address, - port = aws_elasticache_replication_group.redis.port - } -} diff --git a/infra/terraform/modules/redis/variables.tf b/infra/terraform/modules/redis/variables.tf deleted file mode 100644 index 3379b1f7c9e..00000000000 --- a/infra/terraform/modules/redis/variables.tf +++ /dev/null @@ -1,29 +0,0 @@ -variable "prefix" { - type = string - description = "The prefix to use for resource names, includes region and env" -} - -variable "node_type" { - type = string - description = "The EC2 node type to use for the Redis instance" -} - -variable "subnet_ids" { - type = list(string) - description = "The subnet to run the cache within" -} - -variable "vpc_id" { - type = string - description = "VPC id" -} - -variable "vpc_cidr_block" { - type = string - description = "VPC cidr block" -} - -variable "region_az_names" { - type = list(string) - description = "Availability zones to use for the multi-AZ redis cluster" -} diff --git a/infra/terraform/modules/temporal_worker/main.tf b/infra/terraform/modules/temporal_worker/main.tf deleted file mode 100644 index b2363cc902b..00000000000 --- a/infra/terraform/modules/temporal_worker/main.tf +++ /dev/null @@ -1,158 +0,0 @@ -locals { - prefix = "${var.prefix}-worker-${var.worker_name}" - log_group_name = "${local.prefix}-log" - param_prefix = "${var.param_prefix}/worker/${var.worker_name}" -} - -module "worker" { - source = "../container_registry" - prefix = local.prefix - ecr_name = "ecr" -} - -resource "aws_iam_role" "execution_role" { - name = "${local.prefix}-exerole" - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "ecs-tasks.amazonaws.com" - } - } - ] - }) - inline_policy { - name = "policy" - # Allow publishing logs and getting secrets - policy = jsonencode({ - Version = "2012-10-17" - Statement = flatten([ - [ - { - Effect = "Allow" - Action = ["logs:CreateLogGroup"] - Resource = ["*"] - } - ], - length(local.shared_secrets) > 0 ? [{ - Effect = "Allow" - Action = ["ssm:GetParameters"] - Resource = [for _, env_var in local.shared_secrets : env_var.valueFrom] - }] : [] - ]) - }) - } - tags = {} -} - -resource "aws_iam_role_policy_attachment" "execution_role" { - role = aws_iam_role.execution_role.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" -} - -# IAM role for the running task -resource "aws_iam_role" "task_role" { - name = "${local.prefix}-taskrole" - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "ecs-tasks.amazonaws.com" - } - }, - ] - }) - inline_policy { - name = "policy" - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - # Enable SSM - { - Action = [ - "ssmmessages:CreateControlChannel", - "ssmmessages:CreateDataChannel", - "ssmmessages:OpenControlChannel", - "ssmmessages:OpenDataChannel" - ], - Effect = "Allow", - Resource = "*" - } - ] - }) - } - - tags = {} -} - -resource "aws_ecs_task_definition" "task" { - family = "${local.prefix}-taskdef" - requires_compatibilities = ["FARGATE"] - cpu = var.cpu - memory = var.memory - network_mode = "awsvpc" - execution_role_arn = aws_iam_role.execution_role.arn - task_role_arn = aws_iam_role.task_role.arn - container_definitions = jsonencode(local.task_definitions) - - runtime_platform { - operating_system_family = "LINUX" - cpu_architecture = "ARM64" - } -} - -resource "aws_ecs_service" "svc" { - depends_on = [aws_iam_role.task_role] - name = "${local.prefix}-svc" - cluster = var.cluster_arn - task_definition = aws_ecs_task_definition.task.arn - enable_execute_command = true - desired_count = var.desired_count - launch_type = "FARGATE" - - network_configuration { - subnets = var.subnets - assign_public_ip = true - security_groups = [ - aws_security_group.app_sg.id, - ] - } - - tags = { Service = "${local.prefix}-svc" } -} - - -resource "aws_security_group" "app_sg" { - name = "${local.prefix}sg" - vpc_id = var.vpc.id - - egress { - from_port = 53 - to_port = 53 - protocol = "tcp" - description = "Allow outbound DNS lookups" - cidr_blocks = ["0.0.0.0/0"] - } - egress { - from_port = 443 - to_port = 443 - protocol = "tcp" - description = "Allow outbound HTTPS connections" - cidr_blocks = ["0.0.0.0/0"] - } - egress { - from_port = 7233 - to_port = 7233 - protocol = "tcp" - description = "Allow outbound GRPC connections to Temporal" - cidr_blocks = ["0.0.0.0/0"] - } - - # NOTE: if you need to connect to other serivces, there must be more egress rules to allow that. -} diff --git a/infra/terraform/modules/temporal_worker/output.tf b/infra/terraform/modules/temporal_worker/output.tf deleted file mode 100644 index e1a8dd2c208..00000000000 --- a/infra/terraform/modules/temporal_worker/output.tf +++ /dev/null @@ -1,4 +0,0 @@ -output "worker_ecr" { - description = "ECR repository for the worker image" - value = module.worker -} diff --git a/infra/terraform/modules/temporal_worker/task_definitions.tf b/infra/terraform/modules/temporal_worker/task_definitions.tf deleted file mode 100644 index f263926d253..00000000000 --- a/infra/terraform/modules/temporal_worker/task_definitions.tf +++ /dev/null @@ -1,62 +0,0 @@ -locals { - task_definitions = [ - { - essential = true - name = local.prefix - image = "${module.worker.url}:latest" - cpu = 0 # let ECS divvy up the available CPU - healthCheck = { - command = var.ecs_health_check - startPeriod = 10 - interval = 10 - retries = 10 - timeout = 5 - } - - environment = concat([ - { name = "HASH_TEMPORAL_SERVER_HOST", value = var.temporal_host }, - { name = "HASH_TEMPORAL_SERVER_PORT", value = var.temporal_port }, - ], - [for env_var in var.env_vars : { name = env_var.name, value = env_var.value } if !env_var.secret] - ) - - secrets = [ - for env_name, ssm_param in aws_ssm_parameter.secret_env_vars : - { name = env_name, valueFrom = ssm_param.arn } - ] - - logConfiguration = { - logDriver = "awslogs" - options = { - "awslogs-create-group" = "true" - "awslogs-group" = local.log_group_name - "awslogs-stream-prefix" = var.worker_name - "awslogs-region" = var.region - } - } - }, - ] - - shared_env_vars = [ - { name = "HASH_TEMPORAL_SERVER_HOST", value = var.temporal_host }, - { name = "HASH_TEMPORAL_SERVER_PORT", value = var.temporal_port }, - ] - - shared_secrets = [ - for env_name, ssm_param in aws_ssm_parameter.secret_env_vars : - { name = env_name, valueFrom = ssm_param.arn } - ] -} - - -resource "aws_ssm_parameter" "secret_env_vars" { - # Only put secrets into SSM - for_each = {for env_var in var.env_vars : env_var.name => env_var if env_var.secret} - - name = "${local.param_prefix}/${each.value.name}" - # Still supports non-secret values - type = "SecureString" - value = sensitive(each.value.value) - overwrite = true - tags = {} -} diff --git a/infra/terraform/modules/temporal_worker/terraform.tf b/infra/terraform/modules/temporal_worker/terraform.tf deleted file mode 100644 index bbfdeeefb2d..00000000000 --- a/infra/terraform/modules/temporal_worker/terraform.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 4.67" - } - } -} - diff --git a/infra/terraform/modules/temporal_worker/variables.tf b/infra/terraform/modules/temporal_worker/variables.tf deleted file mode 100644 index 2a52cbc1be5..00000000000 --- a/infra/terraform/modules/temporal_worker/variables.tf +++ /dev/null @@ -1,90 +0,0 @@ -variable "env" { - type = string - description = "The environment, defaults to the selected workspace." -} - -variable "region" { - type = string - description = "The AWS region" -} - -variable "vpc" { - description = "The VPC to deploy the components within" -} - -variable "prefix" { - type = string - description = "The prefix to use for resource names, includes region and env" -} - -variable "param_prefix" { - type = string - description = "The prefix for Param store" -} - -variable "subnets" { - type = list(string) - description = "The list of subnet IDs that the instance should have attached" -} - -variable "cpu" { - type = number - description = "API service Fargate CPU units" - - validation { - condition = contains([256, 512, 1024, 2048, 4096], var.cpu) - error_message = "Invalid API CPU allocation. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html#w380aac44c17c17" - } -} - -variable "memory" { - type = number - description = "API service Fargate memory (MB). See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html#w380aac44c17c17" -} - -variable "worker_name" { - description = "Name of the worker" - - validation { - condition = can(regex("[a-z]+", var.worker_name)) - error_message = "Must be a lowercase non-empty string" - } -} - -variable "cluster_arn" { - type = string - description = "The ECS Cluster ARN" -} - -variable "temporal_host" { - type = string - description = "The hostname of the Temporal cluster to connect to." -} - -variable "temporal_port" { - type = string - default = "7233" - description = "The port of the Temporal cluster to connect to." -} - -variable "ecs_health_check" { - type = list(string) - description = "The ECS Task Definition Health Check command for the image." -} - -variable "env_vars" { - type = list(object({ - name = string, - secret = bool, - value = string - })) - - default = [] - description = "The environment variables and secrets to pass to the container" -} - -variable "desired_count" { - type = number - default = 1 - description = "The number of instances of the task to run" -} diff --git a/infra/terraform/modules/tunnel/main.tf b/infra/terraform/modules/tunnel/main.tf deleted file mode 100644 index 95e75aa409f..00000000000 --- a/infra/terraform/modules/tunnel/main.tf +++ /dev/null @@ -1,32 +0,0 @@ -/** - * # Terraform module: Tunnel - * - * Module responsible for creating the tunnel infrastructure for accessing private resources. - * For resources in private subnets, we need to create a tunnel through a bastion host. - * - * The module supports both SSH and SSM (Systems Manager) tunneling methods. - * SSM is preferred as it's more secure and doesn't require managing SSH keys. - */ - -data "external" "tunnel" { - program = var.use_ssm ? ["${path.module}/ssm_tunnel.sh"] : ["${path.module}/ssh_tunnel.sh"] - query = var.use_ssm ? { - bastion_instance_id = var.bastion_instance_id, - tunnel_target_host = var.tunnel_target_host, - tunnel_target_port = var.tunnel_target_port, - tunnel_max_attempts = var.tunnel_max_attempts, - local_tunnel_port = var.local_tunnel_port, - timeout = var.timeout, - aws_region = var.aws_region, - } : { - ssh_host = var.ssh_host, - ssh_port = var.ssh_port, - ssh_user = var.ssh_user, - ssh_private_key = var.ssh_private_key, - tunnel_target_host = var.tunnel_target_host, - tunnel_target_port = var.tunnel_target_port, - tunnel_max_attempts = var.tunnel_max_attempts, - local_tunnel_port = var.local_tunnel_port, - timeout = var.timeout, - } -} diff --git a/infra/terraform/modules/tunnel/output.tf b/infra/terraform/modules/tunnel/output.tf deleted file mode 100644 index 0cc15f34d80..00000000000 --- a/infra/terraform/modules/tunnel/output.tf +++ /dev/null @@ -1,9 +0,0 @@ -output "host" { - value = data.external.tunnel.result.host - description = "Tunnelled host" -} - -output "port" { - value = data.external.tunnel.result.port - description = "Tunnelled port" -} diff --git a/infra/terraform/modules/tunnel/ssh_tunnel.sh b/infra/terraform/modules/tunnel/ssh_tunnel.sh deleted file mode 100755 index 78d53ede097..00000000000 --- a/infra/terraform/modules/tunnel/ssh_tunnel.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash - -# To debug, set this to 1 and tail the /tmp/tunnel_logs file. -# Make sure `jq` is installed on the system. -TUNNEL_DEBUG=0 - -input="$(< /dev/stdin)" - -# Check if debugging is enabled. The fallback `${TUNNEL_DEBUG:-0}` ensures a default value of 0 -# if `TUNNEL_DEBUG` is unset. A non-zero value enables debugging. -if [ "${TUNNEL_DEBUG:-0}" -ne 0 ]; then - exec 2>/tmp/tunnel_logs - set -x - env >&2 -fi - -SSH_HOST=$(jq -j '.ssh_host' <<< "$input") -SSH_PORT=$(jq -j '.ssh_port' <<< "$input") -SSH_USER=$(jq -j '.ssh_user' <<< "$input") -SSH_PRIVATE_KEY=$(jq -j '.ssh_private_key' <<< "$input") -TUNNEL_TARGET_HOST=$(jq -j '.tunnel_target_host' <<< "$input") -TUNNEL_TARGET_PORT=$(jq -j '.tunnel_target_port' <<< "$input") -TUNNEL_MAX_ATTEMPTS=$(jq -j '.tunnel_max_attempts' <<< "$input") -LOCAL_TUNNEL_PORT=$(jq -j '.local_tunnel_port' <<< "$input") -TIMEOUT=$(jq -j '.timeout' <<< "$input") - -# Check if nc (netcat) is available for tunnel verification -if ! command -v nc >/dev/null 2>&1; then - echo "Error: nc (netcat) command not found. Please install netcat for tunnel verification." >&2 - exit 1 -fi - -# To allow the private key file to be read -TEMP=$(mktemp) -trap 'rm -f "$TEMP"' EXIT SIGINT SIGTERM -echo "$SSH_PRIVATE_KEY" > "$TEMP" -chmod 600 "$TEMP" - -# Clean up any existing tunnels using the same control socket and port -pkill -f "ssh.*-M -S terraform_ssh_tunnel.*-L $LOCAL_TUNNEL_PORT:" 2>/dev/null || true - -# Start SSH tunnel in background -ssh -o ExitOnForwardFailure=yes \ - -o StrictHostKeyChecking=no \ - -o BatchMode=yes \ - -o ServerAliveInterval=30 \ - -o ServerAliveCountMax=3 \ - -i "$TEMP" \ - -f -N \ - -M -S terraform_ssh_tunnel \ - -L "$LOCAL_TUNNEL_PORT:$TUNNEL_TARGET_HOST:$TUNNEL_TARGET_PORT" \ - -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" - -# Verify the tunnel is working -attempt=0 -while [ $attempt -lt $TUNNEL_MAX_ATTEMPTS ]; do - if nc -z 127.0.0.1 "$LOCAL_TUNNEL_PORT" 2>/dev/null; then - echo "{ \"host\": \"127.0.0.1\", \"port\": \"$LOCAL_TUNNEL_PORT\" }" - exit 0 - fi - sleep 1 - attempt=$((attempt + 1)) -done - -# If we get here, tunnel failed -echo "Failed to establish tunnel to $TUNNEL_TARGET_HOST:$TUNNEL_TARGET_PORT via $SSH_HOST after $TUNNEL_MAX_ATTEMPTS attempts" >&2 -ssh -S terraform_ssh_tunnel -O exit "$SSH_USER@$SSH_HOST" 2>/dev/null || true -exit 1 diff --git a/infra/terraform/modules/tunnel/ssm_tunnel.sh b/infra/terraform/modules/tunnel/ssm_tunnel.sh deleted file mode 100755 index 877fa97090c..00000000000 --- a/infra/terraform/modules/tunnel/ssm_tunnel.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env bash - -# SSM-based tunnel script for PostgreSQL access -# Uses AWS Systems Manager Session Manager for secure tunneling - -input="$(< /dev/stdin)" - -# Parse input parameters -BASTION_INSTANCE_ID=$(jq -j '.bastion_instance_id' <<< "$input") -TUNNEL_TARGET_HOST=$(jq -j '.tunnel_target_host' <<< "$input") -TUNNEL_TARGET_PORT=$(jq -j '.tunnel_target_port' <<< "$input") -TUNNEL_MAX_ATTEMPTS=$(jq -j '.tunnel_max_attempts' <<< "$input") -LOCAL_TUNNEL_PORT=$(jq -j '.local_tunnel_port' <<< "$input") -TIMEOUT=$(jq -j '.timeout' <<< "$input") -AWS_REGION=$(jq -j '.aws_region' <<< "$input") - -# Check if aws cli is available -if ! command -v aws >/dev/null 2>&1; then - echo "Error: aws command not found. Please install AWS CLI." >&2 - exit 1 -fi -echo "AWS CLI found: $(command -v aws)" >&2 - -# Check if nc (netcat) is available for tunnel verification -if ! command -v nc >/dev/null 2>&1; then - echo "Error: nc (netcat) command not found. Please install netcat for tunnel verification." >&2 - exit 1 -fi -echo "Netcat found: $(command -v nc)" >&2 - -# Clean up any existing SSM sessions using the same local port -pkill -f "aws ssm start-session.*localPortNumber=$LOCAL_TUNNEL_PORT" 2>/dev/null || true - -# Start SSM port forwarding session in background -echo "=== Starting SSM session ===" >&2 -echo "Target: $BASTION_INSTANCE_ID" >&2 -echo "Tunnel: $TUNNEL_TARGET_HOST:$TUNNEL_TARGET_PORT -> 127.0.0.1:$LOCAL_TUNNEL_PORT" >&2 -echo "Region: $AWS_REGION" >&2 - -aws ssm start-session \ - --target "$BASTION_INSTANCE_ID" \ - --document-name AWS-StartPortForwardingSessionToRemoteHost \ - --parameters "host=$TUNNEL_TARGET_HOST,portNumber=$TUNNEL_TARGET_PORT,localPortNumber=$LOCAL_TUNNEL_PORT" \ - --region "$AWS_REGION" \ - >/tmp/ssm_session_output_$LOCAL_TUNNEL_PORT.log 2>&1 & - -SSM_PID=$! - -# Store the PID for cleanup -echo "$SSM_PID" > "/tmp/ssm_tunnel_pid_$LOCAL_TUNNEL_PORT" - -# Wait for the tunnel to be established with exponential backoff -# SSM port forwarding needs time after session establishment, especially in CI environments -attempt=0 -echo "=== Waiting for SSM tunnel to establish ===" >&2 -while [ $attempt -lt $TUNNEL_MAX_ATTEMPTS ]; do - echo "Attempt $((attempt + 1))/$TUNNEL_MAX_ATTEMPTS: Testing connection to 127.0.0.1:$LOCAL_TUNNEL_PORT" >&2 - if nc -z 127.0.0.1 "$LOCAL_TUNNEL_PORT" 2>/dev/null; then - echo "=== SSM tunnel established successfully ===" >&2 - echo "{ \"host\": \"127.0.0.1\", \"port\": \"$LOCAL_TUNNEL_PORT\", \"ssm_pid\": \"$SSM_PID\" }" - exit 0 - fi - - # Exponential backoff: 1s, 2s, 4s, 8s, then 8s for remaining attempts - # This gives SSM more time on later attempts when AWS might be slower - delay=$((attempt < 3 ? 2 ** attempt : 8)) - [ $delay -eq 0 ] && delay=1 - - echo "Connection not ready, waiting ${delay} seconds..." >&2 - sleep $delay - attempt=$((attempt + 1)) -done - -# If we get here, tunnel failed -echo "=== SSM Session Output ===" >&2 -cat "/tmp/ssm_session_output_$LOCAL_TUNNEL_PORT.log" >&2 2>/dev/null || echo "No session log found" >&2 -echo "=== End SSM Session Output ===" >&2 - -echo "Failed to establish SSM tunnel to $TUNNEL_TARGET_HOST:$TUNNEL_TARGET_PORT via $BASTION_INSTANCE_ID after $TUNNEL_MAX_ATTEMPTS attempts" >&2 -kill $SSM_PID 2>/dev/null || true -rm -f "/tmp/ssm_tunnel_pid_$LOCAL_TUNNEL_PORT" -exit 1 diff --git a/infra/terraform/modules/tunnel/variables.tf b/infra/terraform/modules/tunnel/variables.tf deleted file mode 100644 index feb5c93461f..00000000000 --- a/infra/terraform/modules/tunnel/variables.tf +++ /dev/null @@ -1,69 +0,0 @@ -variable "use_ssm" { - type = bool - description = "Use SSM Session Manager for tunneling instead of SSH" - default = true -} - -variable "bastion_instance_id" { - type = string - description = "The EC2 instance ID of the bastion host (for SSM tunneling)" - default = null -} - -variable "aws_region" { - type = string - description = "AWS region for SSM session" - default = null -} - -variable "ssh_host" { - type = string - description = "The hostname of the machine to SSH through (for SSH tunneling)" - default = null -} - -variable "ssh_port" { - type = number - description = "The port of the machine to SSH through (for SSH tunneling)" - default = 22 -} - -variable "ssh_user" { - type = string - description = "The user of the machine to SSH through (for SSH tunneling)" - default = null -} - -variable "ssh_private_key" { - type = string - description = "The private key of the machine to SSH through (for SSH tunneling)" - default = null -} - -variable "tunnel_target_host" { - type = string - description = "The target host" -} - -variable "tunnel_target_port" { - type = number - description = "Target port number" -} - -variable "tunnel_max_attempts" { - type = number - description = "Maximum number of attempts to verify tunnel connectivity" - default = 10 -} - -variable "timeout" { - type = string - description = "Connection timeout" - default = "30m" -} - -variable "local_tunnel_port" { - type = number - description = "Local port to bind the tunnel to" - default = 45678 -} diff --git a/infra/terraform/modules/variables/main.tf b/infra/terraform/modules/variables/main.tf deleted file mode 100644 index 9944d49d7bc..00000000000 --- a/infra/terraform/modules/variables/main.tf +++ /dev/null @@ -1,21 +0,0 @@ -/** - * # Terraform AWS module: Variables - * - * Module responsible for creating the variables used in the project. - * The module doesn't add any resources, it is primarily used to - * validate/generate variables that are used in other modules. - */ - -data "aws_availability_zones" "region_availability_zones" { - # Only select Availability Zones and not Local Zones - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } - - # Return only AZs by region - filter { - name = "region-name" - values = [var.region] - } -} diff --git a/infra/terraform/modules/variables/output.tf b/infra/terraform/modules/variables/output.tf deleted file mode 100644 index abb32087971..00000000000 --- a/infra/terraform/modules/variables/output.tf +++ /dev/null @@ -1,29 +0,0 @@ -output "env" { - value = var.env -} - -output "region" { - value = var.region -} - -output "region_short" { - value = var.region_short[var.region] -} - -locals { - az_names = data.aws_availability_zones.region_availability_zones.names - # If 'region_az_names' is greater than the actual number of AZs available, default to using all AZs in region - region_az_names = length(local.az_names) <= var.region_az_count ? local.az_names : slice(local.az_names, 0, var.region_az_count) -} - -output "region_az_names" { - value = local.region_az_names -} - -output "prefix" { - value = "h-${var.project}-${var.env}-${var.region_short[var.region]}" -} - -output "param_prefix" { - value = "/h-${var.project}/${var.env}" -} diff --git a/infra/terraform/modules/variables/terraform.tf b/infra/terraform/modules/variables/terraform.tf deleted file mode 100644 index afe80f79891..00000000000 --- a/infra/terraform/modules/variables/terraform.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 4.67" - } - } -} diff --git a/infra/terraform/modules/variables/variables.tf b/infra/terraform/modules/variables/variables.tf deleted file mode 100644 index ce36e46d319..00000000000 --- a/infra/terraform/modules/variables/variables.tf +++ /dev/null @@ -1,58 +0,0 @@ -variable "project" { - type = string - description = "The project name" - validation { - condition = can(regex("[a-z]+", var.project)) - error_message = "Must be a lowercase non-empty string" - } -} - -variable "env" { - type = string - description = "The environment, defaults to the selected workspace." - - validation { - condition = var.env != "default" - error_message = "Please switch to a workspace that reflects the environment you're executing within. Detected 'default'" - } -} - -variable "region" { - type = string - description = "The AWS region" -} - - -variable "region_az_count" { - type = number - description = "Number of availability zones to use for the infrastructure" - - validation { - condition = var.region_az_count >= 2 && var.region_az_count <= 16 - error_message = "Given number of availability zones not supported. Please provide 2 <= AZs <= 16" - } -} - -# Shorter region prefixes used for naming resources -# Source: https://gist.github.com/colinvh/14e4b7fb6b66c29f79d3#schemes -variable "region_short" { - type = map(string) - default = { - us-east-1 = "usea1" - us-east-2 = "usea2" - us-west-1 = "uswe1" - us-west-2 = "uswe2" - us-gov-west-1 = "ugwe2" - ca-central-1 = "cace1" - eu-west-1 = "euwe1" - eu-west-2 = "euwe2" - eu-central-1 = "euce1" - ap-southeast-1 = "apse1" - ap-southeast-2 = "apse2" - ap-south-1 = "apso1" - ap-northeast-1 = "apne1" - ap-northeast-2 = "apne2" - sa-east-1 = "saea1" - cn-north-1 = "cnno1" - } -} diff --git a/infra/terraform/modules/vault_aws_auth/env.sh b/infra/terraform/modules/vault_aws_auth/env.sh deleted file mode 100755 index ce92ae56200..00000000000 --- a/infra/terraform/modules/vault_aws_auth/env.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -cat << EOF -{ - "in_ci": "${CI:-false}" -} -EOF diff --git a/infra/terraform/modules/vault_aws_auth/main.tf b/infra/terraform/modules/vault_aws_auth/main.tf deleted file mode 100644 index 622c871c360..00000000000 --- a/infra/terraform/modules/vault_aws_auth/main.tf +++ /dev/null @@ -1,39 +0,0 @@ -/** - * # Terraform AWS module: Vault AWS Auth - * - * Module responsible for creating AWS credentials through Vault. - * Our infrastructure is set up in a way that allows us to create - * temporary AWS STS credentials, which this module will conditionally - * do depending on whether or not we're in a CI/CD pipeline. - */ - -data "external" "env" { - program = ["${path.module}/env.sh"] -} - -locals { - in_ci = data.external.env.result["in_ci"] -} - -data "vault_aws_access_credentials" "aws_credentials" { - count = local.in_ci ? 0 : 1 - backend = "aws" - region = var.region - role = "${var.env}-deploy" - type = "sts" -} - -output "access_key" { - value = local.in_ci ? null : data.vault_aws_access_credentials.aws_credentials[0].access_key - description = "Either an access_key to a temporary STS user, or null" -} - -output "secret_key" { - value = local.in_ci ? null : data.vault_aws_access_credentials.aws_credentials[0].secret_key - description = "Either a secret_key to a temporary STS user, or null" -} - -output "token" { - value = local.in_ci ? null : data.vault_aws_access_credentials.aws_credentials[0].security_token - description = "Either a security_token to a temporary STS user, or null" -} diff --git a/infra/terraform/modules/vault_aws_auth/terraform.tf b/infra/terraform/modules/vault_aws_auth/terraform.tf deleted file mode 100644 index bd58ca8132c..00000000000 --- a/infra/terraform/modules/vault_aws_auth/terraform.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - vault = { - source = "hashicorp/vault" - version = "~> 3.15" - } - } -} diff --git a/infra/terraform/modules/vault_aws_auth/variables.tf b/infra/terraform/modules/vault_aws_auth/variables.tf deleted file mode 100644 index 9d3e0913ad6..00000000000 --- a/infra/terraform/modules/vault_aws_auth/variables.tf +++ /dev/null @@ -1,9 +0,0 @@ -variable "region" { - type = string - description = "The AWS region" -} - -variable "env" { - type = string - description = "The environment of the deployment" -} diff --git a/infra/terraform/modules/vpc_spoke_peer/README.md b/infra/terraform/modules/vpc_spoke_peer/README.md deleted file mode 100644 index 313c6c32b14..00000000000 --- a/infra/terraform/modules/vpc_spoke_peer/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# VPC Spoke Peer module - -This module contains configuration for setting up a VPC Spoke Peer. This is used to centralize PrivateLink connections to a single VPC (A VPC Hub), to reduce costs. We are currently not using the VPC Hub, but in case it is deployed, this module will be used to create the VPC Spoke Peer connections. - -The module configures Route53 Resolver rules to allow DNS resolution between the VPC Spoke and the VPC Hub through a VPC Peering Connection. - -Example usage of the module: - -```hcl -data "aws_vpc" "spoke" { - id = "..." -} - -module "spoke" { - source = "../modules/vpc_spoke_peer" - region = var.region - env = var.env - vpc_id = data.aws_vpc.spoke.id - # This should be the private subnet's route table ID. - # If the rtpriv is the main route table of the VPC, you can use the `main_route_table_id` attribute. - rtpriv_id = "..." - # The CIDR the hub was created with - hub_cidr = "10.10.0.0/16" - # This CIDR and the Hub CIDR must not overlap. Other spoke CIDRs must not overlap either. - spoke_cidr = aws_vpc.spoke.cidr_block -} -``` - -The spoke VPC ID doesn't need to be retrieved from the data source, if the containing TF module contains the VPC as a resource, it can be used instead. diff --git a/infra/terraform/modules/vpc_spoke_peer/main.tf b/infra/terraform/modules/vpc_spoke_peer/main.tf deleted file mode 100644 index 0655c736672..00000000000 --- a/infra/terraform/modules/vpc_spoke_peer/main.tf +++ /dev/null @@ -1,60 +0,0 @@ -/** - * # Terraform AWS module: VPC Spoke Peering to VPC Hub - * - * Module responsible for allowing a VPC Spoke (the consumer of this module) - * to peer to a VPC Hub in order to centralize PrivateLink networking. - */ - -module "endpoints" { - source = "../privatelink_endpoints" - region = var.region -} - -locals { - endpoints = module.endpoints.endpoints -} - -data "aws_route53_zone" "endpoints" { - for_each = local.endpoints - name = each.value.phz_name - private_zone = true - tags = { Group = "vpc-hub" } -} - -resource "aws_route53_zone_association" "main_vpc_assoc" { - for_each = data.aws_route53_zone.endpoints - zone_id = each.value.zone_id - vpc_id = var.vpc_id -} - -data "aws_vpc" "vpc_hub" { - filter { - name = "tag:Group" - values = ["vpc-hub"] - } -} - -data "aws_caller_identity" "current" {} - -resource "aws_vpc_peering_connection" "hub_peering" { - auto_accept = true - peer_owner_id = data.aws_caller_identity.current.account_id - peer_vpc_id = data.aws_vpc.vpc_hub.id - vpc_id = var.vpc_id -} - -resource "aws_route" "peer" { - route_table_id = var.rtpriv_id - destination_cidr_block = var.hub_cidr - vpc_peering_connection_id = aws_vpc_peering_connection.hub_peering.id -} - -data "aws_route_table" "rthub" { - vpc_id = data.aws_vpc.vpc_hub.id -} - -resource "aws_route" "hub" { - route_table_id = data.aws_route_table.rthub.id - destination_cidr_block = var.spoke_cidr - vpc_peering_connection_id = aws_vpc_peering_connection.hub_peering.id -} diff --git a/infra/terraform/modules/vpc_spoke_peer/terraform.tf b/infra/terraform/modules/vpc_spoke_peer/terraform.tf deleted file mode 100644 index afe80f79891..00000000000 --- a/infra/terraform/modules/vpc_spoke_peer/terraform.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 4.67" - } - } -} diff --git a/infra/terraform/modules/vpc_spoke_peer/variables.tf b/infra/terraform/modules/vpc_spoke_peer/variables.tf deleted file mode 100644 index 54dd061d2da..00000000000 --- a/infra/terraform/modules/vpc_spoke_peer/variables.tf +++ /dev/null @@ -1,29 +0,0 @@ -variable "region" { - type = string - description = "The AWS region" -} - -variable "env" { - type = string - description = "The environment of the deployment" -} - -variable "vpc_id" { - type = string - description = "The VPC ID of the VPC Spoke to connect to the Hub" -} - -variable "hub_cidr" { - type = string - description = "CIDR of the VPC Hub" -} - -variable "spoke_cidr" { - type = string - description = "CIDR of the VPC Spoke to peer to the Hub" -} - -variable "rtpriv_id" { - type = string - description = "Private route table ID" -} diff --git a/infra/terraform/playbooks/citus.md b/infra/terraform/playbooks/citus.md deleted file mode 100644 index 51874baeb8f..00000000000 --- a/infra/terraform/playbooks/citus.md +++ /dev/null @@ -1,93 +0,0 @@ -# Citus - -This is a playbook for interacting with a self-hosted, single-node Citus cluster on EC2. We're not currently running such a cluster, but if we were to, these are the steps we would have to take. - -> **Warning** -> This playbook is out of date, and assumes that you have the scripts for managing Terraform which are obsolete. It is not recommended to use this playbook until it is updated. - -## Open an SSH session - -1. Get the SSH private key for the instance on 1Password. -2. Save the key to `~/.ssh/hadmin-citus-ec2` -3. Open an SSH session: - -```console -$ ssh -i ~/.ssh/hadmin-citus-ec2 hadmin@$(h-tfinfo --deployment citus --output instance_ip) -.. -``` - -## Pull the Citus Docker image - -1. List the images stored in the deployment's ECR repository: - -```console -$ h-list-images --service citus -.. -``` - -1. [Open an SSH session to the EC2 instance](#open-an-ssh-session). -2. Set the image tag you wish to deploy from step 1: - -```console -$ image_tag=$IMAGE_TAG -.. -``` - -1. Log-in to Docker: - -```console -$ aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REGISTRY_URL -.. -``` - -1. Pull an image with the tag `$IMAGE_TAG`: - -```console -$ docker pull $ECR_REPO_URL:$IMAGE_TAG -.. -``` - -## Start a new database - -1. Get the Citus 'superuser' password from 1Password. -2. [Open an SSH session to the EC2 instance](#open-an-ssh-session). -3. [Pull the Citus Docker image](#pull-the-citus-docker-image) -4. Get the Citus 'superuser' password from 1Password and set it as a - variable: - -```console -$ superuser_password=$PASSWORD -.. -``` - -1. If the database container is already running, stop it (`docker ps`, `docker stop`). -2. Run a Docker container: - -```console -$ docker run --rm \ - -p 5432:5432 \ - -e POSTGRES_PASSWORD=$superuser_password \ - -e POSTGRES_USER=superuser \ - -e POSTGRES_DB=postgres \ - -e PGDATA=/var/lib/postgresql/data/pgdata \ - -e POSTGRES_INITDB_ARGS="--auth-host=scram-sha-256" \ - -v /data:/var/lib/postgresql/data/pgdata \ - $ECR_REPO_URL:$image_tag \ - -c "config_file=/etc/postgresql/postgresql.conf" \ - -c "hba_file=/etc/postgresql/pg_hba.conf" -.. -``` - -## Connect to the database using psql - -1. Get the password for the desired user from 1Password. -2. Connect with user `$USER`: - -```console -$ psql -h $(h-tfinfo --deployment citus --output instance_ip) -U 5432 -d postgres $USER -p -.. -``` - -## Running the schema migration script - -See [root README](../README.md#how-do-i-migrate-the-database-after-it-has-been-deployed) for a detailed description. diff --git a/infra/terraform/playbooks/db_recovery.md b/infra/terraform/playbooks/db_recovery.md deleted file mode 100644 index 65691c70164..00000000000 --- a/infra/terraform/playbooks/db_recovery.md +++ /dev/null @@ -1,34 +0,0 @@ -# HASH DB recovery playbook - -Something went terribly wrong with the database, here’s how to recover. - -## Identify snapshot to restore - -For the production DB located in `us-east-1` see the [snapshots](https://us-east-1.console.aws.amazon.com/rds/home?region=us-east-1#snapshots-list:tab=automated). Identify the snapshot to restore from, usually the one from the current morning or the day before if the incident happened. -The snapshots for HASH would be prefixed with `rds:h-hash-prod-usea1-pg-` - -## Restore database - -In the resource definition for the RDS instance you can provide a `snapshot-identifier` to restore from. In the [`../hash/postgres/postgres.tf`](../hash/postgres/postgres.tf) file, provide the snapshot name identified in the previous step and run through the Terraform apply process. You may have to add a `create_before_destroy` lifecycle block to the resource to force Terraform to recreate the database, which would work best if you provide the resource a different name. - -```diff -resource "aws_db_instance" "postgres" { - # ... -+ identifier = "${var.prefix}-pgYYMMDD" -+ snapshot_identifier = "rds:h-hash-prod-usea1-pg-YYYY-MM-DD-HH-mm" -+ lifecycle { -+ create_before_destroy = true -+ } -} -``` - -The `apply` step will take a while, as the database is being recreated in its entirety from the snapshot. You can check the status of database by running this AWS CLI command: - -```console -$ aws rds wait db-instance-available --db-instance-identifier $(h-hash-prod-usea1-pgYYMMDD) -.. -``` - -This command will block the current terminal until the DB instance has the `available` status. - -At the end of the `apply` process, the ECS cluster should be automatically updated to point to the new, restored database. diff --git a/infra/terraform/vpc_hub/README.md b/infra/terraform/vpc_hub/README.md deleted file mode 100644 index 723915f575e..00000000000 --- a/infra/terraform/vpc_hub/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# VPC Hub - -This Terraform Module creates a VPC Hub for VPC peering connection to VPC Spokes. The goal of this VPC is to centralize VPC PrivateLink connections to AWS services so we don't have to create VPC PrivateLink connections in each VPC Spoke for all services we use. This will allow us to only pay for the VPC Interface Endpoints in the VPC Hub once. - -A VPC Spoke is simply just another VPC which wants to access AWS services without going through the public internet. - -```mermaid -graph LR - S[AWS Service] - H[VPC Hub] - V1[VPC Spoke X] - V2[VPC Spoke Y] - - H -- PrivateLink <--> S - V1 -- VPC Peering Connection <--> H - V2 <--> H - -``` - -## Considerations for using the VPC Hub - -When a VPC Spoke is to be connected to the Hub, it's essential that theres a non-ambiguous network route to the Spoke. Essentially this means our infrastructure should have unique CIDR blocks for each environment in every project. If this is not the case, the VPC Hub will not know which Spoke to route traffic back to. I suggest reading this [AWS documentation site](https://docs.aws.amazon.com/vpc/latest/peering/peering-configurations-partial-access.html#peering-incorrect-response-routing) to understand why this is the case. - -If we were to use this VPC Hub, we have to make an active decision to use unique CIDR blocks for each environment in every project in AWS. A common CIDR delegation scheme such as the following would be beneficial to stick to: - -```text -10.0.0.0/14 ~ [Project Root] ~ e.g. 10.0.0.0 - 10.3.255.255 - This is a logical part of the network and never actually allocated in AWS. - -10.0.0.0/16 ~ [Environment] ~ e.g. 10.0.0.0 - 10.0.255.255 - Each VPC gets a /16 allocation to house each environment for each project. - We make it possible for each project to have 4 environments, - which allows up to 64 projects in total in the project's "root network". - AWS has a max VPC count of 10, so this is "overprovisioned" to give a lot of - room for expansion if AWS were to increase this limitation. - -10.0.0.0/17 ~ [Network tier] ~ e.g. 10.0.0.0 - 10.0.127.255 - The main network of the project environment. An environment has a private - and public subnet tier. - - The first /17 can be for the private subnet tier. and - - the second /17 for the public subnet tier. - - There's should be a network tier for each Availability Zone used in the - region. For example for a 2-AZ VPC setup would have 4 subnets, 2 for private - and 2 for public subnet tiers. - -10.0.0.0/24 ~ [Subnet] ~ e.g. 10.0.0.0 - 10.0.0.255 - We allow 256 hosts per subnet. - Modification of the delegation of network tiers can be changed (i.e. to /19) - to allow for larger subnets, but less Availability Zones if required. -``` - -## Usage - -You'll be able to use the VPC Hub by simply creating the resources with Terraform: - -```console -$ terraform init -.. -$ terraform workspace new prod -.. -$ terraform apply -.. -``` - -VPC Spokes must import the [`vpc_spoke_peer`](../modules/vpc_spoke_peer/) module and configure the correct CIDRs. The Hub CIDR is configured when applying this Hub Terraform module and should be part of the logical network delegation. - -## Resources used for the setup - -- https://github.com/aws-samples/hub-and-spoke-with-shared-services-vpc-terraform -- https://aws.amazon.com/blogs/networking-and-content-delivery/centralize-access-using-vpc-interface-endpoints/ -- https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-routing.html -- https://docs.aws.amazon.com/vpc/latest/peering/peering-configurations-partial-access.html#peering-incorrect-response-routing diff --git a/infra/terraform/vpc_hub/main.tf b/infra/terraform/vpc_hub/main.tf deleted file mode 100644 index 3e6fccf945c..00000000000 --- a/infra/terraform/vpc_hub/main.tf +++ /dev/null @@ -1,239 +0,0 @@ -module "variables" { - source = "../modules/variables" - env = terraform.workspace - region = var.region - region_az_count = var.region_az_count - project = "vpchub" -} - -locals { - env = module.variables.env - region = module.variables.region - prefix = module.variables.prefix - param_prefix = module.variables.param_prefix - region_az_names = module.variables.region_az_names -} - -resource "aws_vpc" "main" { - cidr_block = var.vpc_hub_cidr - enable_dns_support = true - enable_dns_hostnames = true - tags = { Name = "${local.prefix}-vpc", Group = "vpc-hub" } -} - -/* Disabled as we don't want to include flow logs in the VPC Hub for now. -# Flow logs in VPC -resource "aws_flow_log" "flow_log" { - tags = { Name = "${local.prefix}-flowvpc" } - - iam_role_arn = aws_iam_role.flow_log.arn - log_destination = aws_cloudwatch_log_group.flow_log.arn - traffic_type = "ALL" - vpc_id = aws_vpc.main.id -} - -resource "aws_cloudwatch_log_group" "flow_log" { - name = "${local.prefix}-flowlogvpc" -} - -resource "aws_iam_role" "flow_log" { - name = "${local.prefix}-flowlogrole" - - assume_role_policy = < v } - - zone_id = aws_route53_zone.private_hosted_zone[each.value.endpoint].id - name = each.value.alias - type = "A" - - alias { - name = aws_vpc_endpoint.endpoint[each.value.endpoint].dns_entry[0].dns_name - zone_id = aws_vpc_endpoint.endpoint[each.value.endpoint].dns_entry[0].hosted_zone_id - evaluate_target_health = true - } - - # ignore changes to alias. AWS interprets the wildcard "*" as ASCII "\052", which causes - # Terraform to think the value has changed when it hasn't. - lifecycle { - ignore_changes = [alias["name"]] - } -} diff --git a/infra/terraform/vpc_hub/prod-usea1.tfvars b/infra/terraform/vpc_hub/prod-usea1.tfvars deleted file mode 100644 index 4c0d526ca05..00000000000 --- a/infra/terraform/vpc_hub/prod-usea1.tfvars +++ /dev/null @@ -1,3 +0,0 @@ -region = "us-east-1" -region_az_count = 2 -vpc_hub_cidr = "10.0.0.0/16" diff --git a/infra/terraform/vpc_hub/terraform.tf b/infra/terraform/vpc_hub/terraform.tf deleted file mode 100644 index 271e4297871..00000000000 --- a/infra/terraform/vpc_hub/terraform.tf +++ /dev/null @@ -1,22 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 4.67" - } - vault = { - source = "hashicorp/vault" - version = "~> 3.15" - } - } - - required_version = ">= 1.2" - - backend "s3" { - bucket = "hash-terraform-state-s3-backend" - workspace_key_prefix = "vpchub" - key = "state" - region = "us-east-1" - encrypt = true - } -} diff --git a/infra/terraform/vpc_hub/variables.tf b/infra/terraform/vpc_hub/variables.tf deleted file mode 100644 index dfc6cb7f622..00000000000 --- a/infra/terraform/vpc_hub/variables.tf +++ /dev/null @@ -1,14 +0,0 @@ -variable "region" { - type = string - description = "The AWS region" -} - -variable "region_az_count" { - type = number - description = "Number of availability zones to use for the infrastructure" -} - -variable "vpc_hub_cidr" { - type = number - description = "The CIDR block of the VPC Hub" -}