From d38d1b0c92818ce7011638b7729448056d7aaf31 Mon Sep 17 00:00:00 2001 From: Edd Almond <102675624+eddalmond1@users.noreply.github.com> Date: Tue, 9 Dec 2025 12:49:20 +0000 Subject: [PATCH 1/8] eli-420 updating workflow to use a custom 'reporting' environment --- .github/workflows/monthly-capacity-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/monthly-capacity-report.yml b/.github/workflows/monthly-capacity-report.yml index 9d6febc69..ee0be9503 100644 --- a/.github/workflows/monthly-capacity-report.yml +++ b/.github/workflows/monthly-capacity-report.yml @@ -15,7 +15,7 @@ permissions: jobs: generate-report: runs-on: ubuntu-latest - environment: prod + environment: reporting steps: - name: Checkout code From 1d1f4ee71cd43a48de280991b21a729ecc24cfee Mon Sep 17 00:00:00 2001 From: Edd Almond <102675624+eddalmond1@users.noreply.github.com> Date: Tue, 9 Dec 2025 12:56:35 +0000 Subject: [PATCH 2/8] amending account id --- .github/workflows/monthly-capacity-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/monthly-capacity-report.yml b/.github/workflows/monthly-capacity-report.yml index ee0be9503..748dd2aae 100644 --- a/.github/workflows/monthly-capacity-report.yml +++ b/.github/workflows/monthly-capacity-report.yml @@ -29,7 +29,7 @@ jobs: - name: "Configure AWS Credentials" uses: aws-actions/configure-aws-credentials@v5 with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role + role-to-assume: arn:aws:iam::${{ secrets.AWS_PROD_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role aws-region: eu-west-2 - name: Generate dashboard report From df2ed9a55d786142da822447dc96c0b039fd5f31 Mon Sep 17 00:00:00 2001 From: Edd Almond <102675624+eddalmond1@users.noreply.github.com> Date: Tue, 9 Dec 2025 14:27:19 +0000 Subject: [PATCH 3/8] eli-420 adding github permissions --- .github/workflows/monthly-capacity-report.yml | 2 +- .../stacks/iams-developer-roles/github_actions_policies.tf | 3 +++ .../stacks/iams-developer-roles/iams_permissions_boundary.tf | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/monthly-capacity-report.yml b/.github/workflows/monthly-capacity-report.yml index 748dd2aae..2200f357b 100644 --- a/.github/workflows/monthly-capacity-report.yml +++ b/.github/workflows/monthly-capacity-report.yml @@ -29,7 +29,7 @@ jobs: - name: "Configure AWS Credentials" uses: aws-actions/configure-aws-credentials@v5 with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_PROD_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role + role-to-assume: arn:aws:iam::${{ secrets.AWS_DEV_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role aws-region: eu-west-2 - name: Generate dashboard report diff --git a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf index ed9cd263b..8fe92f012 100644 --- a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf +++ b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf @@ -663,6 +663,8 @@ resource "aws_iam_policy" "cloudwatch_management" { "cloudwatch:ListTagsForResource", "cloudwatch:TagResource", "cloudwatch:UntagResource", + "cloudwatch:GetDashboard", + "cloudwatch:GetMetricWidgetImage", "sns:CreateTopic", "sns:DeleteTopic", @@ -683,6 +685,7 @@ resource "aws_iam_policy" "cloudwatch_management" { "arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:aws-wafv2-logs-*", "arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:aws-waf-logs-*", "arn:aws:cloudwatch:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:alarm:*", + "arn:aws:cloudwatch::${data.aws_caller_identity.current.account_id}:dashboard/Demand_And_Capacity_*", "arn:aws:sns:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:cloudwatch-security-alarms*", "arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/default-eligibility-signposting-api*", ] diff --git a/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf b/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf index 9bfc6cfc9..ecda1d674 100644 --- a/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf +++ b/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf @@ -26,6 +26,8 @@ data "aws_iam_policy_document" "permissions_boundary" { "cloudwatch:ListTagsForResource", "cloudwatch:TagResource", "cloudwatch:UntagResource", + "cloudwatch:GetDashboard", + "cloudwatch:GetMetricWidgetImage", # DynamoDB - table management "dynamodb:DescribeTimeToLive", From f97c7ce6ea217b70090d9319ab6f0860fc165bfa Mon Sep 17 00:00:00 2001 From: Edd Almond <102675624+eddalmond1@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:37:59 +0000 Subject: [PATCH 4/8] eli-420 adding back prod AWS to job (we can extend to other tiers) --- .github/workflows/monthly-capacity-report.yml | 2 +- .../iams-developer-roles/github_actions_policies.tf | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/monthly-capacity-report.yml b/.github/workflows/monthly-capacity-report.yml index 2200f357b..748dd2aae 100644 --- a/.github/workflows/monthly-capacity-report.yml +++ b/.github/workflows/monthly-capacity-report.yml @@ -29,7 +29,7 @@ jobs: - name: "Configure AWS Credentials" uses: aws-actions/configure-aws-credentials@v5 with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_DEV_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role + role-to-assume: arn:aws:iam::${{ secrets.AWS_PROD_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role aws-region: eu-west-2 - name: Generate dashboard report diff --git a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf index 8fe92f012..581225318 100644 --- a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf +++ b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf @@ -639,6 +639,8 @@ resource "aws_iam_policy" "firehose_readonly" { } resource "aws_iam_policy" "cloudwatch_management" { + #checkov:skip=CKV_AWS_355: GetMetricWidgetImage requires wildcard resource + #checkov:skip=CKV_AWS_290: GetMetricWidgetImage requires wildcard resource name = "cloudwatch-management" description = "Allow GitHub Actions to manage CloudWatch logs, alarms, and SNS topics" path = "/service-policies/" @@ -646,6 +648,14 @@ resource "aws_iam_policy" "cloudwatch_management" { policy = jsonencode({ Version = "2012-10-17", Statement = [ + { + Effect = "Allow", + Action = [ + # GetMetricWidgetImage does not support resource-level permissions + "cloudwatch:GetMetricWidgetImage" + ], + Resource = "*" + }, { Effect = "Allow", Action = [ @@ -664,7 +674,6 @@ resource "aws_iam_policy" "cloudwatch_management" { "cloudwatch:TagResource", "cloudwatch:UntagResource", "cloudwatch:GetDashboard", - "cloudwatch:GetMetricWidgetImage", "sns:CreateTopic", "sns:DeleteTopic", From 46102badd1b9668a5ad6f56adb33ff202329019e Mon Sep 17 00:00:00 2001 From: Edd Almond <102675624+eddalmond1@users.noreply.github.com> Date: Sun, 14 Dec 2025 15:36:29 +0000 Subject: [PATCH 5/8] eli-420 improving format and layout of reports --- scripts/generate_dashboard_report.py | 203 ++++++++++++++++++--------- 1 file changed, 134 insertions(+), 69 deletions(-) diff --git a/scripts/generate_dashboard_report.py b/scripts/generate_dashboard_report.py index 611162673..515d8703e 100644 --- a/scripts/generate_dashboard_report.py +++ b/scripts/generate_dashboard_report.py @@ -7,6 +7,35 @@ from datetime import datetime from pathlib import Path +# Context definitions for widgets +WIDGET_CONTEXT = { + "DynamoDB": "Shows the consumed write capacity units for the DynamoDB table. High usage may indicate need for scaling.", + "Lambda": "Number of times the Lambda function was invoked. Spikes may indicate increased traffic or retries.", + "5xx": "Server-side errors. Should be zero ideally.", + "4xx": "Client-side errors. Frequent 4xx errors might indicate issues with client requests.", + "Latency": "Response time of the service. Lower is better.", + "CPU": "CPU utilization percentage. Consistently high CPU might require instance upsizing.", + "Memory": "Memory usage. Ensure there is sufficient headroom.", + "Errors": "Count of error events.", + "Throttles": "Number of throttled requests. Indicates capacity limits are being hit." +} + +def get_widget_description(title): + """ + Get a description for a widget based on keywords in its title. + """ + title_lower = title.lower() + description_parts = [] + + for key, desc in WIDGET_CONTEXT.items(): + if key.lower() in title_lower: + description_parts.append(desc) + + if not description_parts: + return "Performance metric visualization." + + return " ".join(description_parts) + def generate_html_report(images_dir='dashboard_exports', output_file=None): """ Generate an HTML report with all dashboard widget images. @@ -35,7 +64,7 @@ def generate_html_report(images_dir='dashboard_exports', output_file=None): # Get dashboard name and timestamp from definition file dashboard_name = "Monthly Demand And Capacity Report - EliD" - report_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + report_date = datetime.now().strftime('%d %B %Y at %H:%M') # Build HTML html_content = f""" @@ -43,8 +72,18 @@ def generate_html_report(images_dir='dashboard_exports', output_file=None):
-