diff --git a/.gitignore b/.gitignore index 5a4b0d9..31f9544 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,7 @@ override.tf.json # Include override files you do wish to add to version control using negated pattern # # !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* +*tfplan* diff --git a/README.md b/README.md index 3026805..856f3ac 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,10 @@ -# Terraform Module Template - - -> **Warning**: -> This is a template document. Remember to **remove** all text in _italics_ and **update** Module name, Repo name and links/badges to the acual name of your GitHub repository/module!!! - - - - -![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white) +# Atlantis in Azure Container Group Terraform Module +![Azure](https://img.shields.io/badge/azure-%230072C6.svg?style=for-the-badge&logo=microsoftazure&logoColor=white) ![Terraform](https://img.shields.io/badge/terraform-%235835CC.svg?style=for-the-badge&logo=terraform&logoColor=white) -![License](https://badgen.net/github/license/getindata/terraform-module-template/) -![Release](https://badgen.net/github/release/getindata/terraform-module-template/) +![License](https://badgen.net/github/license/getindata/terraform-azurerm-atlantis/) +![Release](https://badgen.net/github/release/getindata/terraform-azurerm-atlantis/)

@@ -21,30 +13,36 @@ --- -_Brief Description of MODULE:_ +Terraform Module for deploying [Atlantis](https://www.runatlantis.io/) in Azure Container Group instance. -* _What it does_ -* _What techonlogies it uses_ +This module takes advantage of [terraform-null-atlantis-repo-config](https://github.com/getindata/terraform-null-atlantis-repo-config/), +which supplies a set of predefined custom workflows that are ready to use. ## USAGE -_Example usage of the module - terraform code snippet_ - ```terraform -module "template" { - source = "github.com/getindata/terraform-module-template" - - example_var = "foo" +module "atlantis" { + source = "github.com/getindata/terraform-azurerm-atlantis" + + resource_group_name = "example-rg" + + atlantis_server_config = { + repo_allowlist = "github.com/getindata/*" + } + + repo_config_repos = [ + { + id = "/.*/" + allowed_overrides = ["workflow", "apply_requirements", "delete_source_branch_on_merge"] + allow_custom_workflows = true + } + ] } ``` -## NOTES - -_Additional information that should be made public, for ex. how to solve known issues, additional descriptions/suggestions_ - ## EXAMPLES -- [Full example](examples/full-example) +- [Complete example](examples/complete) @@ -56,22 +54,43 @@ _Additional information that should be made public, for ex. how to solve known i | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [additional\_tag\_map](#input\_additional\_tag\_map) | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration. | `map(string)` | `{}` | no | +| [atlantis\_server\_config](#input\_atlantis\_server\_config) | Atlantis server config. If any option is not available here, it can be passed by `environment_variables` variable |

object({
allow_draft_prs = optional(string)
allow_fork_prs = optional(string)
allow_repo_config = optional(string)
atlantis_url = optional(string)
automerge = optional(string)
autoplan_file_list = optional(string)
autoplan_modules = optional(string)
autoplan_modules_from_projects = optional(string)
azuredevops_hostname = optional(string)
azuredevops_webhook_password = optional(string)
azuredevops_webhook_user = optional(string)
azuredevops_token = optional(string)
azuredevops_user = optional(string)
bitbucket_base_url = optional(string)
bitbucket_token = optional(string)
bitbucket_user = optional(string)
bitbucket_webhook_secret = optional(string)
checkout_strategy = optional(string)
config = optional(string)
data_dir = optional(string)
default_tf_version = optional(string)
disable_apply = optional(string)
disable_apply_all = optional(string)
disable_autoplan = optional(string)
disable_markdown_folding = optional(string)
disable_repo_locking = optional(string)
enable_policy_checks = optional(string)
enable_regexp_cmd = optional(string)
enable_diff_markdown_format = optional(string)
gh_hostname = optional(string)
gh_token = optional(string)
gh_user = optional(string)
gh_webhook_secret = optional(string)
gh_org = optional(string)
gh_app_id = optional(string)
gh_app_slug = optional(string)
gh_app_key_file = optional(string)
gh_app_key = optional(string)
gh_team_allowlist = optional(string)
gh_allow_mergeable_bypass_apply = optional(string)
gitlab_hostname = optional(string)
gitlab_token = optional(string)
gitlab_user = optional(string)
gitlab_webhook_secret = optional(string)
help = optional(string)
hide_prev_plan_comments = optional(string)
locking_db_type = optional(string)
log_level = optional(string)
markdown_template_overrides_dir = optional(string)
parallel_pool_size = optional(string)
port = optional(string)
quiet_policy_checks = optional(string)
redis_host = optional(string)
redis_password = optional(string)
redis_port = optional(string)
redis_db = optional(string)
redis_tls_enabled = optional(string)
redis_insecure_skip_verify = optional(string)
repo_config = optional(string)
repo_config_json = optional(string)
repo_whitelist = optional(string)
repo_allowlist = optional(string)
require_approval = optional(string)
require_mergeable = optional(string)
silence_fork_pr_errors = optional(string)
silence_whitelist_errors = optional(string)
silence_allowlist_errors = optional(string)
silence_no_projects = optional(string)
silence_vcs_status_no_plans = optional(string)
skip_clone_no_changes = optional(string)
slack_token = optional(string)
ssl_cert_file = optional(string)
ssl_key_file = optional(string)
stats_namespace = optional(string)
tf_download_url = optional(string)
tfe_hostname = optional(string)
tfe_local_execution_mode = optional(string)
tfe_token = optional(string)
var_file_allowlist = optional(string)
vcs_status_name = optional(string)
write_git_creds = optional(string)
web_basic_auth = optional(string)
web_username = optional(string)
web_password = optional(string)
websocket_check_origin = optional(string)
})
| `{}` | no | | [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | +| [container\_diagnostics\_log\_analytics](#input\_container\_diagnostics\_log\_analytics) | Log Analytics workspace to be used with container logs |
object({
workspace_id = string
workspace_key = string
log_type = optional(string, "ContainerInsights")
})
| `null` | no | +| [container\_group\_diagnostics\_setting](#input\_container\_group\_diagnostics\_setting) | Azure Monitor diagnostics for container group resource |
object({
workspace_resource_id = optional(string)
})
| `null` | no | | [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional\_tag\_map, which are merged. | `any` |
{
"additional_tag_map": {},
"attributes": [],
"delimiter": null,
"descriptor_formats": {},
"enabled": true,
"environment": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"labels_as_tags": [
"unset"
],
"name": null,
"namespace": null,
"regex_replace_chars": null,
"stage": null,
"tags": {},
"tenant": null
}
| no | +| [cpu](#input\_cpu) | The required number of CPU cores of the Atlantis container | `number` | `1` | no | | [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no | | [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.
Map of maps. Keys are names of descriptors. Values are maps of the form
`{
format = string
labels = list(string)
}`
(Type is `any` so the map values can later be enhanced to provide additional options.)
`format` is a Terraform format string to be passed to the `format()` function.
`labels` is a list of labels, in order, to pass to `format()` function.
Label values will be normalized before being passed to `format()` so they will be
identical to how they appear in `id`.
Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no | +| [dns\_name\_label](#input\_dns\_name\_label) | The DNS label/name for the container group's IP. If not provided it will use the name of the resource | `string` | `null` | no | +| [dns\_name\_servers](#input\_dns\_name\_servers) | DNS name servers configured with containers | `list(string)` | `[]` | no | | [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | | [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | -| [example\_var](#input\_example\_var) | Example varible passed into the module | `string` | n/a | yes | +| [environment\_variables](#input\_environment\_variables) | A list of environment variables to be set on the container | `map(string)` | `{}` | no | | [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no | +| [identity](#input\_identity) | Managed identity block. For type possible values are: SystemAssigned and UserAssigned |
object({
type = optional(string, "SystemAssigned")
identity_ids = optional(list(string), [])
system_assigned_identity_role_assignments = optional(list(object({
scope = string
role_definition_name = string
})), [])
})
| `null` | no | +| [image](#input\_image) | Container image with Atlantis | `string` | `"ghcr.io/runatlantis/atlantis"` | no | +| [image\_registry\_credential](#input\_image\_registry\_credential) | Credentials for ACR, so the images can be pulled by the container instance |
list(object({
username = string
password = string
server = string
}))
| `[]` | no | | [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no | | [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no | | [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no | | [labels\_as\_tags](#input\_labels\_as\_tags) | Set of labels (ID elements) to include as tags in the `tags` output.
Default is to include all labels.
Tags with empty values will not be included in the `tags` output.
Set to `[]` to suppress all generated tags.
**Notes:**
The value of the `name` tag, if included, will be the `id`, not the `name`.
Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be
changed in later chained modules. Attempts to change it will be silently ignored. | `set(string)` |
[
"default"
]
| no | +| [location](#input\_location) | Location where resources will be deployed. If not provided it will be read from resource group location | `string` | `null` | no | +| [memory](#input\_memory) | The required memory of the Atlantis container in GB | `number` | `2` | no | | [name](#input\_name) | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no | | [namespace](#input\_namespace) | ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique | `string` | `null` | no | +| [port](#input\_port) | Port on which Atlantis is listening | `number` | `4141` | no | | [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no | +| [repo\_config\_file](#input\_repo\_config\_file) | Configures config file generation if enabled |
object({
enabled = optional(bool, false)
path = optional(string, ".")
name = optional(string, "repo_config.yaml")
format = optional(string, "yaml")
})
| `{}` | no | +| [repo\_config\_repos](#input\_repo\_config\_repos) | Map of repositories and their configs. Refer to https://www.runatlantis.io/docs/server-side-repo-config.html#example-server-side-repo |
list(object({
id = optional(string, "/.*/")
branch = optional(string)
apply_requirements = optional(list(string))
allowed_overrides = optional(list(string))
allowed_workflows = optional(list(string))
allow_custom_workflows = optional(bool)
delete_source_branch_on_merge = optional(bool)
pre_workflow_hooks = optional(list(object({
run = string
})))
post_workflow_hooks = optional(list(object({
run = string
})))
workflow = optional(string)
######### Helpers #########
allow_all_server_side_workflows = optional(bool, false)
terragrunt_atlantis_config = optional(object({
enabled = optional(bool, false)
output = optional(string, "atlantis.yaml")
automerge = optional(bool)
autoplan = optional(bool)
parallel = optional(bool)
cascade_dependencies = optional(bool)
filter = optional(string)
use_project_markers = optional(bool)
}), {})
}))
| `[]` | no | +| [repo\_config\_repos\_common\_config](#input\_repo\_config\_repos\_common\_config) | Common config that will be merged into each item of the repos list |
object({
id = optional(string)
branch = optional(string)
apply_requirements = optional(list(string))
allowed_overrides = optional(list(string))
allowed_workflows = optional(list(string))
allow_custom_workflows = optional(bool)
delete_source_branch_on_merge = optional(bool)
pre_workflow_hooks = optional(list(object({
run = string
})))
post_workflow_hooks = optional(list(object({
run = string
})))
workflow = optional(string)
######### Helpers #########
allow_all_server_side_workflows = optional(bool, false)
terragrunt_atlantis_config = optional(object({
enabled = optional(bool, false)
output = optional(string, "atlantis.yaml")
autoplan = optional(bool, false)
parallel = optional(bool, false)
filter = optional(string)
}), {})
})
| `{}` | no | +| [repo\_config\_use\_predefined\_workflows](#input\_repo\_config\_use\_predefined\_workflows) | Indicates wherever predefined workflows should be added to the generated repo config file | `bool` | `true` | no | +| [repo\_config\_workflows](#input\_repo\_config\_workflows) | List of custom workflow that will be added to the repo config file |
map(object({
plan = optional(object({
steps = any
}))
apply = optional(object({
steps = any
}))
policy_check = optional(object({
steps = any
}))
}))
| `{}` | no | +| [resource\_group\_name](#input\_resource\_group\_name) | Azure resource group name where resources will be deployed | `string` | n/a | yes | +| [secure\_environment\_variables](#input\_secure\_environment\_variables) | A list of sensitive environment variables to be set on the container | `map(string)` | `{}` | no | +| [secure\_environment\_variables\_from\_key\_vault](#input\_secure\_environment\_variables\_from\_key\_vault) | A list of sensitive environment variables to be set on the container read from Azure Key Vault |
map(object({
key_vault_id = string
name = string
}))
| `{}` | no | | [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no | +| [subnet\_ids](#input\_subnet\_ids) | The subnet resource IDs for a container group. At the moment it supports 1 subnet maximum | `list(string)` | `[]` | no | | [tags](#input\_tags) | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`).
Neither the tag keys nor the tag values will be modified by this module. | `map(string)` | `{}` | no | | [tenant](#input\_tenant) | ID element \_(Rarely used, not included by default)\_. A customer identifier, indicating who this instance of a resource is for | `string` | `null` | no | @@ -79,32 +98,31 @@ _Additional information that should be made public, for ex. how to solve known i | Name | Source | Version | |------|--------|---------| +| [atlantis\_repo\_config](#module\_atlantis\_repo\_config) | getindata/atlantis-repo-config/null | 1.1.0 | +| [azure\_container\_group](#module\_azure\_container\_group) | getindata/container-group/azurerm | 1.1.0 | | [this](#module\_this) | cloudposse/label/null | 0.25.0 | ## Outputs | Name | Description | |------|-------------| -| [example\_output](#output\_example\_output) | Example output of the module | +| [atlantis\_webhook\_url](#output\_atlantis\_webhook\_url) | Url of the Atlantis webhook used by git platforms like GitLab or GitHub | +| [container\_group\_id](#output\_container\_group\_id) | ID of the container group | +| [container\_group\_name](#output\_container\_group\_name) | Name of the container group | ## Providers -| Name | Version | -|------|---------| -| [null](#provider\_null) | 3.1.1 | +No providers. ## Requirements | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.13.0 | -| [null](#requirement\_null) | 3.1.1 | +| [terraform](#requirement\_terraform) | >= 1.3 | ## Resources -| Name | Type | -|------|------| -| [null_resource.output_input](https://registry.terraform.io/providers/hashicorp/null/3.1.1/docs/resources/resource) | resource | +No resources. ## CONTRIBUTING @@ -121,7 +139,7 @@ Apache 2 Licensed. See [LICENSE](LICENSE) for full details. - + Made with [contrib.rocks](https://contrib.rocks). diff --git a/examples/complete/Makefile b/examples/complete/Makefile new file mode 100644 index 0000000..be0992b --- /dev/null +++ b/examples/complete/Makefile @@ -0,0 +1,11 @@ +init: + terraform init + +plan: + terraform plan -var-file fixtures.west-europe.tfvars -out tfplan + +apply: + terraform apply tfplan + +destroy: + terraform destroy -var-file fixtures.west-europe.tfvars diff --git a/examples/complete/README.md b/examples/complete/README.md new file mode 100644 index 0000000..2791696 --- /dev/null +++ b/examples/complete/README.md @@ -0,0 +1,61 @@ +# Complete Example + +```terraform +resource "random_id" "this" { + keepers = { + namespace = module.this.namespace + tenant = module.this.tenant + environment = module.this.environment + stage = module.this.stage + attributes = join("", module.this.attributes) + } + + byte_length = 3 +} + +module "resource_group" { + source = "getindata/resource-group/azurerm" + version = "1.2.0" + + context = module.this.context + + name = var.resource_group_name + location = var.location +} + +module "this_atlantis" { + source = "../../" + + context = module.this.context + + resource_group_name = module.resource_group.name + location = module.resource_group.location + + attributes = [random_id.this.hex] + + atlantis_server_config = var.atlantis_server_config + atlantis_repo_config = var.atlantis_repo_config + + secure_environment_variables = var.secure_environment_variables + + identity = {} +} +``` + +## Usage + +1. Create `terraform.tfvars` file +2. Populate it with: + ```terraform + secure_environment_variables = { + ATLANTIS_GITLAB_TOKEN = "" + ATLANTIS_GITLAB_USER = "" + ATLANTIS_GITLAB_WEBHOOK_SECRET = "" + } + ``` +3. Run the commands from below: + ``` + terraform init + terraform plan -var-file fixtures.west-europe.tfvars -out tf.plan + terraform apply tf.plan + ``` diff --git a/examples/full-example/context.tf b/examples/complete/context.tf similarity index 100% rename from examples/full-example/context.tf rename to examples/complete/context.tf diff --git a/examples/complete/fixtures.west-europe.tfvars b/examples/complete/fixtures.west-europe.tfvars new file mode 100644 index 0000000..496ec41 --- /dev/null +++ b/examples/complete/fixtures.west-europe.tfvars @@ -0,0 +1,31 @@ +namespace = "getindata" +environment = "example" +location = "West Europe" +resource_group_name = "atlantis-example" + +descriptor_formats = { + resource-group = { + labels = ["name"] + format = "%v-rg" + } + container-group = { + labels = ["namespace", "environment", "stage", "name", "attributes"] + format = "%v-%v-%v-%v-%v-aci" + } +} + +tags = { + Terraform = "True" +} + +atlantis_server_config = { + repo_allowlist = "gitlab.com/getindata/*" +} + +repo_config_repos = [ + { + id = "/.*/" + allowed_overrides = ["workflow", "apply_requirements", "delete_source_branch_on_merge"] + allow_custom_workflows = true + } +] diff --git a/examples/complete/main.tf b/examples/complete/main.tf new file mode 100644 index 0000000..6486ee9 --- /dev/null +++ b/examples/complete/main.tf @@ -0,0 +1,39 @@ +resource "random_id" "this" { + keepers = { + namespace = module.this.namespace + tenant = module.this.tenant + environment = module.this.environment + stage = module.this.stage + attributes = join("", module.this.attributes) + } + + byte_length = 3 +} + +module "resource_group" { + source = "getindata/resource-group/azurerm" + version = "1.2.0" + + context = module.this.context + + name = var.resource_group_name + location = var.location +} + +module "this_atlantis" { + source = "../../" + + context = module.this.context + + resource_group_name = module.resource_group.name + location = module.resource_group.location + + attributes = [random_id.this.hex] + + atlantis_server_config = var.atlantis_server_config + repo_config_repos = var.repo_config_repos + + secure_environment_variables = var.secure_environment_variables + + identity = {} +} diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf new file mode 100644 index 0000000..f3d2431 --- /dev/null +++ b/examples/complete/outputs.tf @@ -0,0 +1,4 @@ +output "atlantis_outputs" { + description = "Atlantis outputs" + value = module.this_atlantis +} diff --git a/examples/complete/providers.tf b/examples/complete/providers.tf new file mode 100644 index 0000000..ab91b24 --- /dev/null +++ b/examples/complete/providers.tf @@ -0,0 +1,3 @@ +provider "azurerm" { + features {} +} diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf new file mode 100644 index 0000000..9afd823 --- /dev/null +++ b/examples/complete/variables.tf @@ -0,0 +1,57 @@ +variable "location" { + type = string + description = "The Azure Region where the Resource Group should exist" +} + +variable "resource_group_name" { + type = string + description = "Resource group name" +} + +variable "atlantis_server_config" { + description = "Atlantis server config. If any option is not available here, it can be passed by `environment_variables` variable" + type = object({ + repo_config_json = optional(string) + repo_allowlist = optional(string) + }) + default = {} +} + +variable "repo_config_repos" { + description = "Map of repositories and their configs. Refer to https://www.runatlantis.io/docs/server-side-repo-config.html#example-server-side-repo" + type = list(object({ + id = optional(string, "/.*/") + branch = optional(string) + apply_requirements = optional(list(string)) + allowed_overrides = optional(list(string)) + allowed_workflows = optional(list(string)) + allow_custom_workflows = optional(bool) + delete_source_branch_on_merge = optional(bool) + pre_workflow_hooks = optional(list(object({ + run = string + }))) + post_workflow_hooks = optional(list(object({ + run = string + }))) + workflow = optional(string) + ######### Helpers ######### + allow_all_server_side_workflows = optional(bool, false) + terragrunt_atlantis_config = optional(object({ + enabled = optional(bool, false) + output = optional(string, "atlantis.yaml") + automerge = optional(bool) + autoplan = optional(bool) + parallel = optional(bool) + cascade_dependencies = optional(bool) + filter = optional(string) + use_project_markers = optional(bool) + }), {}) + })) + default = [] +} + +variable "secure_environment_variables" { + description = "A list of sensitive environment variables to be set on the container" + type = map(string) + default = {} +} diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf new file mode 100644 index 0000000..7b56fb3 --- /dev/null +++ b/examples/complete/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.3" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 3.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.0" + } + } +} diff --git a/examples/full-example/main.tf b/examples/full-example/main.tf deleted file mode 100644 index 1883559..0000000 --- a/examples/full-example/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -module "terraform_module_template" { - source = "../../" - context = module.this.context - - example_var = "This is example value." -} diff --git a/examples/full-example/outputs.tf b/examples/full-example/outputs.tf deleted file mode 100644 index c2007a2..0000000 --- a/examples/full-example/outputs.tf +++ /dev/null @@ -1,4 +0,0 @@ -output "example_output" { - description = "Example output of the module" - value = module.terraform_module_template -} diff --git a/examples/full-example/providers.tf b/examples/full-example/providers.tf deleted file mode 100644 index c793099..0000000 --- a/examples/full-example/providers.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider "null" { - # Configuration options -} diff --git a/examples/full-example/versions.tf b/examples/full-example/versions.tf deleted file mode 100644 index ea4e1c7..0000000 --- a/examples/full-example/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 0.13.0" - - required_providers { - null = { - source = "hashicorp/null" - version = "3.1.1" - } - } -} diff --git a/locals.tf b/locals.tf index d55ec57..0e5836c 100644 --- a/locals.tf +++ b/locals.tf @@ -1,7 +1,18 @@ locals { - # Get a name from the descriptor. If not available, use default naming convention. - # Trim and replace function are used to avoid bare delimiters on both ends of the name and situation of adjacent delimiters. - name_from_descriptor = trim(replace( - lookup(module.this.descriptors, "module-resource-name", module.this.id), "/${module.this.delimiter}${module.this.delimiter}+/", "" - ), module.this.delimiter) + atlantis_environment_variables_from_terraform_config = { for k in keys(var.atlantis_server_config) : + "ATLANTIS_${upper(replace(k, "-", "_"))}" => var.atlantis_server_config[k] } + atlantis_environment_variables_msi = merge( + var.identity != null ? { ARM_USE_MSI = "true" } : {}, + try(length(var.identity.system_assigned_identity_role_assignments), 0) > 0 ? { ARM_CLIENT_ID = one(var.identity.system_assigned_identity_role_assignments) } : {}, + ) + atlantis_environment_variables = merge( + local.atlantis_environment_variables_msi, + local.atlantis_environment_variables_from_terraform_config, + { ATLANTIS_REPO_CONFIG_JSON = coalesce( + lookup(local.atlantis_environment_variables_from_terraform_config, "ATLANTIS_REPO_CONFIG_JSON", null), + module.atlantis_repo_config.repos_config_json + ) }, + var.environment_variables + ) + atlantis_secure_environment_variables = merge(var.secure_environment_variables) } diff --git a/main.tf b/main.tf index 065807b..ffd9b41 100644 --- a/main.tf +++ b/main.tf @@ -1,13 +1,49 @@ -# Example resource that outputs the input value and -# echoes it's base64 encoded version locally +module "atlantis_repo_config" { + source = "getindata/atlantis-repo-config/null" + version = "1.1.0" -resource "null_resource" "output_input" { - triggers = { - name = local.name_from_descriptor - input = var.example_var - } + repos = var.repo_config_repos + repos_common_config = var.repo_config_repos_common_config + + workflows = var.repo_config_workflows + use_predefined_workflows = var.repo_config_use_predefined_workflows + + repo_config_file = var.repo_config_file +} + +module "azure_container_group" { + source = "getindata/container-group/azurerm" + version = "1.1.0" - provisioner "local-exec" { - command = "echo ${var.example_var} | base64" + context = module.this.context + + resource_group_name = var.resource_group_name + location = var.location + + name = coalesce(var.name, "atlantis") + + containers = { + atlantis = { + image = var.image + cpu = var.cpu + memory = var.memory + ports = [ + { + port = var.port + } + ] + commands = ["atlantis", "server"] + environment_variables = local.atlantis_environment_variables + secure_environment_variables = local.atlantis_secure_environment_variables + secure_environment_variables_from_key_vault = var.secure_environment_variables_from_key_vault + } } + + subnet_ids = var.subnet_ids + dns_name_label = var.dns_name_label + dns_name_servers = var.dns_name_servers + identity = var.identity + image_registry_credential = var.image_registry_credential + container_diagnostics_log_analytics = var.container_diagnostics_log_analytics + container_group_diagnostics_setting = var.container_group_diagnostics_setting } diff --git a/outputs.tf b/outputs.tf index 6f13a49..ced3ce8 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,6 +1,14 @@ -# Example output from the module +output "container_group_id" { + description = "ID of the container group" + value = module.azure_container_group.id +} + +output "container_group_name" { + description = "Name of the container group" + value = module.azure_container_group.name +} -output "example_output" { - description = "Example output of the module" - value = var.example_var +output "atlantis_webhook_url" { + description = "Url of the Atlantis webhook used by git platforms like GitLab or GitHub" + value = format("http://%s:%s/events", module.azure_container_group.fqdn, var.port) } diff --git a/variables.tf b/variables.tf index ef5f536..f8d65b7 100644 --- a/variables.tf +++ b/variables.tf @@ -1,6 +1,315 @@ -# Example, compulsory input variable +variable "resource_group_name" { + description = "Azure resource group name where resources will be deployed" + type = string +} + +variable "location" { + description = "Location where resources will be deployed. If not provided it will be read from resource group location" + type = string + default = null +} -variable "example_var" { - description = "Example varible passed into the module" +variable "image" { + description = "Container image with Atlantis" type = string + default = "ghcr.io/runatlantis/atlantis" +} + +variable "cpu" { + description = "The required number of CPU cores of the Atlantis container" + type = number + default = 1 +} + +variable "memory" { + description = "The required memory of the Atlantis container in GB" + type = number + default = 2 +} + +variable "port" { + description = "Port on which Atlantis is listening" + type = number + default = 4141 +} + +variable "atlantis_server_config" { + description = "Atlantis server config. If any option is not available here, it can be passed by `environment_variables` variable" + type = object({ + allow_draft_prs = optional(string) + allow_fork_prs = optional(string) + allow_repo_config = optional(string) + atlantis_url = optional(string) + automerge = optional(string) + autoplan_file_list = optional(string) + autoplan_modules = optional(string) + autoplan_modules_from_projects = optional(string) + azuredevops_hostname = optional(string) + azuredevops_webhook_password = optional(string) + azuredevops_webhook_user = optional(string) + azuredevops_token = optional(string) + azuredevops_user = optional(string) + bitbucket_base_url = optional(string) + bitbucket_token = optional(string) + bitbucket_user = optional(string) + bitbucket_webhook_secret = optional(string) + checkout_strategy = optional(string) + config = optional(string) + data_dir = optional(string) + default_tf_version = optional(string) + disable_apply = optional(string) + disable_apply_all = optional(string) + disable_autoplan = optional(string) + disable_markdown_folding = optional(string) + disable_repo_locking = optional(string) + enable_policy_checks = optional(string) + enable_regexp_cmd = optional(string) + enable_diff_markdown_format = optional(string) + gh_hostname = optional(string) + gh_token = optional(string) + gh_user = optional(string) + gh_webhook_secret = optional(string) + gh_org = optional(string) + gh_app_id = optional(string) + gh_app_slug = optional(string) + gh_app_key_file = optional(string) + gh_app_key = optional(string) + gh_team_allowlist = optional(string) + gh_allow_mergeable_bypass_apply = optional(string) + gitlab_hostname = optional(string) + gitlab_token = optional(string) + gitlab_user = optional(string) + gitlab_webhook_secret = optional(string) + help = optional(string) + hide_prev_plan_comments = optional(string) + locking_db_type = optional(string) + log_level = optional(string) + markdown_template_overrides_dir = optional(string) + parallel_pool_size = optional(string) + port = optional(string) + quiet_policy_checks = optional(string) + redis_host = optional(string) + redis_password = optional(string) + redis_port = optional(string) + redis_db = optional(string) + redis_tls_enabled = optional(string) + redis_insecure_skip_verify = optional(string) + repo_config = optional(string) + repo_config_json = optional(string) + repo_whitelist = optional(string) + repo_allowlist = optional(string) + require_approval = optional(string) + require_mergeable = optional(string) + silence_fork_pr_errors = optional(string) + silence_whitelist_errors = optional(string) + silence_allowlist_errors = optional(string) + silence_no_projects = optional(string) + silence_vcs_status_no_plans = optional(string) + skip_clone_no_changes = optional(string) + slack_token = optional(string) + ssl_cert_file = optional(string) + ssl_key_file = optional(string) + stats_namespace = optional(string) + tf_download_url = optional(string) + tfe_hostname = optional(string) + tfe_local_execution_mode = optional(string) + tfe_token = optional(string) + var_file_allowlist = optional(string) + vcs_status_name = optional(string) + write_git_creds = optional(string) + web_basic_auth = optional(string) + web_username = optional(string) + web_password = optional(string) + websocket_check_origin = optional(string) + }) + default = {} +} + +########################################### +## Atlantis Repos config ## +########################################### + +variable "repo_config_repos" { + description = "Map of repositories and their configs. Refer to https://www.runatlantis.io/docs/server-side-repo-config.html#example-server-side-repo" + type = list(object({ + id = optional(string, "/.*/") + branch = optional(string) + apply_requirements = optional(list(string)) + allowed_overrides = optional(list(string)) + allowed_workflows = optional(list(string)) + allow_custom_workflows = optional(bool) + delete_source_branch_on_merge = optional(bool) + pre_workflow_hooks = optional(list(object({ + run = string + }))) + post_workflow_hooks = optional(list(object({ + run = string + }))) + workflow = optional(string) + ######### Helpers ######### + allow_all_server_side_workflows = optional(bool, false) + terragrunt_atlantis_config = optional(object({ + enabled = optional(bool, false) + output = optional(string, "atlantis.yaml") + automerge = optional(bool) + autoplan = optional(bool) + parallel = optional(bool) + cascade_dependencies = optional(bool) + filter = optional(string) + use_project_markers = optional(bool) + }), {}) + })) + default = [] +} + +variable "repo_config_repos_common_config" { + description = "Common config that will be merged into each item of the repos list" + type = object({ + id = optional(string) + branch = optional(string) + apply_requirements = optional(list(string)) + allowed_overrides = optional(list(string)) + allowed_workflows = optional(list(string)) + allow_custom_workflows = optional(bool) + delete_source_branch_on_merge = optional(bool) + pre_workflow_hooks = optional(list(object({ + run = string + }))) + post_workflow_hooks = optional(list(object({ + run = string + }))) + workflow = optional(string) + ######### Helpers ######### + allow_all_server_side_workflows = optional(bool, false) + terragrunt_atlantis_config = optional(object({ + enabled = optional(bool, false) + output = optional(string, "atlantis.yaml") + autoplan = optional(bool, false) + parallel = optional(bool, false) + filter = optional(string) + }), {}) + }) + default = {} +} + +variable "repo_config_workflows" { + description = "List of custom workflow that will be added to the repo config file" + type = map(object({ + plan = optional(object({ + steps = any + })) + apply = optional(object({ + steps = any + })) + policy_check = optional(object({ + steps = any + })) + })) + default = {} +} + +variable "repo_config_use_predefined_workflows" { + description = "Indicates wherever predefined workflows should be added to the generated repo config file" + type = bool + default = true +} + +variable "repo_config_file" { + description = "Configures config file generation if enabled" + type = object({ + enabled = optional(bool, false) + path = optional(string, ".") + name = optional(string, "repo_config.yaml") + format = optional(string, "yaml") + }) + default = {} + + validation { + condition = contains(["yaml", "json"], var.repo_config_file.format) + error_message = "Invalid format provided. Allowed values: yaml, json" + } +} + +########################################### +## Rest of Azure Container Group Configs ## +########################################### + +variable "environment_variables" { + description = "A list of environment variables to be set on the container" + type = map(string) + default = {} +} + +variable "secure_environment_variables" { + description = "A list of sensitive environment variables to be set on the container" + type = map(string) + default = {} +} + +variable "secure_environment_variables_from_key_vault" { + description = "A list of sensitive environment variables to be set on the container read from Azure Key Vault" + type = map(object({ + key_vault_id = string + name = string + })) + default = {} +} + +variable "subnet_ids" { + description = "The subnet resource IDs for a container group. At the moment it supports 1 subnet maximum" + type = list(string) + default = [] +} + +variable "dns_name_label" { + description = "The DNS label/name for the container group's IP. If not provided it will use the name of the resource" + type = string + default = null +} + +variable "dns_name_servers" { + description = "DNS name servers configured with containers" + type = list(string) + default = [] +} + +variable "identity" { + description = "Managed identity block. For type possible values are: SystemAssigned and UserAssigned" + type = object({ + type = optional(string, "SystemAssigned") + identity_ids = optional(list(string), []) + system_assigned_identity_role_assignments = optional(list(object({ + scope = string + role_definition_name = string + })), []) + }) + default = null +} + +variable "image_registry_credential" { + description = "Credentials for ACR, so the images can be pulled by the container instance" + type = list(object({ + username = string + password = string + server = string + })) + default = [] +} + +variable "container_diagnostics_log_analytics" { + description = "Log Analytics workspace to be used with container logs" + type = object({ + workspace_id = string + workspace_key = string + log_type = optional(string, "ContainerInsights") + }) + default = null +} + +variable "container_group_diagnostics_setting" { + description = "Azure Monitor diagnostics for container group resource" + type = object({ + workspace_resource_id = optional(string) + }) + default = null } diff --git a/versions.tf b/versions.tf index 32b2f24..6964268 100644 --- a/versions.tf +++ b/versions.tf @@ -1,12 +1,3 @@ -# Example configuration of terraform providers - terraform { - required_version = ">= 0.13.0" - - required_providers { - null = { - source = "hashicorp/null" - version = "3.1.1" - } - } + required_version = ">= 1.3" }