Skip to content
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

Add self service portal #35

Merged
merged 10 commits into from
Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 1 addition & 1 deletion .github/workflows/auto-context.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Create Pull Request
if: steps.update.outputs.create_pull_request == 'true'
uses: cloudposse/actions/github/create-pull-request@0.22.0
uses: cloudposse/actions/github/create-pull-request@0.30.0
with:
token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }}
committer: 'cloudpossebot <11232728+cloudpossebot@users.noreply.github.com>'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/auto-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
fi

- name: Auto Test
uses: cloudposse/actions/github/repository-dispatch@0.22.0
uses: cloudposse/actions/github/repository-dispatch@0.30.0
# match users by ID because logins (user names) are inconsistent,
# for example in the REST API Renovate Bot is `renovate[bot]` but
# in GraphQL it is just `renovate`, plus there is a non-bot
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/chatops.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: "Handle common commands"
uses: cloudposse/actions/github/slash-command-dispatch@0.22.0
uses: cloudposse/actions/github/slash-command-dispatch@0.30.0
with:
token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }}
reaction-token: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -24,7 +24,7 @@ jobs:
- name: "Checkout commit"
uses: actions/checkout@v2
- name: "Run tests"
uses: cloudposse/actions/github/slash-command-dispatch@0.22.0
uses: cloudposse/actions/github/slash-command-dispatch@0.30.0
with:
token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }}
reaction-token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ Available targets:
| <a name="input_security_group_delete_timeout"></a> [security\_group\_delete\_timeout](#input\_security\_group\_delete\_timeout) | How long to retry on `DependencyViolation` errors during security group deletion from<br>lingering ENIs left by certain AWS services such as Elastic Load Balancing. | `string` | `"15m"` | no |
| <a name="input_security_group_description"></a> [security\_group\_description](#input\_security\_group\_description) | The description to assign to the created Security Group.<br>Warning: Changing the description causes the security group to be replaced. | `string` | `null` | no |
| <a name="input_security_group_name"></a> [security\_group\_name](#input\_security\_group\_name) | The name to assign to the created security group. Must be unique within the VPC.<br>If not provided, will be derived from the `null-label.context` passed in.<br>If `create_before_destroy` is true, will be used as a name prefix. | `list(string)` | `[]` | no |
| <a name="input_self_service_portal_enabled"></a> [self\_service\_portal\_enabled](#input\_self\_service\_portal\_enabled) | Specify whether to enable the self-service portal for the Client VPN endpoint | `bool` | `false` | no |
| <a name="input_self_service_saml_provider_arn"></a> [self\_service\_saml\_provider\_arn](#input\_self\_service\_saml\_provider\_arn) | The ARN of the IAM SAML identity provider for the self service portal if type is federated-authentication. | `string` | `null` | no |
| <a name="input_server_common_name"></a> [server\_common\_name](#input\_server\_common\_name) | Unique Common Name for Server self-signed certificate | `string` | `null` | no |
| <a name="input_split_tunnel"></a> [split\_tunnel](#input\_split\_tunnel) | Indicates whether split-tunnel is enabled on VPN endpoint. Default value is false. | `bool` | `false` | no |
| <a name="input_stage"></a> [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no |
Expand Down
2 changes: 2 additions & 0 deletions docs/terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
| <a name="input_security_group_delete_timeout"></a> [security\_group\_delete\_timeout](#input\_security\_group\_delete\_timeout) | How long to retry on `DependencyViolation` errors during security group deletion from<br>lingering ENIs left by certain AWS services such as Elastic Load Balancing. | `string` | `"15m"` | no |
| <a name="input_security_group_description"></a> [security\_group\_description](#input\_security\_group\_description) | The description to assign to the created Security Group.<br>Warning: Changing the description causes the security group to be replaced. | `string` | `null` | no |
| <a name="input_security_group_name"></a> [security\_group\_name](#input\_security\_group\_name) | The name to assign to the created security group. Must be unique within the VPC.<br>If not provided, will be derived from the `null-label.context` passed in.<br>If `create_before_destroy` is true, will be used as a name prefix. | `list(string)` | `[]` | no |
| <a name="input_self_service_portal_enabled"></a> [self\_service\_portal\_enabled](#input\_self\_service\_portal\_enabled) | Specify whether to enable the self-service portal for the Client VPN endpoint | `bool` | `false` | no |
| <a name="input_self_service_saml_provider_arn"></a> [self\_service\_saml\_provider\_arn](#input\_self\_service\_saml\_provider\_arn) | The ARN of the IAM SAML identity provider for the self service portal if type is federated-authentication. | `string` | `null` | no |
| <a name="input_server_common_name"></a> [server\_common\_name](#input\_server\_common\_name) | Unique Common Name for Server self-signed certificate | `string` | `null` | no |
| <a name="input_split_tunnel"></a> [split\_tunnel](#input\_split\_tunnel) | Indicates whether split-tunnel is enabled on VPN endpoint. Default value is false. | `bool` | `false` | no |
| <a name="input_stage"></a> [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no |
Expand Down
42 changes: 23 additions & 19 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
locals {
enabled = module.this.enabled

security_group_enabled = local.enabled && var.create_security_group
mutual_enabled = local.enabled && var.authentication_type == "certificate-authentication"
federated_enabled = local.enabled && var.authentication_type == "federated-authentication"
logging_enabled = local.enabled && var.logging_enabled
export_client_certificate = local.mutual_enabled && var.export_client_certificate
certificate_backends = ["ACM", "SSM"]
saml_provider_arn = local.federated_enabled ? try(aws_iam_saml_provider.default[0].arn, var.saml_provider_arn) : null
root_certificate_chain_arn = local.mutual_enabled ? module.self_signed_cert_root.certificate_arn : null
cloudwatch_log_group = local.logging_enabled ? module.cloudwatch_log.log_group_name : null
cloudwatch_log_stream = local.logging_enabled ? var.logging_stream_name : null
ca_common_name = var.ca_common_name != null ? var.ca_common_name : "${module.this.id}.vpn.ca"
root_common_name = var.root_common_name != null ? var.root_common_name : "${module.this.id}.vpn.client"
server_common_name = var.server_common_name != null ? var.server_common_name : "${module.this.id}.vpn.server"
client_conf_tmpl_path = var.client_conf_tmpl_path == null ? "${path.module}/templates/client-config.ovpn.tpl" : var.client_conf_tmpl_path
security_group_enabled = local.enabled && var.create_security_group
mutual_enabled = local.enabled && var.authentication_type == "certificate-authentication"
federated_enabled = local.enabled && var.authentication_type == "federated-authentication"
self_service_portal_enabled = local.federated_enabled && var.self_service_portal_enabled
logging_enabled = local.enabled && var.logging_enabled

export_client_certificate = local.mutual_enabled && var.export_client_certificate
certificate_backends = ["ACM", "SSM"]
saml_provider_arn = local.federated_enabled ? try(aws_iam_saml_provider.default[0].arn, var.saml_provider_arn) : null
root_certificate_chain_arn = local.mutual_enabled ? module.self_signed_cert_root.certificate_arn : null
self_service_saml_provider_arn = local.self_service_portal_enabled ? var.self_service_saml_provider_arn : null
cloudwatch_log_group = local.logging_enabled ? module.cloudwatch_log.log_group_name : null
cloudwatch_log_stream = local.logging_enabled ? var.logging_stream_name : null
ca_common_name = var.ca_common_name != null ? var.ca_common_name : "${module.this.id}.vpn.ca"
root_common_name = var.root_common_name != null ? var.root_common_name : "${module.this.id}.vpn.client"
server_common_name = var.server_common_name != null ? var.server_common_name : "${module.this.id}.vpn.server"
client_conf_tmpl_path = var.client_conf_tmpl_path == null ? "${path.module}/templates/client-config.ovpn.tpl" : var.client_conf_tmpl_path
}

module "self_signed_cert_ca" {
Expand Down Expand Up @@ -152,11 +155,13 @@ resource "aws_ec2_client_vpn_endpoint" "default" {
description = module.this.id
server_certificate_arn = module.self_signed_cert_server.certificate_arn
client_cidr_block = var.client_cidr
self_service_portal = local.self_service_portal_enabled ? "enabled" : "disabled"

authentication_options {
type = var.authentication_type
saml_provider_arn = local.saml_provider_arn
root_certificate_chain_arn = local.root_certificate_chain_arn
type = var.authentication_type
saml_provider_arn = local.saml_provider_arn
root_certificate_chain_arn = local.root_certificate_chain_arn
self_service_saml_provider_arn = local.self_service_saml_provider_arn
}

connection_log_options {
Expand All @@ -165,8 +170,7 @@ resource "aws_ec2_client_vpn_endpoint" "default" {
cloudwatch_log_stream = local.cloudwatch_log_stream
}

dns_servers = var.dns_servers

dns_servers = var.dns_servers
split_tunnel = var.split_tunnel

tags = module.this.tags
Expand Down
4 changes: 2 additions & 2 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ clean:
all: module examples/complete

## Run basic sanity checks against the module itself
module: export TESTS ?= installed lint get-modules module-pinning get-plugins provider-pinning validate terraform-docs input-descriptions output-descriptions
module: export TESTS ?= installed lint module-pinning provider-pinning validate terraform-docs input-descriptions output-descriptions
module: deps
$(call RUN_TESTS, ../)

## Run tests against example
examples/complete: export TESTS ?= installed lint get-modules get-plugins validate
examples/complete: export TESTS ?= installed lint validate
examples/complete: deps
$(call RUN_TESTS, ../$@)
3 changes: 1 addition & 2 deletions test/src/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export TF_CLI_ARGS_init ?= -get-plugins=true
export TERRAFORM_VERSION ?= $(shell curl -s https://checkpoint-api.hashicorp.com/v1/check/terraform | jq -r -M '.current_version' | cut -d. -f1-2)
export TERRAFORM_VERSION ?= $(shell curl -s https://checkpoint-api.hashicorp.com/v1/check/terraform | jq -r -M '.current_version' | cut -d. -f1)

.DEFAULT_GOAL : all
.PHONY: all
Expand Down
126 changes: 85 additions & 41 deletions test/src/examples_complete_test.go
Original file line number Diff line number Diff line change
@@ -1,49 +1,93 @@
package test

import (
"math/rand"
"strconv"
"testing"
"time"

"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
"github.com/gruntwork-io/terratest/modules/random"
"github.com/gruntwork-io/terratest/modules/terraform"
test_structure "github.com/gruntwork-io/terratest/modules/test-structure"
"github.com/stretchr/testify/assert"
"os"
"strings"
"testing"
)

func cleanup(t *testing.T, terraformOptions *terraform.Options, tempTestFolder string) {
terraform.Destroy(t, terraformOptions)
os.RemoveAll(tempTestFolder)
}

// Test the Terraform module in examples/complete using Terratest.
func TestExamplesComplete(t *testing.T) {
t.Parallel()

rand.Seed(time.Now().UnixNano())
randID := strconv.Itoa(rand.Intn(100000))
attributes := []string{randID}

terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: "../../examples/complete",
Upgrade: true,
// Variables to pass to our Terraform code using -var-file options
VarFiles: []string{"fixtures.us-east-2.tfvars"},
// We always include a random attribute so that parallel tests
// and AWS resources do not interfere with each other
Vars: map[string]interface{}{
"attributes": attributes,
},
}
// At the end of the test, run `terraform destroy` to clean up any resources that were created
defer terraform.Destroy(t, terraformOptions)

// This will run `terraform init` and `terraform apply` and fail the test if there are any errors
terraform.InitAndApply(t, terraformOptions)

// Run `terraform output` to get the value of an output variable
vpnEndpointArn := terraform.Output(t, terraformOptions, "vpn_endpoint_arn")
vpnEndpointId := terraform.Output(t, terraformOptions, "vpn_endpoint_id")
vpnEndpointDnsName := terraform.Output(t, terraformOptions, "vpn_endpoint_dns_name")
clientConfiguration := terraform.Output(t, terraformOptions, "client_configuration")

assert.NotNil(t, vpnEndpointArn)
assert.NotNil(t, vpnEndpointId)
assert.NotNil(t, vpnEndpointDnsName)
assert.NotNil(t, clientConfiguration)
t.Parallel()
randId := strings.ToLower(random.UniqueId())
attributes := []string{randId}

rootFolder := "../../"
terraformFolderRelativeToRoot := "examples/complete"
varFiles := []string{"fixtures.us-east-2.tfvars"}

tempTestFolder := test_structure.CopyTerraformFolderToTemp(t, rootFolder, terraformFolderRelativeToRoot)

terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: tempTestFolder,
Upgrade: true,
// Variables to pass to our Terraform code using -var-file options
VarFiles: varFiles,
Vars: map[string]interface{}{
"attributes": attributes,
},
}

// At the end of the test, run `terraform destroy` to clean up any resources that were created
defer cleanup(t, terraformOptions, tempTestFolder)

// This will run `terraform init` and `terraform apply` and fail the test if there are any errors
terraform.InitAndApply(t, terraformOptions)

// Run `terraform output` to get the value of an output variable
vpnEndpointArn := terraform.Output(t, terraformOptions, "vpn_endpoint_arn")
vpnEndpointId := terraform.Output(t, terraformOptions, "vpn_endpoint_id")
vpnEndpointDnsName := terraform.Output(t, terraformOptions, "vpn_endpoint_dns_name")
clientConfiguration := terraform.Output(t, terraformOptions, "client_configuration")

assert.NotNil(t, vpnEndpointArn)
assert.NotNil(t, vpnEndpointId)
assert.NotNil(t, vpnEndpointDnsName)
assert.NotNil(t, clientConfiguration)
}

func TestExamplesCompleteDisabled(t *testing.T) {
t.Parallel()
randId := strings.ToLower(random.UniqueId())
attributes := []string{randId}

rootFolder := "../../"
terraformFolderRelativeToRoot := "examples/complete"
varFiles := []string{"fixtures.us-east-2.tfvars"}

tempTestFolder := test_structure.CopyTerraformFolderToTemp(t, rootFolder, terraformFolderRelativeToRoot)

terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: tempTestFolder,
Upgrade: true,
// Variables to pass to our Terraform code using -var-file options
VarFiles: varFiles,
Vars: map[string]interface{}{
"attributes": attributes,
"enabled": "false",
},
}

// At the end of the test, run `terraform destroy` to clean up any resources that were created
defer cleanup(t, terraformOptions, tempTestFolder)

// This will run `terraform init` and `terraform apply` and fail the test if there are any errors
terraform.InitAndApply(t, terraformOptions)

// Get all the output and lookup a field. Pass if the field is missing or empty.
example := terraform.OutputAll(t, terraformOptions)["vpn_endpoint_id"]

// Verify we're getting back the outputs we expect
assert.Empty(t, example, "When disabled, module should have no outputs.")
}
Loading