From 93892a1dafcd79d8c928da7beab63aee4e3b02f3 Mon Sep 17 00:00:00 2001 From: Wellyson Freitas Date: Fri, 26 Jul 2024 16:25:40 +0200 Subject: [PATCH] Setup infra --- .github/workflows/provision.yaml | 9 ++- README.md | 8 +-- docs/README.md | 6 -- docs/diagrams/c4-container.puml | 16 +++--- terraform/kms.tf | 1 + terraform/rds.tf | 99 ++++++++++++++++++++++++++++++++ terraform/s3.tf | 35 ++++++++++- terraform/ses.tf | 3 + terraform/variables.tf | 5 ++ terraform/vpc.tf | 41 +++++++++++++ 10 files changed, 199 insertions(+), 24 deletions(-) create mode 100644 terraform/kms.tf create mode 100644 terraform/rds.tf create mode 100644 terraform/ses.tf create mode 100644 terraform/vpc.tf diff --git a/.github/workflows/provision.yaml b/.github/workflows/provision.yaml index aab4431..249b60c 100644 --- a/.github/workflows/provision.yaml +++ b/.github/workflows/provision.yaml @@ -30,6 +30,13 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 +# - name: OpenAPI Generator +# uses: hatamiarash7/openapi-generator@v0.3.0 +# with: +# generator: openapi +# openapi-file: docs/openapi.yaml +# output-dir: .generated + - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: @@ -63,5 +70,5 @@ jobs: run: exit 1 - name: Terraform Apply - if: github.ref == 'refs/heads/main' && github.event_name == 'push' + #if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: terraform apply -auto-approve -input=false diff --git a/README.md b/README.md index 612cf83..ba19646 100644 --- a/README.md +++ b/README.md @@ -93,13 +93,7 @@ Architectural Decision Records (ADRs): ### Schema do BD do MVP -[![Schema do BD do MVP](diagrams/db-schema.png)](diagrams/db-schema.png) - -### Diagramas de Estado - -#### Estados de Consulta - -[![Estados de Consulta](/docs/diagrams/appointment-states.png)](/docs/diagrams/appointment-states.png) +[![Schema do BD do MVP](diagrams/db-schema.png)](docs/diagrams/db-schema.png) ## CI/CD diff --git a/docs/README.md b/docs/README.md index 621c453..35c4266 100644 --- a/docs/README.md +++ b/docs/README.md @@ -180,12 +180,6 @@ Architectural Decision Records (ADRs): [![Schema do BD do MVP](diagrams/db-schema.png)](diagrams/db-schema.png) -### Diagramas de Estado - -#### Estados de Consulta - -[![Estados de Consulta](diagrams/appointment-states.png)](diagrams/appointment-states.png) - ## CI/CD Descrição dos workflows do GitHub Actions: diff --git a/docs/diagrams/c4-container.puml b/docs/diagrams/c4-container.puml index 0cc802b..8056e90 100644 --- a/docs/diagrams/c4-container.puml +++ b/docs/diagrams/c4-container.puml @@ -28,10 +28,10 @@ System_Boundary(self_order_system, "Health&Med") { ContainerDb(booking_db, "Agendamento DB", "Postgres") } - System_Boundary(handbook_service, "Prontuário Service") { - Container(handbook_app, "Prontuário API", "Spring Boot", "API para gerenciamento de prontuários eletrônicos, documentos, e controle de acesso") - ContainerDb(handbook_db, "Prontuário BD", "DynamoDB") - Container(handbook_store, "Prontuário Store", "S3", "Cloud store, encriptografado, com ACL, e replicação para HA") + System_Boundary(medical_record_service, "Prontuário Service") { + Container(medical_record_app, "Prontuário API", "Spring Boot", "API para gerenciamento de prontuários eletrônicos, documentos, e controle de acesso") + ContainerDb(medical_record_db, "Prontuário BD", "DynamoDB") + Container(medical_record_store, "Prontuário Store", "S3", "Cloud store, encriptografado, com ACL, e replicação para HA") } } @@ -45,11 +45,11 @@ Rel(email_system, users, "envia para", $tags="async") Rel(video_system, frontend, "disponibiliza serviço") Rel(frontend, api_gateway, "requests") -Rel(frontend, handbook_store, "baixa/sobe arquivos") +Rel(frontend, medical_record_store, "baixa/sobe arquivos") Rel(api_gateway, user_app, "requests") Rel(api_gateway, booking_app, "requests") -Rel(api_gateway, handbook_app, "requests") +Rel(api_gateway, medical_record_app, "requests") Rel(user_app, user_db, "read/write", "JDBC") Rel(user_app, crm_system, "verifica CRM", "HTTP") @@ -61,7 +61,7 @@ Rel(booking_app, booking_db, "read/write", "JDBC") Rel(booking_app, email_system, "requests", "HTTP", $tags="async") Rel(booking_app, video_system, "requests", "HTTP") -Rel(handbook_app, handbook_db, "read/write", "JDBC") -Rel(handbook_app, handbook_store, "read/write") +Rel(medical_record_app, medical_record_db, "read/write", "JDBC") +Rel(medical_record_app, medical_record_store, "read/write") @enduml diff --git a/terraform/kms.tf b/terraform/kms.tf new file mode 100644 index 0000000..9b889d5 --- /dev/null +++ b/terraform/kms.tf @@ -0,0 +1 @@ +resource "aws_kms_key" "medical_record_bucket_kms_key" {} diff --git a/terraform/rds.tf b/terraform/rds.tf new file mode 100644 index 0000000..6c0d8f9 --- /dev/null +++ b/terraform/rds.tf @@ -0,0 +1,99 @@ +locals { + dbname = "healthmeddb" + username = "master" + port = 5432 +} + +module "db" { + source = "terraform-aws-modules/rds/aws" + version = "6.5.2" + + identifier = local.dbname + + engine = "postgres" + engine_version = "15" + family = "postgres15" + major_engine_version = "15" + instance_class = "db.t3.micro" + + allocated_storage = 10 + max_allocated_storage = 20 + + storage_encrypted = false + + db_name = local.dbname + username = local.username + port = local.port + + db_subnet_group_name = module.vpc.database_subnet_group_name + vpc_security_group_ids = [module.security_group.security_group_id] + + backup_retention_period = 0 + skip_final_snapshot = true + deletion_protection = false +} + +module "security_group" { + source = "terraform-aws-modules/security-group/aws" + version = "~> 5.0" + + name = local.dbname + vpc_id = module.vpc.vpc_id + + ingress_with_cidr_blocks = [ + { + from_port = local.port + to_port = local.port + protocol = "tcp" + cidr_blocks = module.vpc.vpc_cidr_block + }, + ] +} + +module "rds_params" { + source = "terraform-aws-modules/ssm-parameter/aws" + name = "/live/healthmed/db" + type = "String" + + value = jsonencode({ + name : local.dbname, + endpoint : module.db.db_instance_endpoint, + port : local.port + }) +} + +resource "aws_iam_policy" "rds_secrets_read_only_policy" { + name = "HealthmedRDSSecretsReadOnlyPolicy" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" + ], + Resource = module.db.db_instance_master_user_secret_arn + } + ] + }) +} + +resource "aws_iam_policy" "rds_params_read_only_policy" { + name = "HealthmedRDSParamsReadOnlyPolicy" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "ssm:GetParameter", + "ssm:GetParameters" + ], + Resource = module.rds_params.ssm_parameter_arn + } + ] + }) +} diff --git a/terraform/s3.tf b/terraform/s3.tf index 851f4ca..10d5b05 100644 --- a/terraform/s3.tf +++ b/terraform/s3.tf @@ -1,3 +1,8 @@ +resource "aws_s3_bucket" "medical_record_bucket" { + bucket = var.medical_record_bucket_name + tags = var.tags +} + resource "aws_s3_bucket" "website_bucket" { bucket = var.website_bucket_name tags = var.tags @@ -24,7 +29,7 @@ resource "aws_s3_bucket_public_access_block" "website_bucket_public_access_block restrict_public_buckets = false } -resource "aws_s3_bucket_ownership_controls" "s3_bucket_acl_ownership" { +resource "aws_s3_bucket_ownership_controls" "website_bucket_ownership_controls" { bucket = aws_s3_bucket.website_bucket.id rule { object_ownership = "BucketOwnerPreferred" @@ -32,10 +37,36 @@ resource "aws_s3_bucket_ownership_controls" "s3_bucket_acl_ownership" { depends_on = [aws_s3_bucket_public_access_block.website_bucket_public_access_block] } +resource "aws_s3_bucket_ownership_controls" "medical_record_ownership_controls" { + bucket = aws_s3_bucket.medical_record_bucket.id + rule { + object_ownership = "BucketOwnerPreferred" + } +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "medical_record_sse_config" { + bucket = aws_s3_bucket.medical_record_bucket.id + + rule { + apply_server_side_encryption_by_default { + kms_master_key_id = aws_kms_key.medical_record_bucket_kms_key.arn + sse_algorithm = "aws:kms" + } + + bucket_key_enabled = true + } +} + resource "aws_s3_bucket_acl" "website_bucket_acl" { bucket = aws_s3_bucket.website_bucket.id acl = "public-read" - depends_on = [aws_s3_bucket_ownership_controls.s3_bucket_acl_ownership] + depends_on = [aws_s3_bucket_ownership_controls.website_bucket_ownership_controls] +} + +resource "aws_s3_bucket_acl" "medical_record_bucket_acl" { + bucket = aws_s3_bucket.medical_record_bucket.id + acl = "private" + depends_on = [aws_s3_bucket_ownership_controls.medical_record_ownership_controls] } resource "aws_s3_bucket_policy" "website_bucket_policy" { diff --git a/terraform/ses.tf b/terraform/ses.tf new file mode 100644 index 0000000..ea5192b --- /dev/null +++ b/terraform/ses.tf @@ -0,0 +1,3 @@ +resource "aws_ses_email_identity" "email_identity" { + email = "wellyfrs+FIAP-3SOAT-G15@gmail.com" +} diff --git a/terraform/variables.tf b/terraform/variables.tf index d75e8eb..4a16239 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -8,6 +8,11 @@ variable "website_bucket_name" { default = "fiap-3soat-g15-healthmed" } +variable "medical_record_bucket_name" { + type = string + default = "fiap-3soat-g15-healthmed-medical-records" +} + variable "tags" { type = map(string) default = { diff --git a/terraform/vpc.tf b/terraform/vpc.tf new file mode 100644 index 0000000..728bd0d --- /dev/null +++ b/terraform/vpc.tf @@ -0,0 +1,41 @@ +data "aws_availability_zones" "available" {} + +locals { + vpc_name = "healthmed" + vpc_cidr = "10.0.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 2) +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "5.0.0" + + name = local.vpc_name + + azs = local.azs + private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)] + public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 4)] + database_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 8)] + + create_database_subnet_group = true + + # Single NAT Gateway Behaviour + # https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest#nat-gateway-scenarios + enable_nat_gateway = true + single_nat_gateway = true + one_nat_gateway_per_az = false + + enable_dns_hostnames = true + enable_dns_support = true + + # For Kubernetes + public_subnet_tags = { + "kubernetes.io/role/elb" = "1" + } + + private_subnet_tags = { + "kubernetes.io/role/internal-elb" = "1" + } + + tags = var.tags +}