mysystem-cli 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,156 @@
1
+ # --- Security Groups ---
2
+
3
+ # ALB Security Group
4
+ resource "aws_security_group" "alb" {
5
+ name = "${var.app_name}-alb-sg"
6
+ description = "Allow inbound HTTP/HTTPS traffic to ALB"
7
+ vpc_id = aws_vpc.main.id
8
+
9
+ ingress {
10
+ description = "Allow HTTP"
11
+ from_port = 80
12
+ to_port = 80
13
+ protocol = "tcp"
14
+ cidr_blocks = ["0.0.0.0/0"]
15
+ }
16
+
17
+ ingress {
18
+ description = "Allow HTTPS"
19
+ from_port = 443
20
+ to_port = 443
21
+ protocol = "tcp"
22
+ cidr_blocks = ["0.0.0.0/0"]
23
+ }
24
+
25
+ egress {
26
+ from_port = 0
27
+ to_port = 0
28
+ protocol = "-1"
29
+ cidr_blocks = ["0.0.0.0/0"]
30
+ }
31
+
32
+ tags = {
33
+ Name = "${var.app_name}-alb-sg"
34
+ }
35
+ }
36
+
37
+ # ECS Tasks Security Group
38
+ resource "aws_security_group" "ecs" {
39
+ name = "${var.app_name}-ecs-tasks-sg"
40
+ description = "Access to ECS tasks only from ALB"
41
+ vpc_id = aws_vpc.main.id
42
+
43
+ ingress {
44
+ description = "Allow traffic from ALB only"
45
+ from_port = var.container_port
46
+ to_port = var.container_port
47
+ protocol = "tcp"
48
+ security_groups = [aws_security_group.alb.id]
49
+ }
50
+
51
+ egress {
52
+ from_port = 0
53
+ to_port = 0
54
+ protocol = "-1"
55
+ cidr_blocks = ["0.0.0.0/0"]
56
+ }
57
+
58
+ tags = {
59
+ Name = "${var.app_name}-ecs-tasks-sg"
60
+ }
61
+ }
62
+
63
+ # RDS Security Group
64
+ resource "aws_security_group" "db" {
65
+ count = var.enable_database ? 1 : 0
66
+ name = "${var.app_name}-db-sg"
67
+ description = "Access to RDS from ECS tasks only"
68
+ vpc_id = aws_vpc.main.id
69
+
70
+ ingress {
71
+ description = "Allow PostgreSQL access from ECS tasks"
72
+ from_port = 5432
73
+ to_port = 5432
74
+ protocol = "tcp"
75
+ security_groups = [aws_security_group.ecs.id]
76
+ }
77
+
78
+ egress {
79
+ from_port = 0
80
+ to_port = 0
81
+ protocol = "-1"
82
+ cidr_blocks = ["0.0.0.0/0"]
83
+ }
84
+
85
+ tags = {
86
+ Name = "${var.app_name}-db-sg"
87
+ }
88
+ }
89
+
90
+ # --- IAM Roles ---
91
+
92
+ # ECS Task Execution Role (Required to pull ECR images & push logs to CloudWatch)
93
+ resource "aws_iam_role" "ecs_execution" {
94
+ name = "${var.app_name}-ecs-execution-role"
95
+
96
+ assume_role_policy = jsonencode({
97
+ Version = "2012-10-17"
98
+ Statement = [
99
+ {
100
+ Action = "sts:AssumeRole"
101
+ Effect = "Allow"
102
+ Principal = {
103
+ Service = "ecs-tasks.amazonaws.com"
104
+ }
105
+ }
106
+ ]
107
+ })
108
+ }
109
+
110
+ resource "aws_iam_role_policy_attachment" "ecs_execution" {
111
+ role = aws_iam_role.ecs_execution.name
112
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
113
+ }
114
+
115
+ # ECS Task Role (Permissions for the application itself)
116
+ resource "aws_iam_role" "ecs_task" {
117
+ name = "${var.app_name}-ecs-task-role"
118
+
119
+ assume_role_policy = jsonencode({
120
+ Version = "2012-10-17"
121
+ Statement = [
122
+ {
123
+ Action = "sts:AssumeRole"
124
+ Effect = "Allow"
125
+ Principal = {
126
+ Service = "ecs-tasks.amazonaws.com"
127
+ }
128
+ }
129
+ ]
130
+ })
131
+ }
132
+
133
+ # Add standard policy for ECS task (e.g. basic S3 or secrets reading if needed, can be expanded)
134
+ resource "aws_iam_policy" "ecs_task_policy" {
135
+ name = "${var.app_name}-ecs-task-policy"
136
+ description = "Basic policy for ECS task execution"
137
+
138
+ policy = jsonencode({
139
+ Version = "2012-10-17"
140
+ Statement = [
141
+ {
142
+ Action = [
143
+ "ssm:GetParameters",
144
+ "secretsmanager:GetSecretValue"
145
+ ]
146
+ Effect = "Allow"
147
+ Resource = "*"
148
+ }
149
+ ]
150
+ })
151
+ }
152
+
153
+ resource "aws_iam_role_policy_attachment" "ecs_task" {
154
+ role = aws_iam_role.ecs_task.name
155
+ policy_arn = aws_iam_policy.ecs_task_policy.arn
156
+ }
@@ -0,0 +1,46 @@
1
+ variable "aws_region" {
2
+ type = string
3
+ description = "AWS region to deploy resources"
4
+ default = "us-east-1"
5
+ }
6
+
7
+ variable "app_name" {
8
+ type = string
9
+ description = "Application name (used for naming resources)"
10
+ }
11
+
12
+ variable "environment" {
13
+ type = string
14
+ description = "Deployment environment"
15
+ default = "production"
16
+ }
17
+
18
+ variable "container_port" {
19
+ type = number
20
+ description = "Port the application container listens on"
21
+ default = 3000
22
+ }
23
+
24
+ variable "container_cpu" {
25
+ type = number
26
+ description = "CPU units for the ECS task (1024 = 1 vCPU)"
27
+ default = 256
28
+ }
29
+
30
+ variable "container_memory" {
31
+ type = number
32
+ description = "Memory for the ECS task in MB"
33
+ default = 512
34
+ }
35
+
36
+ variable "enable_database" {
37
+ type = bool
38
+ description = "Whether to provision an RDS PostgreSQL database"
39
+ default = true
40
+ }
41
+
42
+ variable "sentry_dsn" {
43
+ type = string
44
+ description = "Sentry Data Source Name (DSN) for error tracking"
45
+ default = ""
46
+ }
@@ -0,0 +1,68 @@
1
+ data "aws_availability_zones" "available" {
2
+ state = "available"
3
+ }
4
+
5
+ resource "aws_vpc" "main" {
6
+ cidr_block = "10.0.0.0/16"
7
+ enable_dns_hostnames = true
8
+ enable_dns_support = true
9
+
10
+ tags = {
11
+ Name = "${var.app_name}-vpc"
12
+ }
13
+ }
14
+
15
+ resource "aws_subnet" "public" {
16
+ count = 2
17
+ vpc_id = aws_vpc.main.id
18
+ cidr_block = "10.0.${count.index}.0/24"
19
+ availability_zone = data.aws_availability_zones.available.names[count.index]
20
+ map_public_ip_on_launch = true
21
+
22
+ tags = {
23
+ Name = "${var.app_name}-public-subnet-${count.index}"
24
+ }
25
+ }
26
+
27
+ resource "aws_subnet" "private" {
28
+ count = 2
29
+ vpc_id = aws_vpc.main.id
30
+ cidr_block = "10.0.${count.index + 10}.0/24"
31
+ availability_zone = data.aws_availability_zones.available.names[count.index]
32
+
33
+ tags = {
34
+ Name = "${var.app_name}-private-subnet-${count.index}"
35
+ }
36
+ }
37
+
38
+ resource "aws_internet_gateway" "gw" {
39
+ vpc_id = aws_vpc.main.id
40
+
41
+ tags = {
42
+ Name = "${var.app_name}-igw"
43
+ }
44
+ }
45
+
46
+ resource "aws_route_table" "public" {
47
+ vpc_id = aws_vpc.main.id
48
+
49
+ route {
50
+ cidr_block = "0.0.0.0/0"
51
+ gateway_id = aws_internet_gateway.gw.id
52
+ }
53
+
54
+ tags = {
55
+ Name = "${var.app_name}-public-rt"
56
+ }
57
+ }
58
+
59
+ resource "aws_route_table_association" "public" {
60
+ count = 2
61
+ subnet_id = aws_subnet.public[count.index].id
62
+ route_table_id = aws_route_table.public.id
63
+ }
64
+
65
+ # Note: We do not provision a NAT Gateway by default to save ~$32/month in costs.
66
+ # Fargate tasks run in the public subnets but are secured via Security Groups
67
+ # allowing traffic ONLY from the Application Load Balancer (ALB).
68
+ # This provides enterprise-level routing security at near-zero baseline network cost.
@@ -0,0 +1,97 @@
1
+ # AWS WAFv2 Web ACL (Web Application Firewall) for ALB
2
+ # Enforces enterprise-grade security rules at the network edge to block SQLi, XSS, and common exploits.
3
+
4
+ resource "aws_wafv2_web_acl" "main" {
5
+ name = "${var.app_name}-waf"
6
+ description = "WAF Web ACL protecting ${var.app_name} Load Balancer"
7
+ scope = "REGIONAL"
8
+
9
+ default_action {
10
+ allow {}
11
+ }
12
+
13
+ # Rule 1: AWS Managed Common Rule Set (OWASP Top 10 protections)
14
+ rule {
15
+ name = "AWS-AWSManagedRulesCommonRuleSet"
16
+ priority = 10
17
+
18
+ override_action {
19
+ none {}
20
+ }
21
+
22
+ statement {
23
+ managed_rule_group_statement {
24
+ name = "AWSManagedRulesCommonRuleSet"
25
+ vendor_name = "AWS"
26
+ }
27
+ }
28
+
29
+ visibility_config {
30
+ cloudwatch_metrics_enabled = true
31
+ metric_name = "${var.app_name}-common-rules"
32
+ sampled_requests_enabled = true
33
+ }
34
+ }
35
+
36
+ # Rule 2: AWS Managed SQL Injection Protection Rule Set
37
+ rule {
38
+ name = "AWS-AWSManagedRulesSQLiRuleSet"
39
+ priority = 20
40
+
41
+ override_action {
42
+ none {}
43
+ }
44
+
45
+ statement {
46
+ managed_rule_group_statement {
47
+ name = "AWSManagedRulesSQLiRuleSet"
48
+ vendor_name = "AWS"
49
+ }
50
+ }
51
+
52
+ visibility_config {
53
+ cloudwatch_metrics_enabled = true
54
+ metric_name = "${var.app_name}-sqli-rules"
55
+ sampled_requests_enabled = true
56
+ }
57
+ }
58
+
59
+ # Rule 3: AWS Managed Known Bad Inputs Rule Set
60
+ rule {
61
+ name = "AWS-AWSManagedRulesKnownBadInputsRuleSet"
62
+ priority = 30
63
+
64
+ override_action {
65
+ none {}
66
+ }
67
+
68
+ statement {
69
+ managed_rule_group_statement {
70
+ name = "AWSManagedRulesKnownBadInputsRuleSet"
71
+ vendor_name = "AWS"
72
+ }
73
+ }
74
+
75
+ visibility_config {
76
+ cloudwatch_metrics_enabled = true
77
+ metric_name = "${var.app_name}-bad-inputs"
78
+ sampled_requests_enabled = true
79
+ }
80
+ }
81
+
82
+ visibility_config {
83
+ cloudwatch_metrics_enabled = true
84
+ metric_name = "${var.app_name}-web-acl"
85
+ sampled_requests_enabled = true
86
+ }
87
+
88
+ tags = {
89
+ Name = "${var.app_name}-waf"
90
+ }
91
+ }
92
+
93
+ # Associate the WAF Web ACL with the Application Load Balancer
94
+ resource "aws_wafv2_web_acl_association" "alb" {
95
+ resource_arn = aws_lb.main.arn
96
+ web_acl_arn = aws_wafv2_web_acl.main.arn
97
+ }
@@ -0,0 +1,40 @@
1
+ # AWS Cost Budget Alert
2
+ # Sends an email notification if the forecasted or actual AWS monthly spend exceeds the specified threshold.
3
+
4
+ variable "billing_email" {
5
+ type = string
6
+ description = "Email address to send AWS budget and billing alerts"
7
+ default = ""
8
+ }
9
+
10
+ variable "budget_limit" {
11
+ type = string
12
+ description = "Monthly budget limit in USD"
13
+ default = "20"
14
+ }
15
+
16
+ resource "aws_budgets_budget" "monthly_cost" {
17
+ count = var.billing_email != "" ? 1 : 0
18
+ name = "${var.app_name}-monthly-budget"
19
+ budget_type = "COST"
20
+ limit_amount = var.budget_limit
21
+ limit_unit = "USD"
22
+ time_period_start = "2026-01-01_00:00"
23
+ time_unit = "MONTHLY"
24
+
25
+ notification {
26
+ comparison_operator = "GREATER_THAN"
27
+ threshold = 80
28
+ threshold_type = "PERCENTAGE"
29
+ notification_type = "ACTUAL"
30
+ subscriber_email_addresses = [var.billing_email]
31
+ }
32
+
33
+ notification {
34
+ comparison_operator = "GREATER_THAN"
35
+ threshold = 100
36
+ threshold_type = "PERCENTAGE"
37
+ notification_type = "FORECASTED"
38
+ subscriber_email_addresses = [var.billing_email]
39
+ }
40
+ }
@@ -0,0 +1,85 @@
1
+ # AWS ACM (SSL Certificate) and Custom Domain Routing for EC2
2
+ # Routes custom domains to the EC2 Elastic IP address
3
+
4
+ variable "enable_custom_domain" {
5
+ type = bool
6
+ description = "Enable custom domain and SSL certificates"
7
+ default = false
8
+ }
9
+
10
+ variable "domain_name" {
11
+ type = string
12
+ description = "Custom domain name (e.g. app.myproduct.com)"
13
+ default = ""
14
+ }
15
+
16
+ variable "dns_provider" {
17
+ type = string
18
+ description = "DNS provider ('route53' or 'external')"
19
+ default = "external"
20
+ }
21
+
22
+ # 1. SSL/TLS Certificate via ACM
23
+ resource "aws_acm_certificate" "cert" {
24
+ count = var.enable_custom_domain ? 1 : 0
25
+ domain_name = var.domain_name
26
+ validation_method = "DNS"
27
+
28
+ lifecycle {
29
+ create_before_destroy = true
30
+ }
31
+
32
+ tags = {
33
+ Name = "${var.app_name}-cert"
34
+ }
35
+ }
36
+
37
+ # --- Route 53 DNS Configuration (Automated Setup) ---
38
+ data "aws_route53_zone" "primary" {
39
+ count = var.enable_custom_domain && var.dns_provider == "route53" ? 1 : 0
40
+ name = join(".", slice(split(".", var.domain_name), length(split(".", var.domain_name)) - 2, length(split(".", var.domain_name))))
41
+ private_zone = false
42
+ }
43
+
44
+ # DNS Validation record in Route53
45
+ resource "aws_route53_record" "cert_validation" {
46
+ count = var.enable_custom_domain && var.dns_provider == "route53" ? 1 : 0
47
+ name = tolist(aws_acm_certificate.cert[0].domain_validation_options)[0].resource_record_name
48
+ type = tolist(aws_acm_certificate.cert[0].domain_validation_options)[0].resource_record_type
49
+ zone_id = data.aws_route53_zone.primary[0].zone_id
50
+ records = [tolist(aws_acm_certificate.cert[0].domain_validation_options)[0].resource_record_value]
51
+ ttl = 60
52
+ }
53
+
54
+ # Validate Certificate in ACM
55
+ resource "aws_acm_certificate_validation" "cert" {
56
+ count = var.enable_custom_domain && var.dns_provider == "route53" ? 1 : 0
57
+ certificate_arn = aws_acm_certificate.cert[0].arn
58
+ validation_record_fqdns = [aws_route53_record.cert_validation[0].fqdn]
59
+ }
60
+
61
+ # Create DNS A Record pointing to the EC2 Elastic IP
62
+ resource "aws_route53_record" "app" {
63
+ count = var.enable_custom_domain && var.dns_provider == "route53" ? 1 : 0
64
+ zone_id = data.aws_route53_zone.primary[0].zone_id
65
+ name = var.domain_name
66
+ type = "A"
67
+ ttl = 300
68
+ records = [aws_eip.app.public_ip]
69
+ }
70
+
71
+ # --- Outputs for External DNS (GoDaddy, Cloudflare, etc.) ---
72
+ output "dns_validation_name" {
73
+ value = var.enable_custom_domain ? tolist(aws_acm_certificate.cert[0].domain_validation_options)[0].resource_record_name : "None"
74
+ description = "CNAME Name to add to your DNS provider for SSL certificate validation"
75
+ }
76
+
77
+ output "dns_validation_value" {
78
+ value = var.enable_custom_domain ? tolist(aws_acm_certificate.cert[0].domain_validation_options)[0].resource_record_value : "None"
79
+ description = "CNAME Value/Alias to add to your DNS provider for SSL certificate validation"
80
+ }
81
+
82
+ output "app_dns_a_record_ip" {
83
+ value = aws_eip.app.public_ip
84
+ description = "Point your domain A Record (IP Address) to this public Elastic IP in your DNS provider"
85
+ }
@@ -0,0 +1,124 @@
1
+ # --- IAM Role for EC2 (SSM & ECR Access) ---
2
+ resource "aws_iam_role" "ec2" {
3
+ name = "${var.app_name}-ec2-role"
4
+
5
+ assume_role_policy = jsonencode({
6
+ Version = "2012-10-17"
7
+ Statement = [
8
+ {
9
+ Action = "sts:AssumeRole"
10
+ Effect = "Allow"
11
+ Principal = {
12
+ Service = "ec2.amazonaws.com"
13
+ }
14
+ }
15
+ ]
16
+ })
17
+ }
18
+
19
+ # Attach policy to allow AWS SSM Session Manager connection (SSH-free secure console access)
20
+ resource "aws_iam_role_policy_attachment" "ssm" {
21
+ role = aws_iam_role.ec2.name
22
+ policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
23
+ }
24
+
25
+ # Attach policy to allow EC2 to pull images from ECR
26
+ resource "aws_iam_role_policy_attachment" "ecr_pull" {
27
+ role = aws_iam_role.ec2.name
28
+ policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
29
+ }
30
+
31
+ resource "aws_iam_instance_profile" "ec2" {
32
+ name = "${var.app_name}-ec2-profile"
33
+ role = aws_iam_role.ec2.name
34
+ }
35
+
36
+ # --- Security Group ---
37
+ resource "aws_security_group" "ec2" {
38
+ name = "${var.app_name}-ec2-sg"
39
+ description = "Allow inbound web traffic"
40
+ vpc_id = aws_vpc.main.id
41
+
42
+ ingress {
43
+ description = "Allow HTTP"
44
+ from_port = 80
45
+ to_port = 80
46
+ protocol = "tcp"
47
+ cidr_blocks = ["0.0.0.0/0"]
48
+ }
49
+
50
+ ingress {
51
+ description = "Allow HTTPS"
52
+ from_port = 443
53
+ to_port = 443
54
+ protocol = "tcp"
55
+ cidr_blocks = ["0.0.0.0/0"]
56
+ }
57
+
58
+ egress {
59
+ from_port = 0
60
+ to_port = 0
61
+ protocol = "-1"
62
+ cidr_blocks = ["0.0.0.0/0"]
63
+ }
64
+
65
+ tags = {
66
+ Name = "${var.app_name}-ec2-sg"
67
+ }
68
+ }
69
+
70
+ # --- AMI Lookup (Amazon Linux 2) ---
71
+ data "aws_ami" "amazon_linux_2" {
72
+ most_recent = true
73
+ owners = ["amazon"]
74
+
75
+ filter {
76
+ name = "name"
77
+ values = ["amzn2-ami-hvm-*-x86_64-gp2"]
78
+ }
79
+ }
80
+
81
+ # --- EC2 Instance ---
82
+ resource "aws_instance" "app" {
83
+ ami = data.aws_ami.amazon_linux_2.id
84
+ instance_type = var.instance_type
85
+ subnet_id = aws_subnet.public.id
86
+ vpc_security_group_ids = [aws_security_group.ec2.id]
87
+ iam_instance_profile = aws_iam_instance_profile.ec2.name
88
+
89
+ # Startup script: installs Docker & Docker Compose
90
+ user_data = <<-EOF
91
+ #!/bin/bash
92
+ # Update system packages
93
+ yum update -y
94
+
95
+ # Install Docker
96
+ amazon-linux-extras install docker -y
97
+ systemctl start docker
98
+ systemctl enable docker
99
+ usermod -a -G docker ec2-user
100
+
101
+ # Install Docker Compose v2
102
+ mkdir -p /usr/local/lib/docker/cli-plugins
103
+ curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
104
+ chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
105
+
106
+ # Create directory for app files
107
+ mkdir -p /home/ec2-user/app
108
+ chown -R ec2-user:ec2-user /home/ec2-user/app
109
+ EOF
110
+
111
+ tags = {
112
+ Name = var.app_name
113
+ }
114
+ }
115
+
116
+ # Assign an Elastic IP to the instance so the public IP address is static/permanent
117
+ resource "aws_eip" "app" {
118
+ instance = aws_instance.app.id
119
+ domain = "vpc"
120
+
121
+ tags = {
122
+ Name = "${var.app_name}-eip"
123
+ }
124
+ }
@@ -0,0 +1,10 @@
1
+ # Amazon ECR (Container Registry) for EC2 deployments
2
+ resource "aws_ecr_repository" "app" {
3
+ name = var.app_name
4
+ image_tag_mutability = "MUTABLE"
5
+ force_destroy = true
6
+
7
+ image_scanning_configuration {
8
+ scan_on_push = true
9
+ }
10
+ }
@@ -0,0 +1,19 @@
1
+ output "ecr_repository_url" {
2
+ value = aws_ecr_repository.app.repository_url
3
+ description = "URL of the ECR repository"
4
+ }
5
+
6
+ output "instance_public_ip" {
7
+ value = aws_eip.app.public_ip
8
+ description = "Permanent public Elastic IP of the EC2 instance"
9
+ }
10
+
11
+ output "instance_id" {
12
+ value = aws_instance.app.id
13
+ description = "The ID of the EC2 instance (used for SSM deploys)"
14
+ }
15
+
16
+ output "application_url" {
17
+ value = "http://${aws_eip.app.public_ip}"
18
+ description = "The web address of your application"
19
+ }
@@ -0,0 +1,22 @@
1
+ terraform {
2
+ required_version = ">= 1.5.0"
3
+ required_providers {
4
+ aws = {
5
+ source = "hashicorp/aws"
6
+ version = "~> 5.0"
7
+ }
8
+ }
9
+ }
10
+
11
+ provider "aws" {
12
+ region = var.aws_region
13
+
14
+ default_tags {
15
+ tags = {
16
+ Environment = var.environment
17
+ ManagedBy = "MySystem"
18
+ Project = var.app_name
19
+ Tier = "Hobby"
20
+ }
21
+ }
22
+ }