From a453b8b508b3d2f697fa1557fef770a62fb77789 Mon Sep 17 00:00:00 2001 From: egorodov <119041467+egorodov@users.noreply.github.com> Date: Tue, 4 Jul 2023 04:07:26 +0300 Subject: [PATCH] [MAINTENANCE] Adding terraform documentation (#102) * Adding documentation for modules --- LICENSE.md => LICENSE | 0 README.md | 99 ++++++------- docs/inframap.png | Bin 0 -> 91920 bytes examples/basic/README.md | 49 +++++++ examples/basic/versions.tf | 4 +- terraform/README.md | 144 ++++++++++--------- terraform/modules/alerting/README.md | 58 ++++++++ terraform/modules/athena-connector/README.md | 55 +++++++ terraform/modules/s3-configs/README.md | 61 ++++++++ terraform/modules/s3-gateway/README.md | 56 ++++++++ terraform/outputs.tf | 2 +- 11 files changed, 405 insertions(+), 123 deletions(-) rename LICENSE.md => LICENSE (100%) create mode 100644 docs/inframap.png create mode 100644 terraform/modules/alerting/README.md create mode 100644 terraform/modules/athena-connector/README.md create mode 100644 terraform/modules/s3-configs/README.md create mode 100644 terraform/modules/s3-gateway/README.md diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/README.md b/README.md index 689261a..60da733 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Data Quality Gate ## Description -Terrafrom module which setup Data-QA solution(bucket,Stepfunctions Pipeline with AWS Lambda, Metadata Storage. Data-QA Reports) in your infrastructure in 'one-click'. AWS Based. Built on top of Great_expectations, Pandas_profiling, Allure +Terraform module which setups DataQA solution in your infrastructure in 'one-click'. AWS Based. Built on top of Great_expectations, Pandas_profiling, Allure ### Data Test Main engine based on GX to profile, generate suites and run tests @@ -15,64 +15,55 @@ Metadata and metrics aggregation ## Solution Architecture ![Preview Image](https://raw.githubusercontent.com/provectus/data-quality-gate/main/architecture.PNG) +## Supported Features + +- AWS Lambda runtime Python 3.9 +- AWS StepFunction pipeline, combining whole DataQA cycle(profiling, test generation, reporting) +- Supports Slack and Jira notifications and reporting +- AWS SNS output message bus, allowing to embed to existing data pipelines +- Web reports delivery through Nginx for companies VPN/IP set +- AWS DynamoDB and Athena integration, allowing to build AWS QuickSight or Grafana dashboards +- Flexible way of config management for underlying technologies such as Allure and GreatExpectation + ## Usage -Could be used as standard Terraform module, the examples of deployments under `examples` directory. -1. Add to terraform DataQA module as in examples -2. Add to terraform state machine `DataTests` step -```terraform -resource "aws_sfn_state_machine" "data_state_machine" { - definition = jsonencode( - { - StartAt = "GetData" - States = { - GetData = { - Next = "DataTests" - Resource = aws_lambda_function.some_get_data.function_name - ResultPath = "$.file" - Type = "Task" - } - DataTests = { - Type = "Task" - Resource = "arn:aws:states:::states:startExecution.sync:2", - End = true - Parameters = { - StateMachineArn = module.data-qa.qa_step_functions_arn - Input = { - files = [ - { - engine = "s3" - source_root = var.data_lake_bucket - run_name = "raw_data" - "source_data.$" = "$.file" - } - ] - } - } - } - } - } - ) - name = "Data-state-machine" - role_arn = aws_iam_role.state_machine.arn // role with perms on lambda:InvokeFunction - type = "STANDARD" - - logging_configuration { - include_execution_data = false - level = "OFF" - } +```hcl +module "data_qa" { + source = "github.com/provectus/data-quality-gate" - tracing_configuration { - enabled = false + data_test_storage_bucket_name = "my-data-settings-dev" + s3_source_data_bucket = "my-data-bucket" + environment = "example" + project = "my-project" + + allure_report_image_uri = "xxxxxxxxxxxx.dkr.ecr.xx-xxxx-x.amazonaws.com/dqg-allure_report:latest" + data_test_image_uri = "xxxxxxxxxxxx.dkr.ecr.xx-xxxx-x.amazonaws.com/dqg-data_test:latest" + push_report_image_uri = "xxxxxxxxxxxx.dkr.ecr.xx-xxxx-x.amazonaws.com/dqg-push_reportt:latest" + + data_reports_notification_settings = { + channel = "DataReportSlackChannelName" + webhook_url = "https://hooks.slack.com/services/xxxxxxxxxxxxxxx" } + + lambda_private_subnet_ids = ["private_subnet_id"] + lambda_security_group_ids = ["security_group_id"] + + reports_vpc_id = "some_vpc_id" + reports_subnet_id = "subnet_id" + reports_whitelist_ips = ["0.0.0.0/0"] } ``` -3. Create AWS Serverless application* - [AthenaDynamoDBConnector](https://us-west-2.console.aws.amazon.com/lambda/home?region=us-west-2#/create/app?applicationId=arn:aws:serverlessrepo:us-east-1:292517598671:applications/AthenaDynamoDBConnector) with parameters: - - SpillBucket - name of bucket created by terraform module - - AthenaCatalogName - The name you will give to this catalog in Athena. It will also be used as the function name. -*Cannot be created automatically by terraform because [terraform-provider-aws/issues/16485](https://github.com/hashicorp/terraform-provider-aws/issues/16485) +## Examples + +Could be used as standard Terraform module, the examples of deployments under `examples` directory. + +- [data-qa-basic](https://github.com/provectus/data-quality-gate/tree/main/examples/basic) - Creates DataQA module which builds AWS infrastructure. + +## Local Development and Testing + +See the [functions](https://github.com/provectus/data-quality-gate/tree/main/functions) for further details. + +## License -4. Create AWS Athena Data Source: -- Data source type -> Amazon DynamoDB -- Connection details -> lambda function -> name of `AthenaCatalogName` from pt.3 +Apache 2 Licensed. See [LICENSE](https://github.com/provectus/data-quality-gate/tree/main/LICENSE) for full details. diff --git a/docs/inframap.png b/docs/inframap.png new file mode 100644 index 0000000000000000000000000000000000000000..a78902d07bd3f3d649dfaa5ac2f6562014aded50 GIT binary patch literal 91920 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

!0seu`Qhrv4iPLX8mj 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 +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.1 | +| [aws](#requirement\_aws) | ~> 4.64.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [data\_qa](#module\_data\_qa) | ../../terraform | n/a | + +## Resources + +No resources. + +## Inputs + +No inputs. + +## Outputs + +No outputs. + diff --git a/examples/basic/versions.tf b/examples/basic/versions.tf index 9eb52cd..ee05458 100644 --- a/examples/basic/versions.tf +++ b/examples/basic/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.1.7" - required_providers { aws = { source = "hashicorp/aws" version = "~> 4.64.0" } } + + required_version = "~> 1.1" } diff --git a/terraform/README.md b/terraform/README.md index 1b123ed..4046ae4 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -1,26 +1,57 @@ +## DataQA terraform module + +![Preview Image](https://raw.githubusercontent.com/provectus/data-quality-gate/main/docs/inframap.png) + +### Pre-requirements + +As part of this solution, it is expected to have the necessary existing infrastructure +- At least 1 Vpc +- At least 1 private subnet in vpc +- At least 1 public subnet in vpc(if you want to see DataQA reports in the Web) +- At least 5 vpc endpoints + - `com.amazonaws.AWS-REGION.dynamodb` + - `com.amazonaws.AWS-REGION.s3` + - `com.amazonaws.AWS-REGION.sns` + - `com.amazonaws.AWS-REGION.monitoring` + - `com.amazonaws.AWS-REGION.secretsmanager` +- At least 1 AWS S3 bucket with data that you want to test +- At least 1 AWS ECR repository + +### List of submodules + +- [Alerting](https://github.com/provectus/data-quality-gate/tree/main/terraform/modules/alerting) - provides basic functionality for AWS CloudWatch metrics alerts and forwards them to the Slack messenger. Also used as message bus for `data_report` lambda +- [Athena connector](https://github.com/provectus/data-quality-gate/tree/main/terraform/modules/athena-connector) - builds AWS Athena data catalog and AWS Lambda to allow query internal DynamoDB data table +- [AWS S3 configs](https://github.com/provectus/data-quality-gate/tree/main/terraform/modules/s3-configs) - creates internal AWS S3 bucket for data quality processing. Additionally pushing Allure and GreatExpectations configs to this bucket +- [AWS S3 Gateway](https://github.com/provectus/data-quality-gate/tree/main/terraform/modules/s3-gateway) - creates AWS EC2 instance that serves HTTP requests to see static reports in the web. + ## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | ~> 1.1 | -| [aws](#requirement\_aws) | >= 4.8.0 | +| [aws](#requirement\_aws) | ~> 4.64.0 | | [local](#requirement\_local) | ~> 2.2.3 | +| [null](#requirement\_null) | ~> 3.2.1 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.8.0 | +| [aws](#provider\_aws) | 4.64.0 | ## Modules | Name | Source | Version | |------|--------|---------| -| [lambda\_function\_allure\_report](#module\_lambda\_function\_allure\_report) | terraform-aws-modules/lambda/aws | 3.3.1 | -| [lambda\_function\_data\_test](#module\_lambda\_function\_data\_test) | terraform-aws-modules/lambda/aws | 3.3.1 | -| [lambda\_function\_push\_report](#module\_lambda\_function\_push\_report) | terraform-aws-modules/lambda/aws | 3.3.1 | -| [slack\_notifier](#module\_slack\_notifier) | ./modules/slack-notification | n/a | +| [athena\_connector](#module\_athena\_connector) | ./modules/athena-connector | n/a | +| [basic\_slack\_alerting](#module\_basic\_slack\_alerting) | ./modules/alerting | n/a | +| [data\_reports\_alerting](#module\_data\_reports\_alerting) | ./modules/alerting | n/a | +| [lambda\_allure\_report](#module\_lambda\_allure\_report) | terraform-aws-modules/lambda/aws | 3.3.1 | +| [lambda\_data\_test](#module\_lambda\_data\_test) | terraform-aws-modules/lambda/aws | 3.3.1 | +| [lambda\_push\_report](#module\_lambda\_push\_report) | terraform-aws-modules/lambda/aws | 3.3.1 | +| [reports\_gateway](#module\_reports\_gateway) | ./modules/s3-gateway | n/a | +| [s3\_bucket](#module\_s3\_bucket) | ./modules/s3-configs | n/a | ## Resources @@ -28,96 +59,77 @@ |------|------| | [aws_appautoscaling_policy.data_qa_report_read_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_policy) | resource | | [aws_appautoscaling_policy.data_qa_report_table_write_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_policy) | resource | -| [aws_appautoscaling_target.data_qa_report_table_read_target](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_target) | resource | -| [aws_appautoscaling_target.data_qa_report_table_write_target](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_target) | resource | -| [aws_cloudfront_distribution.s3_distribution_ip](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution) | resource | -| [aws_cloudfront_origin_access_identity.data_qa_oai](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_identity) | resource | -| [aws_cloudfront_origin_access_identity.never_be_reached](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_identity) | resource | -| [aws_cloudwatch_log_group.state-machine-log-group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | -| [aws_cloudwatch_metric_alarm.lambda_allure_report_error](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource | -| [aws_cloudwatch_metric_alarm.lambda_data_test_error](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource | -| [aws_cloudwatch_metric_alarm.lambda_push_report_error](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource | +| [aws_appautoscaling_target.data_qa_report_table_read](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_target) | resource | +| [aws_appautoscaling_target.data_qa_report_table_write](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_target) | resource | +| [aws_cloudwatch_log_group.state_machine_log_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_dynamodb_table.data_qa_report](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table) | resource | -| [aws_iam_policy.CloudWatchLogsDeliveryFullAccessPolicy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.LambdaInvokeScopedAccessPolicy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.XRayAccessPolicy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.athena](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.basic_lambda_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.data_test_athena](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.cloud_watch_logs_delivery_full_access_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.dynamodb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.lambda_invoke_scoped_access_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.sns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.xray_access_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.step_functions_fast_data_qa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.data_test_athena](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_s3_bucket.settings_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | -| [aws_s3_bucket_lifecycle_configuration.delete_old_reports](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | -| [aws_s3_bucket_policy.cloudfront_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | -| [aws_s3_bucket_public_access_block.public_access_block_fast_data_qa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | -| [aws_s3_bucket_versioning.fast-data-qa-bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | -| [aws_s3_object.expectations_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | -| [aws_s3_object.great_expectations_yml](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | -| [aws_s3_object.mapping_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | -| [aws_s3_object.pipeline_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | -| [aws_s3_object.pks_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | -| [aws_s3_object.sort_keys_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | -| [aws_s3_object.test_config_manifest](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | -| [aws_s3_object.test_configs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | +| [aws_iam_role_policy_attachment.push_report_dynamodb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.push_report_sns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_sfn_state_machine.fast_data_qa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sfn_state_machine) | resource | -| [aws_sns_topic.notifications](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | -| [aws_sns_topic_policy.notification](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_policy) | resource | -| [aws_wafv2_ip_set.vpn_ipset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_ip_set) | resource | -| [aws_wafv2_web_acl.waf_acl](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl) | resource | -| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_iam_policy_document.s3_policy_for_cloudfront](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.slack_notification_sns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [allure\_report\_extra\_vars](#input\_allure\_report\_extra\_vars) | Extra environment variables for allure report lambda | `map(string)` | `{}` | no | | [allure\_report\_image\_uri](#input\_allure\_report\_image\_uri) | Allure report image URI(ECR repository) | `string` | n/a | yes | -| [cloudfront\_allowed\_subnets](#input\_cloudfront\_allowed\_subnets) | list of allowed subnets allows users to get reports from specific IP address spaces | `list(string)` | `null` | no | -| [cloudfront\_location\_restrictions](#input\_cloudfront\_location\_restrictions) | List of regions allowed for CloudFront distribution | `list(string)` |

[
"US",
"CA",
"GB",
"DE",
"TR"
]
| no | -| [create\_cloudwatch\_notifications\_topic](#input\_create\_cloudwatch\_notifications\_topic) | Should sns topic for cloudwatch alerts be created | `bool` | `true` | no | +| [basic\_alert\_notification\_settings](#input\_basic\_alert\_notification\_settings) | Base alert notifications settings. If empty - basic alerts will be disabled |
object({
channel = string
webhook_url = string
})
| `null` | no | +| [data\_reports\_notification\_settings](#input\_data\_reports\_notification\_settings) | Data reports notifications settings. If empty - notifications will be disabled |
object({
channel = string
webhook_url = string
})
| `null` | no | +| [data\_test\_extra\_vars](#input\_data\_test\_extra\_vars) | Extra environment variables for data test lambda | `map(string)` | `{}` | no | | [data\_test\_image\_uri](#input\_data\_test\_image\_uri) | Data test image URI(ECR repository) | `string` | n/a | yes | | [data\_test\_storage\_bucket\_name](#input\_data\_test\_storage\_bucket\_name) | Bucket name which will be used to store data tests and settings for it's execution | `string` | n/a | yes | +| [dynamodb\_autoscaling\_defaults](#input\_dynamodb\_autoscaling\_defaults) | A map of default autoscaling settings | `map(string)` |
{
"scale_in_cooldown": 50,
"scale_out_cooldown": 40,
"target_value": 45
}
| no | +| [dynamodb\_autoscaling\_read](#input\_dynamodb\_autoscaling\_read) | A map of read autoscaling settings. `max_capacity` is the only required key. | `map(string)` |
{
"max_capacity": 200
}
| no | +| [dynamodb\_autoscaling\_write](#input\_dynamodb\_autoscaling\_write) | A map of write autoscaling settings. `max_capacity` is the only required key. | `map(string)` |
{
"max_capacity": 10
}
| no | +| [dynamodb\_hash\_key](#input\_dynamodb\_hash\_key) | The attribute to use as the hash (partition) key. Must also be defined as an attribute | `string` | `"file"` | no | | [dynamodb\_read\_capacity](#input\_dynamodb\_read\_capacity) | Dynamodb report table read capacity | `number` | `20` | no | -| [dynamodb\_report\_table\_autoscaling\_read\_capacity\_settings](#input\_dynamodb\_report\_table\_autoscaling\_read\_capacity\_settings) | Report table autoscaling read capacity |
object({
min = number
max = number
})
|
{
"max": 200,
"min": 50
}
| no | -| [dynamodb\_report\_table\_autoscaling\_write\_capacity\_settings](#input\_dynamodb\_report\_table\_autoscaling\_write\_capacity\_settings) | Report table autoscaling write capacity |
object({
min = number
max = number
})
|
{
"max": 50,
"min": 2
}
| no | -| [dynamodb\_report\_table\_read\_scale\_threshold](#input\_dynamodb\_report\_table\_read\_scale\_threshold) | Dynamodb report table read scale up threshold | `number` | `60` | no | -| [dynamodb\_report\_table\_write\_scale\_threshold](#input\_dynamodb\_report\_table\_write\_scale\_threshold) | Dynamodb report table write scale up threshold | `number` | `70` | no | | [dynamodb\_stream\_enabled](#input\_dynamodb\_stream\_enabled) | Dynamodb report table stream enabled | `bool` | `false` | no | -| [dynamodb\_table\_attributes](#input\_dynamodb\_table\_attributes) | List of nested attribute definitions. Only required for hash\_key and range\_key attributes. Each attribute has two properties: name - (Required) The name of the attribute, type - (Required) Attribute type, which must be a scalar type: S, N, or B for (S)tring, (N)umber or (B)inary data | `list(map(string))` | `[]` | no | +| [dynamodb\_table\_attributes](#input\_dynamodb\_table\_attributes) | List of nested attribute definitions. Only required for hash\_key and range\_key attributes. Each attribute has two properties: name - (Required) The name of the attribute, type - (Required) Attribute type, which must be a scalar type: S, N, or B for (S)tring, (N)umber or (B)inary data | `list(map(string))` |
[
{
"name": "file",
"type": "S"
}
]
| no | | [dynamodb\_write\_capacity](#input\_dynamodb\_write\_capacity) | Dynamodb report table write capacity | `number` | `2` | no | -| [environment](#input\_environment) | Environment name used to build fully qualified tags and resource's names | `string` | `"data-qa-dev"` | no | -| [expectations\_store](#input\_expectations\_store) | Path to the expectations\_store directory, relative to the root TF | `string` | `"../expectations_store"` | no | +| [environment](#input\_environment) | Environment name used to build fully qualified tags and resource's names | `string` | n/a | yes | +| [expectations\_store](#input\_expectations\_store) | Path to the expectations\_store directory, relative to the root TF | `string` | `"../../../expectations_store"` | no | +| [great\_expectation\_path](#input\_great\_expectation\_path) | Path to the great expectations yaml | `string` | `"../../../templates/great_expectations.yml"` | no | | [lambda\_allure\_report\_memory](#input\_lambda\_allure\_report\_memory) | Amount of memory allocated to the lambda function lambda\_allure\_report | `number` | `1024` | no | | [lambda\_data\_test\_memory](#input\_lambda\_data\_test\_memory) | Amount of memory allocated to the lambda function lambda\_data\_test | `number` | `5048` | no | +| [lambda\_private\_subnet\_ids](#input\_lambda\_private\_subnet\_ids) | List of private subnets assigned to lambda | `list(string)` | n/a | yes | | [lambda\_push\_jira\_url](#input\_lambda\_push\_jira\_url) | Lambda function push report env variable JIRA\_URL | `string` | `null` | no | | [lambda\_push\_report\_memory](#input\_lambda\_push\_report\_memory) | Amount of memory allocated to the lambda function lambda\_push\_report | `number` | `1024` | no | | [lambda\_push\_secret\_name](#input\_lambda\_push\_secret\_name) | Lambda function push report env variable JIRA\_URL | `string` | `null` | no | -| [mapping\_path](#input\_mapping\_path) | Path to the mapping description path, relative to the root TF | `string` | `"../configs/mapping.json"` | no | -| [pipeline\_config\_path](#input\_pipeline\_config\_path) | Path to the pipeline description path, relative to the root TF | `string` | `"../configs/pipeline.json"` | no | -| [pks\_path](#input\_pks\_path) | Path to the primary keys description path, relative to the root TF | `string` | `"../configs/pks.json"` | no | +| [lambda\_security\_group\_ids](#input\_lambda\_security\_group\_ids) | List of security group assigned to lambda | `list(string)` | n/a | yes | +| [manifest\_path](#input\_manifest\_path) | Path to the manifests | `string` | `"../../../configs/manifest.json"` | no | +| [mapping\_path](#input\_mapping\_path) | Path to the mapping description path, relative to the root TF | `string` | `"../../../configs/mapping.json"` | no | +| [pipeline\_config\_path](#input\_pipeline\_config\_path) | Path to the pipeline description path, relative to the root TF | `string` | `"../../../configs/pipeline.json"` | no | +| [pks\_path](#input\_pks\_path) | Path to the primary keys description path, relative to the root TF | `string` | `"../../../configs/pks.json"` | no | | [project](#input\_project) | Project name used to build fully qualified tags and resource's names | `string` | `"demo"` | no | +| [push\_report\_extra\_vars](#input\_push\_report\_extra\_vars) | Extra environment variables for push report lambda | `map(string)` | `{}` | no | | [push\_report\_image\_uri](#input\_push\_report\_image\_uri) | Push report image URI(ECR repository) | `string` | n/a | yes | | [redshift\_db\_name](#input\_redshift\_db\_name) | Database name for source redshift cluster | `string` | `null` | no | | [redshift\_secret](#input\_redshift\_secret) | Secret name from AWS SecretsManager for Redshift cluster | `string` | `null` | no | -| [slack\_settings](#input\_slack\_settings) | Slack notifications settings. If null - slack notifications will be disabled |
object({
webhook_url = string
channel = string
username = string
image_uri = string
vpc_id = string
})
| `null` | no | -| [sns\_cloudwatch\_notifications\_topic\_arn](#input\_sns\_cloudwatch\_notifications\_topic\_arn) | SNS topic to send cloudwatch events | `string` | `null` | no | -| [sort\_keys\_path](#input\_sort\_keys\_path) | Path to the sort keys description path, relative to the root TF | `string` | `"../configs/sort_keys.json"` | no | -| [test\_coverage\_path](#input\_test\_coverage\_path) | Path to the tests description path, relative to the root TF | `string` | `"../configs/test_coverage.json"` | no | -| [vpc\_security\_group\_ids](#input\_vpc\_security\_group\_ids) | List of security group assigned to lambda. If null value, default subnet and vpc will be used | `list(string)` | `null` | no | -| [vpc\_subnet\_ids](#input\_vpc\_subnet\_ids) | List of subnet ids to place lambda in. If null value, default subnet and vpc will be used | `list(string)` | `null` | no | +| [reports\_subnet\_id](#input\_reports\_subnet\_id) | Subnet id where gateway instance will be placed | `string` | n/a | yes | +| [reports\_vpc\_id](#input\_reports\_vpc\_id) | Vpc Id where gateway instance will be placed | `string` | n/a | yes | +| [reports\_whitelist\_ips](#input\_reports\_whitelist\_ips) | List of allowed IPs to see reports | `list(string)` | n/a | yes | +| [s3\_source\_data\_bucket](#input\_s3\_source\_data\_bucket) | Bucket name, with the data on which test will be executed | `string` | n/a | yes | +| [sort\_keys\_path](#input\_sort\_keys\_path) | Path to the sort keys description path, relative to the root TF | `string` | `"../../../configs/sort_keys.json"` | no | +| [test\_coverage\_path](#input\_test\_coverage\_path) | Path to the tests description path, relative to the root TF | `string` | `"../../../configs/test_coverage.json"` | no | ## Outputs | Name | Description | |------|-------------| -| [allure\_report\_role\_arn](#output\_allure\_report\_role\_arn) | n/a | -| [bucket](#output\_bucket) | Data quality gate bucket with settings and generated tests | -| [data\_test\_role\_arn](#output\_data\_test\_role\_arn) | n/a | -| [lambda\_allure\_arn](#output\_lambda\_allure\_arn) | n/a | -| [lambda\_data\_test\_arn](#output\_lambda\_data\_test\_arn) | n/a | -| [lambda\_report\_push\_arn](#output\_lambda\_report\_push\_arn) | n/a | -| [report\_push\_role\_arn](#output\_report\_push\_role\_arn) | n/a | -| [step\_function\_arn](#output\_step\_function\_arn) | n/a | +| [bucket](#output\_bucket) | DataQA bucket with settings and generated tests | +| [lambda\_allure\_arn](#output\_lambda\_allure\_arn) | Allure reports generation lambda arn | +| [lambda\_data\_test\_arn](#output\_lambda\_data\_test\_arn) | Data test generation/running lambda arn | +| [lambda\_report\_push\_arn](#output\_lambda\_report\_push\_arn) | Report push to dynamodb lambda arn | +| [step\_function\_arn](#output\_step\_function\_arn) | DataQA step function arn | diff --git a/terraform/modules/alerting/README.md b/terraform/modules/alerting/README.md new file mode 100644 index 0000000..06ece1b --- /dev/null +++ b/terraform/modules/alerting/README.md @@ -0,0 +1,58 @@ +Alerting +======================= + +The module in this folder used for 2 purposes: +- Creating CloudWatch metric alarms for DataQA main AWS StepFunction and forward with alerts to the Slack channel +- Creating data reports message bus that receives custom metrics from `report_push` lambda and forwards them to the Slack channel + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.1 | +| [aws](#requirement\_aws) | ~> 4.64.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.5.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [slack\_notification](#module\_slack\_notification) | terraform-aws-modules/notify-slack/aws | 6.0.0 | + +## Resources + +| Name | Type | +|------|------| +| [aws_cloudwatch_metric_alarm.alarm](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource | +| [aws_kms_ciphertext.slack_url](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_ciphertext) | resource | +| [aws_kms_key.slack](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | +| [aws_sfn_state_machine.step_functions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/sfn_state_machine) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [datapoints\_to\_alarm](#input\_datapoints\_to\_alarm) | The number of datapoints that must be breaching to trigger the alarm. | `number` | `1` | no | +| [evaluation\_periods](#input\_evaluation\_periods) | The number of periods over which data is compared to the specified threshold. | `number` | `1` | no | +| [lambda\_function\_vpc\_security\_group\_ids](#input\_lambda\_function\_vpc\_security\_group\_ids) | List of security group ids when Lambda Function should run in the VPC. | `list(string)` | `null` | no | +| [lambda\_function\_vpc\_subnet\_ids](#input\_lambda\_function\_vpc\_subnet\_ids) | List of subnet ids when Lambda Function should run in the VPC. Usually private or intra subnets. | `list(string)` | `null` | no | +| [period](#input\_period) | The period in seconds over which the specified statistic is applied. | `number` | `60` | no | +| [resource\_name\_prefix](#input\_resource\_name\_prefix) | Resource name prefix used to generate resources | `string` | n/a | yes | +| [slack\_channel](#input\_slack\_channel) | Slack channel to send notifications | `string` | n/a | yes | +| [slack\_sns\_topic\_name](#input\_slack\_sns\_topic\_name) | Sns topic name to forward notifications to | `string` | n/a | yes | +| [slack\_username](#input\_slack\_username) | Slack username which will be used as author of notifications | `string` | n/a | yes | +| [slack\_webhook\_url](#input\_slack\_webhook\_url) | Slack webhook url in form https://hooks.slack.com/services/........ | `string` | n/a | yes | +| [step\_functions\_to\_monitor](#input\_step\_functions\_to\_monitor) | List of step functions for which to create cloudwatch metrics alarm | `set(string)` | `[]` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [sns\_topic\_arn](#output\_sns\_topic\_arn) | Notifications topic arn | + diff --git a/terraform/modules/athena-connector/README.md b/terraform/modules/athena-connector/README.md new file mode 100644 index 0000000..2b15bc1 --- /dev/null +++ b/terraform/modules/athena-connector/README.md @@ -0,0 +1,55 @@ +AWS Athena connector +======================= + +The module in this folder creates AWS Athena DataCatalog and AWS Lambda function that serves requests from AWS Athena to AWS DynamoDB. +It uses official AWS DynamoDB [connector](https://docs.aws.amazon.com/athena/latest/ug/connectors-dynamodb.html). + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.1 | +| [aws](#requirement\_aws) | ~> 4.64.0 | +| [null](#requirement\_null) | ~> 3.2.1 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.5.0 | +| [null](#provider\_null) | 3.2.1 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.athena_connector_lambda_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.athena_connector_lambda_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.athena_connector_basic_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_lambda_function.athena_dynamodb_connector](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource | +| [aws_s3_bucket.athena_spill_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket_public_access_block.public_access_block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | +| [aws_s3_bucket_versioning.athena_spill_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | +| [null_resource.athena_dynamodb_connector](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.delete_athena_dynamodb_connector](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [data\_catalog\_name](#input\_data\_catalog\_name) | Name of athena data catalog | `string` | n/a | yes | +| [delete\_athena\_dynamodb\_connector](#input\_delete\_athena\_dynamodb\_connector) | Set to True to delete athena dynamodb connector | `bool` | `false` | no | +| [primary\_aws\_region](#input\_primary\_aws\_region) | AWS region | `string` | n/a | yes | +| [vpc\_security\_group\_ids](#input\_vpc\_security\_group\_ids) | List of security group assigned to lambda. If null value, default subnet and vpc will be used | `list(string)` | `null` | no | +| [vpc\_subnet\_ids](#input\_vpc\_subnet\_ids) | List of subnet ids to place lambda in. If null value, default subnet and vpc will be used | `list(string)` | `null` | no | + +## Outputs + +No outputs. + diff --git a/terraform/modules/s3-configs/README.md b/terraform/modules/s3-configs/README.md new file mode 100644 index 0000000..73cad82 --- /dev/null +++ b/terraform/modules/s3-configs/README.md @@ -0,0 +1,61 @@ +AWS S3 bucket and configs +======================= + +The Terraform module in this folder is responsible for creating an AWS S3 bucket that used by DataQA as a basic bucket to store configs and generated tests into it. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.1 | +| [aws](#requirement\_aws) | ~> 4.64.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.5.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_s3_bucket.settings_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket_lifecycle_configuration.delete_old_reports](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | +| [aws_s3_bucket_public_access_block.settings_bucket_public_access_block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | +| [aws_s3_bucket_versioning.settings_bucket_versioning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | +| [aws_s3_object.expectations_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | +| [aws_s3_object.great_expectations_yml](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | +| [aws_s3_object.mapping_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | +| [aws_s3_object.pipeline_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | +| [aws_s3_object.pks_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | +| [aws_s3_object.sort_keys_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | +| [aws_s3_object.test_config_manifest](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | +| [aws_s3_object.test_configs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [data\_test\_storage\_bucket\_name](#input\_data\_test\_storage\_bucket\_name) | Bucket name which will be used to store data tests and settings for it's execution | `string` | n/a | yes | +| [environment](#input\_environment) | Environment name used to build fully qualified tags and resource's names | `string` | n/a | yes | +| [expectations\_store](#input\_expectations\_store) | Path to the expectations\_store directory, relative to the root TF | `string` | n/a | yes | +| [great\_expectation\_path](#input\_great\_expectation\_path) | Path to the great expectations yaml | `string` | n/a | yes | +| [manifest\_path](#input\_manifest\_path) | Path to the manifests | `string` | n/a | yes | +| [mapping\_path](#input\_mapping\_path) | Path to the mapping description path, relative to the root TF | `string` | n/a | yes | +| [pipeline\_config\_path](#input\_pipeline\_config\_path) | Path to the pipeline description path, relative to the root TF | `string` | n/a | yes | +| [pks\_path](#input\_pks\_path) | Path to the primary keys description path, relative to the root TF | `string` | n/a | yes | +| [sort\_keys\_path](#input\_sort\_keys\_path) | Path to the sort keys description path, relative to the root TF | `string` | n/a | yes | +| [test\_coverage\_path](#input\_test\_coverage\_path) | Path to the tests description path, relative to the root TF | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [bucket\_name](#output\_bucket\_name) | Name of s3 configs bucket | + diff --git a/terraform/modules/s3-gateway/README.md b/terraform/modules/s3-gateway/README.md new file mode 100644 index 0000000..09f20fc --- /dev/null +++ b/terraform/modules/s3-gateway/README.md @@ -0,0 +1,56 @@ +Nginx AWS S3 gateway +======================== + +The Terraform module in this folder is responsible for creating an Nginx AWS S3 gateway that allows serving static reports from AWS S3 over HTTP and applies IP restrictions. + +Underneath, it creates an AWS EC2 instance in a public subnet and installs Nginx with the s3-gateway module. IP restrictions are implemented as rules for security group ingress and set by the `whitelist_ips` variable. + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.1 | +| [aws](#requirement\_aws) | ~> 4.64.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.5.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_instance_profile.web_instance_profile](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | +| [aws_iam_policy.s3_read](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.instance_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.push_report_dynamodb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_instance.s3_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | +| [aws_security_group.connectable](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_ami.ubuntu](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | +| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_s3_bucket.data_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/s3_bucket) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [bucket\_name](#input\_bucket\_name) | Bucket name to serve by gateway(read-only) | `string` | n/a | yes | +| [env](#input\_env) | Env tag used to tag resources | `string` | n/a | yes | +| [instance\_sg\_ids](#input\_instance\_sg\_ids) | Extra list of security groups for instance | `list(string)` | `[]` | no | +| [instance\_subnet\_id](#input\_instance\_subnet\_id) | Instance subnet id | `string` | n/a | yes | +| [instance\_type](#input\_instance\_type) | Instance type for s3 gateway | `string` | `"t2.micro"` | no | +| [vpc\_id](#input\_vpc\_id) | VpcId for s3 gateway | `string` | n/a | yes | +| [whitelist\_ips](#input\_whitelist\_ips) | Allowed IPs to ssh/http to host | `list(string)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [s3\_gateway\_address](#output\_s3\_gateway\_address) | DNS http address of s3 gateway | + diff --git a/terraform/outputs.tf b/terraform/outputs.tf index 99e4a7c..11dbdf6 100644 --- a/terraform/outputs.tf +++ b/terraform/outputs.tf @@ -19,6 +19,6 @@ output "lambda_report_push_arn" { } output "bucket" { - description = "Data quality gate bucket with settings and generated tests" + description = "DataQA bucket with settings and generated tests" value = module.s3_bucket.bucket_name }