Skip to content

Commit acfe685

Browse files
committed
feature/PI-407-immutable_backups Add backups account iac and test backup in dev
1 parent a678808 commit acfe685

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1942
-22
lines changed

.github/workflows/_deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ jobs:
167167
- uses: actions/checkout@v4
168168
with:
169169
ref: ${{ needs.get-branch-from-workflow-file.outputs.branch_name }}
170-
- if: ${{ env.ACCOUNT != 'mgmt'}}
170+
- if: ${{ env.ACCOUNT != 'mgmt' && env.ACCOUNT != 'backups' }}
171171
uses: ./.github/actions/make/
172172
with:
173173
command: test--smoke WORKSPACE="${{ env.WORKSPACE }}" ACCOUNT="${{ env.ACCOUNT }}"
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
on:
2+
workflow_call:
3+
inputs:
4+
account:
5+
description: The AWS account being deployed
6+
type: string
7+
required: true
8+
workspace:
9+
description: The Terraform workspace being deployed
10+
type: string
11+
required: true
12+
scope:
13+
description: The Terraform scope being deployed
14+
type: string
15+
required: true
16+
17+
permissions:
18+
id-token: write
19+
contents: read
20+
actions: write
21+
22+
env:
23+
ACCOUNT: ${{ inputs.account }}
24+
WORKSPACE: ${{ inputs.workspace }}
25+
CACHE_NAME: ${{ inputs.workspace }}-${{ inputs.account }}-${{ inputs.scope }}
26+
SCOPE: ${{ inputs.scope }}
27+
CI_ROLE_NAME: ${{ secrets.CI_ROLE_NAME }}
28+
29+
jobs:
30+
parse-secrets:
31+
runs-on: [self-hosted, ci]
32+
steps:
33+
- id: parse-secrets
34+
run: |
35+
echo "::add-mask::${{ secrets.CI_ROLE_NAME }}"
36+
37+
get-branch-from-workflow-file:
38+
runs-on: [self-hosted, ci]
39+
needs: [parse-secrets]
40+
outputs:
41+
branch_name: ${{ steps.get_branch.outputs.branch_name }}
42+
steps:
43+
- id: get_branch
44+
run: |
45+
workflow_ref=${{ github.workflow_ref }}
46+
branch_name=${workflow_ref#*refs/heads/}
47+
branch_name=${branch_name#*refs/tags/}
48+
echo "branch_name=${branch_name}" >> $GITHUB_OUTPUT
49+
50+
build:
51+
runs-on: [self-hosted, ci]
52+
needs: get-branch-from-workflow-file
53+
steps:
54+
- uses: actions/checkout@v4
55+
with:
56+
ref: ${{ needs.get-branch-from-workflow-file.outputs.branch_name }}
57+
- if: ${{ env.SCOPE == 'per_workspace'}}
58+
uses: ./.github/actions/make/
59+
with:
60+
command: build
61+
save-to-cache: "true"
62+
restore-from-cache: "false"
63+
cache-suffix: ${{ env.CACHE_NAME }}
64+
- if: ${{ env.SCOPE != 'per_workspace'}}
65+
uses: ./.github/actions/make/
66+
with:
67+
command: poetry--update
68+
save-to-cache: "true"
69+
restore-from-cache: "false"
70+
cache-suffix: ${{ env.CACHE_NAME }}
71+
72+
terraform--init:
73+
needs: [get-branch-from-workflow-file, build]
74+
runs-on: [self-hosted, ci]
75+
steps:
76+
- uses: actions/checkout@v4
77+
with:
78+
ref: ${{ needs.get-branch-from-workflow-file.outputs.branch_name }}
79+
- uses: ./.github/actions/terraform/
80+
with:
81+
command: init
82+
account: ${{ env.ACCOUNT }}
83+
workspace: ${{ env.WORKSPACE }}
84+
scope: ${{ env.SCOPE }}
85+
restore-from-cache: "true"
86+
save-to-cache: "true"
87+
cache-suffix: ${{ env.CACHE_NAME }}
88+
89+
terraform--plan:
90+
needs: [get-branch-from-workflow-file, terraform--init]
91+
runs-on: [self-hosted, ci]
92+
steps:
93+
- uses: actions/checkout@v4
94+
with:
95+
ref: ${{ needs.get-branch-from-workflow-file.outputs.branch_name }}
96+
- uses: ./.github/actions/terraform/
97+
with:
98+
command: plan
99+
account: ${{ env.ACCOUNT }}
100+
workspace: ${{ env.WORKSPACE }}
101+
scope: ${{ env.SCOPE }}
102+
restore-from-cache: "true"
103+
save-to-cache: "true"
104+
cache-suffix: ${{ env.CACHE_NAME }}
105+
106+
terraform--apply:
107+
needs: [get-branch-from-workflow-file, terraform--plan]
108+
environment: ${{ inputs.account }}
109+
runs-on: [self-hosted, ci]
110+
steps:
111+
- uses: actions/checkout@v4
112+
with:
113+
ref: ${{ needs.get-branch-from-workflow-file.outputs.branch_name }}
114+
- uses: ./.github/actions/terraform/
115+
with:
116+
command: apply
117+
account: ${{ env.ACCOUNT }}
118+
workspace: ${{ env.WORKSPACE }}
119+
scope: ${{ env.SCOPE }}
120+
restore-from-cache: "true"
121+
save-to-cache: "true"
122+
cache-suffix: ${{ env.CACHE_NAME }}
123+
124+
set-success:
125+
name: Set Success
126+
needs: [test--smoke, apigee--deploy]
127+
runs-on: [self-hosted, ci]
128+
steps:
129+
- name: Set success env var
130+
run: echo "success"
131+
outputs:
132+
success: "succeeded"
133+
134+
message-slack:
135+
name: Notify slack of deployment
136+
needs: [get-branch-from-workflow-file, set-success]
137+
if: always()
138+
runs-on: [self-hosted, ci]
139+
140+
steps:
141+
- name: Catch failed steps
142+
id: catch-failed-step
143+
uses: ./.github/actions/catch-failed-step
144+
- name: Send job result to slack
145+
id: slack
146+
uses: slackapi/[email protected]
147+
with:
148+
webhook-type: webhook-trigger
149+
payload: |
150+
{
151+
"action_url": "${{ format('{0}/{1}/actions/runs/{2}/attempts/{3}', github.server_url, github.repository, github.run_id, github.run_attempt) }}",
152+
"attempt": ${{ github.run_attempt }},
153+
"account": "${{ env.ACCOUNT }}",
154+
"workspace": "${{ env.WORKSPACE }}",
155+
"caller": "${{ github.triggering_actor }}",
156+
"scope": "${{ env.SCOPE }}",
157+
"branch": "${{ needs.get-branch-from-workflow-file.outputs.branch_name }}",
158+
"result": "${{ needs.set-success.outputs.success && needs.set-success.outputs.success || 'failed' }}",
159+
"result_detail": "${{ needs.set-success.outputs.success && 'None' || steps.catch-failed-step.outputs.failed-step-name }}"
160+
}
161+
env:
162+
SLACK_WEBHOOK_URL: ${{ secrets.DEPLOY_ENV_SLACK_HOOK_URL }}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: "Deploy: Account Wide - Backups"
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
account:
7+
description: Account to deploy
8+
required: true
9+
default: backups
10+
jobs:
11+
deploy:
12+
uses: ./.github/workflows/_deploy_backups.yml
13+
with:
14+
account: ${{ inputs.account }}
15+
workspace: ${{ inputs.account }}
16+
scope: "per_account/${{ inputs.account }}"
17+
secrets: inherit # pragma: allowlist secret
18+
19+
run-name: Deploying account wide to nonprod workspace - ${{ inputs.account }}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: "Deploy: Parameters - Backups"
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
account:
7+
description: Account to deploy
8+
required: true
9+
default: backups
10+
11+
jobs:
12+
deploy:
13+
uses: ./.github/workflows/_deploy_backups.yml
14+
with:
15+
account: ${{ inputs.account }}
16+
workspace: ${{ inputs.account }}
17+
scope: "per_account/${{ inputs.account }}/parameters"
18+
secrets: inherit # pragma: allowlist secret
19+
20+
run-name: Deploying parameters to nonprod workspace - ${{ inputs.account }}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
account_name = "backups"
2+
environment = "backups"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# AWS Backup Module
2+
3+
The AWS Backup Module helps automates the setup of AWS Backup resources in a destination account. It streamlines the process of creating, managing, and standardising backup configurations.
4+
5+
## Inputs
6+
7+
| Name | Description | Type | Default | Required |
8+
| ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------- | :------: |
9+
| <a name="input_account_id"></a> [account_id](#input_account_id) | The id of the account that the vault will be in | `string` | n/a | yes |
10+
| <a name="input_changeable_for_days"></a> [changeable_for_days](#input_changeable_for_days) | How long you want the vault lock to be changeable for, only applies to compliance mode. This value is expressed in days no less than 3 and no greater than 36,500; otherwise, an error will return. | `number` | `14` | no |
11+
| <a name="input_enable_vault_protection"></a> [enable_vault_protection](#input_enable_vault_protection) | Flag which controls if the vault lock is enabled | `bool` | `false` | no |
12+
| <a name="input_kms_key"></a> [kms_key](#input_kms_key) | The KMS key used to secure the vault | `string` | n/a | yes |
13+
| <a name="input_region"></a> [region](#input_region) | The region we should be operating in | `string` | `"eu-west-2"` | no |
14+
| <a name="input_source_account_id"></a> [source_account_id](#input_source_account_id) | The id of the account that backups will come from | `string` | n/a | yes |
15+
| <a name="input_source_account_name"></a> [source_account_name](#input_source_account_name) | The name of the account that backups will come from | `string` | n/a | yes |
16+
| <a name="input_vault_lock_max_retention_days"></a> [vault_lock_max_retention_days](#input_vault_lock_max_retention_days) | The maximum retention period that the vault retains its recovery points | `number` | `365` | no |
17+
| <a name="input_vault_lock_min_retention_days"></a> [vault_lock_min_retention_days](#input_vault_lock_min_retention_days) | The minimum retention period that the vault retains its recovery points | `number` | `365` | no |
18+
| <a name="input_vault_lock_type"></a> [vault_lock_type](#input_vault_lock_type) | The type of lock that the vault should be, will default to governance | `string` | `"governance"` | no |
19+
20+
## Example
21+
22+
```terraform
23+
module "test_backup_vault" {
24+
source = "./modules/aws_backup"
25+
source_account_name = "test"
26+
account_id = local.aws_accounts_ids["backup"]
27+
source_account_id = local.aws_accounts_ids["test"]
28+
kms_key = aws_kms_key.backup_key.arn
29+
enable_vault_protection = true
30+
}
31+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
resource "aws_backup_vault" "vault" {
2+
name = "${var.source_account_name}-backup-vault"
3+
kms_key_arn = var.kms_key
4+
}
5+
6+
output "vault_arn" {
7+
value = aws_backup_vault.vault.arn
8+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
resource "aws_backup_vault_lock_configuration" "vault_lock" {
2+
count = var.enable_vault_protection ? 1 : 0
3+
backup_vault_name = aws_backup_vault.vault.name
4+
changeable_for_days = var.vault_lock_type == "compliance" ? var.changeable_for_days : null
5+
max_retention_days = var.vault_lock_max_retention_days
6+
min_retention_days = var.vault_lock_min_retention_days
7+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
resource "aws_backup_vault_policy" "vault_policy" {
2+
backup_vault_name = aws_backup_vault.vault.name
3+
policy = data.aws_iam_policy_document.vault_policy.json
4+
}
5+
6+
data "aws_iam_policy_document" "vault_policy" {
7+
8+
statement {
9+
sid = "AllowCopyToVault"
10+
effect = "Allow"
11+
12+
principals {
13+
type = "AWS"
14+
identifiers = ["arn:aws:iam::${var.source_account_id}:root"]
15+
}
16+
17+
actions = [
18+
"backup:CopyIntoBackupVault"
19+
]
20+
resources = ["*"]
21+
}
22+
23+
dynamic "statement" {
24+
for_each = var.enable_vault_protection ? [1] : []
25+
content {
26+
sid = "DenyBackupVaultAccess"
27+
effect = "Deny"
28+
29+
principals {
30+
type = "AWS"
31+
identifiers = ["*"]
32+
}
33+
actions = [
34+
"backup:DeleteRecoveryPoint",
35+
"backup:PutBackupVaultAccessPolicy",
36+
"backup:UpdateRecoveryPointLifecycle",
37+
"backup:DeleteBackupVault",
38+
"backup:StartRestoreJob",
39+
"backup:DeleteBackupVaultLockConfiguration",
40+
]
41+
resources = ["*"]
42+
}
43+
}
44+
45+
dynamic "statement" {
46+
for_each = var.enable_vault_protection ? [1] : []
47+
content {
48+
sid = "DenyBackupCopyExceptToSourceAccount"
49+
effect = "Deny"
50+
51+
principals {
52+
type = "AWS"
53+
identifiers = ["arn:aws:iam::${var.account_id}:root"]
54+
}
55+
actions = [
56+
"backup:CopyFromBackupVault"
57+
]
58+
resources = ["*"]
59+
condition {
60+
test = "StringNotEquals"
61+
variable = "backup:CopyTargets"
62+
values = [
63+
"arn:aws:backup:${var.region}:${var.source_account_id}:backup-vault:${var.region}-${var.source_account_id}-backup-vault"
64+
]
65+
}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)