├── .gitignore ├── 01-jenkins-setup ├── README.md ├── ansible │ ├── jenkins-agent.yaml │ ├── jenkins-controller.yaml │ ├── roles │ │ ├── jenkins-agent │ │ │ └── tasks │ │ │ │ ├── java.yaml │ │ │ │ ├── main.yaml │ │ │ │ ├── ssh.yaml │ │ │ │ └── tools.yaml │ │ └── jenkins-controller │ │ │ ├── tasks │ │ │ ├── base.yaml │ │ │ ├── efs.yaml │ │ │ ├── jenkins.yaml │ │ │ └── main.yaml │ │ │ └── templates │ │ │ └── override.conf.j2 │ └── scripts │ │ └── get-ssh-pub.py ├── jenkins-agent.pkr.hcl ├── jenkins-controller.pkr.hcl └── terraform │ ├── agent │ └── main.tf │ ├── efs │ └── main.tf │ ├── iam │ └── main.tf │ ├── lb-asg │ └── main.tf │ └── modules │ ├── ec2 │ ├── main.tf │ └── variable.tf │ ├── efs │ ├── main.tf │ └── variable.tf │ ├── iam │ ├── main.tf │ └── variables.tf │ └── lb-asg │ ├── main.tf │ └── variable.tf ├── 02-consul-sevice-discovery ├── README.md ├── ansible │ ├── backends.yaml │ ├── configs │ │ ├── ansible.cfg │ │ └── inventory.ini │ ├── consul-server.yaml │ ├── load-balancer.yaml │ └── roles │ │ ├── backends │ │ ├── tasks │ │ │ ├── consul.yaml │ │ │ ├── main.yaml │ │ │ └── nginx.yaml │ │ ├── templates │ │ │ ├── backend.json.j2 │ │ │ ├── config.json.j2 │ │ │ └── index.html.j2 │ │ └── vars │ │ │ └── main.yaml │ │ ├── consul │ │ ├── tasks │ │ │ └── main.yaml │ │ ├── templates │ │ │ └── config.json.j2 │ │ └── vars │ │ │ └── main.yaml │ │ └── load-balancer │ │ ├── files │ │ └── load-balancer.conf.ctmpl │ │ ├── handlers │ │ └── main.yaml │ │ ├── tasks │ │ ├── consul.yaml │ │ ├── main.yaml │ │ └── nginx.yaml │ │ ├── templates │ │ └── consul-template.hcl.j2 │ │ └── vars │ │ └── main.yaml └── terraform │ ├── provision.tf │ └── variable.tf ├── 03-scalable-java-app ├── Jenkinsfile ├── README.md ├── ansible │ ├── ami.yml │ ├── ansible.cfg │ ├── files │ │ ├── application.properties │ │ ├── properties.py │ │ └── start.sh │ ├── inventory.ini │ ├── roles │ │ └── java │ │ │ └── tasks │ │ │ ├── app.yaml │ │ │ ├── cloudwatch.yml │ │ │ ├── java.yml │ │ │ ├── main.yml │ │ │ ├── node_exporter.yml │ │ │ └── python.yml │ ├── templates │ │ ├── config.json.j2 │ │ └── node_exporter.service.j2 │ └── vm.pkr.hcl └── terraform │ ├── lb-asg │ └── main.tf │ ├── modules │ ├── lb-asg │ │ ├── main.tf │ │ ├── scripts │ │ │ └── user_data.sh │ │ └── variable.tf │ └── rds │ │ ├── main.tf │ │ └── variables.tf │ └── rds │ ├── main.tf │ └── variables.tf ├── README.md ├── dev-vms └── ubuntu-22 │ └── Vagrantfile └── generic-scripts ├── README.md └── terraform └── ec2 └── instance.tf /.gitignore: -------------------------------------------------------------------------------- 1 | # General 2 | .DS_Store 3 | **/.DS_Store 4 | **/.terraform.lock.hcl 5 | .AppleDouble 6 | .LSOverride 7 | **/manifest.json 8 | .vscode 9 | **/..vscode 10 | 11 | # Icon must end with two \r 12 | Icon 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear in the root of a volume 18 | .DocumentRevisions-V100 19 | .fseventsd 20 | .Spotlight-V100 21 | .TemporaryItems 22 | .Trashes 23 | .VolumeIcon.icns 24 | .com.apple.timemachine.donotpresent 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | *.retry 33 | # Local .terraform directories 34 | **/.terraform/* 35 | 36 | # .tfstate files 37 | *.tfstate 38 | *.tfstate.* 39 | 40 | # Crash log files 41 | crash.log 42 | crash.*.log 43 | 44 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 45 | # password, private keys, and other secrets. These should not be part of version 46 | # control as they are data points which are potentially sensitive and subject 47 | # to change depending on the environment. 48 | *.tfvars 49 | *.tfvars.json 50 | 51 | # Ignore override files as they are usually used to override resources locally and so 52 | # are not checked in 53 | override.tf 54 | override.tf.json 55 | *_override.tf 56 | *_override.tf.json 57 | 58 | # Include override files you do wish to add to version control using negated pattern 59 | # !example_override.tf 60 | 61 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 62 | # example: *tfplan* 63 | 64 | # Ignore CLI configuration files 65 | .terraformrc 66 | terraform.rc -------------------------------------------------------------------------------- /01-jenkins-setup/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Setup Architecture 3 | 4 |

5 | 6 |

7 | 8 | 9 | ## Setup Workflow 10 | 11 | EFS is setup in us-west-2a 12 | Jenkins should be launchd in us-west-2a to access EFS 13 | 14 | ## Packer 15 | 16 | Get AMI ID 17 | 18 | ``` 19 | 20 | #!/bin/bash 21 | 22 | AMI_ID=$(jq -r '.builds[-1].artifact_id' manifest.json | cut -d ":" -f2) 23 | echo $AMI_ID 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/jenkins-agent.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Jenkins on servers 3 | hosts: all 4 | become: true 5 | remote_user: ubuntu 6 | 7 | vars: 8 | public_key_path: "/devops-tools/jenkins/id_rsa.pub" 9 | 10 | roles: 11 | - jenkins-agent 12 | -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/jenkins-controller.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Jenkins on servers 3 | hosts: all 4 | become: true 5 | remote_user: ubuntu 6 | 7 | vars: 8 | efs_mount_dir: "/data" 9 | jenkins_data_dir: "/data/jenkins" 10 | jenkins_lts_version: "2.387.1" 11 | 12 | 13 | roles: 14 | - jenkins-controller 15 | -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/roles/jenkins-agent/tasks/java.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update apt cache 3 | apt: 4 | update_cache: yes 5 | 6 | - name: Install Java JDK 17 7 | apt: 8 | name: openjdk-17-jdk 9 | state: present 10 | -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/roles/jenkins-agent/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: java.yaml 3 | - include_tasks: tools.yaml 4 | - include_tasks: ssh.yaml -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/roles/jenkins-agent/tasks/ssh.yaml: -------------------------------------------------------------------------------- 1 | 2 | - name: Retrieve secret value from AWS Parameter Store 3 | script: "{{ playbook_dir }}/scripts/get-ssh-pub.py {{ public_key_path }}" 4 | args: 5 | executable: /usr/bin/python3 6 | remote_src: yes 7 | register: secret_value 8 | 9 | - name: Print registered variable 10 | debug: 11 | var: secret_value 12 | 13 | - name: Add public key to authorized_keys for ubuntu user 14 | authorized_key: 15 | user: ubuntu 16 | state: present 17 | key: "{{ secret_value.stdout }}" -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/roles/jenkins-agent/tasks/tools.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Python 3 3 | apt: 4 | name: python3 5 | state: present 6 | 7 | - name: Install pip 8 | apt: 9 | name: python3-pip 10 | state: present 11 | 12 | - name: Install boto3 using pip3 13 | become: true 14 | pip: 15 | name: boto3 16 | state: present 17 | 18 | - name: Install AWS CLI using pip 19 | become: true 20 | pip: 21 | name: awscli 22 | state: latest 23 | executable: pip3 24 | 25 | - name: Install Ansible 26 | pip: 27 | name: ansible 28 | state: latest 29 | 30 | - name: Add HashiCorp GPG key 31 | become: yes 32 | shell: "wget -qO- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg" 33 | 34 | - name: Add HashiCorp APT repository 35 | become: yes 36 | shell: "echo 'deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com {{ ansible_distribution_release }} main' | sudo tee /etc/apt/sources.list.d/hashicorp.list" 37 | 38 | - name: Update apt cache 39 | become: yes 40 | become_method: sudo 41 | apt: 42 | update_cache: yes 43 | 44 | - name: Install Terraform 45 | apt: 46 | name: terraform 47 | state: present 48 | 49 | - name: Install Packer 50 | apt: 51 | name: packer 52 | state: present 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/roles/jenkins-controller/tasks/base.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update apt cache 3 | apt: 4 | update_cache: yes 5 | 6 | - name: Install Python 3 7 | apt: 8 | name: python3 9 | state: present 10 | 11 | - name: Install pip 12 | apt: 13 | name: python3-pip 14 | state: present 15 | 16 | - name: Install boto3 using pip3 17 | become: true 18 | pip: 19 | name: boto3 20 | state: present 21 | 22 | - name: Install AWS CLI using pip 23 | become: true 24 | pip: 25 | name: awscli 26 | state: latest 27 | executable: pip3 28 | 29 | - name: Install Java JDK 17 30 | apt: 31 | name: openjdk-17-jdk 32 | state: present 33 | -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/roles/jenkins-controller/tasks/efs.yaml: -------------------------------------------------------------------------------- 1 | - name: Install NFS client 2 | apt: 3 | name: nfs-common 4 | state: present 5 | 6 | - name: Ensure mount directory exists. 7 | file: 8 | path: "{{ efs_mount_dir }}" 9 | state: directory 10 | mode: 0755 11 | 12 | - name: Ensure EFS volume is mounted. 13 | mount: 14 | name: "{{ efs_mount_dir }}" 15 | src: "{{ efs_mount_point }}:/" 16 | fstype: nfs4 17 | opts: nfsvers=4.1 18 | state: mounted -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/roles/jenkins-controller/tasks/jenkins.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add Jenkins APT key 3 | apt_key: 4 | url: https://pkg.jenkins.io/debian-stable/jenkins.io.key 5 | 6 | - name: Add Jenkins APT repository 7 | apt_repository: 8 | repo: deb https://pkg.jenkins.io/debian-stable binary/ 9 | state: present 10 | 11 | - name: Install Jenkins 12 | apt: 13 | name: jenkins={{ jenkins_lts_version }} 14 | state: present 15 | 16 | - name: Stop Jenkins service 17 | systemd: 18 | name: jenkins.service 19 | state: stopped 20 | 21 | - name: Move Jenkins data to new directory 22 | copy: src=/var/lib/jenkins/ dest={{ jenkins_data_dir }} remote_src=yes directory_mode=yes 23 | 24 | - name: Change directory owner 25 | file: 26 | path: "{{ jenkins_data_dir }}" 27 | owner: jenkins 28 | group: jenkins 29 | recurse: yes 30 | 31 | - name: Remove old Jenkins data directory 32 | file: 33 | path: /var/lib/jenkins/ 34 | state: absent 35 | 36 | - name: Create override directory for Jenkins service 37 | become: true 38 | file: 39 | path: /etc/systemd/system/jenkins.service.d 40 | state: directory 41 | 42 | - name: Add override file for Jenkins 43 | template: 44 | src: override.conf.j2 45 | dest: /etc/systemd/system/jenkins.service.d/override.conf 46 | owner: root 47 | group: root 48 | mode: '0644' 49 | 50 | - name: Reload systemd daemon configuration 51 | systemd: 52 | daemon_reload: yes 53 | 54 | - name: Start Jnekins Service 55 | systemd: 56 | name: jenkins 57 | state: started 58 | 59 | -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/roles/jenkins-controller/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: base.yaml 3 | - include_tasks: efs.yaml 4 | - include_tasks: jenkins.yaml -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/roles/jenkins-controller/templates/override.conf.j2: -------------------------------------------------------------------------------- 1 | [Service] 2 | Environment="JENKINS_HOME={{ jenkins_data_dir }}" 3 | WorkingDirectory={{ jenkins_data_dir }} -------------------------------------------------------------------------------- /01-jenkins-setup/ansible/scripts/get-ssh-pub.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | import sys 4 | 5 | client = boto3.client('ssm', region_name='us-west-2') 6 | response = client.get_parameter(Name=sys.argv[1], WithDecryption=True) 7 | print(response['Parameter']['Value']) 8 | -------------------------------------------------------------------------------- /01-jenkins-setup/jenkins-agent.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "ami_id" { 2 | type = string 3 | default = "ami-0735c191cf914754d" 4 | } 5 | 6 | variable "public_key_path" { 7 | type = string 8 | default = "/devops-tools/jenkins/id_rsa.pub" 9 | } 10 | 11 | locals { 12 | app_name = "jenkins-agent" 13 | } 14 | 15 | source "amazon-ebs" "jenkins" { 16 | ami_name = "${local.app_name}" 17 | instance_type = "t2.micro" 18 | region = "us-west-2" 19 | availability_zone = "us-west-2a" 20 | source_ami = "${var.ami_id}" 21 | ssh_username = "ubuntu" 22 | iam_instance_profile = "jenkins-instance-profile" 23 | tags = { 24 | Env = "dev" 25 | Name = "${local.app_name}" 26 | } 27 | } 28 | 29 | build { 30 | sources = ["source.amazon-ebs.jenkins"] 31 | 32 | provisioner "ansible" { 33 | playbook_file = "ansible/jenkins-agent.yaml" 34 | extra_arguments = [ "--extra-vars", "public_key_path=${var.public_key_path}" ] 35 | } 36 | 37 | post-processor "manifest" { 38 | output = "manifest.json" 39 | strip_path = true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /01-jenkins-setup/jenkins-controller.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "ami_id" { 2 | type = string 3 | default = "ami-0735c191cf914754d" 4 | } 5 | 6 | variable "efs_mount_point" { 7 | type = string 8 | default = "" 9 | } 10 | 11 | locals { 12 | app_name = "jenkins-controller" 13 | } 14 | 15 | source "amazon-ebs" "jenkins" { 16 | ami_name = "${local.app_name}" 17 | instance_type = "t2.micro" 18 | region = "us-west-2" 19 | availability_zone = "us-west-2a" 20 | source_ami = "${var.ami_id}" 21 | ssh_username = "ubuntu" 22 | tags = { 23 | Env = "dev" 24 | Name = "${local.app_name}" 25 | } 26 | } 27 | 28 | build { 29 | sources = ["source.amazon-ebs.jenkins"] 30 | 31 | provisioner "ansible" { 32 | playbook_file = "ansible/jenkins-controller.yaml" 33 | extra_arguments = [ "--extra-vars", "ami-id=${var.ami_id} efs_mount_point=${var.efs_mount_point}" ] 34 | } 35 | 36 | post-processor "manifest" { 37 | output = "manifest.json" 38 | strip_path = true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/agent/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-west-2" 3 | } 4 | 5 | module "ec2_instance" { 6 | source = "../modules/ec2" 7 | 8 | instance_name = "jenkins-agent" 9 | ami_id = "ami-047fe714e6e0ac977" 10 | instance_type = "t2.small" 11 | key_name = "techiescamp" 12 | subnet_ids = ["subnet-058a7514ba8adbb07", "subnet-0dbcd1ac168414927", "subnet-032f5077729435858"] 13 | instance_count = 1 14 | } 15 | -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/efs/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-west-2" 3 | } 4 | 5 | module "efs_module" { 6 | source = "../modules/efs" 7 | vpc_id = "vpc-0a5ca4a92c2e10163" 8 | subnet_ids = ["subnet-058a7514ba8adbb07", "subnet-0dbcd1ac168414927", "subnet-032f5077729435858"] 9 | } -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/iam/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-west-2" 3 | } 4 | 5 | module "jenkins_iam" { 6 | source = "../modules/iam" 7 | instance_profile_name = "jenkins-instance-profile" 8 | iam_policy_name = "jenkins-iam-policy" 9 | role_name = "jenkins-role" 10 | } 11 | -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/lb-asg/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-west-2" 3 | } 4 | 5 | module "lb-asg" { 6 | source = "../modules/lb-asg" 7 | subnets = ["subnet-058a7514ba8adbb07", "subnet-0dbcd1ac168414927", "subnet-032f5077729435858"] 8 | ami_id = "ami-0f7cbb73797d9f23d" 9 | instance_type ="t2.small" 10 | key_name = "techiescamp" 11 | environment = "dev" 12 | vpc_id = "vpc-0a5ca4a92c2e10163" 13 | } -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/modules/ec2/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "jenkins-agent" { 2 | name = "jenkins-agent" 3 | description = "Allow SSH inbound traffic from everywhere and all outbound traffic" 4 | 5 | ingress { 6 | from_port = 22 7 | to_port = 22 8 | protocol = "tcp" 9 | cidr_blocks = ["0.0.0.0/0"] 10 | } 11 | 12 | egress { 13 | from_port = 0 14 | to_port = 0 15 | protocol = "-1" 16 | cidr_blocks = ["0.0.0.0/0"] 17 | } 18 | } 19 | 20 | 21 | resource "aws_instance" "example" { 22 | count = var.instance_count 23 | 24 | ami = var.ami_id 25 | instance_type = var.instance_type 26 | key_name = var.key_name 27 | vpc_security_group_ids = [aws_security_group.jenkins-agent.id] 28 | 29 | tags = { 30 | Name = "${var.instance_name}-${count.index + 1}" 31 | } 32 | 33 | subnet_id = element(var.subnet_ids, count.index % length(var.subnet_ids)) 34 | } 35 | -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/modules/ec2/variable.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-west-2" 3 | } 4 | 5 | variable "instance_name" { 6 | type = string 7 | default = "live-test-instance" 8 | } 9 | 10 | variable "ami_id" { 11 | type = string 12 | default = "ami-0735c191cf914754d" 13 | } 14 | 15 | variable "instance_type" { 16 | type = string 17 | default = "t2.small" 18 | } 19 | 20 | variable "key_name" { 21 | type = string 22 | default = "techiescamp" 23 | } 24 | 25 | variable "security_group_ids" { 26 | type = list(string) 27 | default = ["sg-01ce819e8d65269f0"] 28 | } 29 | 30 | variable "instance_count" { 31 | type = number 32 | default = 1 33 | } 34 | 35 | variable "subnet_ids" { 36 | type = list(string) 37 | default = ["subnet-058a7514ba8adbb07", "subnet-0dbcd1ac168414927", "subnet-032f5077729435858"] 38 | } -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/modules/efs/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "efs_sg" { 2 | name_prefix = "efs-sg" 3 | vpc_id = var.vpc_id 4 | 5 | ingress { 6 | from_port = 2049 7 | to_port = 2049 8 | protocol = "tcp" 9 | cidr_blocks = ["0.0.0.0/0"] 10 | } 11 | egress { 12 | from_port = 0 13 | to_port = 0 14 | protocol = "-1" 15 | cidr_blocks = ["0.0.0.0/0"] 16 | } 17 | } 18 | 19 | resource "aws_efs_file_system" "jenkins" { 20 | creation_token = "jenkins" 21 | encrypted = true 22 | performance_mode = "generalPurpose" 23 | throughput_mode = "bursting" 24 | tags = { 25 | Name = "jenkins-efs" 26 | } 27 | } 28 | 29 | resource "aws_efs_mount_target" "jenkins" { 30 | count = length(var.subnet_ids) 31 | file_system_id = aws_efs_file_system.jenkins.id 32 | subnet_id = var.subnet_ids[count.index] 33 | security_groups = [aws_security_group.efs_sg.id] 34 | } 35 | -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/modules/efs/variable.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_id" {} 2 | 3 | variable "subnet_ids" { 4 | type = list 5 | } -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/modules/iam/main.tf: -------------------------------------------------------------------------------- 1 | 2 | # Create an IAM policy 3 | resource "aws_iam_policy" "jenkins_iam_policy" { 4 | name = var.iam_policy_name 5 | 6 | policy = jsonencode({ 7 | Version = "2012-10-17" 8 | Statement = [ 9 | { 10 | Effect = "Allow" 11 | Action = [ 12 | "ssm:GetParameter", 13 | "ssm:GetParameters", 14 | "ssm:GetParametersByPath" 15 | ] 16 | Resource = "*" 17 | } 18 | ] 19 | }) 20 | } 21 | 22 | # Create an IAM role 23 | resource "aws_iam_role" "jenkins_role" { 24 | name = var.role_name 25 | 26 | assume_role_policy = jsonencode({ 27 | Version = "2012-10-17" 28 | Statement = [ 29 | { 30 | Effect = "Allow" 31 | Principal = { 32 | Service = "ec2.amazonaws.com" 33 | } 34 | Action = "sts:AssumeRole" 35 | } 36 | ] 37 | }) 38 | } 39 | 40 | # Attach the IAM policy to the IAM role 41 | resource "aws_iam_policy_attachment" "jenkins_role_policy_attachment" { 42 | name = "Policy Attachement" 43 | policy_arn = aws_iam_policy.jenkins_iam_policy.arn 44 | roles = [aws_iam_role.jenkins_role.name] 45 | } 46 | 47 | # Create an IAM instance profile 48 | resource "aws_iam_instance_profile" "jenkins_instance_profile" { 49 | name = var.instance_profile_name 50 | role = aws_iam_role.jenkins_role.name 51 | } 52 | -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/modules/iam/variables.tf: -------------------------------------------------------------------------------- 1 | variable "instance_profile_name" { 2 | type = string 3 | default = "example-instance-profile" 4 | } 5 | 6 | variable "iam_policy_name" { 7 | type = string 8 | default = "example-policy" 9 | } 10 | 11 | variable "role_name" { 12 | type = string 13 | default = "example-role" 14 | } 15 | -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/modules/lb-asg/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "alb_sg" { 2 | name_prefix = "alb-sg" 3 | 4 | ingress { 5 | from_port = 80 6 | to_port = 80 7 | protocol = "tcp" 8 | cidr_blocks = ["0.0.0.0/0"] 9 | } 10 | 11 | egress { 12 | from_port = 0 13 | to_port = 0 14 | protocol = "-1" 15 | cidr_blocks = ["0.0.0.0/0"] 16 | } 17 | 18 | tags = { 19 | Name = "jenkins-alb-sg" 20 | } 21 | } 22 | 23 | resource "aws_lb" "jenkins" { 24 | name = "jenkins-alb" 25 | internal = false 26 | load_balancer_type = "application" 27 | 28 | subnets = var.subnets 29 | security_groups = [aws_security_group.alb_sg.id] 30 | 31 | tags = { 32 | Environment = var.environment 33 | Terraform = "true" 34 | } 35 | } 36 | 37 | resource "aws_security_group" "instance_sg" { 38 | name_prefix = "jenkins-controller-sg" 39 | 40 | ingress { 41 | from_port = 22 42 | to_port = 22 43 | protocol = "tcp" 44 | cidr_blocks = ["0.0.0.0/0"] 45 | } 46 | 47 | ingress { 48 | from_port = 8080 49 | to_port = 8080 50 | protocol = "tcp" 51 | cidr_blocks = ["0.0.0.0/0"] 52 | } 53 | 54 | egress { 55 | from_port = 0 56 | to_port = 0 57 | protocol = "-1" 58 | cidr_blocks = ["0.0.0.0/0"] 59 | } 60 | 61 | tags = { 62 | Name = "jenkins-controller-sg" 63 | } 64 | } 65 | 66 | 67 | resource "aws_lb_target_group" "jenkins" { 68 | name_prefix = "jks-lb" 69 | port = 8080 70 | protocol = "HTTP" 71 | vpc_id = var.vpc_id 72 | target_type = "instance" 73 | 74 | health_check { 75 | path = "/login" 76 | port = 8080 77 | protocol = "HTTP" 78 | interval = 30 79 | timeout = 5 80 | healthy_threshold = 2 81 | unhealthy_threshold = 2 82 | } 83 | 84 | tags = { 85 | Environment = var.environment 86 | Terraform = "true" 87 | } 88 | } 89 | 90 | resource "aws_lb_listener" "jenkins" { 91 | load_balancer_arn = aws_lb.jenkins.arn 92 | port = 80 93 | protocol = "HTTP" 94 | 95 | default_action { 96 | target_group_arn = aws_lb_target_group.jenkins.arn 97 | type = "forward" 98 | } 99 | } 100 | 101 | resource "aws_launch_template" "jenkins" { 102 | name_prefix = "jenkins-controller-lt" 103 | image_id = var.ami_id 104 | instance_type = var.instance_type 105 | key_name = var.key_name 106 | 107 | network_interfaces { 108 | associate_public_ip_address = true 109 | security_groups = [aws_security_group.instance_sg.id] 110 | } 111 | 112 | lifecycle { 113 | create_before_destroy = true 114 | } 115 | } 116 | 117 | resource "aws_autoscaling_group" "jenkins" { 118 | name = "jenkins-controller-asg" 119 | max_size = 1 120 | min_size = 1 121 | desired_capacity = 1 122 | vpc_zone_identifier = var.subnets 123 | launch_template { 124 | id = aws_launch_template.jenkins.id 125 | version = "$Latest" 126 | } 127 | 128 | tag { 129 | key = "Name" 130 | value = "jenkins-controller" 131 | propagate_at_launch = true 132 | } 133 | 134 | lifecycle { 135 | create_before_destroy = true 136 | } 137 | } 138 | 139 | 140 | resource "aws_autoscaling_attachment" "jenkins" { 141 | autoscaling_group_name = aws_autoscaling_group.jenkins.name 142 | lb_target_group_arn = aws_lb_target_group.jenkins.arn 143 | } 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /01-jenkins-setup/terraform/modules/lb-asg/variable.tf: -------------------------------------------------------------------------------- 1 | variable "ami_id" { 2 | description = "The ID of the Amazon Machine Image (AMI) to use for the EC2 instances." 3 | } 4 | 5 | variable "instance_type" { 6 | description = "The type of EC2 instance to use for the ASG." 7 | } 8 | 9 | variable "key_name" { 10 | description = "The name of the EC2 key pair to use for the instances." 11 | } 12 | 13 | variable "environment" { 14 | description = "The environment name for the resources." 15 | } 16 | 17 | variable "vpc_id" { 18 | description = "The ID of the VPC to use for the resources." 19 | } 20 | 21 | variable "subnets" { 22 | description = "A list of subnet IDs to use for the resources." 23 | type = list(string) 24 | } 25 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/README.md: -------------------------------------------------------------------------------- 1 | ## Project Architecture 2 | 3 | ![service-discovery](https://user-images.githubusercontent.com/106984297/219394737-6f41c9f3-6c34-420b-9da2-9cc819f9c076.png) 4 | 5 | ## Manual Setup 6 | 7 | If you want to practically learn service discovery, I suggest you set up Nginx load balancing using consul service discovery setup manually and learn all the components and workflows involved in it. 8 | 9 | **Checkout the Prctical Guide:** [Service Discovery Example Using Consul & Nginx](https://devopscube.com/service-discovery-example/) 10 | 11 | ## Automation Prerequisites 12 | 13 | - You need to have a valid AWS account 14 | - A workstation or AWS instance configured with AWS CLI, Ansbile and Terrform with full access to provision ec2 instances. 15 | 16 | 17 | ## Provision Instances Using Terraform 18 | 19 | ### Prepare the variable file 20 | 21 | Repalce the parameters in the terraform.tfvars. 22 | 23 | Primarily you need to replace the key name `techiescamp` to the pem key you have in your AWS account. 24 | Other parameters are common or AWS oregon region with default VPC. If you are using a different region and VPC, replace it accordingly. 25 | 26 | ``` 27 | instance_type = "t2.micro" 28 | region = "us-west-2" 29 | ami = "ami-0735c191cf914754d" 30 | key_name = "techiescamp" 31 | ingress_cidr_block = "172.31.0.0/16" 32 | ``` 33 | 34 | ### Initialize terraform 35 | 36 | ``` 37 | terraform init 38 | ``` 39 | ### Dry Run Terraform 40 | 41 | Perform "dry run" of the terraform apply to check what changes will be made to your infrastructure when you apply your configuration. 42 | 43 | ``` 44 | terraform plan -var-file=terraform.tfvars 45 | 46 | ``` 47 | 48 | ### Provision Instances 49 | 50 | Provision the instances by applying the configuration. 51 | ``` 52 | terraform apply -var-file=terraform.tfvars -auto-approve 53 | ``` 54 | 55 | ## Configure Instances Using Ansible 56 | 57 | ### Create a inventory file to configure the instances 58 | 59 | Get all the IP addresses. 60 | 61 | ``` 62 | aws ec2 describe-instances \ 63 | --filters "Name=tag:Name,Values=consul-server,loadbalancer,backend-app" \ 64 | --query 'Reservations[].Instances[].[PrivateIpAddress,PublicIpAddress,Tags[?Key==`Name`].Value[]]' \ 65 | --output text 66 | ``` 67 | 68 | Modify the Ansible Inventory file as shown below. Repalce the IPs and pem key file location as per your Anisble configurations. 69 | 70 | ``` 71 | [consul] 72 | 54.188.111.53 73 | 74 | [load-balancer] 75 | 35.87.82.33 76 | 77 | [backends] 78 | 35.92.38.123 79 | 35.87.82.33 80 | 81 | [all:vars] 82 | ansible_user=ubuntu 83 | ansible_private_key_file=/home/vagrant/.ssh/techiescamp.pem 84 | ``` 85 | 86 | Cd in to ansible directory. 87 | 88 | ### Configure Consul Server 89 | 90 | ``` 91 | ansible-playbook consul-server.yaml 92 | ``` 93 | 94 | 95 | ### Configure Backends. 96 | 97 | Get the Consul server Private IP address and save it in the PRIVATE_IP variable. 98 | 99 | ``` 100 | PRIVATE_IP=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=consul-server" --query "Reservations[].Instances[].PrivateIpAddress" --output text) 101 | ``` 102 | 103 | Exeute the backend playbook 104 | 105 | ``` 106 | ansible-playbook -e "consul_server_address=$PRIVATE_IP" backends.yaml 107 | ``` 108 | ### Configure Load Balancer. 109 | 110 | Get the Consul server Private IP address and save it in the PRIVATE_IP variable. 111 | 112 | ``` 113 | PRIVATE_IP=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=consul-server" --query "Reservations[].Instances[].PrivateIpAddress" --output text) 114 | ``` 115 | Execute the load-balancer playbook 116 | 117 | ``` 118 | ansible-playbook -e "consul_server_address=$PRIVATE_IP:8500" load-balancer.yaml 119 | ``` 120 | 121 | 122 | ## validate the Setup 123 | 124 | If you visit the Loadblancer Ip you should be able to get the custom HTML page with backend server information. 125 | 126 | ## Tear Down the Servers 127 | 128 | cd in to terraform directory and run the following command 129 | 130 | ``` 131 | terraform destroy 132 | ``` 133 | 134 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/backends.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: backends 3 | remote_user: ubuntu 4 | become: yes 5 | 6 | # use command "consul keygen" to generate a new encryption key: 7 | 8 | vars: 9 | encrypt: "ZENZNrsXU336Uma+S4XUj9sxvICj32N7XdEzrbYbRpY=" 10 | 11 | roles: 12 | - backends 13 | 14 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/configs/ansible.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWS-Devops-Projects/devops-projects/6fb5ede7e776fb4401be2dd01cf4f4078a98f8cc/02-consul-sevice-discovery/ansible/configs/ansible.cfg -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/configs/inventory.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWS-Devops-Projects/devops-projects/6fb5ede7e776fb4401be2dd01cf4f4078a98f8cc/02-consul-sevice-discovery/ansible/configs/inventory.ini -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/consul-server.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: consul 3 | remote_user: ubuntu 4 | become: yes 5 | 6 | roles: 7 | - consul 8 | 9 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/load-balancer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: load-balancer 3 | remote_user: ubuntu 4 | become: yes 5 | 6 | roles: 7 | - load-balancer 8 | 9 | vars: 10 | consul_template_url: "https://releases.hashicorp.com/consul-template/0.30.0/consul-template_0.30.0_linux_amd64.zip" 11 | 12 | consul_server_address: 172.31.31.142:8500 -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/backends/tasks/consul.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update and upgrade apt packages 3 | become: true 4 | apt: 5 | upgrade: yes 6 | update_cache: yes 7 | 8 | - name: Install Consul Server 9 | apt: 10 | name: consul 11 | state: latest 12 | 13 | - name: Render Consul Configuration Template 14 | template: 15 | src: config.json.j2 16 | dest: /etc/consul.d/config.json 17 | vars: 18 | server: "false" 19 | datacenter: "dc1" 20 | 21 | - name: Render Consul Backend Template 22 | template: 23 | src: backend.json.j2 24 | dest: /etc/consul.d/backend.json 25 | 26 | - name: Start Consul Agent Template 27 | shell: sudo consul agent -config-dir /etc/consul.d/ & 28 | register: consul_start 29 | ignore_errors: true 30 | 31 | 32 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/backends/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: nginx.yaml 3 | - include: consul.yaml 4 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/backends/tasks/nginx.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check operating system 3 | debug: 4 | msg: "The operating system is {{ ansible_facts['os_family'] }}" 5 | when: ansible_facts['os_family'] == "Debian" 6 | 7 | - name: Update and upgrade apt packages 8 | become: true 9 | apt: 10 | upgrade: yes 11 | update_cache: yes 12 | 13 | - name: Install Nginx on Server 14 | apt: 15 | name: nginx 16 | state: latest 17 | 18 | - name: Copy Nginx Index HTML 19 | template: 20 | src: index.html.j2 21 | dest: /var/www/html/index.html 22 | 23 | - name: Start and enable a service 24 | service: 25 | name: nginx 26 | state: started 27 | enabled: yes 28 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/backends/templates/backend.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "service": { 3 | "Name": "backend", 4 | "Port": 80, 5 | "check": { 6 | "args": ["curl", "localhost"], 7 | "interval": "3s" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/backends/templates/config.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "server": {{ server }}, 3 | "datacenter": "{{ datacenter }}", 4 | "data_dir": "/var/consul", 5 | "encrypt": "{{ encrypt }}", 6 | "log_level": "INFO", 7 | "enable_script_checks": true, 8 | "enable_syslog": true, 9 | "leave_on_terminate": true, 10 | "start_join": [{{ consul_server_address | to_json }}] 11 | } -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/backends/templates/index.html.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Server Information 8 | 9 | 10 | 17 |
18 |
19 |

Server Information

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
IP Address{{ ansible_default_ipv4.address }}
Operating System{{ ansible_os_family }}
System Version{{ ansible_distribution_version }}
Memory{{ ansible_memtotal_mb }} MB
CPU{{ ansible_processor_vcpus }} vCPUs
44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/backends/vars/main.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWS-Devops-Projects/devops-projects/6fb5ede7e776fb4401be2dd01cf4f4078a98f8cc/02-consul-sevice-discovery/ansible/roles/backends/vars/main.yaml -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/consul/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update and upgrade apt packages 3 | become: true 4 | apt: 5 | upgrade: yes 6 | update_cache: yes 7 | 8 | - name: Install Consul Server 9 | apt: 10 | name: consul 11 | state: latest 12 | 13 | - name: Render Consul Configuration Template 14 | template: 15 | src: config.json.j2 16 | dest: /etc/consul.d/consul.json 17 | vars: 18 | bind_addr: 0.0.0.0 19 | client_addr: 0.0.0.0 20 | data_dir: /var/consul 21 | encrypt: "ZENZNrsXU336Uma+S4XUj9sxvICj32N7XdEzrbYbRpY=" 22 | datacenter: dc1 23 | ui: "true" 24 | leave_on_terminate: "true" 25 | server: "true" 26 | log_level: INFO 27 | 28 | # register is a keyword that allows you to capture the output of a task and store it in a variable for later use. 29 | 30 | - name: Start Consul Server 31 | shell: consul agent -dev -config-dir /etc/consul.d/ & 32 | register: consul_start 33 | ignore_errors: true 34 | 35 | # .rc attribute in register variable is used to access the exit code 36 | 37 | - name: Check if Consul agent started 38 | shell: ps -ef | grep consul | grep -v grep 39 | register: consul_check 40 | ignore_errors: true 41 | when: consul_start.rc == 0 42 | 43 | - name: Fail if Consul agent not started 44 | fail: 45 | msg: "Consul agent failed to start in the background" 46 | when: consul_check.rc != 0 47 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/consul/templates/config.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "bind_addr": "{{ bind_addr }}", 3 | "client_addr": "{{ client_addr }}", 4 | "data_dir": "{{ data_dir }}", 5 | "encrypt": "{{ encrypt }}", 6 | "datacenter": "{{ datacenter }}", 7 | "ui": {{ ui }}, 8 | "leave_on_terminate": {{ leave_on_terminate }}, 9 | "server": {{ server }}, 10 | "log_level": "{{ log_level }}" 11 | } 12 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/consul/vars/main.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWS-Devops-Projects/devops-projects/6fb5ede7e776fb4401be2dd01cf4f4078a98f8cc/02-consul-sevice-discovery/ansible/roles/consul/vars/main.yaml -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/load-balancer/files/load-balancer.conf.ctmpl: -------------------------------------------------------------------------------- 1 | upstream backend { 2 | {{- range service "backend" }} 3 | server {{ .Address }}:{{ .Port }}; 4 | {{- end }} 5 | } 6 | 7 | server { 8 | listen 80; 9 | 10 | location / { 11 | proxy_pass http://backend; 12 | } 13 | } -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/load-balancer/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart nginx 3 | service: 4 | name: nginx 5 | state: restarted -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/load-balancer/tasks/consul.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # In this example, the get_url module is used to download the remote file from the URL 4 | 5 | - name: Download zip file 6 | get_url: 7 | url: "{{ consul_template_url }}" 8 | dest: /tmp/consul-template.zip 9 | mode: 0644 10 | 11 | - name: Unpack zip file 12 | apt: 13 | name: unzip 14 | state: latest 15 | 16 | 17 | # Note that remote_src is set to yes in the unarchive module, which means the archive file is located on the remote host, not on the control node. 18 | 19 | - name: Unpack zip file 20 | unarchive: 21 | src: /tmp/consul-template.zip 22 | dest: /usr/local/bin 23 | remote_src: yes 24 | 25 | - name: Copy file from files directory to destination 26 | copy: 27 | src: "files/load-balancer.conf.ctmpl" 28 | dest: "/etc/nginx/conf.d/load-balancer.conf.ctmpl" 29 | remote_src: no 30 | 31 | - name: Copy Nginx Consul Template 32 | template: 33 | src: consul-template.hcl.j2 34 | dest: /etc/nginx/conf.d/consul-template.hcl 35 | 36 | - name: Start Consul Agent Template 37 | shell: consul-template -config=/etc/nginx/conf.d/consul-template.hcl & 38 | 39 | - name: Remove default sites-enabled file 40 | file: 41 | path: "/etc/nginx/sites-enabled/default" 42 | state: absent 43 | notify: 44 | - restart nginx 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/load-balancer/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: nginx.yaml 3 | - include: consul.yaml 4 | 5 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/load-balancer/tasks/nginx.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check operating system 3 | debug: 4 | msg: "The operating system is {{ ansible_facts['os_family'] }}" 5 | when: ansible_facts['os_family'] == "Debian" 6 | 7 | - name: Update and upgrade apt packages 8 | become: true 9 | apt: 10 | upgrade: yes 11 | update_cache: yes 12 | 13 | - name: Install Nginx on Server 14 | apt: 15 | name: nginx 16 | state: latest 17 | 18 | - name: Start and enable a service 19 | service: 20 | name: nginx 21 | state: started 22 | enabled: yes 23 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/load-balancer/templates/consul-template.hcl.j2: -------------------------------------------------------------------------------- 1 | consul { 2 | address = "{{ consul_server_address }}" 3 | 4 | retry { 5 | enabled = true 6 | attempts = 12 7 | backoff = "250ms" 8 | } 9 | } 10 | template { 11 | source = "/etc/nginx/conf.d/load-balancer.conf.ctmpl" 12 | destination = "/etc/nginx/conf.d/load-balancer.conf" 13 | perms = 0600 14 | command = "service nginx reload" 15 | } -------------------------------------------------------------------------------- /02-consul-sevice-discovery/ansible/roles/load-balancer/vars/main.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWS-Devops-Projects/devops-projects/6fb5ede7e776fb4401be2dd01cf4f4078a98f8cc/02-consul-sevice-discovery/ansible/roles/load-balancer/vars/main.yaml -------------------------------------------------------------------------------- /02-consul-sevice-discovery/terraform/provision.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | 5 | resource "aws_security_group" "consul_server_sg" { 6 | name = "consul_server_security_group" 7 | description = "Security group for the consul server" 8 | 9 | ingress { 10 | from_port = 22 11 | to_port = 22 12 | protocol = "tcp" 13 | cidr_blocks = ["0.0.0.0/0"] 14 | } 15 | 16 | ingress { 17 | from_port = 8500 18 | to_port = 8500 19 | protocol = "tcp" 20 | cidr_blocks = ["0.0.0.0/0"] 21 | } 22 | 23 | ingress { 24 | from_port = 0 25 | to_port = 65000 26 | protocol = "tcp" 27 | cidr_blocks = [var.ingress_cidr_block] 28 | } 29 | 30 | egress { 31 | from_port = 0 32 | to_port = 0 33 | protocol = "-1" 34 | cidr_blocks = ["0.0.0.0/0"] 35 | } 36 | 37 | tags = { 38 | Name = "consul-server-sg" 39 | } 40 | } 41 | 42 | resource "aws_security_group" "load_balancer_sg" { 43 | name = "load_balancer_security_group" 44 | description = "Security group for the load balancer" 45 | 46 | ingress { 47 | from_port = 22 48 | to_port = 22 49 | protocol = "tcp" 50 | cidr_blocks = ["0.0.0.0/0"] 51 | } 52 | 53 | ingress { 54 | from_port = 80 55 | to_port = 80 56 | protocol = "tcp" 57 | cidr_blocks = ["0.0.0.0/0"] 58 | } 59 | 60 | egress { 61 | from_port = 0 62 | to_port = 0 63 | protocol = "-1" 64 | cidr_blocks = ["0.0.0.0/0"] 65 | } 66 | 67 | tags = { 68 | Name = "loadbalancer-sg" 69 | } 70 | } 71 | 72 | resource "aws_security_group" "backends_sg" { 73 | name = "backends_security_group" 74 | description = "Security group for the backends" 75 | 76 | ingress { 77 | from_port = 22 78 | to_port = 22 79 | protocol = "tcp" 80 | cidr_blocks = ["0.0.0.0/0"] 81 | } 82 | 83 | ingress { 84 | from_port = 80 85 | to_port = 80 86 | protocol = "tcp" 87 | cidr_blocks = ["0.0.0.0/0"] 88 | } 89 | 90 | egress { 91 | from_port = 0 92 | to_port = 0 93 | protocol = "-1" 94 | cidr_blocks = ["0.0.0.0/0"] 95 | } 96 | 97 | tags = { 98 | Name = "backend-sg" 99 | } 100 | } 101 | 102 | resource "aws_instance" "consul_server" { 103 | ami = var.ami 104 | instance_type = var.instance_type 105 | vpc_security_group_ids = [aws_security_group.consul_server_sg.id] 106 | key_name = var.key_name 107 | 108 | tags = { 109 | Name = "consul-server" 110 | } 111 | } 112 | 113 | resource "aws_instance" "load_balancer" { 114 | ami = var.ami 115 | instance_type = var.instance_type 116 | vpc_security_group_ids = [aws_security_group.load_balancer_sg.id] 117 | key_name = var.key_name 118 | 119 | tags = { 120 | Name = "loadbalancer" 121 | } 122 | } 123 | 124 | resource "aws_instance" "backends" { 125 | count = 2 126 | ami = var.ami 127 | instance_type = var.instance_type 128 | vpc_security_group_ids = [aws_security_group.backends_sg.id] 129 | key_name = var.key_name 130 | 131 | tags = { 132 | Name = "backend-app" 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /02-consul-sevice-discovery/terraform/variable.tf: -------------------------------------------------------------------------------- 1 | variable "instance_type" { 2 | type = string 3 | description = "EC2 Instance Type" 4 | } 5 | 6 | variable "region" { 7 | type = string 8 | description = "The project region" 9 | default = "us-west-2" 10 | } 11 | 12 | variable "ami" { 13 | type = string 14 | description = "The amazon machine image" 15 | } 16 | 17 | variable "key_name" { 18 | type = string 19 | description = "Key Name" 20 | } 21 | 22 | variable "ingress_cidr_block" { 23 | type = string 24 | description = "The ingress CIDR block" 25 | } 26 | -------------------------------------------------------------------------------- /03-scalable-java-app/Jenkinsfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWS-Devops-Projects/devops-projects/6fb5ede7e776fb4401be2dd01cf4f4078a98f8cc/03-scalable-java-app/Jenkinsfile -------------------------------------------------------------------------------- /03-scalable-java-app/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Project Architecture 3 | 4 | ![java-aws](https://user-images.githubusercontent.com/106984297/219648306-42c0d544-f6e6-423d-9802-9f3d5eca43e8.png) 5 | 6 | 7 | # Java Application 8 | 9 | For this project you can use the open source pet clinic application (Java Spring Boot) 10 | 11 | ``` 12 | https://github.com/spring-projects/spring-petclinic 13 | ``` 14 | 15 | # Tools/Services 16 | 17 | - DevOps Tools 18 | - Jenkins 19 | - Packer 20 | - Ansible 21 | - Terraform 22 | 23 | - AWS Services 24 | - Application Load Balancer (L7) 25 | - Autoscaling Group 26 | - AWS secrets manager 27 | - RDS (MySQL) 28 | 29 | # Project Workflow 30 | 31 | - Build Java application 32 | - Use Packer & Ansible to build the AMI With application code 33 | - configure application logging 34 | - configure cloudwatch agent with the application log location. 35 | - Use Teraaform to provision the following 36 | - MySQL RDS instance and store the username and password in AWS secrets manager 37 | - Provision Application Load Blancer 38 | - Create a launch template With the Application AMI 39 | - Provision Autoscaling Group with Launch tempalate that use AMI built by packer and attach it to Loadbalancer. 40 | - Verify application by accessing it using Load Balancer endpoint. 41 | - Verify application logs in Cloudwatch 42 | 43 | ## Java Application 44 | 45 | Spring boot Petclinic application 46 | 47 | ``` 48 | git clone https://github.com/techiescamp/java-spring-petclinic 49 | ``` 50 | 51 | Starting the application with externalized properties file. 52 | 53 | ``` 54 | java -jar spring-petclinic.jar --spring.config.location=/path/to/application.properties --spring.profiles.active=mysql 55 | ``` 56 | 57 | Log Location 58 | 59 | ``` 60 | /opt/petclinic.log 61 | ``` 62 | 63 | Change folder persmissions 64 | 65 | ``` 66 | sudo chown -R petclinic:petclinic /opt 67 | sudo chmod -R u+w /opt 68 | ``` 69 | 70 | ## Useful Resources 71 | 72 | 1. [RDS password Rotation With Terraform](https://advancedweb.hu/how-to-set-up-amazon-rds-password-rotation-with-terraform/) 73 | 74 | 75 | -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/ami.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Java 3 | hosts: all 4 | become: yes 5 | remote_user: ubuntu 6 | 7 | vars: 8 | source_dir: files 9 | dest_dir: /home/ubuntu/ 10 | files: 11 | - application.properties 12 | - properties.py 13 | - script.sh 14 | 15 | roles: 16 | - java -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory = /vagrant/devops-projects/03-scalable-java-app/ansible/inventory.ini 3 | host_key_checking = false 4 | stdout_callback = yaml 5 | callback_enabled = timer 6 | -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/files/application.properties: -------------------------------------------------------------------------------- 1 | # database init, supports mysql too 2 | 3 | # database init, supports mysql too 4 | database=mysql 5 | spring.datasource.url=jdbc:mysql://loacalhost:3306/petclinic 6 | spring.datasource.username=petclinic 7 | spring.datasource.password=petclinic 8 | # SQL is written to be idempotent so this is safe 9 | spring.sql.init.mode=always 10 | 11 | # Web 12 | spring.thymeleaf.mode=HTML 13 | 14 | # JPA 15 | spring.jpa.hibernate.ddl-auto=none 16 | spring.jpa.open-in-view=true 17 | 18 | # Internationalization 19 | spring.messages.basename=messages/messages 20 | 21 | # Actuator 22 | management.endpoints.web.exposure.include=* 23 | # Logging 24 | #logging.config=classpath:logback-spring.xml 25 | logging.level.org.springframework=INFO 26 | # logging.level.org.springframework.web=DEBUG 27 | # logging.level.org.springframework.context.annotation=TRACE 28 | 29 | # Maximum time static resources should be cached 30 | spring.web.resources.cache.cachecontrol.max-age=12h -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/files/properties.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | 4 | region = 'us-west-2' 5 | parameter_store = '/dev/petclinic/rds_endpoint' 6 | secret_name = "rds!db-8f23e7a9-e9f8-4b50-a059-f0e310e18134" 7 | file_path = "/home/ubuntu/application.properties" 8 | 9 | ssm = boto3.client('ssm', region_name=region) 10 | 11 | redis_endpoint = ssm.get_parameter(Name=parameter_store)['Parameter']['Value'] 12 | 13 | secrets_client = boto3.client('secretsmanager') 14 | 15 | response = secrets_client.get_secret_value(SecretId=secret_name) 16 | 17 | secret_value = response['SecretString'] 18 | 19 | database_details = json.loads(secret_value) 20 | 21 | with open(file_path, 'r') as f: 22 | file_contents = f.read() 23 | 24 | file_contents = file_contents.replace("spring.datasource.url=jdbc:mysql://localhost:3306/petclinic", f"spring.datasource.url={redis_endpoint}") 25 | file_contents = file_contents.replace("spring.datasource.username=petclinic", f"spring.datasource.username={database_details['username']}") 26 | file_contents = file_contents.replace("spring.datasource.password=petclinic", f"spring.datasource.password={database_details['password']}") 27 | 28 | with open(file_path, 'w') as f: 29 | f.write(file_contents) 30 | -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/files/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | JAR_FILE=/home/ubuntu/spring-petclinic.jar 4 | APP_PROPERTIES=/home/ubuntu/application.properties 5 | PROPERTIES_SCRIPT=/home/ubuntu/properties.py 6 | 7 | python3 ${PROPERTIES_SCRIPT} 8 | 9 | sudo java -jar ${JAR_FILE} --spring.config.location= ${APP_PROPERTIES} --spring.profiles.active=mysql & -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/inventory.ini: -------------------------------------------------------------------------------- 1 | 2 | [all:vars] 3 | ansible_user=ubuntu 4 | ansible_private_key_file=/home/vagrant/.ssh/aswin-key.pem -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/roles/java/tasks/app.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Copy files 3 | copy: 4 | src: "{{ source_dir }}/{{ item }}" 5 | dest: "{{ dest_dir }}/{{ item }}" 6 | with_items: "{{ files }}" 7 | 8 | - name: Make start.sh script executable 9 | become: true 10 | file: 11 | path: /home/ubuntu/start.sh 12 | mode: '+x' -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/roles/java/tasks/cloudwatch.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Downloading packages 4 | command: wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb 5 | 6 | - name: Install CloudWatch 7 | command: dpkg -i amazon-cloudwatch-agent.deb 8 | 9 | - name: Create CloudWatch Agent configuration file 10 | template: 11 | src: config.json.j2 12 | dest: /opt/aws/amazon-cloudwatch-agent/bin/config.json 13 | 14 | - name: Install collectd 15 | apt: 16 | name: collectd 17 | state: latest 18 | 19 | - name: Start CloudWatch 20 | command: amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s 21 | -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/roles/java/tasks/java.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update and upgrade apt packages 3 | become: true 4 | apt: 5 | upgrade: yes 6 | update_cache: yes 7 | 8 | - name: Install JRE 9 | become: true 10 | apt: 11 | name: openjdk-17-jre 12 | state: latest 13 | 14 | - name: Make start.sh script executable 15 | become: true 16 | file: 17 | path: /home/ubuntu/start.sh 18 | mode: '+x' -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/roles/java/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: java.yml 3 | - include_tasks: python.yml 4 | - include_tasks: cloudwatch.yml 5 | - include_tasks: node_exporter.yml 6 | - include_tasks: app.yaml 7 | 8 | -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/roles/java/tasks/node_exporter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Download node_exporter 3 | get_url: 4 | url: "https://github.com/prometheus/node_exporter/releases/download/v1.5.0/node_exporter-1.5.0.linux-amd64.tar.gz" 5 | dest: "/tmp/node_exporter-1.5.0.linux-amd64.tar.gz" 6 | 7 | - name: Extract node_exporter 8 | unarchive: 9 | src: "/tmp/node_exporter-1.5.0.linux-amd64.tar.gz" 10 | dest: "/tmp/" 11 | remote_src: yes 12 | 13 | - name: Move file 14 | command: 15 | cmd: mv /tmp/node_exporter-1.5.0.linux-amd64/node_exporter /usr/local/bin 16 | 17 | - name: Create new group 18 | group: 19 | name: node_exporter 20 | state: present 21 | system: true 22 | 23 | - name: Create new user and add to new group 24 | user: 25 | name: node_exporter 26 | state: present 27 | system: true 28 | group: node_exporter 29 | shell: /bin/bash 30 | createhome: yes 31 | 32 | - name: Create node_exporter service file 33 | template: 34 | src: "node_exporter.service.j2" 35 | dest: "/etc/systemd/system/node_exporter.service" 36 | 37 | - name: Start service 38 | shell: | 39 | sudo systemctl daemon-reload 40 | sudo systemctl start node_exporter 41 | sudo systemctl enable node_exporter 42 | -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/roles/java/tasks/python.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Python3 3 | become: true 4 | apt: 5 | name: python3 6 | state: present 7 | 8 | - name: Install pip3 9 | become: true 10 | apt: 11 | name: python3-pip 12 | state: present 13 | 14 | - name: Install boto3 using pip3 15 | become: true 16 | pip: 17 | name: boto3 18 | state: present 19 | 20 | - name: Install AWS CLI using pip 21 | become: true 22 | pip: 23 | name: awscli 24 | state: latest 25 | executable: pip3 -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/templates/config.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "agent": { 3 | "metrics_collection_interval": 10, 4 | "run_as_user": "root" 5 | }, 6 | "logs": { 7 | "logs_collected": { 8 | "files": { 9 | "collect_list": [ 10 | { 11 | "file_path": "/var/log/petclinic.log", 12 | "log_group_name": "petclinic-log", 13 | "log_stream_name": "{instance_id}", 14 | "retention_in_days": 14 15 | } 16 | ] 17 | } 18 | } 19 | }, 20 | "metrics": { 21 | "aggregation_dimensions": [ 22 | [ 23 | "InstanceId" 24 | ] 25 | ], 26 | "append_dimensions": { 27 | "AutoScalingGroupName": "${aws:AutoScalingGroupName}", 28 | "ImageId": "${aws:ImageId}", 29 | "InstanceId": "${aws:InstanceId}", 30 | "InstanceType": "${aws:InstanceType}" 31 | }, 32 | "metrics_collected": { 33 | "collectd": { 34 | "metrics_aggregation_interval": 60 35 | }, 36 | "disk": { 37 | "measurement": [ 38 | "used_percent" 39 | ], 40 | "metrics_collection_interval": 10, 41 | "resources": [ 42 | "*" 43 | ] 44 | }, 45 | "mem": { 46 | "measurement": [ 47 | "mem_used_percent" 48 | ], 49 | "metrics_collection_interval": 10 50 | }, 51 | "statsd": { 52 | "metrics_aggregation_interval": 60, 53 | "metrics_collection_interval": 10, 54 | "service_address": ":8125" 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/templates/node_exporter.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Node Exporter 3 | After=network.target 4 | 5 | [Service] 6 | User=node_exporter 7 | Group=node_exporter 8 | Type=simple 9 | ExecStart=/usr/local/bin/node_exporter 10 | 11 | [Install] 12 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /03-scalable-java-app/ansible/vm.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "ami_id" { 2 | type = string 3 | default = "ami-0735c191cf914754d" 4 | } 5 | 6 | locals { 7 | app_name = "pet-clinic" 8 | } 9 | 10 | source "amazon-ebs" "nginx" { 11 | ami_name = "PACKER-${local.app_name}" 12 | instance_type = "t2.micro" 13 | region = "us-west-2" 14 | source_ami = "${var.ami_id}" 15 | ssh_username = "ubuntu" 16 | tags = { 17 | Env = "DEMO" 18 | Name = "PACKER-${local.app_name}" 19 | } 20 | } 21 | 22 | build { 23 | sources = ["source.amazon-ebs.nginx"] 24 | 25 | provisioner "file" { 26 | source = "${WORKSPACE}/target/spring-petclinic.jar" 27 | destination = "/home/ubuntu/spring-petclinic.jar" 28 | } 29 | 30 | provisioner "ansible" { 31 | playbook_file = "ami.yml" 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /03-scalable-java-app/terraform/lb-asg/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-west-2" 3 | } 4 | 5 | module "lb-asg" { 6 | source = "../modules/lb-asg" 7 | subnets = ["subnet-058a7514ba8adbb07", "subnet-04b1f595ef8c29542", "subnet-0dbcd1ac168414927"] 8 | ami_id = "ami-1234567890abcdef" 9 | instance_type ="t2.small" 10 | key_name = "techiescamp" 11 | environment = "dev" 12 | vpc_id = "vpc-0a5ca4a92c2e10163" 13 | } -------------------------------------------------------------------------------- /03-scalable-java-app/terraform/modules/lb-asg/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "alb_sg" { 2 | name_prefix = "alb-sg" 3 | 4 | ingress { 5 | from_port = 80 6 | to_port = 80 7 | protocol = "tcp" 8 | cidr_blocks = ["0.0.0.0/0"] 9 | } 10 | 11 | egress { 12 | from_port = 0 13 | to_port = 0 14 | protocol = "-1" 15 | cidr_blocks = ["0.0.0.0/0"] 16 | } 17 | 18 | tags = { 19 | Name = "petclinic-alb-sg" 20 | } 21 | } 22 | 23 | resource "aws_lb" "petclinic" { 24 | name = "petclinic-alb" 25 | internal = false 26 | load_balancer_type = "application" 27 | 28 | subnets = var.subnets 29 | security_groups = [aws_security_group.alb_sg.id] 30 | 31 | tags = { 32 | Environment = var.environment 33 | Terraform = "true" 34 | } 35 | 36 | enable_deletion_protection = true 37 | } 38 | 39 | resource "aws_security_group" "instance_sg" { 40 | name_prefix = "instance-sg" 41 | 42 | ingress { 43 | from_port = 22 44 | to_port = 22 45 | protocol = "tcp" 46 | cidr_blocks = ["0.0.0.0/0"] 47 | } 48 | 49 | ingress { 50 | from_port = 8080 51 | to_port = 8080 52 | protocol = "tcp" 53 | security_groups = [aws_security_group.alb_sg.id] 54 | } 55 | 56 | egress { 57 | from_port = 0 58 | to_port = 0 59 | protocol = "-1" 60 | cidr_blocks = ["0.0.0.0/0"] 61 | } 62 | 63 | tags = { 64 | Name = "petclinic-instance-sg" 65 | } 66 | } 67 | 68 | 69 | resource "aws_lb_target_group" "petclinic" { 70 | name_prefix = "pc-lb" 71 | port = 8080 72 | protocol = "HTTP" 73 | vpc_id = var.vpc_id 74 | target_type = "instance" 75 | 76 | health_check { 77 | path = "/health" 78 | port = 8080 79 | protocol = "HTTP" 80 | interval = 30 81 | timeout = 5 82 | healthy_threshold = 2 83 | unhealthy_threshold = 2 84 | } 85 | 86 | tags = { 87 | Environment = var.environment 88 | Terraform = "true" 89 | } 90 | } 91 | 92 | resource "aws_autoscaling_group" "petclinic" { 93 | name = "petclinic-asg" 94 | max_size = 3 95 | min_size = 1 96 | desired_capacity = 2 97 | vpc_zone_identifier = var.subnets 98 | launch_configuration = aws_launch_configuration.petclinic.id 99 | 100 | tag { 101 | key = "Name" 102 | value = "petclinic-app" 103 | propagate_at_launch = true 104 | } 105 | 106 | lifecycle { 107 | create_before_destroy = true 108 | } 109 | } 110 | 111 | resource "aws_launch_configuration" "petclinic" { 112 | name_prefix = "petclinic-lc" 113 | image_id = var.ami_id 114 | instance_type = var.instance_type 115 | security_groups = [aws_security_group.instance_sg.id] 116 | key_name = var.key_name 117 | user_data = filebase64("${path.module}/scripts/user_data.sh") 118 | 119 | lifecycle { 120 | create_before_destroy = true 121 | } 122 | } 123 | 124 | resource "aws_autoscaling_attachment" "petclinic" { 125 | autoscaling_group_name = aws_autoscaling_group.petclinic.name 126 | alb_target_group_arn = aws_lb_target_group.petclinic.arn 127 | } 128 | -------------------------------------------------------------------------------- /03-scalable-java-app/terraform/modules/lb-asg/scripts/user_data.sh: -------------------------------------------------------------------------------- 1 | nohup java -jar petclinic-app.jar --spring.config.location=/ubuntu/deployment/application.properties --spring.profiles.active=mysql & -------------------------------------------------------------------------------- /03-scalable-java-app/terraform/modules/lb-asg/variable.tf: -------------------------------------------------------------------------------- 1 | variable "ami_id" { 2 | description = "The ID of the Amazon Machine Image (AMI) to use for the EC2 instances." 3 | } 4 | 5 | variable "instance_type" { 6 | description = "The type of EC2 instance to use for the ASG." 7 | } 8 | 9 | variable "key_name" { 10 | description = "The name of the EC2 key pair to use for the instances." 11 | } 12 | 13 | variable "environment" { 14 | description = "The environment name for the resources." 15 | } 16 | 17 | variable "vpc_id" { 18 | description = "The ID of the VPC to use for the resources." 19 | } 20 | 21 | variable "subnets" { 22 | description = "A list of subnet IDs to use for the resources." 23 | type = list(string) 24 | } 25 | -------------------------------------------------------------------------------- /03-scalable-java-app/terraform/modules/rds/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_secretsmanager_secret" "rds_secret" { 2 | name = "/dev/petclinic/db" 3 | } 4 | 5 | resource "random_password" "password" { 6 | length = 8 7 | special = true 8 | override_special = "_@%" 9 | } 10 | 11 | resource "aws_secretsmanager_secret_version" "rds_secret_value" { 12 | secret_id = aws_secretsmanager_secret.rds_secret.id 13 | secret_string = jsonencode({ 14 | username = "petclinic", 15 | password = random_password.password.result, 16 | }) 17 | } 18 | 19 | # Create a DB security group 20 | resource "aws_security_group" "rds_security_group" { 21 | name = "rds-security-group" 22 | description = "Security group for RDS instance" 23 | 24 | ingress { 25 | from_port = 3306 26 | to_port = 3306 27 | protocol = "tcp" 28 | cidr_blocks = ["0.0.0.0/0"] 29 | } 30 | 31 | egress { 32 | from_port = 0 33 | to_port = 0 34 | protocol = "-1" 35 | cidr_blocks = ["0.0.0.0/0"] 36 | } 37 | } 38 | 39 | 40 | resource "aws_db_instance" "rds_instance" { 41 | identifier = "petclinic-mysql-rds" 42 | engine = "mysql" 43 | instance_class = "db.t2.micro" 44 | allocated_storage = 10 45 | storage_type = "gp2" 46 | username = jsondecode(aws_secretsmanager_secret_version.rds_secret_value.secret_string)["username"] 47 | password = jsondecode(aws_secretsmanager_secret_version.rds_secret_value.secret_string)["password"] 48 | db_subnet_group_name = "default" 49 | vpc_security_group_ids = [aws_security_group.rds_security_group.id] 50 | backup_retention_period = 7 51 | delete_automated_backups = true 52 | copy_tags_to_snapshot = true 53 | publicly_accessible = true 54 | skip_final_snapshot = true 55 | apply_immediately = true 56 | 57 | tags = { 58 | Name = "petclinic-rds" 59 | } 60 | 61 | } 62 | 63 | # Data source to retrieve RDS endpoint 64 | data "aws_db_instance" "rds_instance" { 65 | db_instance_identifier = aws_db_instance.rds_instance.id 66 | } 67 | 68 | 69 | resource "aws_ssm_parameter" "rds_endpoint" { 70 | name = "/dev/petclinic/rds_endpoint" 71 | type = "String" 72 | value = data.aws_db_instance.rds_instance.endpoint 73 | } 74 | -------------------------------------------------------------------------------- /03-scalable-java-app/terraform/modules/rds/variables.tf: -------------------------------------------------------------------------------- 1 | variable "update_rds_endpoint" { 2 | type = bool 3 | default = true 4 | } -------------------------------------------------------------------------------- /03-scalable-java-app/terraform/rds/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-west-2" 3 | } 4 | 5 | module "rds" { 6 | source = "../modules/rds" 7 | } -------------------------------------------------------------------------------- /03-scalable-java-app/terraform/rds/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | default = "us-west-2" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DevOps Projects 2 | 3 | DevOps Real World Projects for Aspiring DevOps Engineers [Beginner to Advanced] 4 | 5 | ## List of DevOps Project Ideas 6 | 7 | All the DevOps Real World Projects are Documented in [Real World DevOps Projects](https://devopscube.com/devops-projects/) blog 8 | -------------------------------------------------------------------------------- /dev-vms/ubuntu-22/Vagrantfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWS-Devops-Projects/devops-projects/6fb5ede7e776fb4401be2dd01cf4f4078a98f8cc/dev-vms/ubuntu-22/Vagrantfile -------------------------------------------------------------------------------- /generic-scripts/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Ansible Commands 3 | 4 | ``` 5 | ansible-playbook playbook.yml" 6 | ``` 7 | 8 | ## Terraform Commands 9 | 10 | ``` 11 | terraform plan -var="reinstance_name=aswin-test-vm" -var="instance_type=t2.micro" 12 | 13 | terraform apply -var="reinstance_name=aswin-test-vm" -var="instance_type=t2.micro" --auto-approve 14 | 15 | terraform destroy -var="reinstance_name=aswin-test-vm" -var="instance_type=t2.micro" --auto-approve 16 | 17 | ``` -------------------------------------------------------------------------------- /generic-scripts/terraform/ec2/instance.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-west-2" 3 | } 4 | 5 | variable "instance_name" { 6 | type = string 7 | default = "live-test-instance" 8 | } 9 | 10 | variable "ami_id" { 11 | type = string 12 | default = "ami-0735c191cf914754d" 13 | } 14 | 15 | variable "instance_type" { 16 | type = string 17 | default = "t2.small" 18 | } 19 | 20 | variable "key_name" { 21 | type = string 22 | default = "techiescamp" 23 | } 24 | 25 | variable "security_group_ids" { 26 | type = list(string) 27 | default = ["sg-01ce819e8d65269f0"] 28 | } 29 | 30 | 31 | resource "aws_instance" "example" { 32 | ami = var.ami_id 33 | instance_type = var.instance_type 34 | key_name = var.key_name 35 | vpc_security_group_ids = var.security_group_ids 36 | tags = { 37 | Name = var.instance_name 38 | } 39 | 40 | subnet_id = data.aws_subnet.default.id 41 | 42 | } 43 | 44 | data "aws_subnet" "default" { 45 | vpc_id = data.aws_vpc.default.id 46 | cidr_block = "172.31.32.0/20" 47 | } 48 | 49 | 50 | data "aws_vpc" "default" { 51 | default = true 52 | } 53 | 54 | 55 | --------------------------------------------------------------------------------