Skip to content

Commit

Permalink
fills out main.tf with proposed solution for adding SFTP users (#2)
Browse files Browse the repository at this point in the history
* fills out main.tf with proposed solution for adding SFTP users. started to modify the tests a bit. i think outputs and variables are done?

* minor fixes

* Auto Format

* Fixes tests. Uses policy documents instead of inline policies.

* Auto Format

Co-authored-by: cloudpossebot <11232728+cloudpossebot@users.noreply.github.com>
  • Loading branch information
bradj and cloudpossebot committed Jul 28, 2021
1 parent 7b3f33d commit 38cf918
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 65 deletions.
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,19 +141,26 @@ Available targets:

| Name | Version |
|------|---------|
| <a name="provider_random"></a> [random](#provider\_random) | >= 2.2 |
| <a name="provider_aws"></a> [aws](#provider\_aws) | n/a |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_iam_label"></a> [iam\_label](#module\_iam\_label) | cloudposse/label/null | 0.24.1 |
| <a name="module_this"></a> [this](#module\_this) | cloudposse/label/null | 0.24.1 |

## Resources

| Name | Type |
|------|------|
| [random_integer.example](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) | resource |
| [aws_iam_policy.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_transfer_server.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_server) | resource |
| [aws_transfer_ssh_key.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_ssh_key) | resource |
| [aws_transfer_user.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_user) | resource |
| [aws_iam_policy_document.allows_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |

## Inputs

Expand All @@ -165,24 +172,26 @@ Available targets:
| <a name="input_delimiter"></a> [delimiter](#input\_delimiter) | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.<br>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
| <a name="input_enabled"></a> [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no |
| <a name="input_environment"></a> [environment](#input\_environment) | Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
| <a name="input_example"></a> [example](#input\_example) | Example variable | `string` | `"hello world"` | no |
| <a name="input_force_destroy"></a> [force\_destroy](#input\_force\_destroy) | Forces the AWS Transfer Server to be destroyed | `bool` | `false` | no |
| <a name="input_iam_attributes"></a> [iam\_attributes](#input\_iam\_attributes) | Additional attributes to add to the IDs of the IAM role and policy | `list(string)` | `[]` | no |
| <a name="input_id_length_limit"></a> [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).<br>Set to `0` for unlimited length.<br>Set to `null` for default, which is `0`.<br>Does not affect `id_full`. | `number` | `null` | no |
| <a name="input_label_key_case"></a> [label\_key\_case](#input\_label\_key\_case) | The letter case of label keys (`tag` names) (i.e. `name`, `namespace`, `environment`, `stage`, `attributes`) to use in `tags`.<br>Possible values: `lower`, `title`, `upper`.<br>Default value: `title`. | `string` | `null` | no |
| <a name="input_label_order"></a> [label\_order](#input\_label\_order) | The naming order of the id output and Name tag.<br>Defaults to ["namespace", "environment", "stage", "name", "attributes"].<br>You can omit any of the 5 elements, but at least one must be present. | `list(string)` | `null` | no |
| <a name="input_label_value_case"></a> [label\_value\_case](#input\_label\_value\_case) | The letter case of output label values (also used in `tags` and `id`).<br>Possible values: `lower`, `title`, `upper` and `none` (no transformation).<br>Default value: `lower`. | `string` | `null` | no |
| <a name="input_name"></a> [name](#input\_name) | Solution name, e.g. 'app' or 'jenkins' | `string` | `null` | no |
| <a name="input_namespace"></a> [namespace](#input\_namespace) | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | `string` | `null` | no |
| <a name="input_regex_replace_chars"></a> [regex\_replace\_chars](#input\_regex\_replace\_chars) | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.<br>If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no |
| <a name="input_region"></a> [region](#input\_region) | n/a | `string` | `"us-east-1"` | no |
| <a name="input_sftp_users"></a> [sftp\_users](#input\_sftp\_users) | List of SFTP usernames and public keys | <pre>map(object({<br> user_name = string,<br> public_key = string<br> }))</pre> | `{}` | no |
| <a name="input_stage"></a> [stage](#input\_stage) | Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | Additional tags (e.g. `map('BusinessUnit','XYZ')` | `map(string)` | `{}` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_example"></a> [example](#output\_example) | Example output |
| <a name="output_id"></a> [id](#output\_id) | ID of the created example |
| <a name="output_random"></a> [random](#output\_random) | Stable random number for this example |
| <a name="output_transfer_endpoint"></a> [transfer\_endpoint](#output\_transfer\_endpoint) | The endpoint of the Transfer Server |
<!-- markdownlint-restore -->


Expand Down
19 changes: 14 additions & 5 deletions docs/terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,26 @@

| Name | Version |
|------|---------|
| <a name="provider_random"></a> [random](#provider\_random) | >= 2.2 |
| <a name="provider_aws"></a> [aws](#provider\_aws) | n/a |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_iam_label"></a> [iam\_label](#module\_iam\_label) | cloudposse/label/null | 0.24.1 |
| <a name="module_this"></a> [this](#module\_this) | cloudposse/label/null | 0.24.1 |

## Resources

| Name | Type |
|------|------|
| [random_integer.example](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) | resource |
| [aws_iam_policy.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_transfer_server.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_server) | resource |
| [aws_transfer_ssh_key.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_ssh_key) | resource |
| [aws_transfer_user.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_user) | resource |
| [aws_iam_policy_document.allows_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |

## Inputs

Expand All @@ -35,22 +42,24 @@
| <a name="input_delimiter"></a> [delimiter](#input\_delimiter) | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.<br>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
| <a name="input_enabled"></a> [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no |
| <a name="input_environment"></a> [environment](#input\_environment) | Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
| <a name="input_example"></a> [example](#input\_example) | Example variable | `string` | `"hello world"` | no |
| <a name="input_force_destroy"></a> [force\_destroy](#input\_force\_destroy) | Forces the AWS Transfer Server to be destroyed | `bool` | `false` | no |
| <a name="input_iam_attributes"></a> [iam\_attributes](#input\_iam\_attributes) | Additional attributes to add to the IDs of the IAM role and policy | `list(string)` | `[]` | no |
| <a name="input_id_length_limit"></a> [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).<br>Set to `0` for unlimited length.<br>Set to `null` for default, which is `0`.<br>Does not affect `id_full`. | `number` | `null` | no |
| <a name="input_label_key_case"></a> [label\_key\_case](#input\_label\_key\_case) | The letter case of label keys (`tag` names) (i.e. `name`, `namespace`, `environment`, `stage`, `attributes`) to use in `tags`.<br>Possible values: `lower`, `title`, `upper`.<br>Default value: `title`. | `string` | `null` | no |
| <a name="input_label_order"></a> [label\_order](#input\_label\_order) | The naming order of the id output and Name tag.<br>Defaults to ["namespace", "environment", "stage", "name", "attributes"].<br>You can omit any of the 5 elements, but at least one must be present. | `list(string)` | `null` | no |
| <a name="input_label_value_case"></a> [label\_value\_case](#input\_label\_value\_case) | The letter case of output label values (also used in `tags` and `id`).<br>Possible values: `lower`, `title`, `upper` and `none` (no transformation).<br>Default value: `lower`. | `string` | `null` | no |
| <a name="input_name"></a> [name](#input\_name) | Solution name, e.g. 'app' or 'jenkins' | `string` | `null` | no |
| <a name="input_namespace"></a> [namespace](#input\_namespace) | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | `string` | `null` | no |
| <a name="input_regex_replace_chars"></a> [regex\_replace\_chars](#input\_regex\_replace\_chars) | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.<br>If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no |
| <a name="input_region"></a> [region](#input\_region) | n/a | `string` | `"us-east-1"` | no |
| <a name="input_sftp_users"></a> [sftp\_users](#input\_sftp\_users) | List of SFTP usernames and public keys | <pre>map(object({<br> user_name = string,<br> public_key = string<br> }))</pre> | `{}` | no |
| <a name="input_stage"></a> [stage](#input\_stage) | Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | Additional tags (e.g. `map('BusinessUnit','XYZ')` | `map(string)` | `{}` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_example"></a> [example](#output\_example) | Example output |
| <a name="output_id"></a> [id](#output\_id) | ID of the created example |
| <a name="output_random"></a> [random](#output\_random) | Stable random number for this example |
| <a name="output_transfer_endpoint"></a> [transfer\_endpoint](#output\_transfer\_endpoint) | The endpoint of the Transfer Server |
<!-- markdownlint-restore -->
11 changes: 11 additions & 0 deletions examples/complete/fixtures.us-east-2.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,14 @@ stage = "test"

name = "example"

sftp_users = {
"brad" = {
user_name = "brad",
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCYN8IRq10gKJj5/y2IHYFFRXHctJ+VblcsOnwvsbUIB+PKMLLWd5ySpW30s8OFVcxpMu2VXzXVKRGGbOUbZEN7MqH9xkW8eV6tSfXsZK2osLdIQ3QG3eSyoN4gPFlDBkZSzmkb2oaaclGPGRezbzDnp+oz8IiC5ZE8aprq3Xk850fifIEEOhJtVsrL84uwgx4LGZMMQmLdf6xm2SMSMx53zDPtSnlSeMlC2qUz6LBC41gwObQDoh0j3svsENf8FS8iIkdX50NaRoZvhJU0Oud5A7bj3zz0xtKn6uQZnL9hb6ttvp2/mNe1CKBZt9hUdrn4SHPs0sbWYbQLTzp+9okcg8LCe7qnFdHH7xQGp17SAgi5f91RPOUWtqvkOC5yoVaveR82KZObU+HSCfT/PObLjdUDtWrZABp4VM/u5t9Fn6BQ+eRSAiCIqLQlizs9kpKO8LYX7CagxRJz8KtRXfhndA3nTFq35vml8rD5hKsTrtbSkycmytQZ8TF7IwuN0amRfZ7Iwb3/eLTEv6jp5PKKVprBvnjDH1ipn/AwidsKrbCCVquKg0X/7rwVLrvMuYAtlxPLqjqZpvfTwXBwLlHTEuCvuh/Y/TpjJqqxCnbY/6R4TcabHVGsA4b1kVajRbvVPZPGVcWs+XvycO4Y8KR/hZZGGxK16SVFGbrnhX1D2Q== developer@developers.local"
},
"kenny" = {
user_name = "kenny",
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCYN8IRq10gKJj5/y2IHYFFRXHctJ+VblcsOnwvsbUIB+PKMLLWd5ySpW30s8OFVcxpMu2VXzXVKRGGbOUbZEN7MqH9xkW8eV6tSfXsZK2osLdIQ3QG3eSyoN4gPFlDBkZSzmkb2oaaclGPGRezbzDnp+oz8IiC5ZE8aprq3Xk850fifIEEOhJtVsrL84uwgx4LGZMMQmLdf6xm2SMSMx53zDPtSnlSeMlC2qUz6LBC41gwObQDoh0j3svsENf8FS8iIkdX50NaRoZvhJU0Oud5A7bj3zz0xtKn6uQZnL9hb6ttvp2/mNe1CKBZt9hUdrn4SHPs0sbWYbQLTzp+9okcg8LCe7qnFdHH7xQGp17SAgi5f91RPOUWtqvkOC5yoVaveR82KZObU+HSCfT/PObLjdUDtWrZABp4VM/u5t9Fn6BQ+eRSAiCIqLQlizs9kpKO8LYX7CagxRJz8KtRXfhndA3nTFq35vml8rD5hKsTrtbSkycmytQZ8TF7IwuN0amRfZ7Iwb3/eLTEv6jp5PKKVprBvnjDH1ipn/AwidsKrbCCVquKg0X/7rwVLrvMuYAtlxPLqjqZpvfTwXBwLlHTEuCvuh/Y/TpjJqqxCnbY/6R4TcabHVGsA4b1kVajRbvVPZPGVcWs+XvycO4Y8KR/hZZGGxK16SVFGbrnhX1D2Q== developer@developers.local"

}
}
8 changes: 7 additions & 1 deletion examples/complete/main.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
provider "aws" {
region = var.region
}

module "example" {
source = "../.."

example = var.example
region = var.region

sftp_users = var.sftp_users

context = module.this.context
}
13 changes: 4 additions & 9 deletions examples/complete/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ output "id" {
value = module.example.id
}

output "example" {
description = "Output \"example\" from example module"
value = module.example.example
}

output "random" {
description = "Output \"random\" from example module"
value = module.example.random
}
output "transfer_endpoint" {
description = "Endpoint for your SFTP connection"
value = module.example.transfer_endpoint
}
11 changes: 9 additions & 2 deletions examples/complete/variables.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
variable "example" {
type = string
variable "region" {
type = string
}

variable "sftp_users" {
type = map(object({
user_name = string,
public_key = string
}))
description = "The value which will be passed to the example module"
}
2 changes: 1 addition & 1 deletion examples/complete/versions.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 0.12.26"
required_version = ">= 0.13.7"

required_providers {
local = {
Expand Down
85 changes: 77 additions & 8 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,13 +1,82 @@
resource "random_integer" "example" {
count = module.this.enabled ? 1 : 0
locals {
enabled = module.this.enabled
}

resource "aws_transfer_server" "default" {
identity_provider_type = "SERVICE_MANAGED"
protocols = ["SFTP"] # SFTP, FTPS, FTP
domain = "S3" # EFS, S3
endpoint_type = "PUBLIC" # VPC, PUBLIC
force_destroy = var.force_destroy

tags = module.this.tags
}

resource "aws_transfer_user" "default" {
for_each = local.enabled ? var.sftp_users : {}

server_id = aws_transfer_server.default.id
role = aws_iam_role.default.arn

user_name = each.value.user_name

tags = module.this.tags
}

resource "aws_transfer_ssh_key" "default" {
for_each = local.enabled ? var.sftp_users : {}

server_id = aws_transfer_server.default.id

user_name = each.value.user_name
body = each.value.public_key

depends_on = [
aws_transfer_user.default
]
}

# IAM
module "iam_label" {
source = "cloudposse/label/null"
version = "0.24.1"

min = 1
max = 50000
keepers = {
example = var.example
attributes = var.iam_attributes

context = module.this.context
}

data "aws_iam_policy_document" "assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]

principals {
type = "Service"
identifiers = ["transfer.amazonaws.com"]
}
}
}

locals {
example = format("%v %v", var.example, join("", random_integer.example[*].result))
data "aws_iam_policy_document" "allows_s3" {
statement {
sid = "S3AccessForAWSTransferusers"
effect = "Allow"

actions = ["s3:*"]

resources = [
"arn:aws:s3:::*"
]
}
}

resource "aws_iam_policy" "default" {
name = module.iam_label.id
policy = data.aws_iam_policy_document.allows_s3.json
}

resource "aws_iam_role" "default" {
name = module.iam_label.id
assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
managed_policy_arns = [aws_iam_policy.default.arn]
}
11 changes: 3 additions & 8 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ output "id" {
value = module.this.enabled ? module.this.id : null
}

output "example" {
description = "Example output"
value = module.this.enabled ? local.example : null
}

output "random" {
description = "Stable random number for this example"
value = module.this.enabled ? join("", random_integer.example[*].result) : null
output "transfer_endpoint" {
description = "The endpoint of the Transfer Server"
value = aws_transfer_server.default.endpoint
}
33 changes: 10 additions & 23 deletions test/src/examples_complete_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package test

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

Expand All @@ -18,8 +20,6 @@ func TestExamplesComplete(t *testing.T) {
randID := strconv.Itoa(rand.Intn(100000))
attributes := []string{randID}

exampleInput := "Hello, world!"

terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: "../../examples/complete",
Expand All @@ -30,7 +30,6 @@ func TestExamplesComplete(t *testing.T) {
// and AWS resources do not interfere with each other
Vars: map[string]interface{}{
"attributes": attributes,
"example": exampleInput,
},
}
// At the end of the test, run `terraform destroy` to clean up any resources that were created
Expand All @@ -41,12 +40,15 @@ func TestExamplesComplete(t *testing.T) {

// Run `terraform output` to get the value of an output variable
id := terraform.Output(t, terraformOptions, "id")
example := terraform.Output(t, terraformOptions, "example")
random := terraform.Output(t, terraformOptions, "random")
transfer_endpoint := terraform.Output(t, terraformOptions, "transfer_endpoint")

// Verify we're getting back the outputs we expect
// Ensure we get a random number appended
assert.Equal(t, exampleInput+" "+random, example)
expectedTransferEndpoint := "server.transfer.us-east-2.amazonaws.com"
assert.True(t,
strings.HasSuffix(transfer_endpoint, expectedTransferEndpoint),
fmt.Sprintf("Transfer endpoint should end with %v", expectedTransferEndpoint))

// Ensure we get the attribute included in the ID
assert.Equal(t, "eg-ue2-test-example-"+randID, id)

Expand All @@ -61,23 +63,8 @@ func TestExamplesComplete(t *testing.T) {
terraform.Apply(t, terraformOptions)

id2 := terraform.Output(t, terraformOptions, "id")
example2 := terraform.Output(t, terraformOptions, "example")
random2 := terraform.Output(t, terraformOptions, "random")
transfer_endpoint2 := terraform.Output(t, terraformOptions, "transfer_endpoint")

assert.Equal(t, id, id2, "Expected `id` to be stable")
assert.Equal(t, example, example2, "Expected `example` to be stable")
assert.Equal(t, random, random2, "Expected `random` to be stable")

// Then we run change the example and run it a third time and
// verify that the random number changed
newExample := "Goodbye"
terraformOptions.Vars["example"] = newExample
terraform.Apply(t, terraformOptions)

example3 := terraform.Output(t, terraformOptions, "example")
random3 := terraform.Output(t, terraformOptions, "random")

assert.NotEqual(t, random, random3, "Expected `random` to change when `example` changed")
assert.Equal(t, newExample+" "+random3, example3, "Expected `example` to use new random number")

assert.Equal(t, transfer_endpoint, transfer_endpoint2, "Expected `transfer_endpoint` to be stable")
}
28 changes: 25 additions & 3 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
variable "example" {
description = "Example variable"
default = "hello world"
variable "region" {
type = string
default = "us-east-1"
}

variable "sftp_users" {
type = map(object({
user_name = string,
public_key = string
}))

default = {}
description = "List of SFTP usernames and public keys"
}

variable "force_destroy" {
type = bool
default = false
description = "Forces the AWS Transfer Server to be destroyed"
}

variable "iam_attributes" {
type = list(string)
description = "Additional attributes to add to the IDs of the IAM role and policy"
default = []
}

0 comments on commit 38cf918

Please sign in to comment.