{"id":15090875,"url":"https://github.com/roeeelnekave/crash-api-application-part-2","last_synced_at":"2026-01-30T09:04:39.407Z","repository":{"id":256048153,"uuid":"849440639","full_name":"roeeelnekave/crash-api-application-part-2","owner":"roeeelnekave","description":"Deploying a Crash Management API with Ansible and Cloudformation and Automation Using Jenkins ECS with Jenkins Ec2 Agent and monitoring through Grafana, Promethues","archived":false,"fork":false,"pushed_at":"2024-10-02T05:30:21.000Z","size":168,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-22T10:15:18.303Z","etag":null,"topics":["ansible","cloudformation","flask","gunicorn","jenkins","nginx"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/roeeelnekave.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-29T15:45:02.000Z","updated_at":"2024-10-02T05:30:24.000Z","dependencies_parsed_at":"2024-09-08T18:47:16.271Z","dependency_job_id":"f122fde2-05af-4780-a745-404c61657dde","html_url":"https://github.com/roeeelnekave/crash-api-application-part-2","commit_stats":null,"previous_names":["roeeelnekave/crash-api-application-part-2"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/roeeelnekave/crash-api-application-part-2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roeeelnekave%2Fcrash-api-application-part-2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roeeelnekave%2Fcrash-api-application-part-2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roeeelnekave%2Fcrash-api-application-part-2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roeeelnekave%2Fcrash-api-application-part-2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/roeeelnekave","download_url":"https://codeload.github.com/roeeelnekave/crash-api-application-part-2/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roeeelnekave%2Fcrash-api-application-part-2/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260046240,"owners_count":22950819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","cloudformation","flask","gunicorn","jenkins","nginx"],"created_at":"2024-09-25T10:34:27.201Z","updated_at":"2026-01-30T09:04:39.342Z","avatar_url":"https://github.com/roeeelnekave.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# How to deploy this project using CloudFormation\n\n## Prerequisites\n\n1. AWS Account\n2. Basic Understanding of AWS services (VPC, EFS, ECS, IAM)\n3. Docker and Docker Compose\n4. Basic understanding of the CloudFormation template\n5. AWS CLI\n\n## STEPS\n\n# Directory\n\n```bash\nmkdir -p ./jenkins\n```\n\n\n\n- **We need a Dockerfile to customize our Jenkins image. Create a Dockerfile:**\n- Create a folder for your project and inside that folder create a file named `./jenkins/Dockerfile` and paste the following into the Dockerfile:\n\n```Dockerfile\nFROM amazonlinux:2023\nRUN yum install -y \\\n    python3 \\\n    python3-pip \\\n    git \\\n    zip \\\n    unzip \\\n    tar \\\n    gzip \\\n    wget \\\n    jq \\\n    openssh-server \\\n    openssh-clients \\\n    which \\\n    findutils \\\n    python3-pip \u0026\u0026 \\\n    python3 -m pip install awscli \u0026\u0026 \\\n    python3 -m pip install boto3 \u0026\u0026 \\\n    wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo \u0026\u0026 \\\n    rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key \u0026\u0026 \\\n    yum upgrade -y \u0026\u0026 \\\n    yum install -y fontconfig \u0026\u0026 \\\n    dnf install java-17-amazon-corretto -y \u0026\u0026 \\\n    yum install -y jenkins \u0026\u0026 \\\n    python3 -m pip install ansible \u0026\u0026 \\\n    yum clean all\nEXPOSE 8080\nCMD [\"java\", \"-jar\", \"/usr/share/java/jenkins.war\"]\n```\n\n- **Create a CloudFormation file for deploying our infrastructure:**\n- Create a file named `./jenkins/main.yaml` and paste the following into the YAML template to create a deploy the infrastructure this will create VPC, IGW, ECS, IAM:\n\n```yaml\nAWSTemplateFormatVersion: '2010-09-09'\nDescription: 'CloudFormation template for VPC with 3 public and 3 private subnets'\nParameters:\n  ImageURL:\n    Type: String\n    Description: 'Image URL for the ECR repo'\n\n\nResources:\n  VPC:\n    Type: AWS::EC2::VPC\n    Properties:\n      CidrBlock: 10.0.0.0/16\n      EnableDnsHostnames: true\n      EnableDnsSupport: true\n      InstanceTenancy: default\n      Tags:\n        - Key: Name\n          Value: project-vpc\n\n  InternetGateway:\n    Type: AWS::EC2::InternetGateway\n\n  InternetGatewayAttachment:\n    Type: AWS::EC2::VPCGatewayAttachment\n    Properties:\n      VpcId: !Ref VPC\n      InternetGatewayId: !Ref InternetGateway\n\n  PublicSubnet1:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 0, !GetAZs '' ]\n      CidrBlock: 10.0.1.0/24\n      MapPublicIpOnLaunch: true\n      Tags:\n        - Key: Name\n          Value: project-subnet-public1-us-east-1a\n\n  PublicSubnet2:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 1, !GetAZs '' ]\n      CidrBlock: 10.0.2.0/24\n      MapPublicIpOnLaunch: true\n      Tags:\n        - Key: Name\n          Value: project-subnet-public2-us-east-1b\n\n  PublicSubnet3:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 2, !GetAZs '' ]\n      CidrBlock: 10.0.3.0/24\n      MapPublicIpOnLaunch: true\n      Tags:\n        - Key: Name\n          Value: project-subnet-public3-us-east-1c\n\n  PrivateSubnet1:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 0, !GetAZs '' ]\n      CidrBlock: 10.0.4.0/24\n      MapPublicIpOnLaunch: false\n      Tags:\n        - Key: Name\n          Value: project-subnet-private1-us-east-1a\n\n  PrivateSubnet2:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 1, !GetAZs '' ]\n      CidrBlock: 10.0.5.0/24\n      MapPublicIpOnLaunch: false\n      Tags:\n        - Key: Name\n          Value: project-subnet-private2-us-east-1b\n\n  PrivateSubnet3:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 2, !GetAZs '' ]\n      CidrBlock: 10.0.6.0/24\n      MapPublicIpOnLaunch: false\n      Tags:\n        - Key: Name\n          Value: project-subnet-private3-us-east-1c\n\n  PublicRouteTable:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags:\n        - Key: Name\n          Value: project-rtb-public\n\n  DefaultPublicRoute:\n    Type: AWS::EC2::Route\n    DependsOn: InternetGatewayAttachment\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      DestinationCidrBlock: 0.0.0.0/0\n      GatewayId: !Ref InternetGateway\n\n  PublicSubnet1RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      SubnetId: !Ref PublicSubnet1\n\n  PublicSubnet2RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      SubnetId: !Ref PublicSubnet2\n\n  PublicSubnet3RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      SubnetId: !Ref PublicSubnet3\n\n  NATGateway1:\n    Type: AWS::EC2::NatGateway\n    Properties:\n      AllocationId: !GetAtt NATGateway1EIP.AllocationId\n      SubnetId: !Ref PublicSubnet1\n\n  NATGateway1EIP:\n    Type: AWS::EC2::EIP\n    DependsOn: InternetGatewayAttachment\n    Properties:\n      Domain: vpc\n\n  PrivateRouteTable1:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags:\n        - Key: Name\n          Value: project-rtb-private1-us-east-1a\n\n  DefaultPrivateRoute1:\n    Type: AWS::EC2::Route\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable1\n      DestinationCidrBlock: 0.0.0.0/0\n      NatGatewayId: !Ref NATGateway1\n  \n\n\n  PrivateSubnet1RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable1\n      SubnetId: !Ref PrivateSubnet1\n\n  PrivateRouteTable2:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags:\n        - Key: Name\n          Value: project-rtb-private2-us-east-1b\n\n  DefaultPrivateRoute2:\n    Type: AWS::EC2::Route\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable2\n      DestinationCidrBlock: 0.0.0.0/0\n      NatGatewayId: !Ref NATGateway1\n\n  PrivateSubnet2RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable2\n      SubnetId: !Ref PrivateSubnet2\n\n  PrivateRouteTable3:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags:\n        - Key: Name\n          Value: project-rtb-private3-us-east-1c\n\n  DefaultPrivateRoute3:\n    Type: AWS::EC2::Route\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable3\n      DestinationCidrBlock: 0.0.0.0/0\n      NatGatewayId: !Ref NATGateway1\n\n  PrivateSubnet3RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable3\n      SubnetId: !Ref PrivateSubnet3\n  ECSSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: \"ECS Security Group\"\n      VpcId: !Ref VPC\n      SecurityGroupIngress:\n        - IpProtocol: tcp\n          FromPort: 8080\n          ToPort: 8080\n          CidrIp: 0.0.0.0/0\n      SecurityGroupEgress:\n        - IpProtocol: \"-1\"\n          CidrIp: 0.0.0.0/0\n  EFSSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: \"EFS Security Group\"\n      VpcId: !Ref VPC\n      SecurityGroupIngress:\n        - IpProtocol: tcp\n          FromPort: 2049\n          ToPort: 2049\n          SourceSecurityGroupId: !Ref ECSSecurityGroup \n      SecurityGroupEgress:\n        - IpProtocol: \"-1\"\n          CidrIp: 0.0.0.0/0\n\n  # Policy for ECS Task\n  ECSTaskPolicy:\n    Type: AWS::IAM::Policy\n    Properties:\n      PolicyName: !Sub \"ECSExecutionTaskPolicy-${AWS::StackName}\"\n      PolicyDocument:\n        Version: \"2012-10-17\"\n        Statement:\n          - Effect: Allow\n            Action:\n              - ecr:GetAuthorizationToken\n              - ecr:BatchCheckLayerAvailability\n              - ecr:GetDownloadUrlForLayer\n              - ecr:BatchGetImage\n              - logs:CreateLogStream\n              - logs:PutLogEvents\n            Resource: \"*\"\n      Roles:\n        - !Ref ECSEFSmountTaskRole\n\n  # Policy for EFS Mount\n  EFSMountPolicy:\n    Type: AWS::IAM::Policy\n    Properties:\n      PolicyName: !Sub \"ECSMountPolicy-${AWS::StackName}\"\n      PolicyDocument:\n        Version: \"2012-10-17\"\n        Statement:\n          - Sid: AllowDescribe\n            Effect: Allow\n            Action:\n              - elasticfilesystem:DescribeAccessPoints\n              - elasticfilesystem:DescribeFileSystems\n              - elasticfilesystem:DescribeMountTargets\n              - ec2:DescribeAvailabilityZones\n            Resource: \"*\"\n          - Sid: AllowCreateAccessPoint\n            Effect: Allow\n            Action:\n              - elasticfilesystem:*\n            Resource: \"*\"\n      Roles:\n        - !Ref ECSEFSmountTaskRole\n\n  # IAM Role for ECS Tasks\n  ECSEFSmountTaskRole:\n    Type: AWS::IAM::Role\n    Properties:\n      AssumeRolePolicyDocument:\n        Version: \"2012-10-17\"\n        Statement:\n          - Effect: Allow\n            Principal:\n              Service: ecs-tasks.amazonaws.com\n            Action: sts:AssumeRole\n      Policies:\n        - PolicyName: !Sub \"ECSTaskPolicy-${AWS::StackName}\"\n          PolicyDocument:\n            Version: \"2012-10-17\"\n            Statement:\n              - Effect: Allow\n                Action:\n                  - ecr:GetAuthorizationToken\n                  - ecr:BatchCheckLayerAvailability\n                  - ecr:GetDownloadUrlForLayer\n                  - ecr:BatchGetImage\n                  - logs:CreateLogStream\n                  - logs:PutLogEvents\n                Resource: \"*\" \n  EFSSystem:\n    Type: AWS::EFS::FileSystem\n    Properties:\n      Encrypted: true\n      FileSystemTags:\n        - Key: Name\n          Value: JenkinsEFS\n  JenkinsHomeVolume1:\n    Type: AWS::EFS::MountTarget\n    Properties:\n      FileSystemId: !Ref EFSSystem\n      SubnetId: !Ref PublicSubnet1\n      SecurityGroups:\n        - !Ref EFSSecurityGroup\n  JenkinsHomeVolume2:\n    Type: AWS::EFS::MountTarget\n    Properties:\n      FileSystemId: !Ref EFSSystem\n      SubnetId: !Ref PublicSubnet2\n      SecurityGroups:\n        - !Ref EFSSecurityGroup\n  JenkinsHomeVolume3:\n    Type: AWS::EFS::MountTarget\n    Properties:\n      FileSystemId: !Ref EFSSystem\n      SubnetId: !Ref PublicSubnet3\n      SecurityGroups:\n        - !Ref EFSSecurityGroup\n  ECSCluster:\n    Type: AWS::ECS::Cluster\n    Properties:\n      ClusterName: JenkinsCluster\n      CapacityProviders:\n        - FARGATE\n        - FARGATE_SPOT\n      DefaultCapacityProviderStrategy:\n        - CapacityProvider: FARGATE\n          Weight: 1\n        - CapacityProvider: FARGATE_SPOT\n          Weight: 1\n      Configuration:\n        ExecuteCommandConfiguration:\n          Logging: DEFAULT\n  ECSLogGroup: \n    Type: AWS::Logs::LogGroup\n    Properties: \n      LogGroupName: !Sub \"/ecs/test-${AWS::StackName}\"\n      RetentionInDays: 7\n\n  ECSTaskDefinition:\n    Type: AWS::ECS::TaskDefinition\n    Properties:\n      ExecutionRoleArn: !Ref ECSEFSmountTaskRole\n      TaskRoleArn: !Ref ECSEFSmountTaskRole\n      NetworkMode: awsvpc\n      RequiresCompatibilities:\n        - FARGATE\n      RuntimePlatform:\n        OperatingSystemFamily: LINUX\n        CpuArchitecture: ARM64\n      Family: my-jenkins-task-00\n      Cpu: \"1024\"\n      Memory: \"2048\"\n      ContainerDefinitions:\n        - Name: jenkins\n          Image: !Ref ImageURL\n          Cpu: 1024\n          Memory: 2048\n          MemoryReservation: 1024\n          Essential: true\n          PortMappings:\n            - ContainerPort: 8080\n              Protocol: tcp\n          LinuxParameters:\n            InitProcessEnabled: true\n          MountPoints:\n            - SourceVolume: efs-volume\n              ContainerPath: /root/.jenkins\n          LogConfiguration:\n            LogDriver: awslogs\n            Options:\n              mode: non-blocking\n              max-buffer-size: 25m\n              awslogs-group: !Ref ECSLogGroup\n              awslogs-region: us-east-1\n              awslogs-create-group: \"true\"\n              awslogs-stream-prefix: efs-task\n      Volumes:\n        - Name: efs-volume\n          EFSVolumeConfiguration:\n            FilesystemId: !Ref EFSSystem\n            RootDirectory: /\n            TransitEncryption: ENABLED\n\n  ECSService:\n    Type: AWS::ECS::Service\n    Properties:\n      Cluster: !Ref ECSCluster  \n      TaskDefinition: !Ref ECSTaskDefinition\n      LaunchType: FARGATE\n      ServiceName: ebs\n      SchedulingStrategy: REPLICA\n      DesiredCount: 1\n      NetworkConfiguration:\n        AwsvpcConfiguration:\n          AssignPublicIp: ENABLED\n          SecurityGroups: \n            - !Ref ECSSecurityGroup\n          Subnets:\n            - !Ref PublicSubnet1\n            - !Ref PublicSubnet2\n            - !Ref PublicSubnet3\n      PlatformVersion: LATEST\n      DeploymentConfiguration:\n        MaximumPercent: 200\n        MinimumHealthyPercent: 100\n        DeploymentCircuitBreaker:\n          Enable: true\n          Rollback: true\n      DeploymentController:\n        Type: ECS\n      Tags: []\n      EnableECSManagedTags: true\n\nOutputs:\n  VPCID:\n    Description: The ID of the created VPC\n    Value: !Ref VPC\n\n  PublicSubnet1ID:\n    Description: The ID of Public Subnet 1\n    Value: !Ref PublicSubnet1\n\n  PublicSubnet2ID:\n    Description: The ID of Public Subnet 2\n    Value: !Ref PublicSubnet2\n```\n**Create the bash script and update the Repository Name, aws region and stack name if you desire and  `./jenkins/bash-script.sh` required**\n\n```bash\n#!/bin/bash\n\n# update the stack name\nSTACK_NAME=\"jenkins-efs-ecs-1\"\n# update to your desired aws region\nAWS_REGION=\"us-east-1\"\n\n# Set or update the repository name\nREPOSITORY_NAME=\"jenkins\"\n\n# Set the image tag\nIMAGE_TAG=\"latest\"\n\nAWS_ACCOUNT_ID=$(aws sts get-caller-identity --query \"Account\" --output --region $AWS_REGION)\n# Create the ECR repository\naws ecr describe-repositories --repository-names \"${REPOSITORY_NAME}\" --region $AWS_REGION \u003e /dev/null 2\u003e\u00261\nif [ $? -ne 0 ]\nthen\n    aws ecr create-repository --repository-name \"${REPOSITORY_NAME}\" --region $AWS_REGION \u003e /dev/null\nfi\n\n\n# Build the Docker image\ndocker build -t $REPOSITORY_NAME:$IMAGE_TAG .\n\n# Get the ECR login command\nLOGIN_COMMAND=$(aws ecr get-login-password | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com)\n# Push the image to ECR\ndocker tag $REPOSITORY_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPOSITORY_NAME:$IMAGE_TAG\ndocker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPOSITORY_NAME:$IMAGE_TAG\nIMAGE_URI=\"${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPOSITORY_NAME}:${IMAGE_TAG}\"\n\naws cloudformation update-stack \\\n  --stack-name \"${STACK_NAME}\" \\\n  --template-body file://main.yaml \\\n  --capabilities CAPABILITY_NAMED_IAM \\\n  --parameters ParameterKey=ImageURL,ParameterValue=\"${IMAGE_URI}\" \\\n  --region \"${AWS_REGION}\"\n```\n- Go to the AWS Console Dashboard navigate to Cloudformation and click on `jenkins-ecs-efs` see the creation process after stack creation is complete go to ecs click on service and click on task access jenkins with the public ip:8080\n# Deploying a Crash Management API with Ansible and Cloudformation and Automation Using Jenkins ECS with Jenkins Ec2 Agent and monitoring through Grafana, Promethues\n### Prerequities\n\n1. Python3 and python-venv\n2.  Ansible\n3. Docker\n4. aws-cli\n\n**Create a Github repository make it public clone to pc and open the project  and To Create directory structure copy and paste the following in your terminal of your project**\n```bash\n mkdir -p ./cloudformation\n mkdir -p ./templates\n mkdir -p ./ansible\n mkdir -p ./ansible/roles\n cd ./ansible/roles\n ansible-galaxy init grafana\n ansible-galaxy init prometheus\n ansible-galaxy init crashapi \n```\n\n#### Setup the infrastructure \n**Create an Elastic IP**\n1. **Step 1**: Log in to the AWS Management Console\n  - Open your web browser and go to AWS Management Console.\n  - Enter your AWS credentials to log in.\n2. **Step 2**: Navigate to the EC2 Dashboard\n  - Once logged in, find the Services menu at the top of the page and click on it.\n  - In the search bar, type EC2 and select EC2 under Compute from the dropdown list.\n  - You will be directed to the EC2 Dashboard.\n3. **Step 3**: Allocate a New Elastic IP\n  - In the EC2 Dashboard, look for the Network \u0026 Security section in the left-hand sidebar.\n  - Click on Elastic IPs.\n  - You will see a page that lists all your current Elastic IPs (if any). Click on the **Allocate -Elastic IP** address button at the top-right corner of the page.\n4. **Step 4**: Leave everything to default click on **Allocate**.\n5. **Step 5**: Click the ip you have just created copy the ip address and allocation id the allocation starts with `eipalloc-XXXXXXXXXXXX`\n\n#### Repeat the process for crash api server to create elastic ip\n\n- create a `cloudformation` folder in your root directory of your project  `mkdir cloudformation` inside cloudformation folder.\n- To create a `main.yaml` inside the cloudformation `touch ./cloudformation/main.yaml`\n- Inside the main.yaml paste the following make sure to replace elastic ip allocation id in cloudformation template\n```yaml\nAWSTemplateFormatVersion: '2010-09-09'\nDescription: 'CloudFormation template for VPC with 3 public and 3 private subnets'\nParameters:\n  ImageURL:\n    Type: String\n    Description: 'Image URL for the ECR repo'\n\n\nResources:\n  VPC:\n    Type: AWS::EC2::VPC\n    Properties:\n      CidrBlock: 10.0.0.0/16\n      EnableDnsHostnames: true\n      EnableDnsSupport: true\n      InstanceTenancy: default\n      Tags:\n        - Key: Name\n          Value: project-vpc\n\n  InternetGateway:\n    Type: AWS::EC2::InternetGateway\n\n  InternetGatewayAttachment:\n    Type: AWS::EC2::VPCGatewayAttachment\n    Properties:\n      VpcId: !Ref VPC\n      InternetGatewayId: !Ref InternetGateway\n\n  PublicSubnet1:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 0, !GetAZs '' ]\n      CidrBlock: 10.0.1.0/24\n      MapPublicIpOnLaunch: true\n      Tags:\n        - Key: Name\n          Value: project-subnet-public1-us-east-1a\n\n  PublicSubnet2:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 1, !GetAZs '' ]\n      CidrBlock: 10.0.2.0/24\n      MapPublicIpOnLaunch: true\n      Tags:\n        - Key: Name\n          Value: project-subnet-public2-us-east-1b\n\n  PublicSubnet3:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 2, !GetAZs '' ]\n      CidrBlock: 10.0.3.0/24\n      MapPublicIpOnLaunch: true\n      Tags:\n        - Key: Name\n          Value: project-subnet-public3-us-east-1c\n\n  PrivateSubnet1:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 0, !GetAZs '' ]\n      CidrBlock: 10.0.4.0/24\n      MapPublicIpOnLaunch: false\n      Tags:\n        - Key: Name\n          Value: project-subnet-private1-us-east-1a\n\n  PrivateSubnet2:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 1, !GetAZs '' ]\n      CidrBlock: 10.0.5.0/24\n      MapPublicIpOnLaunch: false\n      Tags:\n        - Key: Name\n          Value: project-subnet-private2-us-east-1b\n\n  PrivateSubnet3:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: !Select [ 2, !GetAZs '' ]\n      CidrBlock: 10.0.6.0/24\n      MapPublicIpOnLaunch: false\n      Tags:\n        - Key: Name\n          Value: project-subnet-private3-us-east-1c\n\n  PublicRouteTable:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags:\n        - Key: Name\n          Value: project-rtb-public\n\n  DefaultPublicRoute:\n    Type: AWS::EC2::Route\n    DependsOn: InternetGatewayAttachment\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      DestinationCidrBlock: 0.0.0.0/0\n      GatewayId: !Ref InternetGateway\n\n  PublicSubnet1RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      SubnetId: !Ref PublicSubnet1\n\n  PublicSubnet2RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      SubnetId: !Ref PublicSubnet2\n\n  PublicSubnet3RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      SubnetId: !Ref PublicSubnet3\n\n  NATGateway1:\n    Type: AWS::EC2::NatGateway\n    Properties:\n      AllocationId: !GetAtt NATGateway1EIP.AllocationId\n      SubnetId: !Ref PublicSubnet1\n\n  NATGateway1EIP:\n    Type: AWS::EC2::EIP\n    DependsOn: InternetGatewayAttachment\n    Properties:\n      Domain: vpc\n\n  PrivateRouteTable1:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags:\n        - Key: Name\n          Value: project-rtb-private1-us-east-1a\n\n  DefaultPrivateRoute1:\n    Type: AWS::EC2::Route\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable1\n      DestinationCidrBlock: 0.0.0.0/0\n      NatGatewayId: !Ref NATGateway1\n  \n\n\n  PrivateSubnet1RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable1\n      SubnetId: !Ref PrivateSubnet1\n\n  PrivateRouteTable2:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags:\n        - Key: Name\n          Value: project-rtb-private2-us-east-1b\n\n  DefaultPrivateRoute2:\n    Type: AWS::EC2::Route\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable2\n      DestinationCidrBlock: 0.0.0.0/0\n      NatGatewayId: !Ref NATGateway1\n\n  PrivateSubnet2RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable2\n      SubnetId: !Ref PrivateSubnet2\n\n  PrivateRouteTable3:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags:\n        - Key: Name\n          Value: project-rtb-private3-us-east-1c\n\n  DefaultPrivateRoute3:\n    Type: AWS::EC2::Route\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable3\n      DestinationCidrBlock: 0.0.0.0/0\n      NatGatewayId: !Ref NATGateway1\n\n  PrivateSubnet3RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable3\n      SubnetId: !Ref PrivateSubnet3\n  ECSSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: \"ECS Security Group\"\n      VpcId: !Ref VPC\n      SecurityGroupIngress:\n        - IpProtocol: tcp\n          FromPort: 8080\n          ToPort: 8080\n          CidrIp: 0.0.0.0/0\n      SecurityGroupEgress:\n        - IpProtocol: \"-1\"\n          CidrIp: 0.0.0.0/0\n  EFSSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: \"EFS Security Group\"\n      VpcId: !Ref VPC\n      SecurityGroupIngress:\n        - IpProtocol: tcp\n          FromPort: 2049\n          ToPort: 2049\n          SourceSecurityGroupId: !Ref ECSSecurityGroup \n      SecurityGroupEgress:\n        - IpProtocol: \"-1\"\n          CidrIp: 0.0.0.0/0\n\n  # Policy for ECS Task\n  ECSTaskPolicy:\n    Type: AWS::IAM::Policy\n    Properties:\n      PolicyName: !Sub \"ECSExecutionTaskPolicy-${AWS::StackName}\"\n      PolicyDocument:\n        Version: \"2012-10-17\"\n        Statement:\n          - Effect: Allow\n            Action:\n              - ecr:GetAuthorizationToken\n              - ecr:BatchCheckLayerAvailability\n              - ecr:GetDownloadUrlForLayer\n              - ecr:BatchGetImage\n              - logs:CreateLogStream\n              - logs:PutLogEvents\n            Resource: \"*\"\n      Roles:\n        - !Ref ECSEFSmountTaskRole\n\n  # Policy for EFS Mount\n  EFSMountPolicy:\n    Type: AWS::IAM::Policy\n    Properties:\n      PolicyName: !Sub \"ECSMountPolicy-${AWS::StackName}\"\n      PolicyDocument:\n        Version: \"2012-10-17\"\n        Statement:\n          - Sid: AllowDescribe\n            Effect: Allow\n            Action:\n              - elasticfilesystem:DescribeAccessPoints\n              - elasticfilesystem:DescribeFileSystems\n              - elasticfilesystem:DescribeMountTargets\n              - ec2:DescribeAvailabilityZones\n            Resource: \"*\"\n          - Sid: AllowCreateAccessPoint\n            Effect: Allow\n            Action:\n              - elasticfilesystem:*\n            Resource: \"*\"\n      Roles:\n        - !Ref ECSEFSmountTaskRole\n\n  # IAM Role for ECS Tasks\n  ECSEFSmountTaskRole:\n    Type: AWS::IAM::Role\n    Properties:\n      AssumeRolePolicyDocument:\n        Version: \"2012-10-17\"\n        Statement:\n          - Effect: Allow\n            Principal:\n              Service: ecs-tasks.amazonaws.com\n            Action: sts:AssumeRole\n      Policies:\n        - PolicyName: !Sub \"ECSTaskPolicy-${AWS::StackName}\"\n          PolicyDocument:\n            Version: \"2012-10-17\"\n            Statement:\n              - Effect: Allow\n                Action:\n                  - ecr:GetAuthorizationToken\n                  - ecr:BatchCheckLayerAvailability\n                  - ecr:GetDownloadUrlForLayer\n                  - ecr:BatchGetImage\n                  - logs:CreateLogStream\n                  - logs:PutLogEvents\n                Resource: \"*\" \n  EFSSystem:\n    Type: AWS::EFS::FileSystem\n    Properties:\n      Encrypted: true\n      FileSystemTags:\n        - Key: Name\n          Value: JenkinsEFS\n  JenkinsHomeVolume1:\n    Type: AWS::EFS::MountTarget\n    Properties:\n      FileSystemId: !Ref EFSSystem\n      SubnetId: !Ref PublicSubnet1\n      SecurityGroups:\n        - !Ref EFSSecurityGroup\n  JenkinsHomeVolume2:\n    Type: AWS::EFS::MountTarget\n    Properties:\n      FileSystemId: !Ref EFSSystem\n      SubnetId: !Ref PublicSubnet2\n      SecurityGroups:\n        - !Ref EFSSecurityGroup\n  JenkinsHomeVolume3:\n    Type: AWS::EFS::MountTarget\n    Properties:\n      FileSystemId: !Ref EFSSystem\n      SubnetId: !Ref PublicSubnet3\n      SecurityGroups:\n        - !Ref EFSSecurityGroup\n  ECSCluster:\n    Type: AWS::ECS::Cluster\n    Properties:\n      ClusterName: JenkinsCluster\n      CapacityProviders:\n        - FARGATE\n        - FARGATE_SPOT\n      DefaultCapacityProviderStrategy:\n        - CapacityProvider: FARGATE\n          Weight: 1\n        - CapacityProvider: FARGATE_SPOT\n          Weight: 1\n      Configuration:\n        ExecuteCommandConfiguration:\n          Logging: DEFAULT\n  ECSLogGroup: \n    Type: AWS::Logs::LogGroup\n    Properties: \n      LogGroupName: !Sub \"/ecs/test-${AWS::StackName}\"\n      RetentionInDays: 7\n\n  ECSTaskDefinition:\n    Type: AWS::ECS::TaskDefinition\n    Properties:\n      ExecutionRoleArn: !Ref ECSEFSmountTaskRole\n      TaskRoleArn: !Ref ECSEFSmountTaskRole\n      NetworkMode: awsvpc\n      RequiresCompatibilities:\n        - FARGATE\n      RuntimePlatform:\n        OperatingSystemFamily: LINUX\n        CpuArchitecture: ARM64\n      Family: my-jenkins-task-00\n      Cpu: \"1024\"\n      Memory: \"2048\"\n      ContainerDefinitions:\n        - Name: jenkins\n          Image: !Ref ImageURL\n          Cpu: 1024\n          Memory: 2048\n          MemoryReservation: 1024\n          Essential: true\n          PortMappings:\n            - ContainerPort: 8080\n              Protocol: tcp\n          LinuxParameters:\n            InitProcessEnabled: true\n          MountPoints:\n            - SourceVolume: efs-volume\n              ContainerPath: /root/.jenkins\n          LogConfiguration:\n            LogDriver: awslogs\n            Options:\n              mode: non-blocking\n              max-buffer-size: 25m\n              awslogs-group: !Ref ECSLogGroup\n              awslogs-region: us-east-1\n              awslogs-create-group: \"true\"\n              awslogs-stream-prefix: efs-task\n      Volumes:\n        - Name: efs-volume\n          EFSVolumeConfiguration:\n            FilesystemId: !Ref EFSSystem\n            RootDirectory: /\n            TransitEncryption: ENABLED\n\n  ECSService:\n    Type: AWS::ECS::Service\n    Properties:\n      Cluster: !Ref ECSCluster  \n      TaskDefinition: !Ref ECSTaskDefinition\n      LaunchType: FARGATE\n      ServiceName: ebs\n      SchedulingStrategy: REPLICA\n      DesiredCount: 1\n      NetworkConfiguration:\n        AwsvpcConfiguration:\n          AssignPublicIp: ENABLED\n          SecurityGroups: \n            - !Ref ECSSecurityGroup\n          Subnets:\n            - !Ref PublicSubnet1\n            - !Ref PublicSubnet2\n            - !Ref PublicSubnet3\n      PlatformVersion: LATEST\n      DeploymentConfiguration:\n        MaximumPercent: 200\n        MinimumHealthyPercent: 100\n        DeploymentCircuitBreaker:\n          Enable: true\n          Rollback: true\n      DeploymentController:\n        Type: ECS\n      Tags: []\n      EnableECSManagedTags: true\n\nOutputs:\n  VPCID:\n    Description: The ID of the created VPC\n    Value: !Ref VPC\n\n  PublicSubnet1ID:\n    Description: The ID of Public Subnet 1\n    Value: !Ref PublicSubnet1\n\n  PublicSubnet2ID:\n    Description: The ID of Public Subnet 2\n    Value: !Ref PublicSubnet2\n```\n\n\n- **Now we create the individual configuration to setup the server configuration using ansible and we will use ansible roles**\n\n    - Inside your `ansible` create a `ansible.cfg` file `touch ./ansible/ansible.cfg` inside we are disablling the `host_key_checking`\n      ```conf\n       [defaults]\n       host_key_checking = False\n      ```    \n    - Now we create a `inventory` inside `ansible` folder `touch ./ansible/inventory` we are creating the `ansible_user` in inventory file to serve the server dynamically, change the ip addresses for respective server this will be our `elastic ip` \n    ```yaml\n    [grafana]\n    100.29.106.209 ansible_user=ubuntu\n    [prometheus]\n    44.203.140.254 ansible_user=ubuntu \n    [crashapi]\n    44.203.140.254 ansible_user=ubuntu\n    ```\n    - Create `main.yaml` inside `ansible` folder `touch ./ansible/main.yaml`  we are deploying multiple host for `grafana`, `promethues`, `crashapi` paste to following inside `./ansible/main.yaml` \n```yaml\n---\n- hosts: grafana\n  become: true\n  roles:\n    - role: grafana\n    - role: prometheus\n\n\n\n- hosts: crashapi\n  become: true\n \n  tasks:\n    - name: Install CrashAPI\n      include_role:\n        name: crashapi\n```\n\n**1.Paste the following to configure app, node_exporter and nginx in `./ansible/roles/crashapi/tasks/main.yml`**\n\n\n\n```yaml\n---\n- name: Installing the flask app and creating the systemd service\n  import_tasks: install-app.yml\n\n- name: Installing the node exporter and creating the systemd service\n  import_tasks: node_exporter.yml\n\n- name: Install nginx and configure ssl\n  import_tasks: nginx.yml\n```\n\n**2. Create a file  `touch ./ansible/roles/crashapi/tasks/install-app.yml` and Paste the following to configure app in `./ansible/roles/crashapi/tasks/install-app.yml`** in Clone github repository replace with your github repo url\n\n\n```yaml\n- name: Update package lists (on Debian/Ubuntu)\n  apt:\n    update_cache: yes\n\n- name: Install Python3, pip, and venv\n  apt:\n    name: \"{{ item }}\"\n    state: latest\n    update_cache: yes\n  loop: \"{{ packages }}\"\n\n- name: Manually create the initial virtualenv\n  command: python3 -m venv \"{{ venv_dir }}\"\n  args:\n    creates: \"{{ venv_dir }}\"\n\n- name: Clone a GitHub repository\n  git:\n    repo: https://github.com/roeeelnekave/crash-api-application-part-2.git #Replace with your repo url\n    dest: \"{{ app_dir }}\"\n    clone: yes\n    update: yes\n\n- name: Install requirements inside the virtual environment\n  command: \"{{ venv_dir }}/bin/pip install -r {{ app_dir }}/requirements.txt\"\n  become: true\n\n- name: Ensure application directory exists\n  file:\n    path: \"{{ app_dir }}\"\n    state: directory\n    owner: \"{{ user }}\"\n    group: \"{{ group }}\"\n\n- name: Ensure virtual environment directory exists\n  file:\n    path: \"{{ venv_dir }}\"\n    state: directory\n    owner: \"{{ user }}\"\n    group: \"{{ group }}\"\n\n- name: Create systemd service file\n  template:\n    src: crashapi.service.j2\n    dest: /etc/systemd/system/{{ service_name }}.service\n  become: true\n\n- name: Reload systemd to pick up the new service\n  systemd:\n    daemon_reload: yes\n\n- name: Start and enable the Flask app service\n  systemd:\n    name: \"{{ service_name }}\"\n    state: started\n    enabled: yes\n\n- name: Check status of the Flask app service\n  command: systemctl status {{ service_name }}\n  register: service_status\n  ignore_errors: yes\n\n- name: Display service status\n  debug:\n    msg: \"{{ service_status.stdout_lines }}\"\n```\n\n\n**3. Create a file  `touch ./ansible/roles/crashapi/tasks/node_exporter.yml` and Paste the following to configure node_exporter in `./ansible/roles/crashapi/tasks/node_exporter.yml`**\n\n\n```yaml\n- name: Download Node Exporter binary\n  get_url:\n    url: https://github.com/prometheus/node_exporter/releases/download/v1.0.1/node_exporter-1.0.1.linux-amd64.tar.gz\n    dest: /tmp/node_exporter-1.0.1.linux-amd64.tar.gz\n\n- name: Create Node Exporter group\n  group:\n    name: node_exporter\n    state: present\n\n- name: Create Node Exporter user\n  user:\n    name: node_exporter\n    group: node_exporter\n    shell: /sbin/nologin\n    create_home: no\n\n- name: Create Node Exporter directory\n  file:\n    path: /etc/node_exporter\n    state: directory\n    owner: node_exporter\n    group: node_exporter\n\n- name: Unpack Node Exporter binary\n  unarchive:\n    src: /tmp/node_exporter-1.0.1.linux-amd64.tar.gz\n    dest: /tmp/\n    remote_src: yes\n\n- name: Remove the Node Exporter binary if it exists\n  file:\n    path: /usr/bin/node_exporter\n    state: absent\n\n- name: Install Node Exporter binary\n  copy:\n    src: \"/tmp/node_exporter-1.0.1.linux-amd64/node_exporter\"\n    dest: /usr/bin/node_exporter\n    owner: node_exporter\n    group: node_exporter\n    mode: '0755'\n    remote_src: yes\n  become: true\n\n- name: Create Node Exporter service file\n  template:\n    src: nodeexporter.service.j2\n    dest: /usr/lib/systemd/system/node_exporter.service\n  become: true\n\n- name: Reload systemd\n  systemd:\n    daemon_reload: yes\n\n- name: Start Node Exporter service\n  systemd:\n    name: node_exporter\n    state: started\n    enabled: yes\n\n- name: Clean up\n  file:\n    path: /tmp/node_exporter-1.0.1.linux-amd64.tar.gz\n    state: absent\n  when: clean_up is defined and clean_up\n```\n\n**4. Create a file  `touch ./ansible/roles/crashapi/tasks/nginx.yml` and Paste the following to configure nginx in `./ansible/roles/crashapi/tasks/nginx.yml`**\n\n\n```yaml\n---\n- name: Update the apt package index\n  apt:\n    update_cache: yes\n\n- name: Install Nginx and certbot\n  apt:\n    name:\n      - nginx\n      - certbot\n      - python3-certbot-nginx\n    state: present\n\n- name: Remove nginx default configuration\n  file:\n    path: /etc/nginx/sites-enabled/default\n    state: absent\n\n- name: Copy Nginx configuration\n  template:\n    src: app.conf.j2\n    dest: /etc/nginx/sites-available/crash-api.conf\n\n- name: Enable Nginx configuration for Crash-api\n  file:\n    src: /etc/nginx/sites-available/crash-api.conf\n    dest: /etc/nginx/sites-enabled/crash-api.conf\n    state: link\n  become: true\n\n- name: Test Nginx configuration\n  command: nginx -t\n  become: true\n\n- name: Restart Nginx\n  service:\n    name: nginx\n    state: restarted\n  become: true\n\n- name: Obtain SSL certificate\n  shell: certbot --nginx -d {{ crashapi_domain_name}} --non-interactive --agree-tos --email {{ email_user }}\n  become: true\n```\n\n**5. Create a varaiable to load on our files `./ansible/roles/crashapi/vars/main.yml` and paste the following in this file**\n\n\n```yaml\n---\n# vars file for crashapi\napp_dir: /home/ubuntu/flask-crash-api\nvenv_dir: /home/ubuntu/flaskenv\ngunicorn_config: /home/ubuntu/flask-crash-api/gunicorn.py\nservice_name: myflaskapp\nuser: ubuntu\ngroup: ubuntu\npackages:\n  - python3\n  - python3-pip\n  - python3-venv\ncrashapi_domain_name: \"crashapi.example.com\"\nemail_user: example@example.com\n```\n\n**6. To create a nginx config for app, create the   `touch ./ansible/roles/crashapi/templates/app.conf.j2` and paste the following in `./ansible/roles/crashapi/templates/app.conf.j2`**\n\n\n```jinja\nserver {\n  listen 80;\n  server_name {{ crashapi_domain_name }}; \n\n  location / {\n    proxy_pass http://localhost:5000;  # Forward requests to Flask app\n    proxy_set_header Host $host;\n    proxy_set_header X-Real-IP $remote_addr;\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_set_header X-Forwarded-Proto $scheme;\n  }\n}\n```\n**7. To create a systemd service for Gunicorn, create the   `touch ./ansible/roles/crashapi/templates/crashapi.service.j2` and paste the following in `./ansible/roles/crashapi/templates/crashapi.service.j2`**\n\n```bash\n[Unit]\nDescription=Gunicorn instance to serve myflaskapp\nAfter=network.target\n\n[Service]\nUser={{ user }}\nGroup={{ group }}\nWorkingDirectory={{ app_dir }}\nExecStart={{ venv_dir }}/bin/gunicorn -c {{ gunicorn_config }} app:app\n\n[Install]\nWantedBy=multi-user.target\n```\n**8. To create a systemd service for Node Exporter, create the   `touch ./ansible/roles/crashapi/templates/nodeexporter.service.j2` and paste the following in `./ansible/roles/crashapi/templates/nodeexporter.service.j2`**\n\n\n```bash\n[Unit]\nDescription=Node Exporter\nDocumentation=https://prometheus.io/docs/guides/node-exporter/\nWants=network-online.target\nAfter=network-online.target\n\n[Service]\nUser=node_exporter\nGroup=node_exporter\nType=simple\nRestart=on-failure\nExecStart=/usr/bin/node_exporter \\\n  --web.listen-address=:9200\n\n[Install]\nWantedBy=multi-user.target\n```\n\n### Now let's configure instance for the grafana\n\n1. **to configure grafana paste the following in this file `./ansible/roles/grafana/tasks/main.yml`**\n\n\n\n```yaml\n---\n\n- name: Update Packages\n  apt:\n    update_cache: yes\n  tags: packages\n\n- name: Install Packages\n  apt:\n    name: \"{{ item }}\"\n    state: present\n  loop: \"{{ packages }}\"\n  tags: packages\n\n- name: Ensure /etc/apt/keyrings/ directory exists\n  file:\n    path: /etc/apt/keyrings/\n    state: directory\n    mode: '0755'\n  become: true\n  tags: create_directory\n\n- name: Download Grafana GPG key\n  ansible.builtin.get_url:\n    url: https://apt.grafana.com/gpg.key\n    dest: /tmp/grafana.gpg.key\n  tags: download_gpg_key\n\n- name: Convert Grafana GPG key to binary format\n  ansible.builtin.command: |\n    gpg --dearmor -o /etc/apt/keyrings/grafana.gpg /tmp/grafana.gpg.key\n  become: true\n  tags: dearmor_gpg_key\n\n- name: Clean up temporary GPG key file\n  ansible.builtin.file:\n    path: /tmp/grafana.gpg.key\n    state: absent\n  tags: cleanup_gpg_key\n\n- name: Add Grafana stable repository\n  ansible.builtin.lineinfile:\n    path: /etc/apt/sources.list.d/grafana.list\n    line: 'deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main'\n    create: yes\n  become: true\n  tags: add_stable_repo\n\n- name: Add Grafana beta repository (optional)\n  ansible.builtin.lineinfile:\n    path: /etc/apt/sources.list.d/grafana.list\n    line: 'deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com beta main'\n    create: yes\n  become: true\n  tags: add_beta_repo\n\n- name: Update the list of available packages\n  ansible.builtin.apt:\n    update_cache: yes\n  become: true\n  tags: update_package_list\n\n- name: Install grafana\n  apt:\n    name: \"{{ item }}\"\n    state: present\n  loop: \"{{ grafana }}\"\n  tags: grafana\n\n- name: Ensure Grafana server is enabled and started\n  ansible.builtin.systemd:\n    name: grafana-server\n    enabled: yes\n    state: started\n  become: true\n  tags: grafana_server\n\n- name: Check Grafana server status\n  ansible.builtin.systemd:\n    name: grafana-server\n    state: started\n  register: grafana_status\n  become: true\n  tags: check_grafana_status\n\n- name: Display Grafana server status\n  ansible.builtin.debug:\n    var: grafana_status\n  tags: display_grafana_status\n\n- name: Remove default Nginx configuration\n  file:\n    path: /etc/nginx/sites-enabled/default\n    state: absent\n  become: true\n  tags: remove_default_nginx_config\n\n- name: Deploy Grafana Nginx configuration\n  template:\n    src: grafana.conf.j2\n    dest: /etc/nginx/sites-available/grafana.conf\n\n- name: Enable Grafana Nginx configuration\n  file:\n    src: /etc/nginx/sites-available/grafana.conf\n    dest: /etc/nginx/sites-enabled/grafana.conf\n    state: link\n  become: true\n  tags: enable_grafana_nginx_config\n\n- name: Test Nginx configuration\n  command: nginx -t\n  become: true\n  tags: test_nginx_config\n\n- name: Restart Nginx\n  service:\n    name: nginx\n    state: restarted\n  become: true\n  tags: restart_nginx\n\n- name: Obtain SSL certificates with Certbot\n  command: certbot --nginx -d {{ grafana_domain_name }} --non-interactive --agree-tos --email {{ user_email }}\n  register: certbot_result\n  ignore_errors: true\n  become: true\n```\n\n2. **To set the variables paste the following in `./ansible/roles/grafana/vars/main.yml`**\n\n\n```yaml\n---\n\npackages:\n  - apt-transport-https\n  - software-properties-common\n  - wget\n  - nginx\n  - certbot\n  - python3-certbot-nginx\n\ngrafana:\n  - grafana\n  - grafana-enterprise\n\ngrafana_domain_name: \"grafana.example.com\"\nemail: \"example@example.com\"\n```\n\n3. **To configure the grafana with nginx create the file `touch ./ansible/roles/grafana/templates/grafana.conf.j2` and paste the following in `./ansible/roles/grafana/templates/grafana.conf.j2`**\n\n```conf\nserver {\n    listen 80;\n    server_name {{ grafana_domain_name }};  # Replace with your domain or IP address\n\n    location / {\n        proxy_pass http://localhost:3000;  # Forward requests to Grafana\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n\n    # Optional: Handle WebSocket connections for Grafana Live\n    location /api/live/ {\n        proxy_pass http://localhost:3000/api/live/;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \"upgrade\";\n        proxy_set_header Host $host;\n    }\n}\n```\n\n### Configure the Promethues server with ansible\n\n1.**Paste the following in `./ansible/roles/prometheus/tasks/main.yml`**\n\n```yaml\n---\n- name: Update system packages\n  apt:\n    update_cache: yes\n\n- name: Create a system group for Prometheus\n  group:\n    name: \"{{ prometheus_group }}\"\n    system: yes\n\n- name: Create a system user for Prometheus\n  user:\n    name: \"{{ prometheus_user }}\"\n    shell: /sbin/nologin\n    system: yes\n    group: \"{{ prometheus_group }}\"\n\n- name: Create directories for Prometheus\n  file:\n    path: \"{{ item }}\"\n    state: directory\n    owner: \"{{ prometheus_user }}\"\n    group: \"{{ prometheus_group }}\"\n  loop:\n    - \"{{ prometheus_config_dir }}\"\n    - \"{{ prometheus_data_dir }}\"\n\n- name: Download Prometheus\n  get_url:\n    url: \"https://github.com/prometheus/prometheus/releases/download/v{{ prometheus_version }}/prometheus-{{ prometheus_version }}.linux-amd64.tar.gz\"\n    dest: /tmp/prometheus.tar.gz\n\n- name: Extract Prometheus\n  unarchive:\n    src: /tmp/prometheus.tar.gz\n    dest: /tmp/\n    remote_src: yes\n\n- name: Move Prometheus binaries\n  command: mv /tmp/prometheus-{{ prometheus_version }}.linux-amd64/{{ item }} \"{{ prometheus_install_dir }}/\"\n  loop:\n    - prometheus\n    - promtool\n\n- name: Remove existing console_libraries directory\n  file:\n    path: \"{{ prometheus_config_dir }}/console_libraries\"\n    state: absent\n    \n- name: Remove existing console directory\n  file:\n    path: \"{{ prometheus_config_dir }}/consoles\"\n    state: absent\n\n- name: Remove existing prometheus.yml file\n  file:\n    path: \"{{ prometheus_config_dir }}/prometheus.yml\"\n    state: absent\n\n- name: Move configuration files\n  command: mv /tmp/prometheus-{{ prometheus_version }}.linux-amd64/{{ item }} \"{{ prometheus_config_dir }}/\"\n  loop:\n    - prometheus.yml\n    - consoles\n    - console_libraries\n\n\n- name: Set ownership for configuration files\n  file:\n    path: \"{{ prometheus_config_dir }}/{{ item }}\"\n    owner: \"{{ prometheus_user }}\"\n    group: \"{{ prometheus_group }}\"\n    state: directory\n  loop:\n    - consoles\n    - console_libraries\n\n- name: Create Prometheus systemd service file\n  template:\n    src: prometheus.service.j2\n    dest: /etc/systemd/system/prometheus.service\n  become: true\n\n- name: Reload systemd\n  command: systemctl daemon-reload\n  become: true\n\n- name: Enable and start Prometheus service\n  systemd:\n    name: prometheus\n    enabled: yes\n    state: started\n  become: true \n```\n2. **To set default varaibles paste the following in `./ansible/roles/prometheus/defaults/main.yml` don't forget to replace `crash_api_ip` with your application server ip**\n```yaml\n---\nprometheus_version: \"2.54.0\"\nprometheus_user: \"prometheus\"\nprometheus_group: \"prometheus\"\nprometheus_install_dir: \"/usr/local/bin\"\nprometheus_config_dir: \"/etc/prometheus\"\nprometheus_data_dir: \"/var/lib/prometheus\"\ncrash_api_ip: \"127.0.0.1\"\n```\n3. **To create a services for systemd create a file in  `touch ./ansible/roles/prometheus/templates/promethues.service.j2` and paste the following `./ansible/roles/prometheus/templates/promethues.service.j2`**\n```bash\n[Unit]\nDescription=Prometheus\nWants=network-online.target\nAfter=network-online.target\n\n[Service]\nUser={{ prometheus_user }}\nGroup={{ prometheus_group }}\nType=simple\nExecStart={{ prometheus_install_dir }}/prometheus \\\n  --config.file {{ prometheus_config_dir }}/prometheus.yml \\\n  --storage.tsdb.path {{ prometheus_data_dir }} \\\n  --web.console.templates={{ prometheus_config_dir }}/consoles \\\n  --web.console.libraries={{ prometheus_config_dir }}/console_libraries\n\n[Install]\nWantedBy=multi-user.target\n\n```\n4. **To create a promethues configuration create a file in  `touch ./ansible/roles/prometheus/templates/promethues.yml.j2` and paste the following `./ansible/roles/prometheus/templates/promethues.yml.j2`**\n\n```yaml\nglobal:\n  scrape_interval: 15s\n\nscrape_configs:\n  - job_name: 'prometheus'\n    static_configs:\n      - targets: ['localhost:9090']\n  - job_name: 'crash-api'\n    static_configs:\n      - targets: ['{{ crash_api_ip }}:9100']\n```\n\n### Now let's create the python app\n1. **Create `./app.py` in root directory of your project and paste the following**\n```python\nfrom flask import Flask, request, render_template, jsonify, redirect\nimport requests\n\napp = Flask(__name__)\n\n# Route for the input form\n@app.route('/', methods=['GET', 'POST'])\ndef index():\n    if request.method == 'POST':\n        state_case = request.form['stateCase']\n        case_year = request.form['caseYear']\n        state = request.form['state']\n        return redirect(f'/results?stateCase={state_case}\u0026caseYear={case_year}\u0026state={state}')\n    return render_template('index.html')\n\n# Route for displaying results\n@app.route('/results')\ndef results():\n    state_case = request.args.get('stateCase')\n    case_year = request.args.get('caseYear')\n    state = request.args.get('state')\n    \n    # Call the NHTSA Crash API\n    url = f\"https://crashviewer.nhtsa.dot.gov/CrashAPI/crashes/GetCaseDetails?stateCase={state_case}\u0026caseYear={case_year}\u0026state={state}\u0026format=json\"\n    response = requests.get(url)\n    \n    if response.status_code != 200:\n        return render_template('results.html', data={\"error\": \"Failed to retrieve data from the API.\"})\n\n    data = response.json()  # Assuming the API returns JSON data\n\n    return render_template('results.html', data=data)\n\n# API endpoint for cURL\n@app.route('/api/crashdata', methods=['GET'])\ndef api_crashdata():\n    state_case = request.args.get('stateCase')\n    case_year = request.args.get('caseYear')\n    state = request.args.get('state')\n    \n    # Call the NHTSA Crash API\n    url = f\"https://crashviewer.nhtsa.dot.gov/CrashAPI/crashes/GetCaseDetails?stateCase={state_case}\u0026caseYear={case_year}\u0026state={state}\u0026format=json\"\n    response = requests.get(url)\n    \n    if response.status_code != 200:\n        return jsonify({\"error\": \"Failed to retrieve data from the API.\"}), response.status_code\n\n    data = response.json()\n\n    return jsonify(data)\n\nif __name__ == '__main__':\n    app.run(debug=True)\n```\n2. **Create `./guniucorn.py` and paste following**:\n```python\nbind = \"0.0.0.0:5000\"\nworkers = 2\n```\n3. **Create a html to load template `touch ./templates/index.html` and paste the following `./templates/index.html`**\n\n```html\n        \u003c!DOCTYPE html\u003e\n        \u003chtml lang=\"en\"\u003e\n        \u003chead\u003e\n            \u003cmeta charset=\"UTF-8\"\u003e\n            \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\n            \u003ctitle\u003eCrash Data Input\u003c/title\u003e\n        \u003c/head\u003e\n        \u003cbody\u003e\n            \u003ch1\u003eEnter Crash Data Parameters\u003c/h1\u003e\n            \u003cform method=\"POST\"\u003e\n                \u003clabel for=\"stateCase\"\u003eState Case:\u003c/label\u003e\n                \u003cinput type=\"text\" id=\"stateCase\" name=\"stateCase\" required\u003e\n                \n                \u003clabel for=\"caseYear\"\u003eCase Year:\u003c/label\u003e\n                \u003cinput type=\"text\" id=\"caseYear\" name=\"caseYear\" required\u003e\n                \n                \u003clabel for=\"state\"\u003eState:\u003c/label\u003e\n                \u003cinput type=\"text\" id=\"state\" name=\"state\" required\u003e\n                \n                \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n            \u003c/form\u003e\n        \u003c/body\u003e\n        \u003c/html\u003e\n```\n4.  **Create a html to load template `touch ./templates/results.html` and paste the following `./templates/results.html`**\n```html\n        \u003c!DOCTYPE html\u003e\n        \u003chtml lang=\"en\"\u003e\n        \u003chead\u003e\n            \u003cmeta charset=\"UTF-8\"\u003e\n            \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\n            \u003ctitle\u003eCrash Data Results\u003c/title\u003e\n        \u003c/head\u003e\n        \u003cbody\u003e\n            \u003ch1\u003eCrash Data Results\u003c/h1\u003e\n            \u003cpre\u003e{{ data | tojson(indent=2) }}\u003c/pre\u003e\n            \u003ca href=\"/\"\u003eGo Back\u003c/a\u003e\n        \u003c/body\u003e\n        \u003c/html\u003e\n```\n5. **Create a `./requirements.txt` file and paste the following**\n```bash\nblinker==1.8.2\ncertifi==2024.7.4\ncharset-normalizer==3.3.2\nclick==8.1.7\nFlask==3.0.3\nidna==3.7\nitsdangerous==2.2.0\nJinja2==3.1.4\nMarkupSafe==2.1.5\nrequests==2.32.3\nurllib3==2.2.2\nWerkzeug==3.0.3\ngunicorn\n```\n### Now create a jenkins pipeline script \n1. Create a `./Jenkinsfile` inside the root directory of the project\n\n```groovy\npipeline {\n    agent any\n    \n    parameters {\n        string(name: 'grafana_domain_name', defaultValue: 'grafana.example.com', description: 'Grafana domain name')\n        string(name: 'crashapi_domain_name', defaultValue: 'api.example.com', description: 'Crash API domain name')\n        string(name: 'email_user', defaultValue: 'user@example.com', description: 'Email user')\n        string(name: 'ssh_credentials_id', defaultValue: 'your-credential-id', description: 'ID of the SSH private key credential')\n    }\n    \n    stages {\n        stage(\"Deploy Main CloudFormation\") {\n            steps {\n                script {\n                    // Set AWS credentials\n                    withCredentials([string(credentialsId: 'aws_access_key_id', variable: 'aws_access_key_id'), \n                                     string(credentialsId: 'aws_secret_access_key', variable: 'aws_secret_access_key')]) {\n                        sh '''\n                            aws configure set aws_access_key_id $aws_access_key_id\n                            aws configure set aws_secret_access_key $aws_secret_access_key\n                            aws configure set default.region us-east-1\n                        '''\n                    }\n\n                    // Fetch existing CloudFormation stack outputs\n                    def output1 = sh(script: 'aws cloudformation describe-stacks --stack-name jenkins-efs-ecs-1 --query \"Stacks[0].Outputs\"', returnStdout: true).trim()\n                    def jsonOutput1 = readJSON(text: output1)\n\n                    // Extract parameters from the stack outputs\n                    def VPCID = jsonOutput1.find { it.OutputKey == 'VPCID' }.OutputValue\n                    def PublicSubnet1 = jsonOutput1.find { it.OutputKey == 'PublicSubnet1ID' }.OutputValue\n                    def PublicSubnet2 = jsonOutput1.find { it.OutputKey == 'PublicSubnet2ID' }.OutputValue\n\n                    // Run AWS CloudFormation create-stack command\n                    def createStack = sh(\n                        script: \"\"\"\n                            aws cloudformation create-stack --stack-name grafanaPrometheus --template-body file://cloudformation/main.yaml \\\n                            --parameters ParameterKey=VPCID,ParameterValue=${VPCID} \\\n                            ParameterKey=PublicSubnet1,ParameterValue=${PublicSubnet1} \\\n                            ParameterKey=PublicSubnet2,ParameterValue=${PublicSubnet2}\n                        \"\"\",\n                        returnStatus: true\n                    )\n\n                    // Check if CloudFormation stack creation was successful\n                    if (createStack == 0) {\n                        echo \"CloudFormation stack creation started successfully.\"\n\n                        Wait for the stack creation to complete\n                        def waitForStack = sh(\n                            script: 'aws cloudformation wait stack-create-complete --stack-name grafanaPrometheus',\n                            returnStatus: true\n                        )\n\n                        Check if waiting for stack creation was successful\n                        if (waitForStack == 0) {\n                            echo \"CloudFormation stack creation completed successfully.\"\n\n                            // Retrieve public IPs of EC2 instances from CloudFormation outputs\n                            def output = sh(script: 'aws cloudformation describe-stacks --stack-name grafanaPrometheus --query \"Stacks[0].Outputs\"', returnStdout: true).trim()\n                            def jsonOutput = readJSON(text: output)\n\n                            // Extract IPs from outputs\n                            def grafanaIp = jsonOutput.find { it.OutputKey == 'GrafanaPublicIP' }.OutputValue\n                            def crashApiIp = jsonOutput.find { it.OutputKey == 'CrashAppPublicIP' }.OutputValue\n\n                            // Create Ansible inventory content\n                            def inventoryContent = \"\"\"\n[grafana]\n${grafanaIp} ansible_user=ubuntu\n\n[crashapi]\n${crashApiIp} ansible_user=ubuntu\n\"\"\"                          \n                            // Write inventory to a file\n                            writeFile file: 'ansible/inventory', text: inventoryContent\n\n                        } else {\n                            error \"Failed to wait for CloudFormation stack creation to complete.\"\n                        }\n                    } else {\n                        error \"Failed to create CloudFormation stack.\"\n                    }\n                }\n            }\n        }\n        \n       stage(\"Deploy Grafana, Prometheus, and Crash API Server\") {\n    steps {\n        dir('ansible') {\n            withCredentials([sshUserPrivateKey(credentialsId: 'TestKey', keyFileVariable: 'TestKey')]) {\n                // Display inventory\n                script {\n                    def output = sh(script: 'aws cloudformation describe-stacks --stack-name grafanaPrometheus --query \"Stacks[0].Outputs\"', returnStdout: true).trim()\n                    def jsonOutput = readJSON(text: output)\n                    def crashApiIp = jsonOutput.find { it.OutputKey == 'CrashAppPublicIP' }.OutputValue\n                \n                sh \"cat inventory\"\n                // Save private key\n                sh \"echo ${TestKey} \u003e key.pem\"\n                sh \"chmod 400 key.pem\"\n                sh \"cat key.pem\"\n                // Run Ansible playbook\n                sh \"\"\"\n                    ansible-playbook -i inventory --private-key ${TestKey} \\\n                    --extra-vars 'crash_api_ip=${crashApiIp} grafana_domain_name=${params.grafana_domain_name} efs_id=fs-0952230233c19bafa crashapi_domain_name=${params.crashapi_domain_name} email_user=${params.email_user}' \\\n                    main.yaml\n                \"\"\"\n                }\n            }\n        }\n    }\n}\n    }\n}\n```\n## Do a git push\n```bash\ngit add .\ngit commit -m \"Adding the required files\"\ngit push\n```\n### Running the pipeline \n\n\n\n\n### Steps Jenkins CI \n\n\n\n\n\n#### Steps to Create Access Key and Secret Key\n1. Sign in to the AWS Management Console:\n2. Go to the AWS Management Console at https://aws.amazon.com/console/.\n3. Enter your account credentials to log in.\n4. Navigate to IAM:\n5. In the AWS Management Console, search for \"IAM\" in the services search bar and select IAM.\n6. Select Users:\n7. In the IAM dashboard, click on Users in the left navigation pane.\n8. Choose the User:\n9. Click on the name of the user for whom you want to create access keys. If you need to create a a new user, click on Add user, enter a username, and select Programmatic access.\n10. Access Security Credentials:\n11. After selecting the user, click on the Security credentials tab.\n12. Create Access Key:\n    - In the Access keys section, click on Create access key.\n    - If the button is disabled, it means the user already has two active access keys, and you will need to delete one before creating a new one.\n13. Configure Access Key:\n   - You will be directed to a page that provides options for creating the access key. You can optionally add a description to help identify the key later.\n14. Click on Create access key.\n15. Retrieve Access Key:\n   - After the access key is created, you will see the Access key ID and Secret access key.\n**Important: This is your only opportunity to view or download the secret access key. Click Show to reveal it or choose to Download .csv file to save it securely.**\n16. Secure Your Keys:\n    - Store the access key ID and secret access key in a secure location. Do not share these keys publicly or hard-code them into your applications.\n17. Complete the Process:\n    - After saving your keys, click Done to finish the process.\n**Important Notes**\n**Access Key ID: This is a public identifier and can be shared.**\n**Secret Access Key: This should be kept confidential and secure. If you lose it, you must create a new access key.**\n**You can have a maximum of two access keys per IAM user. If you need more, deactivate or delete existing keys.**\n\n## Again in AWS console\n1. Navigate to Ec2 Dashboard\n2. Click on key pairs.\n3. Create a Key pair give it a Name TestKey and Click on Create.\n4. Save it Downloads folder\n# Go to Jenkins DashBoard\n1. Go to manage jenkins -\u003e Credentials -\u003e Under *Stores scoped to Jenkins* click on **System** -\u003e Global credentials (unrestricted) -\u003e Add Credentials\n2. On **Kind** select as `SSH username with private key`\n3. On **ID** give it a unique name like `TestKey`\n4. On **Description** give it a Description like `key agent to deploy grafana, promethues and application in aws`.\n5. On **Username** give it the server username in our case it's `ubuntu`\n6. On **Private Key** section select `Enter Directly` under key click `Add` and copy the contents of the keypair `TestKey.pem` and paste it there then click on **Create** \n\n 7. Again Go to manage jenkins -\u003e Credentials -\u003e Under *Stores scoped to Jenkins* click on **System** -\u003e Global credentials (unrestricted) -\u003e Add Credentials \n 8. Kind select `Secret Text`\n 9. ID `aws_access_key_id` and on secret paste the value of access key from the aws that you have just created.\n 10. Description `access key for  aws` then click on **Create** \n11. Again Go to manage jenkins -\u003e Credentials -\u003e Under *Stores scoped to Jenkins* click on **System** -\u003e Global credentials (unrestricted) -\u003e Add Credentials \n 12. Kind select `Secret Text`\n 9. ID `aws_secret_access_key` and on secret paste the value of secret key from the aws that you have just created.\n 10. Description `secret key for  aws` then click on **Create** \n\n**Now lets create a pipeline to deploy our application**\n\n1. Go to Jenkins DashBoard\n2. Click on **+ New Item**\n3. Give it a name like `server-deployment`\n4. Scroll Down to **Pipeline** select `Definition` as **Pipeline script from SCM**.\n5. On **SCM** select **Git** on  **Repositories** give your repository url from github.\n6. Under **Branches to build** in `Branch Specifier (blank for 'any')` edit that as `main` then Click on **Save**.\n7. Click on **Build Now**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froeeelnekave%2Fcrash-api-application-part-2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Froeeelnekave%2Fcrash-api-application-part-2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froeeelnekave%2Fcrash-api-application-part-2/lists"}