Skip to content

feat: Added support for GitHub app #276

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ module "atlantis" {
certificate_arn = "arn:aws:acm:eu-west-1:135367859851:certificate/70e008e1-c0e1-4c7e-9670-7bb5bd4f5a84"

# Atlantis
atlantis_github_user = "atlantis-bot"
atlantis_github_user_token = "examplegithubtoken"
atlantis_github_app_id = "1234567"
atlantis_github_app_key = "-----BEGIN RSA PRIVATE KEY-----(...)"
atlantis_repo_allowlist = ["github.com/terraform-aws-modules/*"]
}
```
Expand Down Expand Up @@ -129,6 +129,39 @@ Make sure that both private and public subnets were created in the same set of a

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

### Using GitHub App
An Atlantis GitHub App can be generated using multiple methods:

- 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.
- 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).
1. You create a GitHub App and give it a name - that name must be unique across the world (you can change it later).
2. Provide a valid Homepage URL (this can be the atlantis server url, for instance https://atlantis.mydomain.com)
3. Provide a valid Webhook URL. The Atlantis webhook server path is located by default at https://atlantis.mydomain.com/events
4. Generate a Webhook Secret - this is used for Atlantis to trust the deliveries. This is your github_webhook_secret.
5. Generate a Private Key - this is your github_app_key
6. On the App's settings page (at the top) you find the App ID. This is your github_app_id
7. On the Permissions & Events you need to setup all the permissions and events according to Atlantis documentation

Now you need to install the App on your organization.

A self-provisioned GitHub App usually has two parts: the App and the Installation.

The App part is the first step and its where you setup all the requirements, such as authentication, webhook, permissions, etc...
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.

Once you have your GitHub App registered you will be able to access/manage the required parameters:

- `atlantis_github_app_id` to identify the GitHub app.
- `atlantis_github_app_key` to interact with GitHub.
- `atlantis_github_webhook_secret` to receive and validate incoming webhook invocations from GitHub.

#### GitHub Personal Access Token (PAT) is no longer recommended

While still supported, the use of GitHub Personal Access Token (PAT) is no longer the recommended method in favor of GitHub App.

[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.


### Secure Atlantis with ALB Built-in Authentication

#### OpenID Connect (OIDC)
Expand Down Expand Up @@ -273,6 +306,7 @@ allow_github_webhooks = true
| [aws_route53_record.atlantis](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.atlantis_aaaa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_ssm_parameter.atlantis_bitbucket_user_token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
| [aws_ssm_parameter.atlantis_github_app_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
| [aws_ssm_parameter.atlantis_github_user_token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
| [aws_ssm_parameter.atlantis_gitlab_user_token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
| [aws_ssm_parameter.webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
Expand Down Expand Up @@ -313,6 +347,9 @@ allow_github_webhooks = true
| <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 |
| <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 |
| <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 |
| <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 |
| <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 |
| <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 |
| <a name="input_atlantis_github_user"></a> [atlantis\_github\_user](#input\_atlantis\_github\_user) | GitHub username that is running the Atlantis command | `string` | `""` | no |
| <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 |
| <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 |
Expand All @@ -328,6 +365,7 @@ allow_github_webhooks = true
| <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 |
| <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 |
| <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 |
| <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 |
| <a name="input_azs"></a> [azs](#input\_azs) | A list of availability zones in the region | `list(string)` | `[]` | no |
| <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 |
| <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 |
Expand Down
24 changes: 15 additions & 9 deletions examples/github-complete/README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
# Complete Atlantis example with GitHub Webhooks
# Complete Atlantis example with GitHub App and Webhooks

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

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

GitHub's personal access token can be generated at https://github.com/settings/tokens

## Usage

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:
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:

```bash
$ terraform init
$ terraform plan
$ terraform apply
```

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:

```bash
$ terraform plan
$ terraform apply

```

Note - if you receive the following error when running apply:

`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"`
Expand Down Expand Up @@ -45,7 +51,6 @@ Go to https://eu-west-1.console.aws.amazon.com/ecs/home?region=eu-west-1#/settin
|------|--------|---------|
| <a name="module_atlantis"></a> [atlantis](#module\_atlantis) | ../../ | n/a |
| <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 |
| <a name="module_github_repository_webhook"></a> [github\_repository\_webhook](#module\_github\_repository\_webhook) | ../../modules/github-repository-webhook | n/a |

## Resources

Expand All @@ -61,20 +66,21 @@ Go to https://eu-west-1.console.aws.amazon.com/ecs/home?region=eu-west-1#/settin
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <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 |
| <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 |
| <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 |
| <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 |
| <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 |
| <a name="input_github_owner"></a> [github\_owner](#input\_github\_owner) | Github owner | `string` | n/a | yes |
| <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 |
| <a name="input_github_token"></a> [github\_token](#input\_github\_token) | Github token | `string` | n/a | yes |
| <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 |
| <a name="input_github_webhook_secret"></a> [github\_webhook\_secret](#input\_github\_webhook\_secret) | Webhook secret | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <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 |
| <a name="output_atlantis_repo_allowlist"></a> [atlantis\_repo\_allowlist](#output\_atlantis\_repo\_allowlist) | Git repositories where webhook should be created |
| <a name="output_atlantis_url"></a> [atlantis\_url](#output\_atlantis\_url) | URL of Atlantis |
| <a name="output_ecs_task_definition"></a> [ecs\_task\_definition](#output\_ecs\_task\_definition) | Task definition for ECS service (used for external triggers) |
| <a name="output_github_webhook_secret"></a> [github\_webhook\_secret](#output\_github\_webhook\_secret) | Github webhook secret |
| <a name="output_github_webhook_urls"></a> [github\_webhook\_urls](#output\_github\_webhook\_urls) | Github webhook URL |
| <a name="output_task_role_arn"></a> [task\_role\_arn](#output\_task\_role\_arn) | The Atlantis ECS task role arn |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
38 changes: 18 additions & 20 deletions examples/github-complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,23 @@ module "atlantis" {
permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/cloud/developer-boundary-policy"
path = "/delegatedadmin/developer/"

# Atlantis
atlantis_github_user = var.github_user
atlantis_github_user_token = var.github_token
atlantis_repo_allowlist = [for repo in var.github_repo_names : "github.com/${var.github_owner}/${repo}"]
# Bootstrapping a new Github App
atlantis_github_user = var.bootstrap_github_app ? "fake" : ""
atlantis_github_user_token = var.bootstrap_github_app ? "fake" : ""

# Atlantis w/ GitHub app
################################################################################
# Suggestion: instead of allocating the values of the atlantis_github_app_key
# and atlantis_github_webhook_secret in the tfvars file,it is suggested to
# upload the values in the AWS Parameter Store of the atlantis account and
# call the values via the data source function
# (e.g. data.aws_ssm_parameter.ghapp_key.value) for security reasons.
################################################################################

atlantis_github_app_id = var.bootstrap_github_app ? "" : var.github_app_id
atlantis_github_app_key = var.bootstrap_github_app ? "" : var.github_app_key
atlantis_github_webhook_secret = var.bootstrap_github_app ? "" : var.github_webhook_secret
atlantis_repo_allowlist = [for repo in var.github_repo_names : "github.com/${var.github_owner}/${repo}"]

# ALB access
alb_ingress_cidr_blocks = var.alb_ingress_cidr_blocks
Expand Down Expand Up @@ -125,22 +138,6 @@ module "atlantis" {
tags = local.tags
}

################################################################################
# GitHub Webhooks
################################################################################

module "github_repository_webhook" {
source = "../../modules/github-repository-webhook"

github_owner = var.github_owner
github_token = var.github_token

atlantis_repo_allowlist = var.github_repo_names

webhook_url = module.atlantis.atlantis_url_events
webhook_secret = module.atlantis.webhook_secret
}

################################################################################
# ALB Access Log Bucket + Policy
################################################################################
Expand Down Expand Up @@ -255,3 +252,4 @@ data "aws_iam_policy_document" "atlantis_access_log_bucket_policy" {
}
}
}

17 changes: 5 additions & 12 deletions examples/github-complete/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ output "atlantis_url" {
value = module.atlantis.atlantis_url
}

output "atlantis_github_app_setup_url" {
description = "URL to create a new Github App with Atlantis"
value = "${module.atlantis.atlantis_url}/github-app/setup"
}

output "atlantis_repo_allowlist" {
description = "Git repositories where webhook should be created"
value = module.atlantis.atlantis_repo_allowlist
Expand All @@ -18,15 +23,3 @@ output "ecs_task_definition" {
description = "Task definition for ECS service (used for external triggers)"
value = module.atlantis.ecs_task_definition
}

# Webhooks
output "github_webhook_urls" {
description = "Github webhook URL"
value = module.github_repository_webhook.repository_webhook_urls
}

output "github_webhook_secret" {
description = "Github webhook secret"
value = module.github_repository_webhook.repository_webhook_secret
sensitive = true
}
8 changes: 6 additions & 2 deletions examples/github-complete/terraform.tfvars.sample
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
domain = "mydomain.com"
alb_ingress_cidr_blocks = ["x.x.x.x/32"]
github_owner = "myorg"
github_user = "atlantis"
github_token = "mygithubpersonalaccesstokenforatlantis"
github_repo_names = ["mycoolrepo1", "mycoolrepo2"]
bootstrap_github_app = true
github_app_id = "mygithubappid"
github_app_key = <<-EOL
-----BEGIN RSA PRIVATE KEY-----(...)
EOL
github_webhook_secret = "mywebhooksecretforatlantis"
27 changes: 18 additions & 9 deletions examples/github-complete/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,32 @@ variable "alb_ingress_cidr_blocks" {
type = list(string)
}

variable "github_token" {
description = "Github token"
variable "github_owner" {
description = "Github owner"
type = string
}

variable "github_owner" {
description = "Github owner"
variable "github_repo_names" {
description = "List of Github repositories that should be monitored by Atlantis"
type = list(string)
}

variable "github_app_id" {
type = string
description = "GitHub App ID that is running the Atlantis command"
}

variable "github_user" {
description = "Github user for Atlantis to utilize when performing Github activities"
variable "github_app_key" {
description = "The PEM encoded private key for the GitHub App"
type = string
}

variable "github_repo_names" {
description = "List of Github repositories that should be monitored by Atlantis"
type = list(string)
variable "github_webhook_secret" {
description = "Webhook secret"
type = string
}

variable "bootstrap_github_app" {
description = "Flag to configure Atlantis to bootstrap a new Github App"
type = bool
}
Loading