CloudFormationForm.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import React, { useEffect, useState, useContext, useMemo } from "react";
  2. import styled from "styled-components";
  3. import { v4 as uuidv4 } from 'uuid';
  4. import api from "shared/api";
  5. import aws from "assets/aws.png";
  6. import { Context } from "shared/Context";
  7. import Text from "./porter/Text";
  8. import Spacer from "./porter/Spacer";
  9. import InputRow from "./form-components/InputRow";
  10. import SaveButton from "./SaveButton";
  11. import Fieldset from "./porter/Fieldset";
  12. import Input from "./porter/Input";
  13. import Button from "./porter/Button";
  14. import DocsHelper from "./DocsHelper";
  15. type Props = {
  16. goBack: () => void;
  17. AWSAccountID: string;
  18. setAWSAccountID: (id: string) => void;
  19. proceed: (id: string) => void;
  20. };
  21. const CloudFormationForm: React.FC<Props> = ({
  22. goBack,
  23. proceed,
  24. AWSAccountID,
  25. setAWSAccountID
  26. }) => {
  27. const [grantPermissionsError, setGrantPermissionsError] = useState("");
  28. const [roleStatus, setRoleStatus] = useState("");
  29. const { currentProject } = useContext(Context);
  30. const getExternalId = () => {
  31. let externalId = localStorage.getItem(AWSAccountID)
  32. console.log(externalId)
  33. if (!externalId) {
  34. externalId = uuidv4()
  35. localStorage.setItem(AWSAccountID, externalId);
  36. }
  37. return externalId
  38. }
  39. const checkIfRoleExists = () => {
  40. let externalId = getExternalId();
  41. let targetARN = `arn:aws:iam::${AWSAccountID}:role/porter-role`
  42. setRoleStatus("loading");
  43. api
  44. .preflightCheckAWSRole(
  45. "<token>",
  46. {
  47. target_arn: targetARN,
  48. external_id: externalId,
  49. },
  50. {
  51. id: currentProject.id,
  52. }
  53. )
  54. .then(({ data }) => {
  55. setRoleStatus("successful");
  56. proceed(targetARN);
  57. })
  58. .catch((err) => {
  59. console.log(err);
  60. setRoleStatus("Role does not exist in the AWS account.");
  61. });
  62. };
  63. const directToCloudFormation = () => {
  64. let externalId = getExternalId();
  65. window.open(
  66. `https://console.aws.amazon.com/cloudformation/home?
  67. #/stacks/create/review?templateURL=https://porter-role.s3.us-east-2.amazonaws.com/cloudformation-policy.json&stackName=PorterRole&param_ExternalIdParameter=${externalId}`
  68. )
  69. }
  70. const renderContent = () => {
  71. return (
  72. <>
  73. <Spacer y={1} />
  74. <Fieldset>
  75. <Text size={16} weight={500}>
  76. Log in to AWS and "Create stack"
  77. </Text>
  78. <Spacer height="15px" />
  79. <Text color="helper">
  80. Provide your AWS account ID to log in and grant Porter access to AWS. You will need to select "Create stack" after being redirected to the AWS console below.
  81. </Text>
  82. <Spacer y={1} />
  83. <Input
  84. label={
  85. <Flex>
  86. 👤 AWS account ID
  87. <i
  88. className="material-icons"
  89. onClick={() => {
  90. window.open("https://console.aws.amazon.com/billing/home?region=us-east-1#/account", "_blank")
  91. }}
  92. >
  93. help_outline
  94. </i>
  95. </Flex>
  96. }
  97. value={AWSAccountID}
  98. setValue={(e) => {
  99. setGrantPermissionsError("");
  100. setAWSAccountID(e);
  101. }}
  102. placeholder="ex: 915037676314"
  103. />
  104. <Spacer y={1} />
  105. <Button
  106. onClick={() => {
  107. if (AWSAccountID.length === 12) {
  108. directToCloudFormation();
  109. } else {
  110. setGrantPermissionsError("Invalid AWS account ID");
  111. }
  112. }}
  113. status={grantPermissionsError}
  114. errorText={grantPermissionsError}
  115. color="#1E2631"
  116. withBorder
  117. >
  118. <ButtonImg src={aws} /> Grant permissions
  119. </Button>
  120. </Fieldset>
  121. <Spacer y={1} />
  122. <SaveButton
  123. onClick={() => {
  124. checkIfRoleExists()
  125. }}
  126. status={roleStatus}
  127. statusPosition="right"
  128. clearPosition
  129. text="Continue"
  130. />
  131. </>
  132. );
  133. }
  134. return (
  135. <>
  136. <Text size={16} weight={500}>
  137. <BackButton width="140px" onClick={goBack}>
  138. <i className="material-icons">first_page</i>
  139. Select cloud
  140. </BackButton>
  141. <Spacer x={1} inline />
  142. <Img src={aws} />
  143. Grant AWS permissions
  144. </Text>
  145. <Spacer y={1} />
  146. <Text color="helper">
  147. Grant Porter permissions to create infrastructure in your AWS account.
  148. </Text>
  149. {renderContent()}
  150. </>
  151. );
  152. };
  153. export default CloudFormationForm;
  154. const Flex = styled.div`
  155. display: flex;
  156. ailgn-items: center;
  157. > i {
  158. margin-left: 10px;
  159. font-size: 16px;
  160. cursor: pointer;
  161. }
  162. `;
  163. const ButtonImg = styled.img`
  164. height: 14px;
  165. margin-right: 12px;
  166. `;
  167. const Img = styled.img`
  168. height: 18px;
  169. margin-right: 15px;
  170. `;
  171. const BackButton = styled.div`
  172. display: flex;
  173. align-items: center;
  174. justify-content: space-between;
  175. cursor: pointer;
  176. font-size: 13px;
  177. height: 35px;
  178. padding: 5px 13px;
  179. padding-right: 15px;
  180. border: 1px solid #ffffff55;
  181. border-radius: 100px;
  182. width: ${(props: { width: string }) => props.width};
  183. color: white;
  184. background: #ffffff11;
  185. :hover {
  186. background: #ffffff22;
  187. }
  188. > i {
  189. color: white;
  190. font-size: 16px;
  191. margin-right: 6px;
  192. margin-left: -2px;
  193. }
  194. `;
  195. const StyledForm = styled.div`
  196. position: relative;
  197. padding: 15px 30px 25px;
  198. border-radius: 5px;
  199. background: #26292e;
  200. border: 1px solid #494b4f;
  201. font-size: 13px;
  202. margin-bottom: 30px;
  203. `;