Skip to content

Commit 27def83

Browse files
abalcobiacalexandredynamikeantonbabenko
authored
feat: Added support for GitHub app (#276)
* added github app support * added github_token variable for webhook * updated example * Add bootstrap option for github-complete example and move Github App docs to the main README * Update examples/github-complete/README.md Co-authored-by: Anton Babenko <[email protected]> * fix terraform_deprecated_index warnings from tflint Co-authored-by: Carlos Alexandre <[email protected]> Co-authored-by: Michael Kania <[email protected]> Co-authored-by: Carlos Alexandre <[email protected]> Co-authored-by: Anton Babenko <[email protected]>
1 parent 0957ffc commit 27def83

File tree

10 files changed

+164
-74
lines changed

10 files changed

+164
-74
lines changed

README.md

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ module "atlantis" {
7474
certificate_arn = "arn:aws:acm:eu-west-1:135367859851:certificate/70e008e1-c0e1-4c7e-9670-7bb5bd4f5a84"
7575
7676
# Atlantis
77-
atlantis_github_user = "atlantis-bot"
78-
atlantis_github_user_token = "examplegithubtoken"
77+
atlantis_github_app_id = "1234567"
78+
atlantis_github_app_key = "-----BEGIN RSA PRIVATE KEY-----(...)"
7979
atlantis_repo_allowlist = ["github.com/terraform-aws-modules/*"]
8080
}
8181
```
@@ -129,6 +129,39 @@ Make sure that both private and public subnets were created in the same set of a
129129

130130
If all provided subnets are public (no NAT gateway) then `ecs_service_assign_public_ip` should be set to `true`.
131131

132+
### Using GitHub App
133+
An Atlantis GitHub App can be generated using multiple methods:
134+
135+
- You can follow Atlantis instructions depicted [here](https://www.runatlantis.io/docs/access-credentials.html#github-app). The Atlantis method mostly automates the GitHub App generation using [GitHub App Manifest](https://docs.github.com/en/developers/apps/building-github-apps/creating-a-github-app-from-a-manifest), but you need an exposed endpoint to complete the process.
136+
- The other method is to manually create the GitHub App as instructed [here](https://docs.github.com/en/developers/apps/building-github-apps/creating-a-github-app).
137+
1. You create a GitHub App and give it a name - that name must be unique across the world (you can change it later).
138+
2. Provide a valid Homepage URL (this can be the atlantis server url, for instance https://atlantis.mydomain.com)
139+
3. Provide a valid Webhook URL. The Atlantis webhook server path is located by default at https://atlantis.mydomain.com/events
140+
4. Generate a Webhook Secret - this is used for Atlantis to trust the deliveries. This is your github_webhook_secret.
141+
5. Generate a Private Key - this is your github_app_key
142+
6. On the App's settings page (at the top) you find the App ID. This is your github_app_id
143+
7. On the Permissions & Events you need to setup all the permissions and events according to Atlantis documentation
144+
145+
Now you need to install the App on your organization.
146+
147+
A self-provisioned GitHub App usually has two parts: the App and the Installation.
148+
149+
The App part is the first step and its where you setup all the requirements, such as authentication, webhook, permissions, etc...
150+
The Installation part is where you add the created App to an organization/personal-account. It is on the installation page where you setup which repositories the application can access and receive events from.
151+
152+
Once you have your GitHub App registered you will be able to access/manage the required parameters:
153+
154+
- `atlantis_github_app_id` to identify the GitHub app.
155+
- `atlantis_github_app_key` to interact with GitHub.
156+
- `atlantis_github_webhook_secret` to receive and validate incoming webhook invocations from GitHub.
157+
158+
#### GitHub Personal Access Token (PAT) is no longer recommended
159+
160+
While still supported, the use of GitHub Personal Access Token (PAT) is no longer the recommended method in favor of GitHub App.
161+
162+
[GitHub Apps](https://docs.github.com/en/developers/apps/getting-started-with-apps/about-apps) provide more control over repository access/permissions and does not require the use of bot accounts.
163+
164+
132165
### Secure Atlantis with ALB Built-in Authentication
133166

134167
#### OpenID Connect (OIDC)
@@ -273,6 +306,7 @@ allow_github_webhooks = true
273306
| [aws_route53_record.atlantis](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
274307
| [aws_route53_record.atlantis_aaaa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
275308
| [aws_ssm_parameter.atlantis_bitbucket_user_token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
309+
| [aws_ssm_parameter.atlantis_github_app_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
276310
| [aws_ssm_parameter.atlantis_github_user_token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
277311
| [aws_ssm_parameter.atlantis_gitlab_user_token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
278312
| [aws_ssm_parameter.webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
@@ -313,6 +347,9 @@ allow_github_webhooks = true
313347
| <a name="input_atlantis_bitbucket_user_token"></a> [atlantis\_bitbucket\_user\_token](#input\_atlantis\_bitbucket\_user\_token) | Bitbucket token of the user that is running the Atlantis command | `string` | `""` | no |
314348
| <a name="input_atlantis_bitbucket_user_token_ssm_parameter_name"></a> [atlantis\_bitbucket\_user\_token\_ssm\_parameter\_name](#input\_atlantis\_bitbucket\_user\_token\_ssm\_parameter\_name) | Name of SSM parameter to keep atlantis\_bitbucket\_user\_token | `string` | `"/atlantis/bitbucket/user/token"` | no |
315349
| <a name="input_atlantis_fqdn"></a> [atlantis\_fqdn](#input\_atlantis\_fqdn) | FQDN of Atlantis to use. Set this only to override Route53 and ALB's DNS name. | `string` | `null` | no |
350+
| <a name="input_atlantis_github_app_id"></a> [atlantis\_github\_app\_id](#input\_atlantis\_github\_app\_id) | GitHub App ID that is running the Atlantis command | `string` | `""` | no |
351+
| <a name="input_atlantis_github_app_key"></a> [atlantis\_github\_app\_key](#input\_atlantis\_github\_app\_key) | GitHub App private key that is running the Atlantis command | `string` | `""` | no |
352+
| <a name="input_atlantis_github_app_key_ssm_parameter_name"></a> [atlantis\_github\_app\_key\_ssm\_parameter\_name](#input\_atlantis\_github\_app\_key\_ssm\_parameter\_name) | Name of SSM parameter to keep atlantis\_github\_app\_key | `string` | `"/atlantis/github/app/key"` | no |
316353
| <a name="input_atlantis_github_user"></a> [atlantis\_github\_user](#input\_atlantis\_github\_user) | GitHub username that is running the Atlantis command | `string` | `""` | no |
317354
| <a name="input_atlantis_github_user_token"></a> [atlantis\_github\_user\_token](#input\_atlantis\_github\_user\_token) | GitHub token of the user that is running the Atlantis command | `string` | `""` | no |
318355
| <a name="input_atlantis_github_user_token_ssm_parameter_name"></a> [atlantis\_github\_user\_token\_ssm\_parameter\_name](#input\_atlantis\_github\_user\_token\_ssm\_parameter\_name) | Name of SSM parameter to keep atlantis\_github\_user\_token | `string` | `"/atlantis/github/user/token"` | no |
@@ -328,6 +365,7 @@ allow_github_webhooks = true
328365
| <a name="input_atlantis_repo_allowlist"></a> [atlantis\_repo\_allowlist](#input\_atlantis\_repo\_allowlist) | List of allowed repositories Atlantis can be used with | `list(string)` | n/a | yes |
329366
| <a name="input_atlantis_security_group_tags"></a> [atlantis\_security\_group\_tags](#input\_atlantis\_security\_group\_tags) | Additional tags to put on the atlantis security group | `map(string)` | `{}` | no |
330367
| <a name="input_atlantis_version"></a> [atlantis\_version](#input\_atlantis\_version) | Verion of Atlantis to run. If not specified latest will be used | `string` | `"latest"` | no |
368+
| <a name="input_atlantis_write_git_creds"></a> [atlantis\_write\_git\_creds](#input\_atlantis\_write\_git\_creds) | Write out a .git-credentials file with the provider user and token to allow cloning private modules over HTTPS or SSH | `string` | `"true"` | no |
331369
| <a name="input_azs"></a> [azs](#input\_azs) | A list of availability zones in the region | `list(string)` | `[]` | no |
332370
| <a name="input_certificate_arn"></a> [certificate\_arn](#input\_certificate\_arn) | ARN of certificate issued by AWS ACM. If empty, a new ACM certificate will be created and validated using Route53 DNS | `string` | `""` | no |
333371
| <a name="input_cidr"></a> [cidr](#input\_cidr) | The CIDR block for the VPC which will be created if `vpc_id` is not specified | `string` | `""` | no |

examples/github-complete/README.md

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
1-
# Complete Atlantis example with GitHub Webhooks
1+
# Complete Atlantis example with GitHub App and Webhooks
22

33
Configuration in this directory creates the necessary infrastructure and resources for running Atlantis on Fargate plus GitHub repository webhooks configured to Atlantis URL.
44

55
An existing Route53 hosted zone and domain is required to deploy this example.
66

7-
GitHub's personal access token can be generated at https://github.com/settings/tokens
8-
97
## Usage
108

11-
To run this code you need to copy `terraform.tfvars.sample` into `terraform.tfvars` and update the values locally or specify them using environment variables (`TF_VAR_github_token=xxx`, `TF_VAR_github_owner=xxx`, etc.). Once ready, execute:
9+
To run this code you need to copy `terraform.tfvars.sample` into `terraform.tfvars` and update the values locally or specify them using environment variables (`TF_VAR_github_app_id=xxx`, `TF_VAR_github_owner=xxx`, etc.). Ensure that `bootstrap_github_app` is `true`. Once ready, execute:
1210

1311
```bash
1412
$ terraform init
1513
$ terraform plan
1614
$ terraform apply
1715
```
1816

17+
Terraform will output a URL to setup a new Github App via Atlantis, which should look something like https://$ATLANTIS_HOST/github-app/setup. Open that URL and go through the setup process. Before closing the window, click the link to install the new GitHub App on you repositories and copy the values `github_app_id`, `github_app_key`, and `github_webhook_secret` into `terraform.tfvars`. You should also set `bootstrap_github_app` to `false` . Now execute:
18+
19+
```bash
20+
$ terraform plan
21+
$ terraform apply
22+
23+
```
24+
1925
Note - if you receive the following error when running apply:
2026

2127
`Error: InvalidParameterException: The new ARN and resource ID format must be enabled to add tags to the service. Opt in to the new format and try again. "atlantiscomplete"`
@@ -45,7 +51,6 @@ Go to https://eu-west-1.console.aws.amazon.com/ecs/home?region=eu-west-1#/settin
4551
|------|--------|---------|
4652
| <a name="module_atlantis"></a> [atlantis](#module\_atlantis) | ../../ | n/a |
4753
| <a name="module_atlantis_access_log_bucket"></a> [atlantis\_access\_log\_bucket](#module\_atlantis\_access\_log\_bucket) | terraform-aws-modules/s3-bucket/aws | ~> 3.0 |
48-
| <a name="module_github_repository_webhook"></a> [github\_repository\_webhook](#module\_github\_repository\_webhook) | ../../modules/github-repository-webhook | n/a |
4954

5055
## Resources
5156

@@ -61,20 +66,21 @@ Go to https://eu-west-1.console.aws.amazon.com/ecs/home?region=eu-west-1#/settin
6166
| Name | Description | Type | Default | Required |
6267
|------|-------------|------|---------|:--------:|
6368
| <a name="input_alb_ingress_cidr_blocks"></a> [alb\_ingress\_cidr\_blocks](#input\_alb\_ingress\_cidr\_blocks) | List of IPv4 CIDR ranges to use on all ingress rules of the ALB - use your personal IP in the form of `x.x.x.x/32` for restricted testing | `list(string)` | n/a | yes |
69+
| <a name="input_bootstrap_github_app"></a> [bootstrap\_github\_app](#input\_bootstrap\_github\_app) | Flag to configure Atlantis to bootstrap a new Github App | `bool` | n/a | yes |
6470
| <a name="input_domain"></a> [domain](#input\_domain) | Route53 domain name to use for ACM certificate. Route53 zone for this domain should be created in advance | `string` | n/a | yes |
71+
| <a name="input_github_app_id"></a> [github\_app\_id](#input\_github\_app\_id) | GitHub App ID that is running the Atlantis command | `string` | n/a | yes |
72+
| <a name="input_github_app_key"></a> [github\_app\_key](#input\_github\_app\_key) | The PEM encoded private key for the GitHub App | `string` | n/a | yes |
6573
| <a name="input_github_owner"></a> [github\_owner](#input\_github\_owner) | Github owner | `string` | n/a | yes |
6674
| <a name="input_github_repo_names"></a> [github\_repo\_names](#input\_github\_repo\_names) | List of Github repositories that should be monitored by Atlantis | `list(string)` | n/a | yes |
67-
| <a name="input_github_token"></a> [github\_token](#input\_github\_token) | Github token | `string` | n/a | yes |
68-
| <a name="input_github_user"></a> [github\_user](#input\_github\_user) | Github user for Atlantis to utilize when performing Github activities | `string` | n/a | yes |
75+
| <a name="input_github_webhook_secret"></a> [github\_webhook\_secret](#input\_github\_webhook\_secret) | Webhook secret | `string` | n/a | yes |
6976

7077
## Outputs
7178

7279
| Name | Description |
7380
|------|-------------|
81+
| <a name="output_atlantis_github_app_setup_url"></a> [atlantis\_github\_app\_setup\_url](#output\_atlantis\_github\_app\_setup\_url) | URL to create a new Github App with Atlantis |
7482
| <a name="output_atlantis_repo_allowlist"></a> [atlantis\_repo\_allowlist](#output\_atlantis\_repo\_allowlist) | Git repositories where webhook should be created |
7583
| <a name="output_atlantis_url"></a> [atlantis\_url](#output\_atlantis\_url) | URL of Atlantis |
7684
| <a name="output_ecs_task_definition"></a> [ecs\_task\_definition](#output\_ecs\_task\_definition) | Task definition for ECS service (used for external triggers) |
77-
| <a name="output_github_webhook_secret"></a> [github\_webhook\_secret](#output\_github\_webhook\_secret) | Github webhook secret |
78-
| <a name="output_github_webhook_urls"></a> [github\_webhook\_urls](#output\_github\_webhook\_urls) | Github webhook URL |
7985
| <a name="output_task_role_arn"></a> [task\_role\_arn](#output\_task\_role\_arn) | The Atlantis ECS task role arn |
8086
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

examples/github-complete/main.tf

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,23 @@ module "atlantis" {
8282
permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/cloud/developer-boundary-policy"
8383
path = "/delegatedadmin/developer/"
8484

85-
# Atlantis
86-
atlantis_github_user = var.github_user
87-
atlantis_github_user_token = var.github_token
88-
atlantis_repo_allowlist = [for repo in var.github_repo_names : "github.com/${var.github_owner}/${repo}"]
85+
# Bootstrapping a new Github App
86+
atlantis_github_user = var.bootstrap_github_app ? "fake" : ""
87+
atlantis_github_user_token = var.bootstrap_github_app ? "fake" : ""
88+
89+
# Atlantis w/ GitHub app
90+
################################################################################
91+
# Suggestion: instead of allocating the values of the atlantis_github_app_key
92+
# and atlantis_github_webhook_secret in the tfvars file,it is suggested to
93+
# upload the values in the AWS Parameter Store of the atlantis account and
94+
# call the values via the data source function
95+
# (e.g. data.aws_ssm_parameter.ghapp_key.value) for security reasons.
96+
################################################################################
97+
98+
atlantis_github_app_id = var.bootstrap_github_app ? "" : var.github_app_id
99+
atlantis_github_app_key = var.bootstrap_github_app ? "" : var.github_app_key
100+
atlantis_github_webhook_secret = var.bootstrap_github_app ? "" : var.github_webhook_secret
101+
atlantis_repo_allowlist = [for repo in var.github_repo_names : "github.com/${var.github_owner}/${repo}"]
89102

90103
# ALB access
91104
alb_ingress_cidr_blocks = var.alb_ingress_cidr_blocks
@@ -125,22 +138,6 @@ module "atlantis" {
125138
tags = local.tags
126139
}
127140

128-
################################################################################
129-
# GitHub Webhooks
130-
################################################################################
131-
132-
module "github_repository_webhook" {
133-
source = "../../modules/github-repository-webhook"
134-
135-
github_owner = var.github_owner
136-
github_token = var.github_token
137-
138-
atlantis_repo_allowlist = var.github_repo_names
139-
140-
webhook_url = module.atlantis.atlantis_url_events
141-
webhook_secret = module.atlantis.webhook_secret
142-
}
143-
144141
################################################################################
145142
# ALB Access Log Bucket + Policy
146143
################################################################################
@@ -255,3 +252,4 @@ data "aws_iam_policy_document" "atlantis_access_log_bucket_policy" {
255252
}
256253
}
257254
}
255+

examples/github-complete/outputs.tf

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ output "atlantis_url" {
44
value = module.atlantis.atlantis_url
55
}
66

7+
output "atlantis_github_app_setup_url" {
8+
description = "URL to create a new Github App with Atlantis"
9+
value = "${module.atlantis.atlantis_url}/github-app/setup"
10+
}
11+
712
output "atlantis_repo_allowlist" {
813
description = "Git repositories where webhook should be created"
914
value = module.atlantis.atlantis_repo_allowlist
@@ -18,15 +23,3 @@ output "ecs_task_definition" {
1823
description = "Task definition for ECS service (used for external triggers)"
1924
value = module.atlantis.ecs_task_definition
2025
}
21-
22-
# Webhooks
23-
output "github_webhook_urls" {
24-
description = "Github webhook URL"
25-
value = module.github_repository_webhook.repository_webhook_urls
26-
}
27-
28-
output "github_webhook_secret" {
29-
description = "Github webhook secret"
30-
value = module.github_repository_webhook.repository_webhook_secret
31-
sensitive = true
32-
}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
domain = "mydomain.com"
22
alb_ingress_cidr_blocks = ["x.x.x.x/32"]
33
github_owner = "myorg"
4-
github_user = "atlantis"
5-
github_token = "mygithubpersonalaccesstokenforatlantis"
64
github_repo_names = ["mycoolrepo1", "mycoolrepo2"]
5+
bootstrap_github_app = true
6+
github_app_id = "mygithubappid"
7+
github_app_key = <<-EOL
8+
-----BEGIN RSA PRIVATE KEY-----(...)
9+
EOL
10+
github_webhook_secret = "mywebhooksecretforatlantis"

examples/github-complete/variables.tf

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,32 @@ variable "alb_ingress_cidr_blocks" {
88
type = list(string)
99
}
1010

11-
variable "github_token" {
12-
description = "Github token"
11+
variable "github_owner" {
12+
description = "Github owner"
1313
type = string
1414
}
1515

16-
variable "github_owner" {
17-
description = "Github owner"
16+
variable "github_repo_names" {
17+
description = "List of Github repositories that should be monitored by Atlantis"
18+
type = list(string)
19+
}
20+
21+
variable "github_app_id" {
1822
type = string
23+
description = "GitHub App ID that is running the Atlantis command"
1924
}
2025

21-
variable "github_user" {
22-
description = "Github user for Atlantis to utilize when performing Github activities"
26+
variable "github_app_key" {
27+
description = "The PEM encoded private key for the GitHub App"
2328
type = string
2429
}
2530

26-
variable "github_repo_names" {
27-
description = "List of Github repositories that should be monitored by Atlantis"
28-
type = list(string)
31+
variable "github_webhook_secret" {
32+
description = "Webhook secret"
33+
type = string
2934
}
3035

36+
variable "bootstrap_github_app" {
37+
description = "Flag to configure Atlantis to bootstrap a new Github App"
38+
type = bool
39+
}

0 commit comments

Comments
 (0)