Skip to content

Implement Automated GitHub Runner Deregistration and Enhanced Infrastructure#45

Merged
kunduso merged 28 commits intomainfrom
runner-deregistration
Aug 23, 2025
Merged

Implement Automated GitHub Runner Deregistration and Enhanced Infrastructure#45
kunduso merged 28 commits intomainfrom
runner-deregistration

Conversation

@kunduso
Copy link
Copy Markdown
Collaborator

@kunduso kunduso commented Aug 23, 2025

This PR implements a comprehensive automated GitHub runner deregistration system along with enhanced infrastructure components for better monitoring, performance, and reliability.

🚀 Key Features

Automated Runner Deregistration System

  • Auto Scaling Lifecycle Hooks: Intercept instance termination events
  • Lambda-based Deregistration: Automated GitHub API calls to remove offline runners
  • SNS Integration: Reliable event notification system
  • Backup Deregistration: Systemd service as fallback mechanism
  • CloudWatch Logging: Comprehensive audit trail for all lifecycle events

Enhanced Infrastructure Components

  • Lambda Layer: Optimized Python dependencies (PyJWT, cryptography) for GitHub API authentication
  • EFS Integration: Persistent shared workspace storage across runner instances
  • CloudWatch Monitoring: Structured logging for registration, execution, and deregistration phases
  • Performance Optimization: Tuned NFS mount parameters and error handling

🔧 Technical Implementation

New Files Added

  • lifecycle-hook.tf: Auto Scaling lifecycle hook, SNS topic, Lambda function, and IAM roles
  • lambda_layer.tf: Lambda layer for Python dependencies
  • lambda_package/lambda_deregistration.py: Lambda function for GitHub API integration
  • scripts/deregister-runner.sh: Backup shell script for systemd service
  • lambda_layer/: Pre-built Python dependencies (PyJWT, cryptography, etc.)

Modified Files

  • scripts/user_data.sh: Enhanced CloudWatch logging, EFS mounting, and deregistration service setup
  • asg.tf: Updated IAM permissions for lifecycle hooks and EFS access
  • cloudwatch.tf: Enhanced log group configuration
  • kms.tf: Additional KMS permissions for SNS and Lambda
  • ssm.tf: Deregistration script parameter storage

🛡️ Security Enhancements

  • KMS Encryption: All SNS topics, Lambda functions, and EFS encrypted with customer-managed keys
  • IAM Least Privilege: Minimal required permissions for each component
  • Secrets Management: GitHub credentials securely stored in AWS Secrets Manager
  • Network Security: EFS mount targets restricted to private subnets

📊 Operational Benefits

  • Zero Manual Intervention: Fully automated runner lifecycle management
  • Improved Monitoring: Comprehensive CloudWatch logging and metrics
  • Enhanced Performance: Persistent workspace storage and dependency caching
  • Cost Optimization: Reduced data transfer and improved resource utilization
  • Reliability: Dual deregistration mechanisms prevent orphaned runners

🔗 Related Issues

This PR addresses multiple enhancement requests and infrastructure improvements:

Closes #25
Closes #41
Closes #42
Closes #43
Closes #44

✅ Testing and Validation

  • Auto Scaling lifecycle hooks trigger correctly on instance termination
  • Lambda function successfully deregisters runners from GitHub organization
  • EFS mounts properly with correct permissions
  • CloudWatch logs capture all lifecycle events
  • Backup deregistration service functions as fallback
  • All components work together seamlessly

📋 Deployment Notes

  1. KMS Permissions: Ensure proper KMS key policies for all services
  2. GitHub App: Verify GitHub App has necessary organization permissions
  3. EFS Performance: Monitor EFS performance metrics after deployment
  4. CloudWatch Costs: Review log retention settings for cost optimization

🎯 Success Criteria

  • ✅ Eliminated orphaned GitHub runners in organization
  • ✅ Comprehensive audit trail for all runner lifecycle events
  • ✅ Persistent workspace storage across instance replacements
  • ✅ Optimized performance for GitHub Actions workflows
  • ✅ Enhanced security with encryption and least privilege access

@kunduso kunduso self-assigned this Aug 23, 2025
@github-actions
Copy link
Copy Markdown

💰 Infracost report

Monthly estimate generated

Changed project Baseline cost Usage cost* Total change New monthly cost
kunduso-org/github-self-hosted-...azon-ec2-terraform/TFplan.JSON +$0 - +$0 $131

*Usage costs can be estimated by updating Infracost Cloud settings, see docs for other options.

Estimate details
Key: * usage cost, ~ changed, + added, - removed

──────────────────────────────────
Project: kunduso-org/github-self-hosted-runner-amazon-ec2-terraform/TFplan.JSON

- aws_cloudwatch_log_group.github_runner
  Monthly cost depends on usage

    - Data ingested
      Monthly cost depends on usage
        -$0.50 per GB

    - Archival Storage
      Monthly cost depends on usage
        -$0.03 per GB

    - Insights queries data scanned
      Monthly cost depends on usage
        -$0.005 per GB

+ aws_cloudwatch_log_group.github_runner_lifecycle
  Monthly cost depends on usage

    + Data ingested
      Monthly cost depends on usage
        +$0.50 per GB

    + Archival Storage
      Monthly cost depends on usage
        +$0.03 per GB

    + Insights queries data scanned
      Monthly cost depends on usage
        +$0.005 per GB

+ aws_lambda_function.runner_deregistration
  Monthly cost depends on usage

    + Requests
      Monthly cost depends on usage
        +$0.20 per 1M requests

    + Ephemeral storage
      Monthly cost depends on usage
        +$0.0000000309 per GB-seconds

    + Duration (first 6B)
      Monthly cost depends on usage
        +$0.0000166667 per GB-seconds

+ aws_sns_topic.runner_lifecycle
  Monthly cost depends on usage

    + API requests (over 1M)
      Monthly cost depends on usage
        +$0.50 per 1M requests

    + HTTP/HTTPS notifications (over 100k)
      Monthly cost depends on usage
        +$0.06 per 100k notifications

    + Email/Email-JSON notifications (over 1k)
      Monthly cost depends on usage
        +$2.00 per 100k notifications

    + Kinesis Firehose notifications
      Monthly cost depends on usage
        +$0.19 per 1M notifications

    + Mobile Push notifications
      Monthly cost depends on usage
        +$0.50 per 1M notifications

    + MacOS notifications
      Monthly cost depends on usage
        +$0.50 per 1M notifications

Monthly cost change for kunduso-org/github-self-hosted-runner-amazon-ec2-terraform/TFplan.JSON
Amount:  $0.00 ($131 → $131)
Percent: 0%

──────────────────────────────────
Key: * usage cost, ~ changed, + added, - removed

*Usage costs can be estimated by updating Infracost Cloud settings, see docs for other options.

71 cloud resources were detected:
∙ 13 were estimated
∙ 58 were free
This comment will be updated when code changes.

@github-actions
Copy link
Copy Markdown

Terraform Format and Style 🖌success

Terraform Initialization ⚙️success

Terraform Plan 📖success

Terraform Validation 🤖success

Show Plan

terraform
data.archive_file.lambda_zip: Reading...
data.archive_file.lambda_layer_pyjwt: Reading...
data.archive_file.lambda_zip: Read complete after 0s [id=85af42f0bfb7d1cfaedb0399f1045465f4d28866]
aws_kms_key.github_runner_secrets: Refreshing state... [id=c77ff6db-241d-4e88-9317-17c07d0ca952]
aws_iam_policy.github_actions_state: Refreshing state... [id=arn:aws:iam::743794601996:policy/github-self-hosted-runner-github-actions-state-policy]
aws_kms_key.cloudwatch_kms_key: Refreshing state... [id=a725a4e2-3f6c-4a07-ac88-25baafbe7b76]
aws_cloudwatch_log_group.github_runner: Refreshing state... [id=/github-runner/github-self-hosted-runner/log]
data.aws_caller_identity.current: Reading...
module.vpc.aws_kms_key.custom_kms_key[0]: Refreshing state... [id=a3fd4228-613d-487c-89c6-74f2235bdd36]
data.aws_availability_zones.available: Reading...
data.aws_ami.ubuntu: Reading...
aws_efs_file_system.github_runner_work: Refreshing state... [id=fs-0b55e9a7011bf7c90]
data.aws_caller_identity.current: Read complete after 0s [id=743794601996]
module.vpc.data.aws_availability_zones.available: Reading...
data.aws_availability_zones.available: Read complete after 0s [id=us-west-2]
module.vpc.data.aws_iam_policy_document.assume_role: Reading...
module.vpc.aws_vpc.this: Refreshing state... [id=vpc-0f94d52581179e6b3]
module.vpc.data.aws_iam_policy_document.assume_role: Read complete after 0s [id=2717921857]
module.vpc.data.aws_caller_identity.current: Reading...
module.vpc.data.aws_availability_zones.available: Read complete after 0s [id=us-west-2]
module.vpc.aws_eip.nat_gateway[0]: Refreshing state... [id=eipalloc-001340df10b3e1b97]
module.vpc.aws_eip.nat_gateway[1]: Refreshing state... [id=eipalloc-0e79188509e4f565a]
aws_iam_role.github_runner: Refreshing state... [id=github-self-hosted-runner-ec2-role]
data.aws_iam_policy_document.ssm_kms: Reading...
module.vpc.data.aws_caller_identity.current: Read complete after 0s [id=743794601996]
data.aws_iam_policy_document.ssm_kms: Read complete after 0s [id=3292091877]
module.vpc.aws_iam_role.vpc_flow_log_role[0]: Refreshing state... [id=github-self-hosted-runner-vpc-flow-role]
aws_kms_alias.key: Refreshing state... [id=alias/github-self-hosted-runner]
aws_kms_alias.github_runner_secrets: Refreshing state... [id=alias/github-self-hosted-runner-secret]
aws_secretsmanager_secret.github_runner_credentials: Refreshing state... [id=arn:aws:secretsmanager:us-west-2:743794601996:secret:github-self-hosted-runner-credentials-v2-QXLXJo]
module.vpc.aws_cloudwatch_log_group.network_flow_logging[0]: Refreshing state... [id=github-self-hosted-runner-flow-logs]
data.aws_ami.ubuntu: Read complete after 0s [id=ami-065778886ef8ec7c8]
module.vpc.aws_kms_alias.key[0]: Refreshing state... [id=alias/github-self-hosted-runner-encrypt-flow-log]
aws_kms_key_policy.encrypt_cloudwatch: Refreshing state... [id=a725a4e2-3f6c-4a07-ac88-25baafbe7b76]
aws_kms_key_policy.encrypt_secret: Refreshing state... [id=c77ff6db-241d-4e88-9317-17c07d0ca952]
data.archive_file.lambda_layer_pyjwt: Read complete after 1s [id=c695122e3691f4ce6af7f8076ecef8b11a043b76]
aws_kms_key.ssm_parameters: Refreshing state... [id=8c717a58-141f-4ddd-88eb-d30645daebdb]
aws_iam_policy.cloudwatch_logs: Refreshing state... [id=arn:aws:iam::743794601996:policy/github-self-hosted-runner-cloudwatch-logs-policy]
module.vpc.aws_kms_key_policy.encrypt_log[0]: Refreshing state... [id=a3fd4228-613d-487c-89c6-74f2235bdd36]
module.vpc.data.aws_iam_policy_document.vpc_flow_log_policy_document[0]: Reading...
module.vpc.data.aws_iam_policy_document.vpc_flow_log_policy_document[0]: Read complete after 0s [id=54070053]
aws_secretsmanager_secret_version.github_runner_credentials: Refreshing state... [id=arn:aws:secretsmanager:us-west-2:743794601996:secret:github-self-hosted-runner-credentials-v2-QXLXJo|terraform-20250823212320163400000001]
aws_kms_alias.ssm_parameters: Refreshing state... [id=alias/github-self-hosted-runner-ssm]
aws_iam_policy.github_runner: Refreshing state... [id=arn:aws:iam::743794601996:policy/github-self-hosted-runner-ec2-policy]
module.vpc.aws_subnet.private[0]: Refreshing state... [id=subnet-0da9beaf2e436c556]
module.vpc.aws_route_table.public[0]: Refreshing state... [id=rtb-03bf97d42f9a82730]
aws_security_group.github_runner: Refreshing state... [id=sg-079a81840121b7da7]
module.vpc.aws_route_table.private[1]: Refreshing state... [id=rtb-044fac8abc330694a]
module.vpc.aws_default_security_group.default: Refreshing state... [id=sg-059fed260f3f7d461]
module.vpc.aws_route_table.private[0]: Refreshing state... [id=rtb-09d2063d896d96860]
module.vpc.aws_subnet.private[1]: Refreshing state... [id=subnet-03e1ed051e9e071c1]
module.vpc.aws_internet_gateway.this_igw[0]: Refreshing state... [id=igw-0a0d939ec05e85391]
aws_security_group.efs: Refreshing state... [id=sg-024a1e389c84b48ea]
module.vpc.aws_subnet.public[0]: Refreshing state... [id=subnet-0febf2c2af76e2c79]
module.vpc.aws_subnet.public[1]: Refreshing state... [id=subnet-09651f70f6184babe]
aws_security_group_rule.github_runner_egress: Refreshing state... [id=sgrule-3110254434]
aws_iam_role_policy_attachment.ssm: Refreshing state... [id=github-self-hosted-runner-ec2-role-20250823150037802000000001]
aws_iam_role_policy_attachment.cloudwatch_logs: Refreshing state... [id=github-self-hosted-runner-ec2-role-20250823150055004200000007]
aws_iam_role.github_actions_runner: Refreshing state... [id=github-self-hosted-runner-github-actions-runner-role]
aws_iam_instance_profile.github_runner: Refreshing state... [id=github-self-hosted-runner-ec2-profile]
aws_iam_role_policy_attachment.github_runner: Refreshing state... [id=github-self-hosted-runner-ec2-role-2025082315011670590000000c]
module.vpc.aws_route.internet_route[0]: Refreshing state... [id=r-rtb-03bf97d42f9a827301080289494]
module.vpc.aws_route_table_association.private[0]: Refreshing state... [id=rtbassoc-03caad7157f63b8cd]
module.vpc.aws_route_table_association.private[1]: Refreshing state... [id=rtbassoc-028d25a5848086fff]
aws_security_group_rule.efs_ingress: Refreshing state... [id=sgrule-1720110692]
aws_efs_mount_target.github_runner_work[0]: Refreshing state... [id=fsmt-06593fb53f8c2522f]
module.vpc.aws_route_table_association.public[1]: Refreshing state... [id=rtbassoc-0d7d71401df749b1a]
module.vpc.aws_route_table_association.public[0]: Refreshing state... [id=rtbassoc-0ef5f4bbe527d85a1]
aws_efs_mount_target.github_runner_work[1]: Refreshing state... [id=fsmt-080b547fe91291e3b]
module.vpc.aws_nat_gateway.public[0]: Refreshing state... [id=nat-0f2a89d17c306f450]
module.vpc.aws_nat_gateway.public[1]: Refreshing state... [id=nat-0a26d68751d3c1b6f]
module.vpc.aws_iam_role_policy.vpc_flow_log_role_policy[0]: Refreshing state... [id=github-self-hosted-runner-vpc-flow-role:github-self-hosted-runner-vpc-flow-policy]
module.vpc.aws_flow_log.network_flow_logging[0]: Refreshing state... [id=fl-0ea1bdd10867b211d]
module.vpc.aws_route.private_route[0]: Refreshing state... [id=r-rtb-09d2063d896d968601080289494]
module.vpc.aws_route.private_route[1]: Refreshing state... [id=r-rtb-044fac8abc330694a1080289494]
aws_ssm_parameter.nat_gateway_public_ips: Refreshing state... [id=/github-self-hosted-runner-ip-address]
aws_launch_template.github_runner: Refreshing state... [id=lt-0fc3f2cf8d3ca7905]
aws_iam_role_policy_attachment.github_actions_admin: Refreshing state... [id=github-self-hosted-runner-github-actions-runner-role-20250823150045535900000004]
aws_iam_role_policy_attachment.github_actions_state: Refreshing state... [id=github-self-hosted-runner-github-actions-runner-role-20250823150045310200000003]
aws_autoscaling_group.github_runner: Refreshing state... [id=github-self-hosted-runner-asg]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place
  - destroy

Terraform will perform the following actions:

  # aws_autoscaling_group.github_runner will be updated in-place
  ~ resource "aws_autoscaling_group" "github_runner" {
        id                               = "github-self-hosted-runner-asg"
        name                             = "github-self-hosted-runner-asg"
        # (31 unchanged attributes hidden)

      ~ launch_template {
            id      = "lt-0fc3f2cf8d3ca7905"
            name    = "github-self-hosted-runner2025082315022253420000000f"
          ~ version = "4" -> (known after apply)
        }

        # (3 unchanged blocks hidden)
    }

  # aws_autoscaling_lifecycle_hook.runner_termination will be created
  + resource "aws_autoscaling_lifecycle_hook" "runner_termination" {
      + autoscaling_group_name  = "github-self-hosted-runner-asg"
      + default_result          = "ABANDON"
      + heartbeat_timeout       = 300
      + id                      = (known after apply)
      + lifecycle_transition    = "autoscaling:EC2_INSTANCE_TERMINATING"
      + name                    = "github-self-hosted-runner-termination-hook"
      + notification_target_arn = (known after apply)
      + role_arn                = (known after apply)
    }

  # aws_cloudwatch_log_group.github_runner will be destroyed
  # (because aws_cloudwatch_log_group.github_runner is not in configuration)
  - resource "aws_cloudwatch_log_group" "github_runner" {
      - arn               = "arn:aws:logs:us-west-2:743794601996:log-group:/github-runner/github-self-hosted-runner/log" -> null
      - id                = "/github-runner/github-self-hosted-runner/log" -> null
      - kms_key_id        = "arn:aws:kms:us-west-2:743794601996:key/a725a4e2-3f6c-4a07-ac88-25baafbe7b76" -> null
      - log_group_class   = "STANDARD" -> null
      - name              = "/github-runner/github-self-hosted-runner/log" -> null
      - retention_in_days = 365 -> null
      - skip_destroy      = false -> null
      - tags              = {
          - "Name" = "github-self-hosted-runner-logs"
        } -> null
      - tags_all          = {
          - "Name"   = "github-self-hosted-runner-logs"
          - "Source" = "https://github.com/kunduso-org/github-self-hosted-runner-amazon-ec2-terraform"
        } -> null
        # (1 unchanged attribute hidden)
    }

  # aws_cloudwatch_log_group.github_runner_lifecycle will be created
  + resource "aws_cloudwatch_log_group" "github_runner_lifecycle" {
      + arn               = (known after apply)
      + id                = (known after apply)
      + kms_key_id        = "arn:aws:kms:us-west-2:743794601996:key/a725a4e2-3f6c-4a07-ac88-25baafbe7b76"
      + log_group_class   = (known after apply)
      + name              = "/github-runner/github-self-hosted-runner/lifecycle"
      + name_prefix       = (known after apply)
      + retention_in_days = 14
      + skip_destroy      = false
      + tags              = {
          + "Name" = "github-self-hosted-runner-lifecycle-logs"
        }
      + tags_all          = {
          + "Name"   = "github-self-hosted-runner-lifecycle-logs"
          + "Source" = "https://github.com/kunduso-org/github-self-hosted-runner-amazon-ec2-terraform"
        }
    }

  # aws_iam_policy.cloudwatch_logs will be updated in-place
  ~ resource "aws_iam_policy" "cloudwatch_logs" {
        id               = "arn:aws:iam::743794601996:policy/github-self-hosted-runner-cloudwatch-logs-policy"
        name             = "github-self-hosted-runner-cloudwatch-logs-policy"
      ~ policy           = jsonencode(
            {
              - Statement = [
                  - {
                      - Action   = [
                          - "logs:CreateLogGroup",
                          - "logs:CreateLogStream",
                          - "logs:PutLogEvents",
                          - "logs:DescribeLogStreams",
                        ]
                      - Effect   = "Allow"
                      - Resource = [
                          - "arn:aws:logs:us-west-2:743794601996:log-group:/github-runner/github-self-hosted-runner/log",
                          - "arn:aws:logs:us-west-2:743794601996:log-group:/github-runner/github-self-hosted-runner/log:*",
                        ]
                    },
                  - {
                      - Action   = [
                          - "kms:Encrypt",
                          - "kms:Decrypt",
                          - "kms:ReEncrypt*",
                          - "kms:GenerateDataKey*",
                          - "kms:DescribeKey",
                        ]
                      - Effect   = "Allow"
                      - Resource = "arn:aws:kms:us-west-2:743794601996:key/a725a4e2-3f6c-4a07-ac88-25baafbe7b76"
                    },
                ]
              - Version   = "2012-10-17"
            }
        ) -> (known after apply)
        tags             = {}
        # (7 unchanged attributes hidden)
    }

  # aws_iam_policy.github_runner will be updated in-place
  ~ resource "aws_iam_policy" "github_runner" {
        id               = "arn:aws:iam::743794601996:policy/github-self-hosted-runner-ec2-policy"
        name             = "github-self-hosted-runner-ec2-policy"
      ~ policy           = jsonencode(
            {
              - Statement = [
                  - {
                      - Action   = [
                          - "secretsmanager:GetSecretValue",
                        ]
                      - Effect   = "Allow"
                      - Resource = "arn:aws:secretsmanager:us-west-2:743794601996:secret:github-self-hosted-runner-credentials-v2-QXLXJo"
                    },
                  - {
                      - Action   = [
                          - "kms:Decrypt",
                          - "kms:DescribeKey",
                        ]
                      - Effect   = "Allow"
                      - Resource = "arn:aws:kms:us-west-2:743794601996:key/c77ff6db-241d-4e88-9317-17c07d0ca952"
                    },
                  - {
                      - Action   = [
                          - "elasticfilesystem:ClientMount",
                          - "elasticfilesystem:ClientWrite",
                        ]
                      - Effect   = "Allow"
                      - Resource = "arn:aws:elasticfilesystem:us-west-2:743794601996:file-system/fs-0b55e9a7011bf7c90"
                    },
                  - {
                      - Action   = [
                          - "sts:AssumeRole",
                        ]
                      - Effect   = "Allow"
                      - Resource = "arn:aws:iam::743794601996:role/github-self-hosted-runner-github-actions-runner-role"
                    },
                ]
              - Version   = "2012-10-17"
            }
        ) -> (known after apply)
        tags             = {}
        # (7 unchanged attributes hidden)
    }

  # aws_iam_role.lambda_deregistration will be created
  + resource "aws_iam_role" "lambda_deregistration" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRole"
                      + Effect    = "Allow"
                      + Principal = {
                          + Service = "lambda.amazonaws.com"
                        }
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + create_date           = (known after apply)
      + force_detach_policies = false
      + id                    = (known after apply)
      + managed_policy_arns   = (known after apply)
      + max_session_duration  = 3600
      + name                  = "github-self-hosted-runner-lambda-deregistration-role"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags_all              = {
          + "Source" = "https://github.com/kunduso-org/github-self-hosted-runner-amazon-ec2-terraform"
        }
      + unique_id             = (known after apply)

      + inline_policy (known after apply)
    }

  # aws_iam_role.lifecycle_hook will be created
  + resource "aws_iam_role" "lifecycle_hook" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRole"
                      + Effect    = "Allow"
                      + Principal = {
                          + Service = "autoscaling.amazonaws.com"
                        }
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + create_date           = (known after apply)
      + force_detach_policies = false
      + id                    = (known after apply)
      + managed_policy_arns   = (known after apply)
      + max_session_duration  = 3600
      + name                  = "github-self-hosted-runner-lifecycle-hook-role"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags_all              = {
          + "Source" = "https://github.com/kunduso-org/github-self-hosted-runner-amazon-ec2-terraform"
        }
      + unique_id             = (known after apply)

      + inline_policy (known after apply)
    }

  # aws_iam_role_policy.lambda_deregistration will be created
  + resource "aws_iam_role_policy" "lambda_deregistration" {
      + id          = (known after apply)
      + name        = "github-self-hosted-runner-lambda-deregistration-policy"
      + name_prefix = (known after apply)
      + policy      = (known after apply)
      + role        = (known after apply)
    }

  # aws_iam_role_policy.lifecycle_hook will be created
  + resource "aws_iam_role_policy" "lifecycle_hook" {
      + id          = (known after apply)
      + name        = "github-self-hosted-runner-lifecycle-hook-policy"
      + name_prefix = (known after apply)
      + policy      = (known after apply)
      + role        = (known after apply)
    }

  # aws_kms_key_policy.encrypt_cloudwatch will be updated in-place
  ~ resource "aws_kms_key_policy" "encrypt_cloudwatch" {
        id                                 = "a725a4e2-3f6c-4a07-ac88-25baafbe7b76"
      ~ policy                             = jsonencode(
          ~ {
              ~ Statement = [
                    {
                        Action    = "kms:*"
                        Effect    = "Allow"
                        Principal = {
                            AWS = "arn:aws:iam::743794601996:root"
                        }
                        Resource  = "*"
                        Sid       = "Enable IAM User Permissions"
                    },
                  ~ {
                      ~ Condition = {
                          ~ ArnEquals = {
                              ~ "kms:EncryptionContext:aws:logs:arn" = [
                                  ~ "arn:aws:logs:us-west-2:743794601996:log-group:/github-runner/github-self-hosted-runner/log" -> "arn:aws:logs:us-west-2:743794601996:log-group:/github-runner/github-self-hosted-runner/lifecycle",
                                ]
                            }
                        }
                        # (4 unchanged attributes hidden)
                    },
                ]
                # (2 unchanged attributes hidden)
            }
        )
        # (2 unchanged attributes hidden)
    }

  # aws_lambda_function.runner_deregistration will be created
  + resource "aws_lambda_function" "runner_deregistration" {
      + architectures                  = (known after apply)
      + arn                            = (known after apply)
      + code_sha256                    = (known after apply)
      + filename                       = "runner_deregistration.zip"
      + function_name                  = "github-self-hosted-runner-deregistration"
      + handler                        = "index.handler"
      + id                             = (known after apply)
      + invoke_arn                     = (known after apply)
      + last_modified                  = (known after apply)
      + layers                         = (known after apply)
      + memory_size                    = 128
      + package_type                   = "Zip"
      + publish                        = false
      + qualified_arn                  = (known after apply)
      + qualified_invoke_arn           = (known after apply)
      + reserved_concurrent_executions = -1
      + role                           = (known after apply)
      + runtime                        = "python3.12"
      + signing_job_arn                = (known after apply)
      + signing_profile_version_arn    = (known after apply)
      + skip_destroy                   = false
      + source_code_hash               = "fMFiWq/NWXhGvBY+NU3Qo0KILSf1yLYG/0aUKLw3G5Y="
      + source_code_size               = (known after apply)
      + tags_all                       = {
          + "Source" = "https://github.com/kunduso-org/github-self-hosted-runner-amazon-ec2-terraform"
        }
      + timeout                        = 60
      + version                        = (known after apply)

      + environment {
          + variables = {
              + "GITHUB_ORGANIZATION" = "kunduso-org"
              + "LIFECYCLE_LOG_GROUP" = "/github-runner/github-self-hosted-runner/lifecycle"
              + "REGION"              = "us-west-2"
              + "SECRET_NAME"         = "github-self-hosted-runner-credentials-v2"
            }
        }

      + ephemeral_storage (known after apply)

      + logging_config (known after apply)

      + tracing_config (known after apply)
    }

  # aws_lambda_layer_version.lambda_layer_pyjwt will be created
  + resource "aws_lambda_layer_version" "lambda_layer_pyjwt" {
      + arn                         = (known after apply)
      + code_sha256                 = (known after apply)
      + compatible_runtimes         = [
          + "python3.12",
        ]
      + created_date                = (known after apply)
      + filename                    = "./lambda_layer.zip"
      + id                          = (known after apply)
      + layer_arn                   = (known after apply)
      + layer_name                  = "pyjwt"
      + signing_job_arn             = (known after apply)
      + signing_profile_version_arn = (known after apply)
      + skip_destroy                = false
      + source_code_hash            = "wIADZnX/t6eAZcPI5PzKpqyeHttwc7KsK0Df1MLVwqs="
      + source_code_size            = (known after apply)
      + version                     = (known after apply)
    }

  # aws_lambda_permission.sns_invoke will be created
  + resource "aws_lambda_permission" "sns_invoke" {
      + action              = "lambda:InvokeFunction"
      + function_name       = "github-self-hosted-runner-deregistration"
      + id                  = (known after apply)
      + principal           = "sns.amazonaws.com"
      + source_arn          = (known after apply)
      + statement_id        = "AllowExecutionFromSNS"
      + statement_id_prefix = (known after apply)
    }

  # aws_launch_template.github_runner will be updated in-place
  ~ resource "aws_launch_template" "github_runner" {
        id                                   = "lt-0fc3f2cf8d3ca7905"
      ~ latest_version                       = 4 -> (known after apply)
        name                                 = "github-self-hosted-runner2025082315022253420000000f"
        tags                                 = {}
      ~ user_data                            = "IyEvYmluL2Jhc2gKc2V0IC1lCgojIFNldHVwIGxvZ2dpbmcKTE9HX0ZJTEU9Ii92YXIvbG9nL2dpdGh1Yi1ydW5uZXItc2V0dXAubG9nIgpleGVjID4gPih0ZWUgLWEgJExPR19GSUxFKQpleGVjIDI+JjEKCmVjaG8gIiQoZGF0ZSk6IFN0YXJ0aW5nIEdpdEh1YiBydW5uZXIgc2V0dXAiCgojIE5ldHdvcmsgY29ubmVjdGl2aXR5IHZhbGlkYXRpb24KZWNobyAiJChkYXRlKTogVmFsaWRhdGluZyBuZXR3b3JrIGNvbm5lY3Rpdml0eS4uLiIKcmV0cnlfY291bnQ9MAptYXhfcmV0cmllcz0xMiAgIyAyIG1pbnV0ZXMgdG90YWwKCnVudGlsIGN1cmwgLXMgLS1jb25uZWN0LXRpbWVvdXQgNSBodHRwczovL2F3cy5hbWF6b24uY29tID4gL2Rldi9udWxsOyBkbwogICAgcmV0cnlfY291bnQ9JCgocmV0cnlfY291bnQgKyAxKSkKICAgIGlmIFsgJHJldHJ5X2NvdW50IC1nZSAkbWF4X3JldHJpZXMgXTsgdGhlbgogICAgICAgIGVjaG8gIiQoZGF0ZSk6IEVSUk9SIC0gTmV0d29yayBjb25uZWN0aXZpdHkgZmFpbGVkIGFmdGVyICRtYXhfcmV0cmllcyBhdHRlbXB0cyIKICAgICAgICBleGl0IDEKICAgIGZpCiAgICBlY2hvICIkKGRhdGUpOiBOZXR3b3JrIG5vdCByZWFkeSwgd2FpdGluZy4uLiAoYXR0ZW1wdCAkcmV0cnlfY291bnQvJG1heF9yZXRyaWVzKSIKICAgIHNsZWVwIDEwCmRvbmUKCmVjaG8gIiQoZGF0ZSk6IE5ldHdvcmsgY29ubmVjdGl2aXR5IGNvbmZpcm1lZCIKCiMgVGVzdCBjcml0aWNhbCBBV1Mgc2VydmljZXMKZWNobyAiJChkYXRlKTogVGVzdGluZyBBV1Mgc2VydmljZXMgY29ubmVjdGl2aXR5Li4uIgphd3Nfc2VydmljZXM9KAogICAgImh0dHBzOi8vczMudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20iCiAgICAiaHR0cHM6Ly9zZWNyZXRzbWFuYWdlci51cy13ZXN0LTIuYW1hem9uYXdzLmNvbSIKICAgICJodHRwczovL2xvZ3MudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20iCikKCmZvciBzZXJ2aWNlIGluICIke2F3c19zZXJ2aWNlc1tAXX0iOyBkbwogICAgZWNobyAiJChkYXRlKTogVGVzdGluZyBjb25uZWN0aXZpdHkgdG8gJHNlcnZpY2UuLi4iCiAgICBpZiAhIGN1cmwgLXMgLS1jb25uZWN0LXRpbWVvdXQgMTAgIiRzZXJ2aWNlIiA+IC9kZXYvbnVsbDsgdGhlbgogICAgICAgIGVjaG8gIiQoZGF0ZSk6IFdBUk5JTkcgLSBDYW5ub3QgcmVhY2ggJHNlcnZpY2UiCiAgICBlbHNlCiAgICAgICAgZWNobyAiJChkYXRlKTogU3VjY2Vzc2Z1bGx5IGNvbm5lY3RlZCB0byAkc2VydmljZSIKICAgIGZpCmRvbmUKCmVjaG8gIiQoZGF0ZSk6IEFXUyBzZXJ2aWNlcyBjb25uZWN0aXZpdHkgdGVzdCBjb21wbGV0ZWQiCgojIEdldCBpbnN0YW5jZSBJRCBmb3IgcnVubmVyIG5hbWluZyB1c2luZyBJTURTdjIKVE9LRU49JChjdXJsIC1YIFBVVCAiaHR0cDovLzE2OS4yNTQuMTY5LjI1NC9sYXRlc3QvYXBpL3Rva2VuIiAtSCAiWC1hd3MtZWMyLW1ldGFkYXRhLXRva2VuLXR0bC1zZWNvbmRzOiAyMTYwMCIpCklOU1RBTkNFX0lEPSQoY3VybCAtSCAiWC1hd3MtZWMyLW1ldGFkYXRhLXRva2VuOiAkVE9LRU4iIC1zIGh0dHA6Ly8xNjkuMjU0LjE2OS4yNTQvbGF0ZXN0L21ldGEtZGF0YS9pbnN0YW5jZS1pZCkKCiMgVXBkYXRlIHN5c3RlbQplY2hvICIkKGRhdGUpOiBVcGRhdGluZyBzeXN0ZW0gcGFja2FnZXMiCmFwdC1nZXQgdXBkYXRlCmFwdC1nZXQgaW5zdGFsbCAteSBjdXJsIGpxIGF3c2NsaSBweXRob24zLXBpcCBnaXQgYmludXRpbHMgbmZzLWNvbW1vbgpwaXAzIGluc3RhbGwgUHlKV1QgcmVxdWVzdHMKZWNobyAiJChkYXRlKTogU3lzdGVtIHBhY2thZ2VzIHVwZGF0ZWQgc3VjY2Vzc2Z1bGx5IgoKIyBJbnN0YWxsIE5GUyBjbGllbnQgZm9yIEVGUyBtb3VudGluZwplY2hvICIkKGRhdGUpOiBJbnN0YWxsaW5nIE5GUyBjbGllbnQiCmFwdC1nZXQgLXkgaW5zdGFsbCBuZnMtY29tbW9uCmVjaG8gIiQoZGF0ZSk6IE5GUyBjbGllbnQgaW5zdGFsbGVkIHN1Y2Nlc3NmdWxseSIKCiMgU2V0dXAgQ2xvdWRXYXRjaCBsb2dnaW5nCmVjaG8gIiQoZGF0ZSk6IFNldHRpbmcgdXAgQ2xvdWRXYXRjaCBsb2dnaW5nIgoKIyBJbnN0YWxsIENsb3VkV2F0Y2ggTG9ncyBhZ2VudApjdXJsIC1vIC90bXAvYW1hem9uLWNsb3Vkd2F0Y2gtYWdlbnQuZGViIGh0dHBzOi8vczMuYW1hem9uYXdzLmNvbS9hbWF6b25jbG91ZHdhdGNoLWFnZW50L2RlYmlhbi9hbWQ2NC9sYXRlc3QvYW1hem9uLWNsb3Vkd2F0Y2gtYWdlbnQuZGViCmRwa2cgLWkgL3RtcC9hbWF6b24tY2xvdWR3YXRjaC1hZ2VudC5kZWIKcm0gL3RtcC9hbWF6b24tY2xvdWR3YXRjaC1hZ2VudC5kZWIKCiMgQ29uZmlndXJlIENsb3VkV2F0Y2ggTG9ncyBhZ2VudApjYXQgPiAvb3B0L2F3cy9hbWF6b24tY2xvdWR3YXRjaC1hZ2VudC9ldGMvYW1hem9uLWNsb3Vkd2F0Y2gtYWdlbnQuanNvbiA8PEVPRgp7CiAgImxvZ3MiOiB7CiAgICAibG9nc19jb2xsZWN0ZWQiOiB7CiAgICAgICJmaWxlcyI6IHsKICAgICAgICAiY29sbGVjdF9saXN0IjogWwogICAgICAgICAgewogICAgICAgICAgICAiZmlsZV9wYXRoIjogIi92YXIvbG9nL2dpdGh1Yi1ydW5uZXItc2V0dXAubG9nIiwKICAgICAgICAgICAgImxvZ19ncm91cF9uYW1lIjogIi9naXRodWItcnVubmVyL2dpdGh1Yi1zZWxmLWhvc3RlZC1ydW5uZXIvbG9nIiwKICAgICAgICAgICAgImxvZ19zdHJlYW1fbmFtZSI6ICJ7aW5zdGFuY2VfaWR9LXNldHVwIiwKICAgICAgICAgICAgInRpbWV6b25lIjogIlVUQyIKICAgICAgICAgIH0sCiAgICAgICAgICB7CiAgICAgICAgICAgICJmaWxlX3BhdGgiOiAiL3Zhci9sb2cvZ2l0aHViLXJ1bm5lci5sb2ciLAogICAgICAgICAgICAibG9nX2dyb3VwX25hbWUiOiAiL2dpdGh1Yi1ydW5uZXIvZ2l0aHViLXNlbGYtaG9zdGVkLXJ1bm5lci9sb2ciLAogICAgICAgICAgICAibG9nX3N0cmVhbV9uYW1lIjogIntpbnN0YW5jZV9pZH0tcnVubmVyIiwKICAgICAgICAgICAgInRpbWV6b25lIjogIlVUQyIKICAgICAgICAgIH0KICAgICAgICBdCiAgICAgIH0KICAgIH0KICB9Cn0KRU9GCgojIFN0YXJ0IENsb3VkV2F0Y2ggYWdlbnQKL29wdC9hd3MvYW1hem9uLWNsb3Vkd2F0Y2gtYWdlbnQvYmluL2FtYXpvbi1jbG91ZHdhdGNoLWFnZW50LWN0bCAtYSBmZXRjaC1jb25maWcgLW0gZWMyIC1zIC1jIGZpbGU6L29wdC9hd3MvYW1hem9uLWNsb3Vkd2F0Y2gtYWdlbnQvZXRjL2FtYXpvbi1jbG91ZHdhdGNoLWFnZW50Lmpzb24KZWNobyAiJChkYXRlKTogQ2xvdWRXYXRjaCBMb2dzIGFnZW50IGNvbmZpZ3VyZWQgYW5kIHN0YXJ0ZWQiCgojIFNldHVwIEVGUyBtb3VudAplY2hvICIkKGRhdGUpOiBTZXR0aW5nIHVwIEVGUyBtb3VudCIKbWtkaXIgLXAgL2hvbWUvcnVubmVyL193b3JrCmVjaG8gImZzLTBiNTVlOWE3MDExYmY3YzkwLmVmcy51cy13ZXN0LTIuYW1hem9uYXdzLmNvbTovIC9ob21lL3J1bm5lci9fd29yayBuZnM0IG5mc3ZlcnM9NC4xLHJzaXplPTEwNDg1NzYsd3NpemU9MTA0ODU3NixoYXJkLHRpbWVvPTYwMCxyZXRyYW5zPTIgMCAwIiA+PiAvZXRjL2ZzdGFiCm1vdW50IC9ob21lL3J1bm5lci9fd29yawplY2hvICIkKGRhdGUpOiBFRlMgbW91bnRlZCBzdWNjZXNzZnVsbHkiCgojIEluc3RhbGwgRG9ja2VyCmVjaG8gIiQoZGF0ZSk6IEluc3RhbGxpbmcgRG9ja2VyIgpjdXJsIC1mc1NMIGh0dHBzOi8vZ2V0LmRvY2tlci5jb20gLW8gZ2V0LWRvY2tlci5zaApzaCBnZXQtZG9ja2VyLnNoCnVzZXJtb2QgLWFHIGRvY2tlciB1YnVudHUKZWNobyAiJChkYXRlKTogRG9ja2VyIGluc3RhbGxlZCBzdWNjZXNzZnVsbHkiCgojIEluc3RhbGwgVGVycmFmb3JtCmVjaG8gIiQoZGF0ZSk6IEluc3RhbGxpbmcgVGVycmFmb3JtIgp3Z2V0IC1PLSBodHRwczovL2FwdC5yZWxlYXNlcy5oYXNoaWNvcnAuY29tL2dwZyB8IGdwZyAtLWRlYXJtb3IgfCB0ZWUgL3Vzci9zaGFyZS9rZXlyaW5ncy9oYXNoaWNvcnAtYXJjaGl2ZS1rZXlyaW5nLmdwZwplY2hvICJkZWIgW3NpZ25lZC1ieT0vdXNyL3NoYXJlL2tleXJpbmdzL2hhc2hpY29ycC1hcmNoaXZlLWtleXJpbmcuZ3BnXSBodHRwczovL2FwdC5yZWxlYXNlcy5oYXNoaWNvcnAuY29tICQobHNiX3JlbGVhc2UgLWNzKSBtYWluIiB8IHRlZSAvZXRjL2FwdC9zb3VyY2VzLmxpc3QuZC9oYXNoaWNvcnAubGlzdAphcHQgdXBkYXRlICYmIGFwdCBpbnN0YWxsIC15IHRlcnJhZm9ybQplY2hvICIkKGRhdGUpOiBUZXJyYWZvcm0gaW5zdGFsbGVkIHN1Y2Nlc3NmdWxseSIKCiMgQ3JlYXRlIHJ1bm5lciB1c2VyCmVjaG8gIiQoZGF0ZSk6IENyZWF0aW5nIHJ1bm5lciB1c2VyIgp1c2VyYWRkIC1tIC1zIC9iaW4vYmFzaCBydW5uZXIKdXNlcm1vZCAtYUcgZG9ja2VyIHJ1bm5lcgplY2hvICIkKGRhdGUpOiBSdW5uZXIgdXNlciBjcmVhdGVkIHN1Y2Nlc3NmdWxseSIKCiMgRG93bmxvYWQgR2l0SHViIEFjdGlvbnMgcnVubmVyCmVjaG8gIiQoZGF0ZSk6IERvd25sb2FkaW5nIEdpdEh1YiBBY3Rpb25zIHJ1bm5lciIKY2QgL2hvbWUvcnVubmVyCmN1cmwgLW8gYWN0aW9ucy1ydW5uZXItbGludXgteDY0LTIuMzIxLjAudGFyLmd6IC1MIGh0dHBzOi8vZ2l0aHViLmNvbS9hY3Rpb25zL3J1bm5lci9yZWxlYXNlcy9kb3dubG9hZC92Mi4zMjEuMC9hY3Rpb25zLXJ1bm5lci1saW51eC14NjQtMi4zMjEuMC50YXIuZ3oKdGFyIHh6ZiBhY3Rpb25zLXJ1bm5lci1saW51eC14NjQtMi4zMjEuMC50YXIuZ3oKY2hvd24gLVIgcnVubmVyOnJ1bm5lciAvaG9tZS9ydW5uZXIKZWNobyAiJChkYXRlKTogR2l0SHViIEFjdGlvbnMgcnVubmVyIGRvd25sb2FkZWQgc3VjY2Vzc2Z1bGx5IgoKIyBHZXQgR2l0SHViIGNyZWRlbnRpYWxzIGZyb20gU2VjcmV0cyBNYW5hZ2VyCmVjaG8gIiQoZGF0ZSk6IFJldHJpZXZpbmcgR2l0SHViIGNyZWRlbnRpYWxzIGZyb20gU2VjcmV0cyBNYW5hZ2VyIgpTRUNSRVQ9JChhd3Mgc2VjcmV0c21hbmFnZXIgZ2V0LXNlY3JldC12YWx1ZSAtLXNlY3JldC1pZCAiZ2l0aHViLXNlbGYtaG9zdGVkLXJ1bm5lci1jcmVkZW50aWFscy12MiIgLS1yZWdpb24gInVzLXdlc3QtMiIgLS1xdWVyeSBTZWNyZXRTdHJpbmcgLS1vdXRwdXQgdGV4dCkKQVBQX0lEPSQoZWNobyAkU0VDUkVUIHwganEgLXIgJy5hcHBfaWQnKQpJTlNUQUxMQVRJT05fSUQ9JChlY2hvICRTRUNSRVQgfCBqcSAtciAnLmluc3RhbGxhdGlvbl9pZCcpClBSSVZBVEVfS0VZPSQoZWNobyAkU0VDUkVUIHwganEgLXIgJy5wcml2YXRlX2tleScpCgojIEZvciBkZWJ1Z2dpbmcgKHNob3dpbmcgb25seSBub24tc2Vuc2l0aXZlIGRhdGEpCmVjaG8gIiQoZGF0ZSk6IEFwcCBJRDogJEFQUF9JRCIKZWNobyAiJChkYXRlKTogSW5zdGFsbGF0aW9uIElEOiAkSU5TVEFMTEFUSU9OX0lEIgplY2hvICIkKGRhdGUpOiBPcmdhbml6YXRpb246IGt1bmR1c28tb3JnIgplY2hvICIkKGRhdGUpOiBHaXRIdWIgY3JlZGVudGlhbHMgcmV0cmlldmVkIHN1Y2Nlc3NmdWxseSIKCiMgR2VuZXJhdGUgSldUIHRva2VuIGZvciBHaXRIdWIgQXBwIGF1dGhlbnRpY2F0aW9uCmVjaG8gIiQoZGF0ZSk6IEdlbmVyYXRpbmcgR2l0SHViIEFwcCBKV1QgdG9rZW4iCgojIENyZWF0ZSBQeXRob24gc2NyaXB0IGZvciBKV1QgZ2VuZXJhdGlvbgpjYXQgPiAvdG1wL2p3dF9zY3JpcHQucHkgPDxFT0ZQWVRIT04KaW1wb3J0IGp3dAppbXBvcnQgdGltZQppbXBvcnQganNvbgppbXBvcnQgcmVxdWVzdHMKaW1wb3J0IHN5cwppbXBvcnQgb3MKCnByaW50KCdERUJVRzogU3RhcnRpbmcgSldUIHNjcmlwdCcsIGZpbGU9c3lzLnN0ZGVycikKCnRyeToKICAgIHByaW50KCdERUJVRzogUmVhZGluZyBlbnZpcm9ubWVudCB2YXJpYWJsZXMnLCBmaWxlPXN5cy5zdGRlcnIpCiAgICBhcHBfaWQgPSBvcy5lbnZpcm9uWydBUFBfSUQnXQogICAgaW5zdGFsbGF0aW9uX2lkID0gb3MuZW52aXJvblsnSU5TVEFMTEFUSU9OX0lEJ10KICAgIHByaXZhdGVfa2V5ID0gb3MuZW52aXJvblsnUFJJVkFURV9LRVknXQogICAgCiAgICAjIENvbnZlcnQgZXNjYXBlZCBuZXdsaW5lcyB0byBhY3R1YWwgbmV3bGluZXMKICAgIHByaXZhdGVfa2V5ID0gcHJpdmF0ZV9rZXkucmVwbGFjZSgnXFxuJywgJ1xuJykKICAgIAogICAgcHJpbnQoJ0RFQlVHOiBBcHAgSUQ6ICcgKyBhcHBfaWQsIGZpbGU9c3lzLnN0ZGVycikKICAgIHByaW50KCdERUJVRzogSW5zdGFsbGF0aW9uIElEOiAnICsgaW5zdGFsbGF0aW9uX2lkLCBmaWxlPXN5cy5zdGRlcnIpCiAgICBwcmludCgnREVCVUc6IFByaXZhdGUga2V5IGxlbmd0aDogJyArIHN0cihsZW4ocHJpdmF0ZV9rZXkpKSwgZmlsZT1zeXMuc3RkZXJyKQogICAgCiAgICBwcmludCgnREVCVUc6IENyZWF0aW5nIEpXVCBwYXlsb2FkJywgZmlsZT1zeXMuc3RkZXJyKQogICAgcGF5bG9hZCA9IHsKICAgICAgICAnaWF0JzogaW50KHRpbWUudGltZSgpKSwKICAgICAgICAnZXhwJzogaW50KHRpbWUudGltZSgpKSArIDYwMCwKICAgICAgICAnaXNzJzogYXBwX2lkCiAgICB9CiAgICAKICAgIHByaW50KCdERUJVRzogRW5jb2RpbmcgSldUIHRva2VuJywgZmlsZT1zeXMuc3RkZXJyKQogICAgdG9rZW4gPSBqd3QuZW5jb2RlKHBheWxvYWQsIHByaXZhdGVfa2V5LCBhbGdvcml0aG09J1JTMjU2JykKICAgIHByaW50KCdERUJVRzogSldUIHRva2VuIGNyZWF0ZWQgc3VjY2Vzc2Z1bGx5JywgZmlsZT1zeXMuc3RkZXJyKQogICAgCiAgICBwcmludCgnREVCVUc6IFByZXBhcmluZyBBUEkgcmVxdWVzdCBoZWFkZXJzJywgZmlsZT1zeXMuc3RkZXJyKQogICAgaGVhZGVycyA9IHsKICAgICAgICAnQXV0aG9yaXphdGlvbic6ICdCZWFyZXIgJyArIHN0cih0b2tlbiksCiAgICAgICAgJ0FjY2VwdCc6ICdhcHBsaWNhdGlvbi92bmQuZ2l0aHViLnYzK2pzb24nCiAgICB9CiAgICAKICAgIGFwaV91cmwgPSAnaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9hcHAvaW5zdGFsbGF0aW9ucy8nICsgaW5zdGFsbGF0aW9uX2lkICsgJy9hY2Nlc3NfdG9rZW5zJwogICAgcHJpbnQoJ0RFQlVHOiBNYWtpbmcgcmVxdWVzdCB0bzogJyArIGFwaV91cmwsIGZpbGU9c3lzLnN0ZGVycikKICAgIAogICAgcmVzcG9uc2UgPSByZXF1ZXN0cy5wb3N0KGFwaV91cmwsIGhlYWRlcnM9aGVhZGVycywgdGltZW91dD0zMCkKICAgIAogICAgcHJpbnQoJ0RFQlVHOiBBUEkgcmVzcG9uc2Ugc3RhdHVzOiAnICsgc3RyKHJlc3BvbnNlLnN0YXR1c19jb2RlKSwgZmlsZT1zeXMuc3RkZXJyKQogICAgCiAgICBpZiByZXNwb25zZS5zdGF0dXNfY29kZSAhPSAyMDE6CiAgICAgICAgcHJpbnQoJ0VSUk9SOiBGYWlsZWQgdG8gZ2V0IGFjY2VzcyB0b2tlbi4gU3RhdHVzOiAnICsgc3RyKHJlc3BvbnNlLnN0YXR1c19jb2RlKSkKICAgICAgICBwcmludCgnUmVzcG9uc2UgYm9keTogJyArIHJlc3BvbnNlLnRleHQpCiAgICAgICAgc3lzLmV4aXQoMSkKICAgIAogICAgcHJpbnQoJ0RFQlVHOiBQYXJzaW5nIHJlc3BvbnNlIEpTT04nLCBmaWxlPXN5cy5zdGRlcnIpCiAgICBhY2Nlc3NfdG9rZW4gPSByZXNwb25zZS5qc29uKClbJ3Rva2VuJ10KICAgIHByaW50KCdERUJVRzogQWNjZXNzIHRva2VuIG9idGFpbmVkIHN1Y2Nlc3NmdWxseScsIGZpbGU9c3lzLnN0ZGVycikKICAgIHByaW50KGFjY2Vzc190b2tlbikKZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgcHJpbnQoJ0VSUk9SOiAnICsgc3RyKGUpLCBmaWxlPXN5cy5zdGRlcnIpCiAgICBpbXBvcnQgdHJhY2ViYWNrCiAgICB0cmFjZWJhY2sucHJpbnRfZXhjKGZpbGU9c3lzLnN0ZGVycikKICAgIHN5cy5leGl0KDEpCkVPRlBZVEhPTgoKIyBQYXNzIHZhcmlhYmxlcyB0byBQeXRob24gc2NyaXB0IHZpYSBlbnZpcm9ubWVudApleHBvcnQgQVBQX0lEPSIkQVBQX0lEIgpleHBvcnQgSU5TVEFMTEFUSU9OX0lEPSIkSU5TVEFMTEFUSU9OX0lEIgpleHBvcnQgUFJJVkFURV9LRVk9IiRQUklWQVRFX0tFWSIKCmVjaG8gIiQoZGF0ZSk6IEV4ZWN1dGluZyBKV1QgZ2VuZXJhdGlvbiBzY3JpcHQuLi4iCgojIFJ1biBKV1Qgc2NyaXB0IHdpdGggdGltZW91dAppZiAhIHRpbWVvdXQgNjAgcHl0aG9uMyAvdG1wL2p3dF9zY3JpcHQucHkgPiAvdG1wL2p3dF9vdXRwdXQudHh0IDI+IC90bXAvand0X2Vycm9yLnR4dDsgdGhlbgogICAgSldUX0VYSVRfQ09ERT0kPwogICAgZWNobyAiJChkYXRlKTogRVJST1IgLSBKV1QgZ2VuZXJhdGlvbiBmYWlsZWQgb3IgdGltZWQgb3V0IHdpdGggZXhpdCBjb2RlICRKV1RfRVhJVF9DT0RFIgogICAgZWNobyAiJChkYXRlKTogQ2hlY2sgQ2xvdWRXYXRjaCBsb2dzIGZvciBkZXRhaWxlZCBlcnJvciBpbmZvcm1hdGlvbiIKICAgIHJtIC1mIC90bXAvand0X3NjcmlwdC5weSAvdG1wL2p3dF9vdXRwdXQudHh0IC90bXAvand0X2Vycm9yLnR4dAogICAgZXhpdCAxCmZpCgpHSVRIVUJfVE9LRU49JChjYXQgL3RtcC9qd3Rfb3V0cHV0LnR4dCkKcm0gLWYgL3RtcC9qd3Rfc2NyaXB0LnB5IC90bXAvand0X291dHB1dC50eHQgL3RtcC9qd3RfZXJyb3IudHh0CgppZiBbIC16ICIkR0lUSFVCX1RPS0VOIiBdIHx8IFsgIiRHSVRIVUJfVE9LRU4iID0gIm51bGwiIF07IHRoZW4KICAgIGVjaG8gIiQoZGF0ZSk6IEVSUk9SIC0gSldUIHRva2VuIGlzIGVtcHR5IG9yIG51bGwiCiAgICBleGl0IDEKZmkKCmVjaG8gIiQoZGF0ZSk6IEdpdEh1YiBBcHAgSldUIHRva2VuIGdlbmVyYXRlZCBzdWNjZXNzZnVsbHkiCgojIEdldCByZWdpc3RyYXRpb24gdG9rZW4gZm9yIG9yZ2FuaXphdGlvbgplY2hvICIkKGRhdGUpOiBHZXR0aW5nIHJlZ2lzdHJhdGlvbiB0b2tlbiBmb3IgR2l0SHViIG9yZ2FuaXphdGlvbiIKT1JHX1VSTD0iaHR0cHM6Ly9naXRodWIuY29tL2t1bmR1c28tb3JnIgplY2hvICIkKGRhdGUpOiBPcmdhbml6YXRpb24gVVJMOiAkT1JHX1VSTCIKCmVjaG8gIiQoZGF0ZSk6IE1ha2luZyBBUEkgcmVxdWVzdCB0byBHaXRIdWIuLi4iCkFQSV9SRVNQT05TRT0kKGN1cmwgLXMgLXcgIkhUVFBfQ09ERTole2h0dHBfY29kZX0iIC1YIFBPU1QgLUggIkF1dGhvcml6YXRpb246IHRva2VuICRHSVRIVUJfVE9LRU4iICJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3Mva3VuZHVzby1vcmcvYWN0aW9ucy9ydW5uZXJzL3JlZ2lzdHJhdGlvbi10b2tlbiIpCkhUVFBfQ09ERT0kKGVjaG8gIiRBUElfUkVTUE9OU0UiIHwgZ3JlcCAtbyAiSFRUUF9DT0RFOlswLTldKiIgfCBjdXQgLWQ6IC1mMikKQVBJX0JPRFk9JChlY2hvICIkQVBJX1JFU1BPTlNFIiB8IHNlZCAncy9IVFRQX0NPREU6WzAtOV0qJC8vJykKCmVjaG8gIiQoZGF0ZSk6IEdpdEh1YiBBUEkgcmVzcG9uc2UgY29kZTogJEhUVFBfQ09ERSIKCmlmIFsgIiRIVFRQX0NPREUiICE9ICIyMDEiIF07IHRoZW4KICAgIGVjaG8gIiQoZGF0ZSk6IEVSUk9SIC0gR2l0SHViIEFQSSByZXF1ZXN0IGZhaWxlZCB3aXRoIEhUVFAgY29kZSAkSFRUUF9DT0RFIgogICAgZWNobyAiJChkYXRlKTogQ2hlY2sgR2l0SHViIEFwcCBwZXJtaXNzaW9ucyBhbmQgaW5zdGFsbGF0aW9uIgogICAgZXhpdCAxCmZpCgpSRUdfVE9LRU49JChlY2hvICIkQVBJX0JPRFkiIHwganEgLXIgJy50b2tlbicpCgppZiBbICIkUkVHX1RPS0VOIiA9ICJudWxsIiBdIHx8IFsgLXogIiRSRUdfVE9LRU4iIF07IHRoZW4KICAgIGVjaG8gIiQoZGF0ZSk6IEVSUk9SIC0gUmVnaXN0cmF0aW9uIHRva2VuIGlzIG51bGwgb3IgZW1wdHkiCiAgICBlY2hvICIkKGRhdGUpOiBHaXRIdWIgQVBJIHJldHVybmVkIGludmFsaWQgcmVzcG9uc2UiCiAgICBleGl0IDEKZmkKZWNobyAiJChkYXRlKTogUmVnaXN0cmF0aW9uIHRva2VuIG9idGFpbmVkIHN1Y2Nlc3NmdWxseSIKCiMgQ29uZmlndXJlIGFuZCBzdGFydCBydW5uZXIKZWNobyAiJChkYXRlKTogQ29uZmlndXJpbmcgR2l0SHViIHJ1bm5lciIKY2hvd24gLVIgcnVubmVyOnJ1bm5lciAvaG9tZS9ydW5uZXIvX3dvcmsKZWNobyAiJChkYXRlKTogUnVubmluZyBjb25maWcuc2ggd2l0aCBwYXJhbWV0ZXJzOiIKZWNobyAiJChkYXRlKTogVVJMOiAkT1JHX1VSTCIKZWNobyAiJChkYXRlKTogTmFtZTogJElOU1RBTkNFX0lEIgplY2hvICIkKGRhdGUpOiBMYWJlbHM6IHVzLXdlc3QtMiIKCmlmICEgc3VkbyAtdSBydW5uZXIgLi9jb25maWcuc2ggLS11cmwgIiRPUkdfVVJMIiAtLXRva2VuICIkUkVHX1RPS0VOIiAtLW5hbWUgIiRJTlNUQU5DRV9JRCIgLS13b3JrIC9ob21lL3J1bm5lci9fd29yayAtLWxhYmVscyAidXMtd2VzdC0yIiAtLXJlcGxhY2UgLS11bmF0dGVuZGVkIDI+JjE7IHRoZW4KICAgIGVjaG8gIiQoZGF0ZSk6IEVSUk9SIC0gUnVubmVyIGNvbmZpZ3VyYXRpb24gZmFpbGVkIgogICAgZXhpdCAxCmZpCmVjaG8gIiQoZGF0ZSk6IEdpdEh1YiBydW5uZXIgY29uZmlndXJlZCBzdWNjZXNzZnVsbHkiCgplY2hvICIkKGRhdGUpOiBTdGFydGluZyBHaXRIdWIgcnVubmVyIgpzdWRvIC11IHJ1bm5lciBub2h1cCAuL3J1bi5zaCA+IC92YXIvbG9nL2dpdGh1Yi1ydW5uZXIubG9nIDI+JjEgJgplY2hvICIkKGRhdGUpOiBHaXRIdWIgcnVubmVyIHN0YXJ0ZWQgaW4gYmFja2dyb3VuZCIKCiMgSW5zdGFsbCBydW5uZXIgYXMgc2VydmljZQplY2hvICIkKGRhdGUpOiBJbnN0YWxsaW5nIHJ1bm5lciBhcyBzZXJ2aWNlIgppZiAhIC4vc3ZjLnNoIGluc3RhbGwgcnVubmVyIDI+JjE7IHRoZW4KICAgIGVjaG8gIiQoZGF0ZSk6IEVSUk9SIC0gRmFpbGVkIHRvIGluc3RhbGwgcnVubmVyIHNlcnZpY2UiCiAgICBleGl0IDEKZmkKCmlmICEgLi9zdmMuc2ggc3RhcnQgMj4mMTsgdGhlbgogICAgZWNobyAiJChkYXRlKTogRVJST1IgLSBGYWlsZWQgdG8gc3RhcnQgcnVubmVyIHNlcnZpY2UiCiAgICBleGl0IDEKZmkKZWNobyAiJChkYXRlKTogUnVubmVyIHNlcnZpY2UgaW5zdGFsbGVkIGFuZCBzdGFydGVkIHN1Y2Nlc3NmdWxseSIKCmVjaG8gIiQoZGF0ZSk6IEdpdEh1YiBydW5uZXIgc2V0dXAgY29tcGxldGVkIHN1Y2Nlc3NmdWxseSI=" -> "IyEvYmluL2Jhc2gKc2V0IC1lCgojIFNldHVwIGxvZ2dpbmcKUkVHSVNUUkFUSU9OX0xPR19GSUxFPSIvdmFyL2xvZy9naXRodWItcnVubmVyLXJlZ2lzdHJhdGlvbi5sb2ciCmV4ZWMgPiA+KHRlZSAtYSAkUkVHSVNUUkFUSU9OX0xPR19GSUxFKQpleGVjIDI+JjEKCmVjaG8gIiQoZGF0ZSk6IFN0YXJ0aW5nIEdpdEh1YiBydW5uZXIgc2V0dXAiCgojIE5ldHdvcmsgY29ubmVjdGl2aXR5IHZhbGlkYXRpb24KZWNobyAiJChkYXRlKTogVmFsaWRhdGluZyBuZXR3b3JrIGNvbm5lY3Rpdml0eS4uLiIKcmV0cnlfY291bnQ9MAptYXhfcmV0cmllcz0xMiAgIyAyIG1pbnV0ZXMgdG90YWwKCnVudGlsIGN1cmwgLXMgLS1jb25uZWN0LXRpbWVvdXQgNSBodHRwczovL2F3cy5hbWF6b24uY29tID4gL2Rldi9udWxsOyBkbwogICAgcmV0cnlfY291bnQ9JCgocmV0cnlfY291bnQgKyAxKSkKICAgIGlmIFsgJHJldHJ5X2NvdW50IC1nZSAkbWF4X3JldHJpZXMgXTsgdGhlbgogICAgICAgIGVjaG8gIiQoZGF0ZSk6IEVSUk9SIC0gTmV0d29yayBjb25uZWN0aXZpdHkgZmFpbGVkIGFmdGVyICRtYXhfcmV0cmllcyBhdHRlbXB0cyIKICAgICAgICBleGl0IDEKICAgIGZpCiAgICBlY2hvICIkKGRhdGUpOiBOZXR3b3JrIG5vdCByZWFkeSwgd2FpdGluZy4uLiAoYXR0ZW1wdCAkcmV0cnlfY291bnQvJG1heF9yZXRyaWVzKSIKICAgIHNsZWVwIDEwCmRvbmUKCmVjaG8gIiQoZGF0ZSk6IE5ldHdvcmsgY29ubmVjdGl2aXR5IGNvbmZpcm1lZCIKCiMgVGVzdCBjcml0aWNhbCBBV1Mgc2VydmljZXMKZWNobyAiJChkYXRlKTogVGVzdGluZyBBV1Mgc2VydmljZXMgY29ubmVjdGl2aXR5Li4uIgphd3Nfc2VydmljZXM9KAogICAgImh0dHBzOi8vczMudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20iCiAgICAiaHR0cHM6Ly9zZWNyZXRzbWFuYWdlci51cy13ZXN0LTIuYW1hem9uYXdzLmNvbSIKICAgICJodHRwczovL2xvZ3MudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20iCikKCmZvciBzZXJ2aWNlIGluICIke2F3c19zZXJ2aWNlc1tAXX0iOyBkbwogICAgZWNobyAiJChkYXRlKTogVGVzdGluZyBjb25uZWN0aXZpdHkgdG8gJHNlcnZpY2UuLi4iCiAgICBpZiAhIGN1cmwgLXMgLS1jb25uZWN0LXRpbWVvdXQgMTAgIiRzZXJ2aWNlIiA+IC9kZXYvbnVsbDsgdGhlbgogICAgICAgIGVjaG8gIiQoZGF0ZSk6IFdBUk5JTkcgLSBDYW5ub3QgcmVhY2ggJHNlcnZpY2UiCiAgICBlbHNlCiAgICAgICAgZWNobyAiJChkYXRlKTogU3VjY2Vzc2Z1bGx5IGNvbm5lY3RlZCB0byAkc2VydmljZSIKICAgIGZpCmRvbmUKCmVjaG8gIiQoZGF0ZSk6IEFXUyBzZXJ2aWNlcyBjb25uZWN0aXZpdHkgdGVzdCBjb21wbGV0ZWQiCgojIEdldCBpbnN0YW5jZSBJRCBmb3IgcnVubmVyIG5hbWluZyB1c2luZyBJTURTdjIKVE9LRU49JChjdXJsIC1YIFBVVCAiaHR0cDovLzE2OS4yNTQuMTY5LjI1NC9sYXRlc3QvYXBpL3Rva2VuIiAtSCAiWC1hd3MtZWMyLW1ldGFkYXRhLXRva2VuLXR0bC1zZWNvbmRzOiAyMTYwMCIpCklOU1RBTkNFX0lEPSQoY3VybCAtSCAiWC1hd3MtZWMyLW1ldGFkYXRhLXRva2VuOiAkVE9LRU4iIC1zIGh0dHA6Ly8xNjkuMjU0LjE2OS4yNTQvbGF0ZXN0L21ldGEtZGF0YS9pbnN0YW5jZS1pZCkKCiMgVXBkYXRlIHN5c3RlbQplY2hvICIkKGRhdGUpOiBVcGRhdGluZyBzeXN0ZW0gcGFja2FnZXMiCmFwdC1nZXQgdXBkYXRlCmFwdC1nZXQgaW5zdGFsbCAteSBjdXJsIGpxIGF3c2NsaSBweXRob24zLXBpcCBnaXQgYmludXRpbHMgbmZzLWNvbW1vbgpwaXAzIGluc3RhbGwgUHlKV1QgcmVxdWVzdHMKZWNobyAiJChkYXRlKTogU3lzdGVtIHBhY2thZ2VzIHVwZGF0ZWQgc3VjY2Vzc2Z1bGx5IgoKIyBJbnN0YWxsIE5GUyBjbGllbnQgZm9yIEVGUyBtb3VudGluZwplY2hvICIkKGRhdGUpOiBJbnN0YWxsaW5nIE5GUyBjbGllbnQiCmFwdC1nZXQgLXkgaW5zdGFsbCBuZnMtY29tbW9uCmVjaG8gIiQoZGF0ZSk6IE5GUyBjbGllbnQgaW5zdGFsbGVkIHN1Y2Nlc3NmdWxseSIKCiMgU2V0dXAgQ2xvdWRXYXRjaCBsb2dnaW5nCmVjaG8gIiQoZGF0ZSk6IFNldHRpbmcgdXAgQ2xvdWRXYXRjaCBsb2dnaW5nIgoKIyBJbnN0YWxsIENsb3VkV2F0Y2ggTG9ncyBhZ2VudApjdXJsIC1vIC90bXAvYW1hem9uLWNsb3Vkd2F0Y2gtYWdlbnQuZGViIGh0dHBzOi8vczMuYW1hem9uYXdzLmNvbS9hbWF6b25jbG91ZHdhdGNoLWFnZW50L2RlYmlhbi9hbWQ2NC9sYXRlc3QvYW1hem9uLWNsb3Vkd2F0Y2gtYWdlbnQuZGViCmRwa2cgLWkgL3RtcC9hbWF6b24tY2xvdWR3YXRjaC1hZ2VudC5kZWIKcm0gL3RtcC9hbWF6b24tY2xvdWR3YXRjaC1hZ2VudC5kZWIKCiMgQ29uZmlndXJlIENsb3VkV2F0Y2ggTG9ncyBhZ2VudApjYXQgPiAvb3B0L2F3cy9hbWF6b24tY2xvdWR3YXRjaC1hZ2VudC9ldGMvYW1hem9uLWNsb3Vkd2F0Y2gtYWdlbnQuanNvbiA8PEVPRgp7CiAgImxvZ3MiOiB7CiAgICAibG9nc19jb2xsZWN0ZWQiOiB7CiAgICAgICJmaWxlcyI6IHsKICAgICAgICAiY29sbGVjdF9saXN0IjogWwogICAgICAgICAgewogICAgICAgICAgICAiZmlsZV9wYXRoIjogIi92YXIvbG9nL2dpdGh1Yi1ydW5uZXItcmVnaXN0cmF0aW9uLmxvZyIsCiAgICAgICAgICAgICJsb2dfZ3JvdXBfbmFtZSI6ICIvZ2l0aHViLXJ1bm5lci9naXRodWItc2VsZi1ob3N0ZWQtcnVubmVyL2xpZmVjeWNsZSIsCiAgICAgICAgICAgICJsb2dfc3RyZWFtX25hbWUiOiAie2luc3RhbmNlX2lkfS9yZWdpc3RyYXRpb24iLAogICAgICAgICAgICAidGltZXpvbmUiOiAiVVRDIgogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgImZpbGVfcGF0aCI6ICIvdmFyL2xvZy9naXRodWItcnVubmVyLWRlcmVnaXN0cmF0aW9uLmxvZyIsCiAgICAgICAgICAgICJsb2dfZ3JvdXBfbmFtZSI6ICIvZ2l0aHViLXJ1bm5lci9naXRodWItc2VsZi1ob3N0ZWQtcnVubmVyL2xpZmVjeWNsZSIsCiAgICAgICAgICAgICJsb2dfc3RyZWFtX25hbWUiOiAie2luc3RhbmNlX2lkfS9kZXJlZ2lzdHJhdGlvbiIsCiAgICAgICAgICAgICJ0aW1lem9uZSI6ICJVVEMiCiAgICAgICAgICB9LAogICAgICAgICAgewogICAgICAgICAgICAiZmlsZV9wYXRoIjogIi92YXIvbG9nL2dpdGh1Yi1ydW5uZXIubG9nIiwKICAgICAgICAgICAgImxvZ19ncm91cF9uYW1lIjogIi9naXRodWItcnVubmVyL2dpdGh1Yi1zZWxmLWhvc3RlZC1ydW5uZXIvbGlmZWN5Y2xlIiwKICAgICAgICAgICAgImxvZ19zdHJlYW1fbmFtZSI6ICJ7aW5zdGFuY2VfaWR9L2V4ZWN1dGlvbiIsCiAgICAgICAgICAgICJ0aW1lem9uZSI6ICJVVEMiCiAgICAgICAgICB9CiAgICAgICAgXQogICAgICB9CiAgICB9CiAgfQp9CkVPRgoKIyBTdGFydCBDbG91ZFdhdGNoIGFnZW50Ci9vcHQvYXdzL2FtYXpvbi1jbG91ZHdhdGNoLWFnZW50L2Jpbi9hbWF6b24tY2xvdWR3YXRjaC1hZ2VudC1jdGwgLWEgZmV0Y2gtY29uZmlnIC1tIGVjMiAtcyAtYyBmaWxlOi9vcHQvYXdzL2FtYXpvbi1jbG91ZHdhdGNoLWFnZW50L2V0Yy9hbWF6b24tY2xvdWR3YXRjaC1hZ2VudC5qc29uCmVjaG8gIiQoZGF0ZSk6IENsb3VkV2F0Y2ggTG9ncyBhZ2VudCBjb25maWd1cmVkIGFuZCBzdGFydGVkIgoKIyBTZXR1cCBFRlMgbW91bnQKZWNobyAiJChkYXRlKTogU2V0dGluZyB1cCBFRlMgbW91bnQiCm1rZGlyIC1wIC9ob21lL3J1bm5lci9fd29yawplY2hvICJmcy0wYjU1ZTlhNzAxMWJmN2M5MC5lZnMudXMtd2VzdC0yLmFtYXpvbmF3cy5jb206LyAvaG9tZS9ydW5uZXIvX3dvcmsgbmZzNCBuZnN2ZXJzPTQuMSxyc2l6ZT0xMDQ4NTc2LHdzaXplPTEwNDg1NzYsaGFyZCx0aW1lbz02MDAscmV0cmFucz0yIDAgMCIgPj4gL2V0Yy9mc3RhYgptb3VudCAvaG9tZS9ydW5uZXIvX3dvcmsKZWNobyAiJChkYXRlKTogRUZTIG1vdW50ZWQgc3VjY2Vzc2Z1bGx5IgoKIyBJbnN0YWxsIERvY2tlcgplY2hvICIkKGRhdGUpOiBJbnN0YWxsaW5nIERvY2tlciIKY3VybCAtZnNTTCBodHRwczovL2dldC5kb2NrZXIuY29tIC1vIGdldC1kb2NrZXIuc2gKc2ggZ2V0LWRvY2tlci5zaAp1c2VybW9kIC1hRyBkb2NrZXIgdWJ1bnR1CmVjaG8gIiQoZGF0ZSk6IERvY2tlciBpbnN0YWxsZWQgc3VjY2Vzc2Z1bGx5IgoKIyBJbnN0YWxsIFRlcnJhZm9ybQplY2hvICIkKGRhdGUpOiBJbnN0YWxsaW5nIFRlcnJhZm9ybSIKd2dldCAtTy0gaHR0cHM6Ly9hcHQucmVsZWFzZXMuaGFzaGljb3JwLmNvbS9ncGcgfCBncGcgLS1kZWFybW9yIHwgdGVlIC91c3Ivc2hhcmUva2V5cmluZ3MvaGFzaGljb3JwLWFyY2hpdmUta2V5cmluZy5ncGcKZWNobyAiZGViIFtzaWduZWQtYnk9L3Vzci9zaGFyZS9rZXlyaW5ncy9oYXNoaWNvcnAtYXJjaGl2ZS1rZXlyaW5nLmdwZ10gaHR0cHM6Ly9hcHQucmVsZWFzZXMuaGFzaGljb3JwLmNvbSAkKGxzYl9yZWxlYXNlIC1jcykgbWFpbiIgfCB0ZWUgL2V0Yy9hcHQvc291cmNlcy5saXN0LmQvaGFzaGljb3JwLmxpc3QKYXB0IHVwZGF0ZSAmJiBhcHQgaW5zdGFsbCAteSB0ZXJyYWZvcm0KZWNobyAiJChkYXRlKTogVGVycmFmb3JtIGluc3RhbGxlZCBzdWNjZXNzZnVsbHkiCgojIENyZWF0ZSBydW5uZXIgdXNlcgplY2hvICIkKGRhdGUpOiBDcmVhdGluZyBydW5uZXIgdXNlciIKdXNlcmFkZCAtbSAtcyAvYmluL2Jhc2ggcnVubmVyCnVzZXJtb2QgLWFHIGRvY2tlciBydW5uZXIKZWNobyAiJChkYXRlKTogUnVubmVyIHVzZXIgY3JlYXRlZCBzdWNjZXNzZnVsbHkiCgojIERvd25sb2FkIEdpdEh1YiBBY3Rpb25zIHJ1bm5lcgplY2hvICIkKGRhdGUpOiBEb3dubG9hZGluZyBHaXRIdWIgQWN0aW9ucyBydW5uZXIiCmNkIC9ob21lL3J1bm5lcgpjdXJsIC1vIGFjdGlvbnMtcnVubmVyLWxpbnV4LXg2NC0yLjMyMS4wLnRhci5neiAtTCBodHRwczovL2dpdGh1Yi5jb20vYWN0aW9ucy9ydW5uZXIvcmVsZWFzZXMvZG93bmxvYWQvdjIuMzIxLjAvYWN0aW9ucy1ydW5uZXItbGludXgteDY0LTIuMzIxLjAudGFyLmd6CnRhciB4emYgYWN0aW9ucy1ydW5uZXItbGludXgteDY0LTIuMzIxLjAudGFyLmd6CmVjaG8gIiQoZGF0ZSk6IEdpdEh1YiBBY3Rpb25zIHJ1bm5lciBkb3dubG9hZGVkIHN1Y2Nlc3NmdWxseSIKCiMgR2V0IEdpdEh1YiBjcmVkZW50aWFscyBmcm9tIFNlY3JldHMgTWFuYWdlcgplY2hvICIkKGRhdGUpOiBSZXRyaWV2aW5nIEdpdEh1YiBjcmVkZW50aWFscyBmcm9tIFNlY3JldHMgTWFuYWdlciIKU0VDUkVUPSQoYXdzIHNlY3JldHNtYW5hZ2VyIGdldC1zZWNyZXQtdmFsdWUgLS1zZWNyZXQtaWQgImdpdGh1Yi1zZWxmLWhvc3RlZC1ydW5uZXItY3JlZGVudGlhbHMtdjIiIC0tcmVnaW9uICJ1cy13ZXN0LTIiIC0tcXVlcnkgU2VjcmV0U3RyaW5nIC0tb3V0cHV0IHRleHQpCkFQUF9JRD0kKGVjaG8gJFNFQ1JFVCB8IGpxIC1yICcuYXBwX2lkJykKSU5TVEFMTEFUSU9OX0lEPSQoZWNobyAkU0VDUkVUIHwganEgLXIgJy5pbnN0YWxsYXRpb25faWQnKQpQUklWQVRFX0tFWT0kKGVjaG8gJFNFQ1JFVCB8IGpxIC1yICcucHJpdmF0ZV9rZXknKQoKIyBGb3IgZGVidWdnaW5nIChzaG93aW5nIG9ubHkgbm9uLXNlbnNpdGl2ZSBkYXRhKQplY2hvICIkKGRhdGUpOiBBcHAgSUQ6ICRBUFBfSUQiCmVjaG8gIiQoZGF0ZSk6IEluc3RhbGxhdGlvbiBJRDogJElOU1RBTExBVElPTl9JRCIKZWNobyAiJChkYXRlKTogT3JnYW5pemF0aW9uOiBrdW5kdXNvLW9yZyIKZWNobyAiJChkYXRlKTogR2l0SHViIGNyZWRlbnRpYWxzIHJldHJpZXZlZCBzdWNjZXNzZnVsbHkiCgojIEdlbmVyYXRlIEpXVCB0b2tlbiBmb3IgR2l0SHViIEFwcCBhdXRoZW50aWNhdGlvbgplY2hvICIkKGRhdGUpOiBHZW5lcmF0aW5nIEdpdEh1YiBBcHAgSldUIHRva2VuIgoKIyBDcmVhdGUgUHl0aG9uIHNjcmlwdCBmb3IgSldUIGdlbmVyYXRpb24KY2F0ID4gL3RtcC9qd3Rfc2NyaXB0LnB5IDw8RU9GUFlUSE9OCmltcG9ydCBqd3QKaW1wb3J0IHRpbWUKaW1wb3J0IGpzb24KaW1wb3J0IHJlcXVlc3RzCmltcG9ydCBzeXMKaW1wb3J0IG9zCgpwcmludCgnREVCVUc6IFN0YXJ0aW5nIEpXVCBzY3JpcHQnLCBmaWxlPXN5cy5zdGRlcnIpCgp0cnk6CiAgICBwcmludCgnREVCVUc6IFJlYWRpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzJywgZmlsZT1zeXMuc3RkZXJyKQogICAgYXBwX2lkID0gb3MuZW52aXJvblsnQVBQX0lEJ10KICAgIGluc3RhbGxhdGlvbl9pZCA9IG9zLmVudmlyb25bJ0lOU1RBTExBVElPTl9JRCddCiAgICBwcml2YXRlX2tleSA9IG9zLmVudmlyb25bJ1BSSVZBVEVfS0VZJ10KICAgIAogICAgIyBDb252ZXJ0IGVzY2FwZWQgbmV3bGluZXMgdG8gYWN0dWFsIG5ld2xpbmVzCiAgICBwcml2YXRlX2tleSA9IHByaXZhdGVfa2V5LnJlcGxhY2UoJ1xcbicsICdcbicpCiAgICAKICAgIHByaW50KCdERUJVRzogQXBwIElEOiAnICsgYXBwX2lkLCBmaWxlPXN5cy5zdGRlcnIpCiAgICBwcmludCgnREVCVUc6IEluc3RhbGxhdGlvbiBJRDogJyArIGluc3RhbGxhdGlvbl9pZCwgZmlsZT1zeXMuc3RkZXJyKQogICAgcHJpbnQoJ0RFQlVHOiBQcml2YXRlIGtleSBsZW5ndGg6ICcgKyBzdHIobGVuKHByaXZhdGVfa2V5KSksIGZpbGU9c3lzLnN0ZGVycikKICAgIAogICAgcHJpbnQoJ0RFQlVHOiBDcmVhdGluZyBKV1QgcGF5bG9hZCcsIGZpbGU9c3lzLnN0ZGVycikKICAgIHBheWxvYWQgPSB7CiAgICAgICAgJ2lhdCc6IGludCh0aW1lLnRpbWUoKSksCiAgICAgICAgJ2V4cCc6IGludCh0aW1lLnRpbWUoKSkgKyA2MDAsCiAgICAgICAgJ2lzcyc6IGFwcF9pZAogICAgfQogICAgCiAgICBwcmludCgnREVCVUc6IEVuY29kaW5nIEpXVCB0b2tlbicsIGZpbGU9c3lzLnN0ZGVycikKICAgIHRva2VuID0gand0LmVuY29kZShwYXlsb2FkLCBwcml2YXRlX2tleSwgYWxnb3JpdGhtPSdSUzI1NicpCiAgICBwcmludCgnREVCVUc6IEpXVCB0b2tlbiBjcmVhdGVkIHN1Y2Nlc3NmdWxseScsIGZpbGU9c3lzLnN0ZGVycikKICAgIAogICAgcHJpbnQoJ0RFQlVHOiBQcmVwYXJpbmcgQVBJIHJlcXVlc3QgaGVhZGVycycsIGZpbGU9c3lzLnN0ZGVycikKICAgIGhlYWRlcnMgPSB7CiAgICAgICAgJ0F1dGhvcml6YXRpb24nOiAnQmVhcmVyICcgKyBzdHIodG9rZW4pLAogICAgICAgICdBY2NlcHQnOiAnYXBwbGljYXRpb24vdm5kLmdpdGh1Yi52Mytqc29uJwogICAgfQogICAgCiAgICBhcGlfdXJsID0gJ2h0dHBzOi8vYXBpLmdpdGh1Yi5jb20vYXBwL2luc3RhbGxhdGlvbnMvJyArIGluc3RhbGxhdGlvbl9pZCArICcvYWNjZXNzX3Rva2VucycKICAgIHByaW50KCdERUJVRzogTWFraW5nIHJlcXVlc3QgdG86ICcgKyBhcGlfdXJsLCBmaWxlPXN5cy5zdGRlcnIpCiAgICAKICAgIHJlc3BvbnNlID0gcmVxdWVzdHMucG9zdChhcGlfdXJsLCBoZWFkZXJzPWhlYWRlcnMsIHRpbWVvdXQ9MzApCiAgICAKICAgIHByaW50KCdERUJVRzogQVBJIHJlc3BvbnNlIHN0YXR1czogJyArIHN0cihyZXNwb25zZS5zdGF0dXNfY29kZSksIGZpbGU9c3lzLnN0ZGVycikKICAgIAogICAgaWYgcmVzcG9uc2Uuc3RhdHVzX2NvZGUgIT0gMjAxOgogICAgICAgIHByaW50KCdFUlJPUjogRmFpbGVkIHRvIGdldCBhY2Nlc3MgdG9rZW4uIFN0YXR1czogJyArIHN0cihyZXNwb25zZS5zdGF0dXNfY29kZSkpCiAgICAgICAgcHJpbnQoJ1Jlc3BvbnNlIGJvZHk6ICcgKyByZXNwb25zZS50ZXh0KQogICAgICAgIHN5cy5leGl0KDEpCiAgICAKICAgIHByaW50KCdERUJVRzogUGFyc2luZyByZXNwb25zZSBKU09OJywgZmlsZT1zeXMuc3RkZXJyKQogICAgYWNjZXNzX3Rva2VuID0gcmVzcG9uc2UuanNvbigpWyd0b2tlbiddCiAgICBwcmludCgnREVCVUc6IEFjY2VzcyB0b2tlbiBvYnRhaW5lZCBzdWNjZXNzZnVsbHknLCBmaWxlPXN5cy5zdGRlcnIpCiAgICBwcmludChhY2Nlc3NfdG9rZW4pCmV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgIHByaW50KCdFUlJPUjogJyArIHN0cihlKSwgZmlsZT1zeXMuc3RkZXJyKQogICAgaW1wb3J0IHRyYWNlYmFjawogICAgdHJhY2ViYWNrLnByaW50X2V4YyhmaWxlPXN5cy5zdGRlcnIpCiAgICBzeXMuZXhpdCgxKQpFT0ZQWVRIT04KCiMgUGFzcyB2YXJpYWJsZXMgdG8gUHl0aG9uIHNjcmlwdCB2aWEgZW52aXJvbm1lbnQKZXhwb3J0IEFQUF9JRD0iJEFQUF9JRCIKZXhwb3J0IElOU1RBTExBVElPTl9JRD0iJElOU1RBTExBVElPTl9JRCIKZXhwb3J0IFBSSVZBVEVfS0VZPSIkUFJJVkFURV9LRVkiCgplY2hvICIkKGRhdGUpOiBFeGVjdXRpbmcgSldUIGdlbmVyYXRpb24gc2NyaXB0Li4uIgoKIyBSdW4gSldUIHNjcmlwdCB3aXRoIHRpbWVvdXQKaWYgISB0aW1lb3V0IDYwIHB5dGhvbjMgL3RtcC9qd3Rfc2NyaXB0LnB5ID4gL3RtcC9qd3Rfb3V0cHV0LnR4dCAyPiAvdG1wL2p3dF9lcnJvci50eHQ7IHRoZW4KICAgIEpXVF9FWElUX0NPREU9JD8KICAgIGVjaG8gIiQoZGF0ZSk6IEVSUk9SIC0gSldUIGdlbmVyYXRpb24gZmFpbGVkIG9yIHRpbWVkIG91dCB3aXRoIGV4aXQgY29kZSAkSldUX0VYSVRfQ09ERSIKICAgIGVjaG8gIiQoZGF0ZSk6IENoZWNrIENsb3VkV2F0Y2ggbG9ncyBmb3IgZGV0YWlsZWQgZXJyb3IgaW5mb3JtYXRpb24iCiAgICBybSAtZiAvdG1wL2p3dF9zY3JpcHQucHkgL3RtcC9qd3Rfb3V0cHV0LnR4dCAvdG1wL2p3dF9lcnJvci50eHQKICAgIGV4aXQgMQpmaQoKR0lUSFVCX1RPS0VOPSQoY2F0IC90bXAvand0X291dHB1dC50eHQpCnJtIC1mIC90bXAvand0X3NjcmlwdC5weSAvdG1wL2p3dF9vdXRwdXQudHh0IC90bXAvand0X2Vycm9yLnR4dAoKaWYgWyAteiAiJEdJVEhVQl9UT0tFTiIgXSB8fCBbICIkR0lUSFVCX1RPS0VOIiA9ICJudWxsIiBdOyB0aGVuCiAgICBlY2hvICIkKGRhdGUpOiBFUlJPUiAtIEpXVCB0b2tlbiBpcyBlbXB0eSBvciBudWxsIgogICAgZXhpdCAxCmZpCgplY2hvICIkKGRhdGUpOiBHaXRIdWIgQXBwIEpXVCB0b2tlbiBnZW5lcmF0ZWQgc3VjY2Vzc2Z1bGx5IgoKIyBHZXQgcmVnaXN0cmF0aW9uIHRva2VuIGZvciBvcmdhbml6YXRpb24KZWNobyAiJChkYXRlKTogR2V0dGluZyByZWdpc3RyYXRpb24gdG9rZW4gZm9yIEdpdEh1YiBvcmdhbml6YXRpb24iCk9SR19VUkw9Imh0dHBzOi8vZ2l0aHViLmNvbS9rdW5kdXNvLW9yZyIKZWNobyAiJChkYXRlKTogT3JnYW5pemF0aW9uIFVSTDogJE9SR19VUkwiCgplY2hvICIkKGRhdGUpOiBNYWtpbmcgQVBJIHJlcXVlc3QgdG8gR2l0SHViLi4uIgpBUElfUkVTUE9OU0U9JChjdXJsIC1zIC13ICJIVFRQX0NPREU6JXtodHRwX2NvZGV9IiAtWCBQT1NUIC1IICJBdXRob3JpemF0aW9uOiB0b2tlbiAkR0lUSFVCX1RPS0VOIiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzL2t1bmR1c28tb3JnL2FjdGlvbnMvcnVubmVycy9yZWdpc3RyYXRpb24tdG9rZW4iKQpIVFRQX0NPREU9JChlY2hvICIkQVBJX1JFU1BPTlNFIiB8IGdyZXAgLW8gIkhUVFBfQ09ERTpbMC05XSoiIHwgY3V0IC1kOiAtZjIpCkFQSV9CT0RZPSQoZWNobyAiJEFQSV9SRVNQT05TRSIgfCBzZWQgJ3MvSFRUUF9DT0RFOlswLTldKiQvLycpCgplY2hvICIkKGRhdGUpOiBHaXRIdWIgQVBJIHJlc3BvbnNlIGNvZGU6ICRIVFRQX0NPREUiCgppZiBbICIkSFRUUF9DT0RFIiAhPSAiMjAxIiBdOyB0aGVuCiAgICBlY2hvICIkKGRhdGUpOiBFUlJPUiAtIEdpdEh1YiBBUEkgcmVxdWVzdCBmYWlsZWQgd2l0aCBIVFRQIGNvZGUgJEhUVFBfQ09ERSIKICAgIGVjaG8gIiQoZGF0ZSk6IENoZWNrIEdpdEh1YiBBcHAgcGVybWlzc2lvbnMgYW5kIGluc3RhbGxhdGlvbiIKICAgIGV4aXQgMQpmaQoKUkVHX1RPS0VOPSQoZWNobyAiJEFQSV9CT0RZIiB8IGpxIC1yICcudG9rZW4nKQoKaWYgWyAiJFJFR19UT0tFTiIgPSAibnVsbCIgXSB8fCBbIC16ICIkUkVHX1RPS0VOIiBdOyB0aGVuCiAgICBlY2hvICIkKGRhdGUpOiBFUlJPUiAtIFJlZ2lzdHJhdGlvbiB0b2tlbiBpcyBudWxsIG9yIGVtcHR5IgogICAgZWNobyAiJChkYXRlKTogR2l0SHViIEFQSSByZXR1cm5lZCBpbnZhbGlkIHJlc3BvbnNlIgogICAgZXhpdCAxCmZpCmVjaG8gIiQoZGF0ZSk6IFJlZ2lzdHJhdGlvbiB0b2tlbiBvYnRhaW5lZCBzdWNjZXNzZnVsbHkiCgojIENvbmZpZ3VyZSBhbmQgc3RhcnQgcnVubmVyCmVjaG8gIiQoZGF0ZSk6IENvbmZpZ3VyaW5nIEdpdEh1YiBydW5uZXIiCmVjaG8gIiQoZGF0ZSk6IFJ1bm5pbmcgY29uZmlnLnNoIHdpdGggcGFyYW1ldGVyczoiCmVjaG8gIiQoZGF0ZSk6IFVSTDogJE9SR19VUkwiCmVjaG8gIiQoZGF0ZSk6IE5hbWU6ICRJTlNUQU5DRV9JRCIKZWNobyAiJChkYXRlKTogTGFiZWxzOiB1cy13ZXN0LTIiCgppZiAhIHN1ZG8gLXUgcnVubmVyIC4vY29uZmlnLnNoIC0tdXJsICIkT1JHX1VSTCIgLS10b2tlbiAiJFJFR19UT0tFTiIgLS1uYW1lICIkSU5TVEFOQ0VfSUQiIC0td29yayAvaG9tZS9ydW5uZXIvX3dvcmsgLS1sYWJlbHMgInVzLXdlc3QtMiIgLS1yZXBsYWNlIC0tdW5hdHRlbmRlZCAyPiYxOyB0aGVuCiAgICBlY2hvICIkKGRhdGUpOiBFUlJPUiAtIFJ1bm5lciBjb25maWd1cmF0aW9uIGZhaWxlZCIKICAgIGV4aXQgMQpmaQplY2hvICIkKGRhdGUpOiBHaXRIdWIgcnVubmVyIGNvbmZpZ3VyZWQgc3VjY2Vzc2Z1bGx5IgoKZWNobyAiJChkYXRlKTogU3RhcnRpbmcgR2l0SHViIHJ1bm5lciIKc3VkbyAtdSBydW5uZXIgbm9odXAgLi9ydW4uc2ggPiAvdmFyL2xvZy9naXRodWItcnVubmVyLmxvZyAyPiYxICYKZWNobyAiJChkYXRlKTogR2l0SHViIHJ1bm5lciBzdGFydGVkIGluIGJhY2tncm91bmQiCgojIEluc3RhbGwgcnVubmVyIGFzIHNlcnZpY2UKZWNobyAiJChkYXRlKTogSW5zdGFsbGluZyBydW5uZXIgYXMgc2VydmljZSIKaWYgISAuL3N2Yy5zaCBpbnN0YWxsIHJ1bm5lciAyPiYxOyB0aGVuCiAgICBlY2hvICIkKGRhdGUpOiBFUlJPUiAtIEZhaWxlZCB0byBpbnN0YWxsIHJ1bm5lciBzZXJ2aWNlIgogICAgZXhpdCAxCmZpCgppZiAhIC4vc3ZjLnNoIHN0YXJ0IDI+JjE7IHRoZW4KICAgIGVjaG8gIiQoZGF0ZSk6IEVSUk9SIC0gRmFpbGVkIHRvIHN0YXJ0IHJ1bm5lciBzZXJ2aWNlIgogICAgZXhpdCAxCmZpCmVjaG8gIiQoZGF0ZSk6IFJ1bm5lciBzZXJ2aWNlIGluc3RhbGxlZCBhbmQgc3RhcnRlZCBzdWNjZXNzZnVsbHkiCgojIFNldHVwIGRlcmVnaXN0cmF0aW9uIHNjcmlwdAplY2hvICIkKGRhdGUpOiBTZXR0aW5nIHVwIHJ1bm5lciBkZXJlZ2lzdHJhdGlvbiBzZXJ2aWNlIgphd3Mgc3NtIGdldC1wYXJhbWV0ZXIgLS1uYW1lICIvZ2l0aHViLXNlbGYtaG9zdGVkLXJ1bm5lci9kZXJlZ2lzdHJhdGlvbi1zY3JpcHQiIC0td2l0aC1kZWNyeXB0aW9uIC0tcmVnaW9uICJ1cy13ZXN0LTIiIC0tcXVlcnkgUGFyYW1ldGVyLlZhbHVlIC0tb3V0cHV0IHRleHQgPiAvdXNyL2xvY2FsL2Jpbi9kZXJlZ2lzdGVyLXJ1bm5lci5zaApjaG1vZCAreCAvdXNyL2xvY2FsL2Jpbi9kZXJlZ2lzdGVyLXJ1bm5lci5zaAoKIyBDcmVhdGUgc3lzdGVtZCBzZXJ2aWNlIGZvciBkZXJlZ2lzdHJhdGlvbgpjYXQgPiAvZXRjL3N5c3RlbWQvc3lzdGVtL2dpdGh1Yi1ydW5uZXItZGVyZWdpc3Rlci5zZXJ2aWNlIDw8RU9GCltVbml0XQpEZXNjcmlwdGlvbj1HaXRIdWIgUnVubmVyIERlcmVnaXN0cmF0aW9uCkRlZmF1bHREZXBlbmRlbmNpZXM9ZmFsc2UKQmVmb3JlPXNodXRkb3duLnRhcmdldCByZWJvb3QudGFyZ2V0IGhhbHQudGFyZ2V0CgpbU2VydmljZV0KVHlwZT1vbmVzaG90ClJlbWFpbkFmdGVyRXhpdD10cnVlCkV4ZWNTdGFydD0vYmluL3RydWUKRXhlY1N0b3A9L3Vzci9sb2NhbC9iaW4vZGVyZWdpc3Rlci1ydW5uZXIuc2gKVGltZW91dFN0b3BTZWM9MzAKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCnN5c3RlbWN0bCBlbmFibGUgZ2l0aHViLXJ1bm5lci1kZXJlZ2lzdGVyLnNlcnZpY2UKc3lzdGVtY3RsIHN0YXJ0IGdpdGh1Yi1ydW5uZXItZGVyZWdpc3Rlci5zZXJ2aWNlCmVjaG8gIiQoZGF0ZSk6IERlcmVnaXN0cmF0aW9uIHNlcnZpY2UgY29uZmlndXJlZCIKCmVjaG8gIiQoZGF0ZSk6IEdpdEh1YiBydW5uZXIgc2V0dXAgY29tcGxldGVkIHN1Y2Nlc3NmdWxseSI="
        # (16 unchanged attributes hidden)

        # (3 unchanged blocks hidden)
    }

  # aws_sns_topic.runner_lifecycle will be created
  + resource "aws_sns_topic" "runner_lifecycle" {
      + arn                         = (known after apply)
      + beginning_archive_time      = (known after apply)
      + content_based_deduplication = false
      + fifo_topic                  = false
      + id                          = (known after apply)
      + name                        = "github-self-hosted-runner-lifecycle"
      + name_prefix                 = (known after apply)
      + owner                       = (known after apply)
      + policy                      = (known after apply)
      + signature_version           = (known after apply)
      + tags_all                    = {
          + "Source" = "https://github.com/kunduso-org/github-self-hosted-runner-amazon-ec2-terraform"
        }
      + tracing_config              = (known after apply)
    }

  # aws_sns_topic_subscription.runner_lifecycle will be created
  + resource "aws_sns_topic_subscription" "runner_lifecycle" {
      + arn                             = (known after apply)
      + confirmation_timeout_in_minutes = 1
      + confirmation_was_authenticated  = (known after apply)
      + endpoint                        = (known after apply)
      + endpoint_auto_confirms          = false
      + filter_policy_scope             = (known after apply)
      + id                              = (known after apply)
      + owner_id                        = (known after apply)
      + pending_confirmation            = (known after apply)
      + protocol                        = "lambda"
      + raw_message_delivery            = false
      + topic_arn                       = (known after apply)
    }

  # aws_ssm_parameter.deregistration_script will be created
  + resource "aws_ssm_parameter" "deregistration_script" {
      + arn            = (known after apply)
      + data_type      = (known after apply)
      + has_value_wo   = (known after apply)
      + id             = (known after apply)
      + insecure_value = (known after apply)
      + key_id         = "arn:aws:kms:us-west-2:743794601996:key/8c717a58-141f-4ddd-88eb-d30645daebdb"
      + name           = "/github-self-hosted-runner/deregistration-script"
      + tags           = {
          + "Name" = "github-self-hosted-runner-deregistration-script"
        }
      + tags_all       = {
          + "Name"   = "github-self-hosted-runner-deregistration-script"
          + "Source" = "https://github.com/kunduso-org/github-self-hosted-runner-amazon-ec2-terraform"
        }
      + tier           = (known after apply)
      + type           = "SecureString"
      + value          = (sensitive value)
      + value_wo       = (write-only attribute)
      + version        = (known after apply)
    }

  # aws_ssm_parameter.nat_gateway_public_ips will be updated in-place
  ~ resource "aws_ssm_parameter" "nat_gateway_public_ips" {
        id              = "/github-self-hosted-runner-ip-address"
      + key_id          = "arn:aws:kms:us-west-2:743794601996:key/8c717a58-141f-4ddd-88eb-d30645daebdb"
        name            = "/github-self-hosted-runner-ip-address"
        tags            = {
            "Name" = "github-self-hosted-runner-ip-addresses"
        }
        # (10 unchanged attributes hidden)
    }

Plan: 12 to add, 6 to change, 1 to destroy.

Warning: 'launch_template' always triggers an instance refresh and can be removed

  with aws_autoscaling_group.github_runner,
  on asg.tf line 170, in resource "aws_autoscaling_group" "github_runner":
 170:     triggers = ["launch_template"]


─────────────────────────────────────────────────────────────────────────────

Saved the plan to: TFplan.JSON

To perform exactly these actions, run the following command to apply:
    terraform apply "TFplan.JSON"

Pushed by: @kunduso, Action: pull_request

@kunduso kunduso merged commit 0d06453 into main Aug 23, 2025
2 of 4 checks passed
@kunduso kunduso deleted the runner-deregistration branch August 23, 2025 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment