Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(infra): fix the remaining terraform constant changes #331

Merged
merged 15 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 63 additions & 4 deletions .circleci/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ commands:
- run:
name: Install and setup node
command: |
printenv
nvm install
nvm use
npm install -g pnpm
Expand All @@ -170,8 +169,6 @@ commands:
echo 'export SECRET_VALUE="$(aws secretsmanager get-secret-value --secret-id CodeBuild/Default --query SecretString --output text)"' >> "$BASH_ENV"
echo 'export TERRAFORM_TOKEN="$(echo $SECRET_VALUE | jq -r '.terraform_token')"' >> "$BASH_ENV"
echo 'export PAGERDUTY_TOKEN="$(echo $SECRET_VALUE | jq -r '.mozilla_pagerduty_token')"' >> "$BASH_ENV"
echo 'export GITHUB_ACCESS_TOKEN="$(echo $SECRET_VALUE | jq -r '.github_access_token')"' >> "$BASH_ENV"
echo 'export GITHUB_TOKEN="$(echo $SECRET_VALUE | jq -r '.github_access_token')"' >> "$BASH_ENV"
- run:
name: Save off terraform token
command: |
Expand All @@ -181,6 +178,60 @@ commands:
rc="${rc}}"
echo "$rc" > ~/.terraformrc

setup_github_bot:
steps:
- run:
name: Get Github Bot Token
command: |
app_id=$GITHUB_APP_ID
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly I couldn't find an orb to do this for us. But this script will take the Github Bot secrets and convert it to a regular access token like we already use, valid for 10 min.

i wish CircleCI let us ask for a github access token like Github Actions do, then this wouldnt be necessary 🤷‍♂️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, we can make this an orb of our own later?

pem="$(echo "$GITHUB_APP_PRIVATE_KEY" | base64 -d)"
installation_id=$GITHUB_INSTALLATION_APP_ID

now=$(date +%s)
iat=$((${now} - 60)) # Issues 60 seconds in the past
exp=$((${now} + 600)) # Expires 15 minutes in the future

b64enc() { openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n'; }

header_json='{
"typ":"JWT",
"alg":"RS256"
}'
# Header encode
header=$( echo -n "${header_json}" | b64enc )

payload_json='{
"iat":'"${iat}"',
"exp":'"${exp}"',
"iss":'"${app_id}"'
}'
# Payload encode
payload=$( echo -n "${payload_json}" | b64enc )

# Signature
header_payload="${header}"."${payload}"
signature=$(
openssl dgst -sha256 -sign <(echo -n "${pem}") \
<(echo -n "${header_payload}") | b64enc
)

# Create JWT
JWT="${header_payload}"."${signature}"

# Make a POST request to GitHub API to get the installation token
response=$(curl -s -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: Bearer $JWT" \
-d "{}" \
"https://api.github.com/app/installations/$installation_id/access_tokens")

# Extract the token from the response
token=$(echo "$response" | jq -r '.token')
echo "export GITHUB_TOKEN=$token" >> $BASH_ENV
echo "export GH_TOKEN=$token" >> $BASH_ENV
echo "export GITHUB_ACCESS_TOKEN=$token" >> $BASH_ENV


jobs:

infrastructure:
Expand Down Expand Up @@ -225,6 +276,7 @@ jobs:
mv tfcmt /home/circleci/tfcmt
chmod a+x /home/circleci/tfcmt
- install_codebuild_secrets
- setup_github_bot
- unless:
# Only compile if we do not use raw hcl
condition: <<parameters.uses_raw_hcl>>
Expand Down Expand Up @@ -278,9 +330,12 @@ jobs:
steps:
- run:
name: Terraform apply
# Re-add this when tfcmt supports ignoring no change applies
# https://github.com/suzuki-shunsuke/tfcmt/issues/1184
# /home/circleci/tfcmt --var target:<< parameters.scope >><<#parameters.dev>>-dev<</parameters.dev>> apply -- terraform apply -auto-approve -lock-timeout=10m
command: |
cd << parameters.stack-output-path >>
/home/circleci/tfcmt --var target:<< parameters.scope >><<#parameters.dev>>-dev<</parameters.dev>> apply -- terraform apply -auto-approve -lock-timeout=10m
terraform apply -auto-approve -lock-timeout=10m
mkdir -p /tmp/workspace
echo "$(terraform output -json)" > /tmp/workspace/tf_output.json
# Persist TF_OUTPUT using workspace
Expand Down Expand Up @@ -431,6 +486,10 @@ jobs:

aws lambda wait function-updated --function-name '<< parameters.function-name >>'

NEW_ENVVARS=$(aws lambda get-function-configuration --function-name '<< parameters.function-name >>' --query "Environment.Variables | merge(@, \`{\"GIT_SHA\":\"$CIRCLE_SHA1\"}\`)")
aws lambda update-function-configuration --function-name '<< parameters.function-name >>' --environment "{ \"Variables\": $NEW_ENVVARS }"
aws lambda wait function-updated --function-name '<< parameters.function-name >>'

versionId=$(aws lambda publish-version \
--function-name '<< parameters.function-name >>' | jq -r .Version)

Expand Down
137 changes: 137 additions & 0 deletions .tfcmt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Default conifg for us to modify from https://suzuki-shunsuke.github.io/tfcmt/config#default-configuration
embedded_var_names: []
templates:
plan_title: "## {{if eq .ExitCode 1}}:x: {{end}}Plan Result{{if .Vars.target}} ({{.Vars.target}}){{end}}"
apply_title: "## :{{if eq .ExitCode 0}}white_check_mark{{else}}x{{end}}: Apply Result{{if .Vars.target}} ({{.Vars.target}}){{end}}"

result: "{{if .Result}}<pre><code>{{ .Result }}</code></pre>{{end}}"
updated_resources: |
{{if .CreatedResources}}
* Create
{{- range .CreatedResources}}
* {{.}}
{{- end}}{{end}}{{if .UpdatedResources}}
* Update
{{- range .UpdatedResources}}
* {{.}}
{{- end}}{{end}}{{if .DeletedResources}}
* Delete
{{- range .DeletedResources}}
* {{.}}
{{- end}}{{end}}{{if .ReplacedResources}}
* Replace
{{- range .ReplacedResources}}
* {{.}}
{{- end}}{{end}}{{if .ImportedResources}}
* Import
{{- range .ImportedResources}}
* {{.}}
{{- end}}{{end}}{{if .MovedResources}}
* Move
{{- range .MovedResources}}
* {{.Before}} => {{.After}}
{{- end}}{{end}}
deletion_warning: |
{{if .HasDestroy}}
### :warning: Resource Deletion will happen :warning:
This plan contains resource delete operation. Please check the plan result very carefully!
{{end}}
changed_result: |
{{if .ChangedResult}}
<details><summary>Change Result (Click me)</summary>
{{wrapCode .ChangedResult}}
</details>
{{end}}
change_outside_terraform: |
{{if .ChangeOutsideTerraform}}
<details><summary>:information_source: Objects have changed outside of Terraform</summary>

_This feature was introduced from [Terraform v0.15.4](https://github.com/hashicorp/terraform/releases/tag/v0.15.4)._
{{wrapCode .ChangeOutsideTerraform}}
</details>
{{end}}
warning: |
{{if .Warning}}
## :warning: Warnings :warning:
{{wrapCode .Warning}}
{{end}}
error_messages: |
{{if .ErrorMessages}}
## :warning: Errors
{{range .ErrorMessages}}
* {{. -}}
{{- end}}{{end}}
guide_apply_failure: ""
guide_apply_parse_error: ""
terraform:
plan:
disable_label: false
template: |
{{template "plan_title" .}}

{{if .Link}}[CI link]({{.Link}}){{end}}

{{template "deletion_warning" .}}
{{template "result" .}}
{{template "updated_resources" .}}

{{template "changed_result" .}}
{{template "change_outside_terraform" .}}
{{template "warning" .}}
{{template "error_messages" .}}
when_add_or_update_only:
label: "{{if .Vars.target}}{{.Vars.target}}/{{end}}add-or-update"
label_color: 1d76db # blue
# disable_label: false
when_destroy:
label: "{{if .Vars.target}}{{.Vars.target}}/{{end}}destroy"
label_color: d93f0b # red
# disable_label: false
when_no_changes:
label: "{{if .Vars.target}}{{.Vars.target}}/{{end}}no-changes"
label_color: 0e8a16 # green
disable_label: true
disable_comment: true
when_plan_error:
label:
label_color:
# disable_label: false
when_parse_error:
template: |
{{template "plan_title" .}}

{{if .Link}}[CI link]({{.Link}}){{end}}

It failed to parse the result.

<details><summary>Details (Click me)</summary>
{{wrapCode .CombinedOutput}}
</details>
apply:
template: |
{{template "apply_title" .}}

{{if .Link}}[CI link]({{.Link}}){{end}}

{{if ne .ExitCode 0}}{{template "guide_apply_failure" .}}{{end}}

{{template "result" .}}

<details><summary>Details (Click me)</summary>
{{wrapCode .CombinedOutput}}
</details>
{{template "error_messages" .}}
when_parse_error:
template: |
{{template "apply_title" .}}

{{if .Link}}[CI link]({{.Link}}){{end}}

{{template "guide_apply_parse_error" .}}

It failed to parse the result.

<details><summary>Details (Click me)</summary>
{{wrapCode .CombinedOutput}}
</details>
# When https://github.com/suzuki-shunsuke/tfcmt/issues/1184 is done, we can make it so tfcmt doesn't comment on a no-op apply!
14 changes: 3 additions & 11 deletions infrastructure/account-data-deleter/src/lambda/base/sqsLambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class SqsLambda extends Construct {
) {
super(scope, name.toLowerCase());

const { sentryDsn, gitSha } = this.getEnvVariableValues();
const { sentryDsn } = this.getEnvVariableValues();

this.lambda = new PocketSQSWithLambdaTarget(this, name.toLowerCase(), {
name: `${stackConfig.prefix}-${name}`,
Expand All @@ -52,9 +52,9 @@ export class SqsLambda extends Construct {
stackConfig.environment === 'Prod' ? 'production' : 'development',
NODE_ENV:
stackConfig.environment === 'Prod' ? 'production' : 'development',
GIT_SHA: gitSha,
SENTRY_DSN: sentryDsn,
},
ignoreEnvironmentVars: ['GIT_SHA'],
handler: 'index.handler',
reservedConcurrencyLimit: config.reservedConcurrencyLimit,
runtime: LAMBDA_RUNTIMES.NODEJS20,
Expand All @@ -77,14 +77,6 @@ export class SqsLambda extends Construct {
},
);

const serviceHash = new dataAwsSsmParameter.DataAwsSsmParameter(
this,
'service-hash',
{
name: `${stackConfig.circleCIPrefix}/SERVICE_HASH`,
},
);

return { sentryDsn: sentryDsn.value, gitSha: serviceHash.value };
return { sentryDsn: sentryDsn.value };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class BatchDeleteLambdaResources extends Construct {
) {
super(scope, name.toLowerCase());

const { sentryDsn, gitSha } = this.getEnvVariableValues();
const { sentryDsn } = this.getEnvVariableValues();

// Tables for retrieving historically deleted user ID lists
// and keeping track of which ones have been processed
Expand All @@ -55,13 +55,13 @@ export class BatchDeleteLambdaResources extends Construct {
timeout: 120,
environment: {
SENTRY_DSN: sentryDsn,
GIT_SHA: gitSha,
ENVIRONMENT:
stackConfig.environment === 'Prod' ? 'production' : 'development',
NODE_ENV:
stackConfig.environment === 'Prod' ? 'production' : 'development',
USER_API: `https://${stackConfig.userApiDomain}`,
},
ignoreEnvironmentVars: ['GIT_SHA'],
vpcConfig: {
securityGroupIds: this.vpc.defaultSecurityGroups.ids,
subnetIds: this.vpc.privateSubnetIds,
Expand Down Expand Up @@ -119,24 +119,28 @@ export class BatchDeleteLambdaResources extends Construct {
);

//permission for scheduledEvent to invoke the batchDeleteLambda
new lambdaPermission.LambdaPermission(this, `${config.prefix}-batchLambda-permission`, {
principal: 'events.amazonaws.com',
action: 'lambda:InvokeFunction',
functionName: this.batchDeleteLambda.lambda.defaultLambda.arn,
sourceArn: scheduledEvent.rule.arn,
});
new lambdaPermission.LambdaPermission(
this,
`${config.prefix}-batchLambda-permission`,
{
principal: 'events.amazonaws.com',
action: 'lambda:InvokeFunction',
functionName: this.batchDeleteLambda.lambda.defaultLambda.arn,
sourceArn: scheduledEvent.rule.arn,
},
);
}

private getEnvVariableValues() {
const sentryDsn = new dataAwsSsmParameter.DataAwsSsmParameter(this, 'sentry-dsn', {
name: `/${stackConfig.name}/${stackConfig.environment}/SENTRY_DSN`,
});

const serviceHash = new dataAwsSsmParameter.DataAwsSsmParameter(this, 'service-hash', {
name: `${stackConfig.circleCIPrefix}/SERVICE_HASH`,
});
const sentryDsn = new dataAwsSsmParameter.DataAwsSsmParameter(
this,
'sentry-dsn',
{
name: `/${stackConfig.name}/${stackConfig.environment}/SENTRY_DSN`,
},
);

return { sentryDsn: sentryDsn.value, gitSha: serviceHash.value };
return { sentryDsn: sentryDsn.value };
}

/**
Expand Down Expand Up @@ -206,23 +210,27 @@ export class BatchDeleteLambdaResources extends Construct {
actions: string[],
) {
const resources = dynamoTables.map((_) => _.arn);
const policy = new iamPolicy.IamPolicy(this, `${name}-lambda-dynamo-policy`, {
name: `${this.name}-${name}-DynamoLambdaPolicy`,
policy: new dataAwsIamPolicyDocument.DataAwsIamPolicyDocument(
this,
`${name}-lambda-dynamo-policy-doc`,
{
statement: [
{
effect: 'Allow',
actions,
resources,
},
],
},
).json,
dependsOn: [lambdaExecutionRole],
});
const policy = new iamPolicy.IamPolicy(
this,
`${name}-lambda-dynamo-policy`,
{
name: `${this.name}-${name}-DynamoLambdaPolicy`,
policy: new dataAwsIamPolicyDocument.DataAwsIamPolicyDocument(
this,
`${name}-lambda-dynamo-policy-doc`,
{
statement: [
{
effect: 'Allow',
actions,
resources,
},
],
},
).json,
dependsOn: [lambdaExecutionRole],
},
);
return new iamRolePolicyAttachment.IamRolePolicyAttachment(
this,
`${name}-execution-role-policy-attachment`,
Expand Down
Loading