├── iac ├── serverless ├── aws │ ├── sam │ │ ├── my-app │ │ │ ├── .npmignore │ │ │ ├── package.json │ │ │ ├── tests │ │ │ │ └── unit │ │ │ │ │ └── test-handler.js │ │ │ └── app.js │ │ ├── samconfig.toml │ │ ├── README.md │ │ ├── events │ │ │ └── event.json │ │ ├── .gitignore │ │ └── template.yaml │ ├── terraform │ │ └── creating-custom-vpc │ │ │ ├── 002.internet.gateway.tf │ │ │ ├── README.md │ │ │ ├── terraform.tfstate.backup │ │ │ ├── 001.vpc.tf │ │ │ ├── 004.nat.tf │ │ │ ├── 000.provider.tf │ │ │ ├── .terraform.lock.hcl │ │ │ ├── 003.subnets.tf │ │ │ ├── 005.routes.tf │ │ │ └── terraform.tfstate │ ├── eks │ │ ├── keda │ │ │ ├── 001-create-eks.yml │ │ │ ├── 002-my-nginx-deploy.yml │ │ │ ├── scalers │ │ │ │ └── datadog │ │ │ │ │ ├── 001-datadog-triggerauth.yml │ │ │ │ │ ├── 002-datadog-scaledobject.yml │ │ │ │ │ ├── 004-fake-traffic.yml │ │ │ │ │ └── 003-datadog-nginx-deploy.yml │ │ │ └── 003-sqs-scaler.yml │ │ ├── amp │ │ │ ├── 000-eks-cluster-ng-t3-medium.yaml │ │ │ ├── PermissionPolicyIngest.json │ │ │ ├── PermissionPolicyQuery.json │ │ │ ├── TrustPolicy.json │ │ │ ├── my_prometheus_values_yaml │ │ │ ├── amp_ingest_override_values.yaml │ │ │ ├── createIRSA-AMPQuery.sh │ │ │ └── createIRSA-AMPIngest.sh │ │ └── launch-template-demo │ │ │ └── 000-eks-cluster-mng-t3medium.yaml │ ├── container-insights │ │ ├── 001-create-cluster.yaml │ │ └── 002-container-insights.sh │ └── ecs │ │ └── 001-ecs-cluster-creation.yaml ├── demo │ ├── acd-srilanka2025 │ │ ├── .gitignore │ │ ├── requirements.txt │ │ └── my_agent.py │ ├── zerotrust │ │ ├── vault-acl-policy.hcl │ │ ├── hashitalks.png │ │ ├── Dockerfile │ │ ├── vault-aws-config.hcl │ │ └── vault-s3-uploader.yaml │ ├── textract │ │ ├── lambda_function.zip │ │ ├── sqs_to_csv_lambda.zip │ │ ├── outputs.tf │ │ ├── Makefile │ │ ├── variables.tf │ │ ├── sqs_to_csv_lambda.py │ │ ├── lambda_function.py │ │ └── main.tf │ ├── kyverno │ │ ├── 0-development-namespace.yml │ │ ├── 3.2-sample-namespace.yml │ │ ├── 4.2-sample-job.yml │ │ ├── 1.2-sample-app-invalid-image-source.yml │ │ ├── 1.3-sample-app-valid-image-source.yml │ │ ├── 2.1-kyverno-clusterpolicy-mutate-label.yml │ │ ├── 1.1-kyverno-policy-image-source.yml │ │ ├── 4.1-kyverno-policy-cleanup.yml │ │ └── 3.1-kyverno-policy-generate-configmap.yml │ ├── keda │ │ ├── 001-create-eks.yml │ │ ├── 002-my-nginx-deploy.yml │ │ └── 003-sqs-scaler.yml │ └── kyvernocon │ │ ├── mistake1_blocking_all_workloads.yaml │ │ ├── tests │ │ └── kyverno-test.yaml │ │ ├── solution3_bad_patching.yaml │ │ ├── solution1_blocking_all_workloads.yaml │ │ ├── mistake3_bad_patching.yaml │ │ ├── solution2_infinite_policy_loops.yaml │ │ └── mistake2_infinite_policy_loops.yaml └── k8s │ ├── keda │ ├── hpa-demo │ │ ├── iaac │ │ │ ├── requirements.txt │ │ │ ├── 002_lb_svc.yaml │ │ │ ├── nodeport_svc.yaml │ │ │ ├── 003_hpa.yaml │ │ │ ├── app.py │ │ │ ├── Dockerfile │ │ │ └── 001_my_app.yaml │ │ ├── load_test.sh │ │ └── 000_hpa_demo.yaml │ ├── dashboard │ │ ├── dashboard-sa.yaml │ │ ├── dashboard-secret.yaml │ │ └── dashboard-cluster-role-binding.yaml │ └── keda-demo │ │ ├── pod-identity │ │ ├── 001-service-account.yaml │ │ ├── 001.yaml │ │ └── 002.yaml │ │ ├── app │ │ ├── app.py │ │ └── Dockerfile │ │ ├── send_messages.sh │ │ └── secrets │ │ └── 001.yaml │ ├── fluentd │ ├── 002-service-account.yaml │ ├── 001-cluster-role.yaml │ ├── 003-cluster-rolebinding.yaml │ ├── 000-eks-cluster-ng-t2micro.yaml │ ├── 005-daemonset-cloudwatch.yaml │ └── 004-config-map.yaml │ ├── iam-roles-with-k8s-service-account │ ├── my-sa.yaml │ └── my-pod.yaml │ ├── alb-demo │ ├── nginx-service.yaml │ ├── nginx-pod.yaml │ ├── mydocker.yaml │ ├── nginx-deployment.yaml │ ├── nginx-service-2.yaml │ ├── mydocker-svc.yaml │ ├── alb-ingress-controller.yaml │ ├── nginx-service-bkp.yaml │ ├── rbac-role.yaml │ ├── nginx-ingress.yaml │ └── alb-ingress-iam-policy.json │ ├── eks-create │ ├── myapp-a-svc.yaml │ ├── nginx-pod.yaml │ ├── eksctl-containerd.yaml │ ├── trust.json │ ├── ingress.yaml │ ├── AWS_WEB_IDENTITY_TOKEN_FILE │ ├── rbac-role-alb-ingress-controller.yaml │ ├── alb-ingress-controller.yaml │ └── iam-policy.json │ ├── falco │ ├── custom_falco_rules.yaml │ ├── test-pod.yaml │ └── README.md │ ├── audit-policy │ └── audit-policy.yaml │ ├── sidecar.yaml │ └── cluster-gcp │ ├── install_worker.sh │ └── install_master.sh ├── .gitignore ├── webapp ├── Dockerfile ├── index.js └── package.json ├── .github └── workflows │ └── aws.yml └── README.md /iac/serverless: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iac/aws/sam/my-app/.npmignore: -------------------------------------------------------------------------------- 1 | tests/* 2 | -------------------------------------------------------------------------------- /iac/demo/acd-srilanka2025/.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /iac/k8s/keda/hpa-demo/iaac/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn 3 | -------------------------------------------------------------------------------- /iac/demo/zerotrust/vault-acl-policy.hcl: -------------------------------------------------------------------------------- 1 | path "aws/creds/dev-role" { 2 | capabilities = ["read"] 3 | } 4 | 5 | -------------------------------------------------------------------------------- /iac/demo/zerotrust/hashitalks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinod827/cloudkit-lab/HEAD/iac/demo/zerotrust/hashitalks.png -------------------------------------------------------------------------------- /iac/demo/textract/lambda_function.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinod827/cloudkit-lab/HEAD/iac/demo/textract/lambda_function.zip -------------------------------------------------------------------------------- /iac/demo/acd-srilanka2025/requirements.txt: -------------------------------------------------------------------------------- 1 | bedrock-agentcore 2 | strands-agents 3 | boto3 4 | strands-agents[otel] 5 | strands-agents-tools -------------------------------------------------------------------------------- /iac/demo/textract/sqs_to_csv_lambda.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinod827/cloudkit-lab/HEAD/iac/demo/textract/sqs_to_csv_lambda.zip -------------------------------------------------------------------------------- /iac/k8s/fluentd/002-service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: fluentd 5 | namespace: kube-system -------------------------------------------------------------------------------- /iac/demo/kyverno/0-development-namespace.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: development 5 | spec: {} 6 | status: {} -------------------------------------------------------------------------------- /iac/demo/kyverno/3.2-sample-namespace.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: test-name-2 5 | labels: 6 | team: infra 7 | -------------------------------------------------------------------------------- /iac/k8s/keda/dashboard/dashboard-sa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: admin-user 5 | namespace: kubernetes-dashboard -------------------------------------------------------------------------------- /iac/demo/zerotrust/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazon/aws-cli:latest 2 | 3 | # Install jq 4 | RUN yum update -y && \ 5 | yum install -y jq && \ 6 | yum clean all 7 | -------------------------------------------------------------------------------- /iac/aws/terraform/creating-custom-vpc/002.internet.gateway.tf: -------------------------------------------------------------------------------- 1 | resource "aws_internet_gateway" "igw" { 2 | vpc_id = aws_vpc.mycustomvpc.id 3 | tags = { 4 | "owner" = "vinod" 5 | "Name" = "IGW" 6 | } 7 | } -------------------------------------------------------------------------------- /iac/aws/terraform/creating-custom-vpc/README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | Creating a custom VPC using OpenTofu. 3 | 4 | ## Architecture 5 | ![Custom AWS VPC](https://drive.google.com/file/d/1-1enJhmxFLkUp2jaqOkKGFqueP6cF68W/view?usp=sharing) 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ./webapp/node_modules/* 2 | ./webapp/package-lock.json 3 | webapp/node_modules/ 4 | webapp/package-lock.json 5 | iac/aws/terraform/creating-custom-vpc/.terraform/ 6 | iac/demo/textract/.terraform.lock.hcl 7 | iac/demo/textract/.terraform/* 8 | 9 | -------------------------------------------------------------------------------- /iac/k8s/iam-roles-with-k8s-service-account/my-sa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: my-sa 5 | namespace: dev 6 | annotations: 7 | eks.amazonaws.com/role-arn: arn:aws:iam::195725532069:role/custom-read-s3-bucket-objects 8 | -------------------------------------------------------------------------------- /iac/k8s/keda/dashboard/dashboard-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: admin-user 5 | namespace: kubernetes-dashboard 6 | annotations: 7 | kubernetes.io/service-account.name: "admin-user" 8 | type: kubernetes.io/service-account-token -------------------------------------------------------------------------------- /iac/aws/terraform/creating-custom-vpc/terraform.tfstate.backup: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "1.7.0", 4 | "serial": 9, 5 | "lineage": "69ea60e4-e317-8e43-6a2c-43fc92d249d3", 6 | "outputs": {}, 7 | "resources": [], 8 | "check_results": null 9 | } 10 | -------------------------------------------------------------------------------- /iac/k8s/fluentd/001-cluster-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: fluentd 5 | rules: 6 | - apiGroups: 7 | - "" 8 | resources: 9 | - namespaces 10 | - pods 11 | verbs: 12 | - get 13 | - list 14 | - watch -------------------------------------------------------------------------------- /iac/demo/keda/001-create-eks.yml: -------------------------------------------------------------------------------- 1 | apiVersion: eksctl.io/v1alpha5 2 | kind: ClusterConfig 3 | metadata: 4 | name: eks-keda-demo 5 | region: us-east-1 6 | version: '1.29' 7 | managedNodeGroups: 8 | - name: ng 9 | instanceType: m4.xlarge 10 | minSize: 1 11 | maxSize: 2 -------------------------------------------------------------------------------- /iac/aws/eks/keda/001-create-eks.yml: -------------------------------------------------------------------------------- 1 | apiVersion: eksctl.io/v1alpha5 2 | kind: ClusterConfig 3 | metadata: 4 | name: eks-keda-test 5 | region: us-east-2 6 | version: '1.21' 7 | managedNodeGroups: 8 | - name: ng 9 | instanceType: m4.xlarge 10 | minSize: 1 11 | maxSize: 2 -------------------------------------------------------------------------------- /iac/aws/terraform/creating-custom-vpc/001.vpc.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "mycustomvpc" { 2 | cidr_block = "10.0.0.0/16" 3 | enable_dns_support = true 4 | enable_dns_hostnames = true 5 | 6 | tags = { 7 | "owner" = "vinod" 8 | "Name" = "my custom VPC" 9 | } 10 | } -------------------------------------------------------------------------------- /iac/k8s/keda/hpa-demo/iaac/002_lb_svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: my-app-svc 5 | namespace: hpa-demo 6 | spec: 7 | selector: 8 | app: my-app 9 | ports: 10 | - protocol: TCP 11 | port: 80 12 | targetPort: 80 13 | type: LoadBalancer 14 | -------------------------------------------------------------------------------- /iac/aws/eks/amp/000-eks-cluster-ng-t3-medium.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: eksctl.io/v1alpha5 3 | kind: ClusterConfig 4 | metadata: 5 | name: eks-amp-prometheus-grafana-demo 6 | region: us-east-1 7 | version: '1.21' 8 | managedNodeGroups: 9 | - name: ng 10 | instanceType: t3.medium 11 | minSize: 1 12 | maxSize: 2 13 | -------------------------------------------------------------------------------- /iac/k8s/fluentd/003-cluster-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: fluentd 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: fluentd 9 | subjects: 10 | - kind: ServiceAccount 11 | name: fluentd 12 | namespace: kube-system -------------------------------------------------------------------------------- /iac/k8s/keda/hpa-demo/load_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Variables 4 | URL="http://a97793fc7ae0048b997070a54aa2d099-2026190512.us-east-1.elb.amazonaws.com/" 5 | DURATION=60 # Duration of load test in seconds 6 | CONCURRENCY=50 # Number of concurrent requests 7 | 8 | # Start load test 9 | ab -n $((DURATION * CONCURRENCY)) -c $CONCURRENCY $URL 10 | -------------------------------------------------------------------------------- /iac/k8s/alb-demo/nginx-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | alb.ingress.kubernetes.io/target-type: ip 6 | name: "nginx-service" 7 | namespace: "default" 8 | spec: 9 | ports: 10 | - port: 80 11 | targetPort: 80 12 | protocol: TCP 13 | type: NodePort 14 | selector: 15 | app: "nginx" 16 | -------------------------------------------------------------------------------- /iac/k8s/eks-create/myapp-a-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app: myapp-a 7 | name: myapp-a 8 | spec: 9 | ports: 10 | - port: 8080 11 | protocol: TCP 12 | targetPort: 8080 13 | selector: 14 | app: myapp-a 15 | type: NodePort 16 | status: 17 | loadBalancer: {} 18 | -------------------------------------------------------------------------------- /iac/k8s/keda/hpa-demo/iaac/nodeport_svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: my-app-svc 5 | namespace: hpa-demo 6 | spec: 7 | selector: 8 | app: my-app 9 | ports: 10 | - protocol: TCP 11 | nodePort: 32000 # Specify the NodePort number 12 | port: 80 13 | targetPort: 80 14 | type: NodePort 15 | -------------------------------------------------------------------------------- /iac/aws/eks/amp/PermissionPolicyIngest.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | {"Effect": "Allow", 5 | "Action": [ 6 | "aps:RemoteWrite", 7 | "aps:GetSeries", 8 | "aps:GetLabels", 9 | "aps:GetMetricMetadata" 10 | ], 11 | "Resource": "*" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /iac/aws/eks/amp/PermissionPolicyQuery.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | {"Effect": "Allow", 5 | "Action": [ 6 | "aps:QueryMetrics", 7 | "aps:GetSeries", 8 | "aps:GetLabels", 9 | "aps:GetMetricMetadata" 10 | ], 11 | "Resource": "*" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /iac/k8s/keda/dashboard/dashboard-cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: admin-user 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: cluster-admin 9 | subjects: 10 | - kind: ServiceAccount 11 | name: admin-user 12 | namespace: kubernetes-dashboard -------------------------------------------------------------------------------- /iac/k8s/alb-demo/nginx-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app: nginx 7 | name: nginx 8 | spec: 9 | containers: 10 | - image: nginx 11 | name: nginx 12 | resources: {} 13 | ports: 14 | - containerPort: 80 15 | dnsPolicy: ClusterFirst 16 | restartPolicy: Always 17 | status: {} 18 | -------------------------------------------------------------------------------- /iac/k8s/keda/keda-demo/pod-identity/001-service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: keda-demo 5 | spec: {} 6 | status: {} 7 | --- 8 | apiVersion: v1 9 | kind: ServiceAccount 10 | metadata: 11 | name: my-keda-serviceaccount 12 | namespace: keda-demo 13 | annotations: 14 | eks.amazonaws.com/role-arn: arn:aws:iam::458419607076:role/IRSA-KEDA-Demo -------------------------------------------------------------------------------- /iac/demo/kyverno/4.2-sample-job.yml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: test-job 5 | namespace: development 6 | spec: 7 | template: 8 | spec: 9 | restartPolicy: Never 10 | containers: 11 | - name: busybox 12 | image: busybox 13 | command: ["echo", "Hello, Welcome to the DevOps Malayalam community! Let's deep dive into Kyverno today."] 14 | -------------------------------------------------------------------------------- /iac/k8s/alb-demo/mydocker.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | run: mydocker 7 | name: mydocker 8 | spec: 9 | containers: 10 | - image: ghcr.io/vinod827/my-docker:1.0 11 | name: mydocker 12 | resources: {} 13 | ports: 14 | - containerPort: 8080 15 | dnsPolicy: ClusterFirst 16 | restartPolicy: Always 17 | status: {} 18 | -------------------------------------------------------------------------------- /iac/k8s/eks-create/nginx-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | run: nginx 7 | name: nginx 8 | namespace: kube-system 9 | spec: 10 | containers: 11 | - image: nginx 12 | name: nginx 13 | resources: {} 14 | dnsPolicy: ClusterFirst 15 | serviceAccountName: alb-ingress-controller 16 | restartPolicy: Always 17 | status: {} 18 | -------------------------------------------------------------------------------- /iac/aws/container-insights/001-create-cluster.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: eksctl.io/v1alpha5 3 | kind: ClusterConfig 4 | metadata: 5 | name: my-karpenter 6 | region: us-west-2 7 | version: '1.21' 8 | managedNodeGroups: 9 | - instanceType: t3.medium 10 | amiFamily: AmazonLinux2 11 | name: my-karpenter-ng 12 | desiredCapacity: 1 13 | minSize: 1 14 | maxSize: 10 15 | iam: 16 | withOIDC: true 17 | -------------------------------------------------------------------------------- /iac/k8s/keda/hpa-demo/iaac/003_hpa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: autoscaling/v1 2 | kind: HorizontalPodAutoscaler 3 | metadata: 4 | name: my-app-hpa 5 | namespace: hpa-demo 6 | spec: 7 | scaleTargetRef: 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | name: my-app 11 | minReplicas: 1 # Set the minimum number of replicas 12 | maxReplicas: 10 # Set the maximum number of replicas 13 | targetCPUUtilizationPercentage: 30 14 | -------------------------------------------------------------------------------- /webapp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | WORKDIR /app 4 | COPY package.json package.json 5 | RUN npm install package.json 6 | COPY . . 7 | 8 | LABEL maintainer="Vinod Kumar Nair " \ 9 | version="1.0.1" 10 | 11 | #Running as non-root user 12 | RUN addgroup -S developers && adduser -S appuser -G developers -h /home/appuser 13 | USER appuser 14 | 15 | CMD npm start --host=0.0.0.0 --port=8080 -------------------------------------------------------------------------------- /iac/k8s/fluentd/000-eks-cluster-ng-t2micro.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: eksctl.io/v1alpha5 3 | kind: ClusterConfig 4 | metadata: 5 | name: my-eks 6 | region: us-east-1 7 | version: "1.21" 8 | managedNodeGroups: 9 | - name: ng 10 | ami: ami-039185a7ec38ba8e5 11 | instanceType: t2.micro 12 | minSize: 1 13 | maxSize: 2 14 | overrideBootstrapCommand: | 15 | #!/bin/bash 16 | /etc/eks/bootstrap.sh my-eks 17 | -------------------------------------------------------------------------------- /iac/k8s/keda/hpa-demo/iaac/app.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | # Create a FastAPI app instance 4 | app = FastAPI() 5 | 6 | # Define a route for the root URL 7 | @app.get("/") 8 | async def read_root(): 9 | return {"message": "Hello everyone, Welcome to the KEDA session!"} 10 | 11 | # Run the app with uvicorn server 12 | if __name__ == "__main__": 13 | import uvicorn 14 | uvicorn.run(app, host="0.0.0.0", port=80) 15 | -------------------------------------------------------------------------------- /iac/k8s/keda/keda-demo/app/app.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | # Create a FastAPI app instance 4 | app = FastAPI() 5 | 6 | # Define a route for the root URL 7 | @app.get("/") 8 | async def read_root(): 9 | return {"message": "Hello everyone, Welcome to the KEDA session!"} 10 | 11 | # Run the app with uvicorn server 12 | if __name__ == "__main__": 13 | import uvicorn 14 | uvicorn.run(app, host="0.0.0.0", port=80) 15 | -------------------------------------------------------------------------------- /iac/k8s/alb-demo/nginx-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Deployment 3 | metadata: 4 | name: "nginx-deployment" 5 | namespace: "default" 6 | spec: 7 | replicas: 3 8 | template: 9 | metadata: 10 | labels: 11 | app: "nginx" 12 | spec: 13 | containers: 14 | - image: nginx:latest 15 | imagePullPolicy: Always 16 | name: "nginx" 17 | ports: 18 | - containerPort: 80 19 | -------------------------------------------------------------------------------- /iac/demo/keda/002-my-nginx-deploy.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: my-nginx 6 | name: my-nginx 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: my-nginx 12 | strategy: {} 13 | template: 14 | metadata: 15 | labels: 16 | app: my-nginx 17 | spec: 18 | containers: 19 | - image: nginx 20 | name: nginx 21 | resources: {} 22 | status: {} 23 | -------------------------------------------------------------------------------- /iac/aws/eks/keda/002-my-nginx-deploy.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: my-nginx 6 | name: my-nginx 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: my-nginx 12 | strategy: {} 13 | template: 14 | metadata: 15 | labels: 16 | app: my-nginx 17 | spec: 18 | containers: 19 | - image: nginx 20 | name: nginx 21 | resources: {} 22 | status: {} 23 | -------------------------------------------------------------------------------- /iac/demo/textract/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_bucket_name" { 2 | value = aws_s3_bucket.textract_bucket.id 3 | } 4 | 5 | output "lambda_function_arn" { 6 | value = aws_lambda_function.textract_lambda.arn 7 | } 8 | 9 | output "sqs_queue_url" { 10 | value = aws_sqs_queue.textract_queue.id 11 | } 12 | 13 | output "csv_lambda_function_arn" { 14 | value = aws_lambda_function.sqs_to_csv_lambda.arn 15 | } 16 | 17 | output "csv_s3_prefix" { 18 | value = var.csv_s3_prefix 19 | } 20 | -------------------------------------------------------------------------------- /iac/demo/kyverno/1.2-sample-app-invalid-image-source.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: myapp 6 | name: myapp 7 | namespace: development 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: myapp 13 | strategy: {} 14 | template: 15 | metadata: 16 | labels: 17 | app: myapp 18 | spec: 19 | containers: 20 | - image: nginx 21 | name: nginx 22 | status: {} 23 | -------------------------------------------------------------------------------- /iac/k8s/eks-create/eksctl-containerd.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: eksctl.io/v1alpha5 3 | kind: ClusterConfig 4 | metadata: 5 | name: containerd-eks 6 | region: us-east-1 7 | version: "1.21" 8 | managedNodeGroups: 9 | - name: containerd 10 | ami: ami-039185a7ec38ba8e5 11 | instanceType: t2.micro 12 | minSize: 1 13 | maxSize: 2 14 | overrideBootstrapCommand: | 15 | #!/bin/bash 16 | /etc/eks/bootstrap.sh containerd-eks --container-runtime containerd 17 | -------------------------------------------------------------------------------- /iac/demo/kyverno/1.3-sample-app-valid-image-source.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: myapp 6 | name: myapp 7 | namespace: development 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: myapp 13 | strategy: {} 14 | template: 15 | metadata: 16 | labels: 17 | app: myapp 18 | spec: 19 | containers: 20 | - image: vinod827/myapp:1.0.1 21 | name: myapp 22 | status: {} 23 | -------------------------------------------------------------------------------- /iac/k8s/falco/custom_falco_rules.yaml: -------------------------------------------------------------------------------- 1 | customRules: 2 | custom-rules.yaml: |- 3 | - rule: Container Breakout Attempt Detected 4 | desc: Detect when a container tries to access sensitive host files 5 | condition: > 6 | container and evt.type=open and fd.name contains "/etc/passwd" 7 | output: "Container Breakout Attempt Detected! (Container ID=%container.id Command=%proc.cmdline File=%fd.name)" 8 | priority: CRITICAL 9 | tags: [container, breakout, security] 10 | -------------------------------------------------------------------------------- /iac/k8s/falco/test-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: breakout-test 5 | namespace: default 6 | spec: 7 | containers: 8 | - name: test 9 | image: alpine 10 | command: ["/bin/sh", "-c"] 11 | args: 12 | - cat /etc/passwd 13 | securityContext: 14 | privileged: false 15 | resources: 16 | limits: 17 | memory: "128Mi" 18 | cpu: "500m" 19 | requests: 20 | memory: "64Mi" 21 | cpu: "250m" 22 | restartPolicy: Never 23 | -------------------------------------------------------------------------------- /iac/k8s/iam-roles-with-k8s-service-account/my-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | labels: 5 | run: my-pod 6 | name: my-pod 7 | namespace: dev 8 | spec: 9 | serviceAccountName: my-sa 10 | initContainers: 11 | - image: amazon/aws-cli 12 | name: my-aws-cli 13 | command: ['aws', 's3', 'ls', 's3://k8s-nest/'] 14 | containers: 15 | - image: nginx 16 | name: my-pod 17 | ports: 18 | - containerPort: 80 19 | dnsPolicy: ClusterFirst 20 | restartPolicy: Always 21 | status: {} 22 | -------------------------------------------------------------------------------- /iac/aws/eks/keda/scalers/datadog/001-datadog-triggerauth.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: datadog-secrets 5 | data: 6 | apiKey: 7 | appKey: 8 | --- 9 | 10 | apiVersion: keda.sh/v1alpha1 11 | kind: TriggerAuthentication 12 | metadata: 13 | name: keda-trigger-auth-datadog-secret 14 | spec: 15 | secretTargetRef: 16 | - parameter: apiKey 17 | name: datadog-secrets 18 | key: apiKey 19 | - parameter: appKey 20 | name: datadog-secrets 21 | key: appKey -------------------------------------------------------------------------------- /iac/k8s/alb-demo/nginx-service-2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | eks.amazonaws.com/fargate-profile: fp-default 6 | run: nginx 7 | name: nginx-service 8 | namespace: "default" 9 | annotations: 10 | alb.ingress.kubernetes.io/target-type: ip 11 | spec: 12 | ports: 13 | - port: 80 14 | protocol: TCP 15 | targetPort: 80 16 | selector: 17 | eks.amazonaws.com/fargate-profile: fp-default 18 | run: nginx 19 | type: NodePort 20 | status: 21 | loadBalancer: {} 22 | -------------------------------------------------------------------------------- /iac/aws/eks/keda/scalers/datadog/002-datadog-scaledobject.yml: -------------------------------------------------------------------------------- 1 | apiVersion: keda.sh/v1alpha1 2 | kind: ScaledObject 3 | metadata: 4 | name: datadog-scaledobject 5 | spec: 6 | scaleTargetRef: 7 | name: nginx 8 | minReplicaCount: 1 9 | maxReplicaCount: 3 10 | pollingInterval: 5 11 | triggers: 12 | - type: datadog 13 | metadata: 14 | query: "avg:nginx.net.request_per_s{kube_deployment:nginx}" 15 | queryValue: "2" 16 | age: "60" 17 | authenticationRef: 18 | name: keda-trigger-auth-datadog-secret -------------------------------------------------------------------------------- /iac/k8s/alb-demo/mydocker-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | eks.amazonaws.com/fargate-profile: fp-default 7 | run: mydocker 8 | name: mydocker-svc 9 | annotations: 10 | alb.ingress.kubernetes.io/target-type: ip 11 | spec: 12 | ports: 13 | - port: 8080 14 | protocol: TCP 15 | targetPort: 8080 16 | selector: 17 | eks.amazonaws.com/fargate-profile: fp-default 18 | run: mydocker 19 | type: NodePort 20 | status: 21 | loadBalancer: {} 22 | -------------------------------------------------------------------------------- /iac/aws/sam/my-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "1.0.0", 4 | "description": "hello world sample for NodeJS", 5 | "main": "app.js", 6 | "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", 7 | "author": "SAM CLI", 8 | "license": "MIT", 9 | "dependencies": { 10 | "axios": "^0.21.1" 11 | }, 12 | "scripts": { 13 | "test": "mocha tests/unit/" 14 | }, 15 | "devDependencies": { 16 | "chai": "^4.2.0", 17 | "mocha": "^8.2.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /iac/demo/kyverno/2.1-kyverno-clusterpolicy-mutate-label.yml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: add-app-label 5 | spec: 6 | rules: 7 | - name: add-app-label-to-pods-deployment 8 | match: 9 | resources: 10 | kinds: 11 | - Pod 12 | - Deployment 13 | mutate: 14 | patchStrategicMerge: 15 | metadata: 16 | labels: # Adds these labels if not provided 17 | company: "mycompany" 18 | app: "Observability" 19 | cost: "shared-infra" 20 | -------------------------------------------------------------------------------- /iac/aws/terraform/creating-custom-vpc/004.nat.tf: -------------------------------------------------------------------------------- 1 | 2 | resource "aws_eip" "nat" { 3 | vpc = true 4 | 5 | tags = { 6 | "Name" = "EIP" 7 | "Owner" = "Vinod" 8 | } 9 | 10 | } 11 | 12 | resource "aws_nat_gateway" "nat" { 13 | allocation_id = aws_eip.nat.id 14 | subnet_id = aws_subnet.public-us-east-1a.id 15 | 16 | tags = { 17 | "Name" = "NAT Gateway" 18 | "Owner" = "Vinod" 19 | } 20 | 21 | # To ensure proper ordering, it is recommended to add an explicit dependency 22 | # on the Internet Gateway for the VPC. 23 | depends_on = [aws_internet_gateway.igw] 24 | } -------------------------------------------------------------------------------- /iac/aws/terraform/creating-custom-vpc/000.provider.tf: -------------------------------------------------------------------------------- 1 | variable aws_region { 2 | default = "us-east-1" 3 | description = "AWS region where the resources will be provisioned" 4 | } 5 | 6 | # Configure the AWS Provider 7 | terraform { 8 | required_providers { 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = "~> 5.0" 12 | } 13 | # helm = { 14 | # source = "hashicorp/aws" 15 | # version = "~> 2.6" 16 | # } 17 | } 18 | } 19 | 20 | # Configure region and profile 21 | provider "aws" { 22 | region = var.aws_region 23 | profile = "myaws" 24 | } -------------------------------------------------------------------------------- /iac/aws/eks/keda/scalers/datadog/004-fake-traffic.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: fake-traffic 6 | name: fake-traffic 7 | spec: 8 | replicas: 3 9 | selector: 10 | matchLabels: 11 | app: fake-traffic 12 | strategy: {} 13 | template: 14 | metadata: 15 | labels: 16 | app: fake-traffic 17 | spec: 18 | containers: 19 | - image: busybox 20 | name: test 21 | command: ["/bin/sh"] 22 | args: ["-c", "while true; do wget -0 /dev/null -o /dev/null http://nginx; sleep 300; done"] 23 | status: {} 24 | -------------------------------------------------------------------------------- /iac/aws/eks/amp/TrustPolicy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Federated": "arn:aws:iam::195725532069:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/36E180609192DF43B0DBAE178070F710" 8 | }, 9 | "Action": "sts:AssumeRoleWithWebIdentity", 10 | "Condition": { 11 | "StringEquals": { 12 | "oidc.eks.us-east-1.amazonaws.com/id/36E180609192DF43B0DBAE178070F710:sub": "system:serviceaccount:prometheus:amp-iamproxy-query-service-account" 13 | } 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /iac/k8s/keda/hpa-demo/iaac/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official Python image as base 2 | FROM python:3.9-slim 3 | 4 | # Set the working directory in the container 5 | WORKDIR /app 6 | 7 | # Copy the requirements file into the container 8 | COPY requirements.txt . 9 | 10 | # Install the Python dependencies 11 | RUN pip install --no-cache-dir -r requirements.txt 12 | 13 | # Copy the FastAPI application code into the container 14 | COPY app.py . 15 | 16 | # Expose port 80 17 | EXPOSE 80 18 | 19 | # Command to run the FastAPI application using uvicorn server 20 | CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"] 21 | -------------------------------------------------------------------------------- /iac/k8s/keda/keda-demo/app/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official Python image as base 2 | FROM python:3.9-slim 3 | 4 | # Set the working directory in the container 5 | WORKDIR /app 6 | 7 | # Copy the requirements file into the container 8 | COPY requirements.txt . 9 | 10 | # Install the Python dependencies 11 | RUN pip install --no-cache-dir -r requirements.txt 12 | 13 | # Copy the FastAPI application code into the container 14 | COPY app.py . 15 | 16 | # Expose port 80 17 | EXPOSE 80 18 | 19 | # Command to run the FastAPI application using uvicorn server 20 | CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"] 21 | -------------------------------------------------------------------------------- /iac/aws/sam/samconfig.toml: -------------------------------------------------------------------------------- 1 | version = 0.1 2 | [default] 3 | [default.deploy] 4 | [default.deploy.parameters] 5 | stack_name = "sam-app" 6 | s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-gjftek9ma4z6" 7 | s3_prefix = "sam-app" 8 | region = "us-east-1" 9 | confirm_changeset = true 10 | capabilities = "CAPABILITY_IAM" 11 | disable_rollback = true 12 | image_repositories = [] 13 | parameter_overrides = "Environment=\"dev\" ExportPrefix=\"MyApp\" BillingMode=\"PROVISIONED\" DefaultLambdaTimeout=\"3\" DefaultLambdaMemory=\"128\" TTLAttribute=\"None\" CDNHostedZoneId=\"Z2FDTNDATAQYW2\" DomainName=\"acloudtiger.com\"" 14 | -------------------------------------------------------------------------------- /iac/demo/kyverno/1.1-kyverno-policy-image-source.yml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: Policy 3 | metadata: 4 | name: enforce-image-source 5 | namespace: development 6 | spec: 7 | validationFailureAction: Enforce 8 | rules: 9 | - name: validate-image-source 10 | match: 11 | resources: 12 | kinds: 13 | - Deployment 14 | validate: 15 | message: "Only images from the Docker Hub account vinod827/ are allowed." 16 | pattern: 17 | spec: 18 | template: 19 | spec: 20 | containers: 21 | - image: "vinod827/*" -------------------------------------------------------------------------------- /iac/demo/kyvernocon/mistake1_blocking_all_workloads.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: deny-all-workloads 5 | labels: 6 | policy: deny-all-workloads 7 | created-by: vinod 8 | talk: kyvernocon-2025 9 | spec: 10 | validationFailureAction: Enforce 11 | rules: 12 | - name: block-all 13 | match: 14 | resources: 15 | kinds: 16 | - Pod 17 | - Deployment 18 | - StatefulSet 19 | validate: 20 | message: "All workloads are blocked!" 21 | pattern: 22 | spec: 23 | containers: [] 24 | -------------------------------------------------------------------------------- /webapp/index.js: -------------------------------------------------------------------------------- 1 | 'use-strict'; 2 | 3 | const request = require('request'); 4 | const bodyPraser = require('body-parser'); 5 | const express = require('express'); 6 | 7 | const app = express(); 8 | 9 | app.set('PORT', (process.env.PORT || 8080)); 10 | app.use(bodyPraser.urlencoded({extended: false})); 11 | app.use(bodyPraser.json()); 12 | 13 | 14 | app.get('/', function(request, response){ 15 | console.log('Welcome to Docker...'); 16 | response.send('Welcome to Docker...'); 17 | }) 18 | 19 | app.listen(app.get('PORT'), function(){ 20 | console.log('App is listening on port: ', app.get('PORT')); 21 | }); -------------------------------------------------------------------------------- /iac/demo/kyvernocon/tests/kyverno-test.yaml: -------------------------------------------------------------------------------- 1 | tests: 2 | - name: bad-patch-test 3 | policies: 4 | - ../mistake3_bad_patching.yaml # adjust relative path if needed 5 | resources: 6 | - | 7 | apiVersion: v1 8 | kind: Pod 9 | metadata: 10 | name: bad-pod 11 | namespace: default 12 | spec: 13 | containers: 14 | - name: nginx 15 | image: nginx:latest 16 | results: 17 | - policy: mistake3_bad_patching 18 | rule: wrong-patch-path 19 | kind: Pod 20 | resources: 21 | - bad-pod 22 | result: fail 23 | -------------------------------------------------------------------------------- /iac/demo/kyvernocon/solution3_bad_patching.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: bad-patch-fixed 5 | labels: 6 | policy: bad-patch-fixed 7 | created-by: vinod 8 | talk: kyvernocon-2025 9 | spec: 10 | validationFailureAction: Enforce 11 | rules: 12 | - name: correct-patch-path 13 | match: 14 | resources: 15 | kinds: 16 | - Pod 17 | mutate: 18 | patchStrategicMerge: 19 | spec: 20 | containers: 21 | - name: nginx 22 | resources: 23 | limits: 24 | cpu: "500m" 25 | -------------------------------------------------------------------------------- /iac/k8s/eks-create/trust.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version":"2012-10-17", 3 | "Statement":[ 4 | { 5 | "Effect":"Allow", 6 | "Principal":{ 7 | "Federated":"arn:aws:iam::195725532069:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/53305FA5F58E742DB5413B977B052B70" 8 | }, 9 | "Action":"sts:AssumeRoleWithWebIdentity", 10 | "Condition":{ 11 | "StringEquals":{ 12 | "https://oidc.eks.us-east-1.amazonaws.com/id/53305FA5F58E742DB5413B977B052B70:sub":"system:serviceaccount:kube-system:alb-ingress-controller" 13 | } 14 | } 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /iac/demo/kyvernocon/solution1_blocking_all_workloads.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: require-label 5 | labels: 6 | policy: require-label 7 | created-by: vinod 8 | talk: kyvernocon-2025 9 | spec: 10 | validationFailureAction: Enforce 11 | rules: 12 | - name: require-team-label 13 | match: 14 | resources: 15 | kinds: 16 | - Pod 17 | - Deployment 18 | - StatefulSet 19 | validate: 20 | message: "All Pods must have a team label" 21 | pattern: 22 | metadata: 23 | labels: 24 | team: "?*" 25 | -------------------------------------------------------------------------------- /iac/demo/kyverno/4.1-kyverno-policy-cleanup.yml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v2beta1 2 | kind: Policy 3 | metadata: 4 | name: cleanup-completed-jobs 5 | namespace: development 6 | spec: 7 | rules: 8 | - name: cleanup-jobs 9 | match: 10 | any: 11 | - resources: 12 | kinds: 13 | - Job 14 | namespaces: 15 | - development 16 | exclude: 17 | any: 18 | - resources: 19 | statuses: 20 | - "!Succeeded" 21 | - "!Failed" # Exclude active Jobs 22 | cleanup: 23 | ttl: 60s # Delete Jobs 1 minute after completion 24 | -------------------------------------------------------------------------------- /iac/k8s/audit-policy/audit-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: audit.k8s.io/v1 2 | kind: Policy 3 | rules: 4 | # Log the request body of configmap changes in kube-system. 5 | - level: Request 6 | resources: 7 | - group: "" # core API group 8 | resources: ["configmaps"] 9 | namespaces: ["kube-system"] 10 | 11 | # Log configmap and secret changes in all other namespaces at the Metadata level. 12 | - level: Metadata 13 | resources: 14 | - group: "" # core API group 15 | resources: ["secrets", "configmaps"] 16 | 17 | # A catch-all rule to log all other requests at the Metadata level. 18 | - level: Metadata 19 | omitStages: 20 | - "RequestReceived" -------------------------------------------------------------------------------- /iac/demo/kyvernocon/mistake3_bad_patching.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: bad-patch 5 | labels: 6 | policy: bad-patch 7 | created-by: vinod 8 | talk: kyvernocon-2025 9 | spec: 10 | validationFailureAction: Enforce 11 | rules: 12 | - name: wrong-patch-path 13 | match: 14 | resources: 15 | kinds: 16 | - Pod 17 | mutate: 18 | patchStrategicMerge: 19 | spec: 20 | containers: 21 | - name: nginx # wrong 22 | #- (name): "*" # correct 23 | resources: 24 | limits: 25 | cpu: "500m" 26 | -------------------------------------------------------------------------------- /iac/demo/kyvernocon/solution2_infinite_policy_loops.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: infinite-loop-fixed 5 | labels: 6 | policy: infinite-loop-fixed 7 | created-by: vinod 8 | talk: kyvernocon-2025 9 | spec: 10 | validationFailureAction: Enforce 11 | rules: 12 | - name: mutate-label 13 | match: 14 | resources: 15 | kinds: 16 | - Pod 17 | exclude: 18 | resources: 19 | selector: 20 | matchLabels: 21 | loop: "true" 22 | mutate: 23 | patchStrategicMerge: 24 | metadata: 25 | labels: 26 | loop: "true" 27 | -------------------------------------------------------------------------------- /iac/k8s/keda/keda-demo/send_messages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Set your AWS profile name 4 | AWS_PROFILE="myaws" 5 | 6 | # Set your SQS queue URL 7 | SQS_QUEUE_URL="https://sqs.us-east-1.amazonaws.com/458419607076/MySQSQueue-ForKEDA-Demo" 8 | 9 | # Set the number of messages to push 10 | NUM_MESSAGES=50 11 | 12 | # Loop to send messages 13 | for ((i=1; i<=$NUM_MESSAGES; i++)); do 14 | MESSAGE_BODY="Message $i" 15 | aws sqs send-message \ 16 | --queue-url "$SQS_QUEUE_URL" \ 17 | --message-body "$MESSAGE_BODY" \ 18 | --profile "$AWS_PROFILE" \ 19 | > /dev/null 2>&1 # Suppress output 20 | echo "Sent message $i" 21 | done 22 | 23 | echo "All messages sent" 24 | -------------------------------------------------------------------------------- /iac/aws/sam/README.md: -------------------------------------------------------------------------------- 1 | # README First
2 | ### Author Vinod Kumar (vinod827@yahoo.com)
3 | 4 | 5 | In this, I have designed and implemented a typical 3 tier serverless based application using **AWS SAM (Serverless Application Model) framework** which can be further integrated with any CI/CD Servers like Jenkins to automate the whole build & deployment process. 6 | 7 | ### Architecture:-
8 | 9 | ![serverless](https://user-images.githubusercontent.com/24762720/145698759-18d0f6a2-06ed-4add-92c3-cf6386b408a4.jpg) 10 | 11 | 12 | 13 | This application is currently deployed & running with all necessary configuration at Route 53 end. 14 | It can be accessed either:- 15 | 1) https://acloudtiger.com -------------------------------------------------------------------------------- /iac/demo/zerotrust/vault-aws-config.hcl: -------------------------------------------------------------------------------- 1 | # Enable AWS secrets engine 2 | path "sys/mounts/aws" { 3 | capabilities = ["create", "update"] 4 | } 5 | 6 | # Configure AWS secrets engine with root credentials 7 | path "aws/config/root" { 8 | capabilities = ["create", "update"] 9 | data = { 10 | access_key = "" 11 | secret_key = "" 12 | region = "us-east-1" 13 | } 14 | } 15 | 16 | # Create a Vault AWS role that generates IAM user credentials 17 | path "aws/roles/dev-role" { 18 | capabilities = ["create", "update"] 19 | data = { 20 | credential_type = "iam_user" 21 | policy_arn = "" 22 | max_ttl = "24h" 23 | ttl = "1h" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /iac/demo/kyvernocon/mistake2_infinite_policy_loops.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: infinite-loop 5 | labels: 6 | policy: infinite-loop 7 | created-by: vinod 8 | talk: kyvernocon-2025 9 | spec: 10 | validationFailureAction: Enforce 11 | rules: 12 | - name: mutate-label 13 | match: 14 | resources: 15 | kinds: 16 | - Pod 17 | # exclude: # Use exclude and matchlabel to prevent kyverno to mutate again 18 | # resources: 19 | # selector: 20 | # matchLabels: 21 | # loop: "true" 22 | mutate: 23 | patchStrategicMerge: 24 | metadata: 25 | labels: 26 | loop: "true" 27 | -------------------------------------------------------------------------------- /webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-awesome-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "mocha", 9 | "test-with-coverage": "nyc --reporter=lcov --reporter=text-lcov --all -x \"./node_modules/*\" -x \"./coverage/*\" npm run test" 10 | }, 11 | "author": "Vinod Kumar Nair", 12 | "license": "ISC", 13 | "dependencies": { 14 | "body-parser": "^1.19.0", 15 | "cowsay": "^1.4.0", 16 | "express": "^4.17.1", 17 | "knex": "^0.95.5", 18 | "mocha-lcov-reporter": "^1.3.0", 19 | "nodemon": "^2.0.7", 20 | "request": "^2.88.0" 21 | }, 22 | "devDependencies": { 23 | "mocha": "^8.4.0", 24 | "nyc": "^15.1.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iac/demo/textract/Makefile: -------------------------------------------------------------------------------- 1 | # Python Lambda files 2 | LAMBDA1_FILE = lambda_function.py 3 | LAMBDA2_FILE = sqs_to_csv_lambda.py 4 | 5 | # Output zip files 6 | LAMBDA1_ZIP = lambda_function.zip 7 | LAMBDA2_ZIP = sqs_to_csv_lambda.zip 8 | 9 | # Default target 10 | all: zip-lambdas 11 | 12 | # Zip the Lambda functions 13 | zip-lambdas: $(LAMBDA1_ZIP) $(LAMBDA2_ZIP) 14 | 15 | $(LAMBDA1_ZIP): $(LAMBDA1_FILE) 16 | @echo "Zipping $(LAMBDA1_FILE) into $(LAMBDA1_ZIP)..." 17 | zip $(LAMBDA1_ZIP) $(LAMBDA1_FILE) 18 | 19 | $(LAMBDA2_ZIP): $(LAMBDA2_FILE) 20 | @echo "Zipping $(LAMBDA2_FILE) into $(LAMBDA2_ZIP)..." 21 | zip $(LAMBDA2_ZIP) $(LAMBDA2_FILE) 22 | 23 | # Clean the zip files 24 | clean: 25 | rm -f $(LAMBDA1_ZIP) $(LAMBDA2_ZIP) 26 | @echo "Cleaned up old zip files!" 27 | 28 | -------------------------------------------------------------------------------- /iac/aws/sam/my-app/tests/unit/test-handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const app = require('../../app.js'); 4 | const chai = require('chai'); 5 | const expect = chai.expect; 6 | var event, context; 7 | 8 | describe('Tests index', function () { 9 | it('verifies successful response', async () => { 10 | const result = await app.lambdaHandler(event, context) 11 | 12 | expect(result).to.be.an('object'); 13 | expect(result.statusCode).to.equal(200); 14 | expect(result.body).to.be.an('string'); 15 | 16 | let response = JSON.parse(result.body); 17 | 18 | expect(response).to.be.an('object'); 19 | expect(response.message).to.be.equal("hello world"); 20 | // expect(response.location).to.be.an("string"); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /iac/aws/eks/amp/my_prometheus_values_yaml: -------------------------------------------------------------------------------- 1 | ## The following is a set of default values for prometheus server helm chart which enable remoteWrite to AMP 2 | ## For the rest of prometheus helm chart values see: https://github.com/prometheus-community/helm-charts/blob/main/charts/prometheus/values.yaml 3 | ## 4 | serviceAccounts: 5 | server: 6 | name: amp-iamproxy-ingest-service-account 7 | annotations: 8 | eks.amazonaws.com/role-arn: arn:aws:iam::195725532069:role/amp-iamproxy-ingest-role 9 | server: 10 | remoteWrite: 11 | - url: https://aps-workspaces.us-east-1.amazonaws.com/workspaces/ws-51d4f189-54f0-4535-a49c-c11bdf5db148/api/v1/remote_write 12 | sigv4: 13 | region: us-east-1 14 | queue_config: 15 | max_samples_per_send: 1000 16 | max_shards: 200 17 | capacity: 2500 18 | -------------------------------------------------------------------------------- /iac/k8s/alb-demo/alb-ingress-controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: alb-ingress-controller 6 | name: alb-ingress-controller 7 | namespace: kube-system 8 | spec: 9 | selector: 10 | matchLabels: 11 | app.kubernetes.io/name: alb-ingress-controller 12 | template: 13 | metadata: 14 | labels: 15 | app.kubernetes.io/name: alb-ingress-controller 16 | spec: 17 | containers: 18 | - name: alb-ingress-controller 19 | args: 20 | - --ingress-class=alb 21 | - --cluster-name=eks-fargate-alb-demo 22 | - --aws-vpc-id=vpc-0dc46d370e38de475 23 | - --aws-region=us-east-1 24 | image: docker.io/amazon/aws-alb-ingress-controller:v1.1.8 25 | serviceAccountName: alb-ingress-controller 26 | -------------------------------------------------------------------------------- /iac/aws/container-insights/002-container-insights.sh: -------------------------------------------------------------------------------- 1 | ClusterName=my-karpenter 2 | RegionName=us-west-2 3 | FluentBitHttpPort='2020' 4 | FluentBitReadFromHead='Off' 5 | [[ ${FluentBitReadFromHead} = 'On' ]] && FluentBitReadFromTail='Off'|| FluentBitReadFromTail='On' 6 | [[ -z ${FluentBitHttpPort} ]] && FluentBitHttpServer='Off' || FluentBitHttpServer='On' 7 | curl https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/quickstart/cwagent-fluent-bit-quickstart.yaml | sed 's/{{cluster_name}}/'${ClusterName}'/;s/{{region_name}}/'${RegionName}'/;s/{{http_server_toggle}}/"'${FluentBitHttpServer}'"/;s/{{http_server_port}}/"'${FluentBitHttpPort}'"/;s/{{read_from_head}}/"'${FluentBitReadFromHead}'"/;s/{{read_from_tail}}/"'${FluentBitReadFromTail}'"/' | kubectl apply -f - -------------------------------------------------------------------------------- /iac/k8s/sidecar.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app: nginx-sidecar 7 | name: nginx-sidecar 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: nginx-sidecar 13 | strategy: {} 14 | template: 15 | metadata: 16 | creationTimestamp: null 17 | labels: 18 | app: nginx-sidecar 19 | spec: 20 | containers: 21 | - image: nginx 22 | name: nginx 23 | resources: {} 24 | volumeMounts: 25 | - name: log-volume 26 | mountPath: /var/log/nginx 27 | - image: bash 28 | name: sidecar 29 | command: ["/bin/sh","-c","tail -f /var/sidecar/access.log"] 30 | volumeMounts: 31 | - name: log-volume 32 | mountPath: /var/sidecar 33 | volumes: 34 | - name: log-volume 35 | emptyDir: {} 36 | status: {} 37 | -------------------------------------------------------------------------------- /iac/k8s/eks-create/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: "2048-ingress" 5 | namespace: "2048-game" 6 | annotations: 7 | kubernetes.io/ingress.class: alb 8 | alb.ingress.kubernetes.io/scheme: internet-facing 9 | alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}' 10 | alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:195725532069:certificate/b6a9e691-b807-4f10-a0bf-0449730ecdf4 11 | alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' 12 | labels: 13 | app: 2048-ingress 14 | spec: 15 | rules: 16 | - http: 17 | paths: 18 | - path: /* 19 | pathType: Prefix 20 | backend: 21 | service: 22 | name: "service-2048" 23 | port: 24 | number: 80 25 | -------------------------------------------------------------------------------- /iac/aws/eks/amp/amp_ingest_override_values.yaml: -------------------------------------------------------------------------------- 1 | serviceAccounts: 2 | server: 3 | name: "amp-iamproxy-ingest-service-account" 4 | annotations: 5 | eks.amazonaws.com/role-arn: "arn:aws:iam::195725532069:role/amp-iamproxy-ingest-role" 6 | server: 7 | sidecarContainers: 8 | aws-sigv4-proxy-sidecar: 9 | image: public.ecr.aws/aws-observability/aws-sigv4-proxy:1.0 10 | args: 11 | - --name 12 | - aps 13 | - --region 14 | - us-east-1 15 | - --host 16 | - aps-workspaces.us-east-1.amazonaws.com 17 | - --port 18 | - :8005 19 | ports: 20 | - name: aws-sigv4-proxy 21 | containerPort: 8005 22 | statefulSet: 23 | enabled: "true" 24 | remoteWrite: 25 | - url: http://localhost:8005/workspaces/ws-51d4f189-54f0-4535-a49c-c11bdf5db148/api/v1/remote_write 26 | 27 | -------------------------------------------------------------------------------- /iac/k8s/keda/hpa-demo/iaac/001_my_app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | creationTimestamp: null 5 | name: hpa-demo 6 | spec: {} 7 | status: {} 8 | 9 | --- 10 | 11 | apiVersion: apps/v1 12 | kind: Deployment 13 | metadata: 14 | labels: 15 | app: my-app 16 | name: my-app 17 | namespace: hpa-demo 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: my-app 23 | template: 24 | metadata: 25 | labels: 26 | app: my-app 27 | spec: 28 | containers: 29 | - image: vinod827/keda-demo:1.0.0 30 | name: my-app 31 | ports: 32 | - containerPort: 80 33 | resources: 34 | requests: 35 | cpu: "100m" # request 100 milliCPU (0.1 CPU) 36 | memory: "128Mi" # request 128 Mebibytes of memory 37 | limits: 38 | cpu: "200m" # limit to 200 milliCPU (0.2 CPU) 39 | memory: "256Mi" # limit to 256 Mebibytes of memory 40 | 41 | -------------------------------------------------------------------------------- /iac/k8s/alb-demo/nginx-service-bkp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | alb.ingress.kubernetes.io/target-type: ip 6 | kubectl.kubernetes.io/last-applied-configuration: | 7 | {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{"alb.ingress.kubernetes.io/target-type":"ip"},"name":"nginx-service","namespace":"default"},"spec":{"ports":[{"port":80,"protocol":"TCP","targetPort":80}],"selector":{"app":"nginx"},"type":"NodePort"}} 8 | creationTimestamp: "2021-08-21T15:54:40Z" 9 | name: nginx-service 10 | namespace: default 11 | resourceVersion: "7181" 12 | uid: b29645ea-36b0-4f74-9c8a-287c41b56fad 13 | spec: 14 | clusterIP: 10.100.180.50 15 | clusterIPs: 16 | - 10.100.180.50 17 | externalTrafficPolicy: Cluster 18 | ports: 19 | - nodePort: 30903 20 | port: 80 21 | protocol: TCP 22 | targetPort: 80 23 | selector: 24 | app: nginx 25 | sessionAffinity: None 26 | type: NodePort 27 | status: 28 | loadBalancer: {} 29 | -------------------------------------------------------------------------------- /iac/k8s/keda/keda-demo/pod-identity/001.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: keda-demo 5 | spec: {} 6 | status: {} 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | labels: 12 | app: my-app-keda 13 | name: my-app-keda 14 | namespace: keda-demo 15 | spec: 16 | replicas: 1 17 | selector: 18 | matchLabels: 19 | app: my-app-keda 20 | template: 21 | metadata: 22 | labels: 23 | app: my-app-keda 24 | spec: 25 | serviceAccountName: keda-operator 26 | containers: 27 | - image: vinod827/keda-demo:1.0.0 28 | name: my-app-keda 29 | ports: 30 | - containerPort: 80 31 | resources: 32 | requests: 33 | cpu: "100m" # request 100 milliCPU (0.1 CPU) 34 | memory: "128Mi" # request 128 Mebibytes of memory 35 | limits: 36 | cpu: "200m" # limit to 200 milliCPU (0.2 CPU) 37 | memory: "256Mi" # limit to 256 Mebibytes of memory 38 | 39 | -------------------------------------------------------------------------------- /iac/k8s/eks-create/AWS_WEB_IDENTITY_TOKEN_FILE: -------------------------------------------------------------------------------- 1 | eyJhbGciOiJSUzI1NiIsImtpZCI6IjVjNjY2YmZjMTcxYzNkM2QzZGU0YWU4ZmIxZjVmNmIxMDIyNmZiZjIifQ.eyJhdWQiOlsic3RzLmFtYXpvbmF3cy5jb20iXSwiZXhwIjoxNjI5NjM4NjA0LCJpYXQiOjE2Mjk1NTIyMDQsImlzcyI6Imh0dHBzOi8vb2lkYy5la3MudXMtZWFzdC0xLmFtYXpvbmF3cy5jb20vaWQvNTMzMDVGQTVGNThFNzQyREI1NDEzQjk3N0IwNTJCNzAiLCJrdWJlcm5ldGVzLmlvIjp7Im5hbWVzcGFjZSI6Imt1YmUtc3lzdGVtIiwicG9kIjp7Im5hbWUiOiJuZ2lueCIsInVpZCI6IjIwZmYwOGFlLTZmZDEtNDcyNi1hY2IyLWQ4ODdlYTQ5MjIwZCJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiYWxiLWluZ3Jlc3MtY29udHJvbGxlciIsInVpZCI6IjM0NjExYTE5LWQxYzEtNDFkMi05M2E5LWZiZGQ4MTI0MjdkNyJ9fSwibmJmIjoxNjI5NTUyMjA0LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWxiLWluZ3Jlc3MtY29udHJvbGxlciJ9.HdwLOGefcf6j1rCcjzYJG23pxHK31_MTJzC508F60senOPzUtnhQZRwgxyqRZODDDZnSatKCYino16tTTu1oOznHnPwpKYQKzIN4QonV4WTSGJQsBezHjLifxF6XdXOTAqayoNHJK-f-Sc5BzIyrxW9FNzJfu2Ul2VfWGEG9roDSzpub_8WQP2mKjawFF59kmd6bLzBAetUhREtLH8b1XJm-XhS0Hy-h9BoLtkvNeNIHekzNbsS9CcJ7z3WGltqEC8KHxCzIQZqgXe8QaVLCJETLZA1gKf4dxSQxboNsCKQFUh2fwTU1_L7IIIEaymZL5gaITlkElwpAmPKXp6M-aw 2 | -------------------------------------------------------------------------------- /iac/demo/kyverno/3.1-kyverno-policy-generate-configmap.yml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: generate-postgres-configmap 5 | spec: 6 | rules: 7 | - name: generate-postgres-configmap 8 | match: 9 | resources: 10 | kinds: 11 | - Namespace 12 | preconditions: 13 | - key: "{{ request.object.metadata.labels.team }}" 14 | operator: Equals 15 | value: "infra" 16 | generate: 17 | apiVersion: v1 18 | kind: ConfigMap 19 | name: postgres-config 20 | namespace: "{{ request.object.metadata.name }}" 21 | synchronize: true 22 | data: 23 | postgres.conf: | 24 | # Sample PostgreSQL Configuration 25 | listen_addresses = '*' 26 | port = 5432 27 | max_connections = 100 28 | shared_buffers = 128MB 29 | work_mem = 4MB 30 | maintenance_work_mem = 64MB 31 | timezone = 'UTC' 32 | log_statement = 'ddl' 33 | -------------------------------------------------------------------------------- /iac/demo/textract/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region to deploy resources" 3 | default = "us-east-1" 4 | 5 | validation { 6 | condition = contains(["us-east-1"], var.region) 7 | error_message = "Only us-east-1 is allowed for deployment." 8 | } 9 | } 10 | 11 | 12 | variable "lambda_function_name" { 13 | description = "Lambda function name" 14 | default = "textract-lambda" 15 | } 16 | 17 | variable "s3_bucket_name" { 18 | description = "Name of the S3 bucket" 19 | default = "acdkochi2024-textract-s3-bucket-demo-" 20 | } 21 | 22 | variable "sqs_queue_name" { 23 | description = "Name of the SQS queue" 24 | default = "textract-sqs-queue" 25 | } 26 | 27 | variable "csv_s3_prefix" { 28 | description = "S3 prefix for storing CSV files" 29 | default = "processed/csv/" 30 | } 31 | 32 | variable "lambda_function_name_2" { 33 | description = "Name for the second Lambda function" 34 | default = "sqs-to-csv-lambda" 35 | } 36 | 37 | variable "aws_profile" { 38 | description = "AWS credentials profile to use" 39 | default = "dev" 40 | } -------------------------------------------------------------------------------- /.github/workflows/aws.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Serverless app to AWS 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | # To trigger the build manually 9 | workflow_dispatch: 10 | 11 | env: 12 | AWS_REGION: ${{ secrets.AWS_REGION }} 13 | 14 | jobs: 15 | deploy: 16 | name: Deploy 17 | runs-on: ubuntu-latest 18 | environment: production 19 | 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v2 23 | 24 | #- name: configure aws credentials 25 | #uses: aws-actions/configure-aws-credentials@v1 26 | #with: 27 | #role-to-assume: arn:aws:iam::195725532069:role/github-actions-role 28 | #role-duration-seconds: 900 29 | #aws-region: ${{ secrets.AWS_REGION }} 30 | 31 | # You can now execute commands that use the credentials👇 32 | - name: Serverless deploy 33 | #run: sls deploy --stage dev 34 | run: echo 'hello' 35 | env: 36 | AWS_REGION: ${{ secrets.AWS_REGION }} 37 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 38 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 39 | -------------------------------------------------------------------------------- /iac/aws/terraform/creating-custom-vpc/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "tofu init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.opentofu.org/hashicorp/aws" { 5 | version = "5.48.0" 6 | constraints = "~> 5.0" 7 | hashes = [ 8 | "h1:cntHD8f3TycbLB4klO+ZQCEDd+bMWeIE3h1DUIAxXlo=", 9 | "zh:212b33b4270a4f20025dec83b181b0e8044ef382491e0c89ad07c64d6dfacff0", 10 | "zh:2dd2dadd6fc8752edb6241bdac1bdd49ce64384527dc335a021d61d3870a0393", 11 | "zh:3d449e369958ab3d0afe2db6be5de22061f8635fe176771c98af41a7f770f1b5", 12 | "zh:3dd6ca9a102c6164683800d8b1b5def29a51d575b5223063961125de81cca136", 13 | "zh:422586cf2ea78f8464c97b95f153acdc84b660b2eb474a100338e360593e2d84", 14 | "zh:70ea10113b724cc69f83e2c1fd65d7d304aaf6bd9f6a45cd1622a5f36506690c", 15 | "zh:84a48c4a7eb8498beb9f5d78bef5e58516e11a8df131042fb43d3dec62dc899b", 16 | "zh:9724c095fb8d8d7695769a828e6cc0de95da264487c91af39a645713b293323c", 17 | "zh:ad9117ef8c7fd8e26aab482a286aa2e641e4887d1816117caa1fd7eaff6a050c", 18 | "zh:ff32af11624e5104fd4ddd38cecd1beb09da9a7be7f49b0d496080667882b90e", 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /iac/aws/terraform/creating-custom-vpc/003.subnets.tf: -------------------------------------------------------------------------------- 1 | resource "aws_subnet" "private-us-east-1a" { 2 | vpc_id = aws_vpc.mycustomvpc.id 3 | cidr_block = "10.0.1.0/24" 4 | availability_zone = "us-east-1a" 5 | 6 | tags = { 7 | "subnet" = "private-us-east-1a" 8 | "Name" = "Private Subnet" 9 | } 10 | } 11 | 12 | resource "aws_subnet" "private-us-east-1b" { 13 | vpc_id = aws_vpc.mycustomvpc.id 14 | cidr_block = "10.0.2.0/24" 15 | availability_zone = "us-east-1b" 16 | 17 | tags = { 18 | "subnet" = "private-us-east-1b" 19 | "Name" = "Private Subnet" 20 | } 21 | } 22 | 23 | resource "aws_subnet" "public-us-east-1a" { 24 | vpc_id = aws_vpc.mycustomvpc.id 25 | cidr_block = "10.0.3.0/24" 26 | availability_zone = "us-east-1a" 27 | map_public_ip_on_launch = true 28 | 29 | tags = { 30 | "subnet" = "public-us-east-1a" 31 | "Name" = "Public Subnet" 32 | } 33 | } 34 | 35 | resource "aws_subnet" "public-us-east-1b" { 36 | vpc_id = aws_vpc.mycustomvpc.id 37 | cidr_block = "10.0.4.0/24" 38 | availability_zone = "us-east-1b" 39 | map_public_ip_on_launch = true 40 | 41 | tags = { 42 | "subnet" = "public-us-east-1b" 43 | "Name" = "Public Subnet" 44 | } 45 | } -------------------------------------------------------------------------------- /iac/k8s/keda/keda-demo/pod-identity/002.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: keda.sh/v1alpha1 2 | kind: ScaledObject 3 | metadata: 4 | name: my-app-keda-scaledobject 5 | namespace: keda-demo 6 | spec: 7 | scaleTargetRef: 8 | name: my-app-keda 9 | kind: Deployment 10 | pollingInterval: 5 # Optional: Set the polling interval for the scaler in seconds 11 | cooldownPeriod: 20 # Optional: Set the cooldown period for the scaler in seconds 12 | minReplicaCount: 0 # Set the minimum number of replicas 13 | maxReplicaCount: 10 # Set the maximum number of replicas 14 | triggers: 15 | - type: aws-sqs-queue 16 | authenticationRef: 17 | name: keda-trigger-auth-aws 18 | metadata: 19 | queueURL: https://sqs.us-east-1.amazonaws.com/458419607076/MySQSQueue-ForKEDA-Demo 20 | queueLength: "5" # batch size 21 | awsRegion: us-east-1 22 | identityOwner: pod # Default is Pod but can be operator as well. DEPRECATED: This parameter is deprecated as of KEDA v2.13 and will be removed in v3. 23 | --- 24 | apiVersion: keda.sh/v1alpha1 25 | kind: TriggerAuthentication 26 | metadata: 27 | name: keda-trigger-auth-aws 28 | namespace: keda-demo 29 | spec: 30 | podIdentity: 31 | provider: aws-kiam # or aws-eks -------------------------------------------------------------------------------- /iac/k8s/alb-demo/rbac-role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: alb-ingress-controller 7 | name: alb-ingress-controller 8 | rules: 9 | - apiGroups: 10 | - "" 11 | - extensions 12 | resources: 13 | - configmaps 14 | - endpoints 15 | - events 16 | - ingresses 17 | - ingresses/status 18 | - services 19 | verbs: 20 | - create 21 | - get 22 | - list 23 | - update 24 | - watch 25 | - patch 26 | - apiGroups: 27 | - "" 28 | - extensions 29 | resources: 30 | - nodes 31 | - pods 32 | - secrets 33 | - services 34 | - namespaces 35 | verbs: 36 | - get 37 | - list 38 | - watch 39 | --- 40 | apiVersion: rbac.authorization.k8s.io/v1 41 | kind: ClusterRoleBinding 42 | metadata: 43 | labels: 44 | app.kubernetes.io/name: alb-ingress-controller 45 | name: alb-ingress-controller 46 | roleRef: 47 | apiGroup: rbac.authorization.k8s.io 48 | kind: ClusterRole 49 | name: alb-ingress-controller 50 | subjects: 51 | - kind: ServiceAccount 52 | name: alb-ingress-controller 53 | namespace: kube-system 54 | -------------------------------------------------------------------------------- /iac/aws/terraform/creating-custom-vpc/005.routes.tf: -------------------------------------------------------------------------------- 1 | resource "aws_route_table" "privateroute" { 2 | vpc_id = aws_vpc.mycustomvpc.id 3 | 4 | route { 5 | cidr_block = "0.0.0.0/0" 6 | nat_gateway_id = aws_nat_gateway.nat.id 7 | } 8 | 9 | tags = { 10 | Name = "private" 11 | } 12 | } 13 | 14 | resource "aws_route_table" "publicroute" { 15 | vpc_id = aws_vpc.mycustomvpc.id 16 | 17 | route { 18 | cidr_block = "0.0.0.0/0" 19 | gateway_id = aws_internet_gateway.igw.id 20 | } 21 | 22 | tags = { 23 | Name = "public" 24 | } 25 | } 26 | 27 | resource "aws_route_table_association" "privateassociation_a" { 28 | subnet_id = aws_subnet.private-us-east-1a.id 29 | route_table_id = aws_route_table.privateroute.id 30 | } 31 | resource "aws_route_table_association" "privateassociation_b" { 32 | subnet_id = aws_subnet.private-us-east-1b.id 33 | route_table_id = aws_route_table.privateroute.id 34 | } 35 | resource "aws_route_table_association" "publicassociation_a" { 36 | subnet_id = aws_subnet.public-us-east-1a.id 37 | route_table_id = aws_route_table.publicroute.id 38 | } 39 | resource "aws_route_table_association" "publicassociation_b" { 40 | subnet_id = aws_subnet.public-us-east-1b.id 41 | route_table_id = aws_route_table.publicroute.id 42 | } -------------------------------------------------------------------------------- /iac/k8s/alb-demo/nginx-ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: "nginx-ingress" 5 | namespace: "default" 6 | annotations: 7 | kubernetes.io/ingress.class: alb 8 | alb.ingress.kubernetes.io/scheme: internet-facing 9 | alb.ingress.kubernetes.io/security-groups: sg-014b302d73097d083 10 | # alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}' 11 | # alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:195725532069:certificate/b6a9e691-b807-4f10-a0bf-0449730ecdf4 12 | # alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' 13 | # alb.ingress.kubernetes.io/backend-protocol: HTTPS 14 | #alb.ingress.kubernetes.io/load-balancer-attributes: "60" 15 | #alb.ingress.kubernetes.io/rewrite-target: / 16 | labels: 17 | app: nginx-ingress 18 | spec: 19 | rules: 20 | - http: 21 | paths: 22 | # - path: /* 23 | # pathType: Prefix 24 | # backend: 25 | # service: 26 | # name: ssl-redirect 27 | # port: 28 | # number: use-annotation 29 | - path: /foo 30 | pathType: Prefix 31 | backend: 32 | service: 33 | name: "nginx-service" 34 | port: 35 | number: 80 36 | - path: /* 37 | pathType: Prefix 38 | backend: 39 | service: 40 | name: "mydocker-svc" 41 | port: 42 | number: 8080 43 | 44 | -------------------------------------------------------------------------------- /iac/k8s/eks-create/rbac-role-alb-ingress-controller.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: alb-ingress-controller 7 | name: alb-ingress-controller 8 | rules: 9 | - apiGroups: 10 | - "" 11 | - extensions 12 | resources: 13 | - configmaps 14 | - endpoints 15 | - events 16 | - ingresses 17 | - ingresses/status 18 | - services 19 | - pods/status 20 | verbs: 21 | - create 22 | - get 23 | - list 24 | - update 25 | - watch 26 | - patch 27 | - apiGroups: 28 | - "" 29 | - extensions 30 | resources: 31 | - nodes 32 | - pods 33 | - secrets 34 | - services 35 | - namespaces 36 | verbs: 37 | - get 38 | - list 39 | - watch 40 | --- 41 | apiVersion: rbac.authorization.k8s.io/v1 42 | kind: ClusterRoleBinding 43 | metadata: 44 | labels: 45 | app.kubernetes.io/name: alb-ingress-controller 46 | name: alb-ingress-controller 47 | roleRef: 48 | apiGroup: rbac.authorization.k8s.io 49 | kind: ClusterRole 50 | name: alb-ingress-controller 51 | subjects: 52 | - kind: ServiceAccount 53 | name: alb-ingress-controller 54 | namespace: kube-system 55 | --- 56 | apiVersion: v1 57 | kind: ServiceAccount 58 | metadata: 59 | labels: 60 | app.kubernetes.io/name: alb-ingress-controller 61 | name: alb-ingress-controller 62 | namespace: kube-system 63 | annotations: 64 | eks.amazonaws.com/role-arn: arn:aws:iam::195725532069:role/eks-alb-ingress-controller 65 | ... 66 | -------------------------------------------------------------------------------- /iac/k8s/keda/hpa-demo/000_hpa_demo.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | creationTimestamp: null 5 | name: hpa-demo 6 | spec: {} 7 | status: {} 8 | --- 9 | apiVersion: apps/v1 10 | kind: Deployment 11 | metadata: 12 | labels: 13 | app: my-app 14 | name: my-app 15 | namespace: hpa-demo 16 | spec: 17 | replicas: 1 18 | selector: 19 | matchLabels: 20 | app: my-app 21 | template: 22 | metadata: 23 | labels: 24 | app: my-app 25 | spec: 26 | containers: 27 | - image: vinod827/keda-demo:1.0.0 28 | name: my-app 29 | ports: 30 | - containerPort: 80 31 | resources: 32 | requests: 33 | cpu: "100m" # request 100 milliCPU (0.1 CPU) 34 | memory: "128Mi" # request 128 Mebibytes of memory 35 | limits: 36 | cpu: "200m" # limit to 200 milliCPU (0.2 CPU) 37 | memory: "256Mi" # limit to 256 Mebibytes of memory 38 | --- 39 | apiVersion: v1 40 | kind: Service 41 | metadata: 42 | name: my-app-svc 43 | namespace: hpa-demo 44 | spec: 45 | selector: 46 | app: my-app 47 | ports: 48 | - protocol: TCP 49 | port: 80 50 | targetPort: 80 51 | type: LoadBalancer 52 | --- 53 | apiVersion: autoscaling/v1 54 | kind: HorizontalPodAutoscaler 55 | metadata: 56 | name: my-app-hpa 57 | namespace: hpa-demo 58 | spec: 59 | scaleTargetRef: 60 | apiVersion: apps/v1 61 | kind: Deployment 62 | name: my-app 63 | minReplicas: 1 # Set the minimum number of replicas 64 | maxReplicas: 2 # Set the maximum number of replicas 65 | targetCPUUtilizationPercentage: 75 66 | -------------------------------------------------------------------------------- /iac/aws/eks/keda/scalers/datadog/003-datadog-nginx-deploy.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: nginx-conf 5 | data: 6 | status.conf: | 7 | server { 8 | listen 81; 9 | location /nginx_status { 10 | stub_status on; 11 | } 12 | } 13 | --- 14 | apiVersion: apps/v1 15 | kind: Deployment 16 | metadata: 17 | labels: 18 | app: nginx 19 | name: nginx 20 | spec: 21 | replicas: 1 22 | selector: 23 | matchLabels: 24 | app: nginx 25 | strategy: {} 26 | template: 27 | metadata: 28 | annotations: 29 | ad.datadoghq.com/nginx.check_names: '["nginx"]' 30 | ad.datadoghq.com/nginx.init_configs: '[{}]' 31 | ad.datadoghq.com/nginx.instances: | 32 | [ 33 | { 34 | "nginx_status_url":"http://%%host%%:81/nginx_status/" 35 | } 36 | ] 37 | labels: 38 | app: nginx 39 | name: nginx 40 | spec: 41 | containers: 42 | - image: nginx 43 | name: nginx 44 | ports: 45 | - containerPort: 80 46 | - containerPort: 81 47 | volumeMounts: 48 | - mountPath: /etc/nginx/conf.d/status.conf 49 | subPath: status.conf 50 | readOnly: true 51 | name: "config" 52 | volumes: 53 | - name: "config" 54 | configMap: 55 | name: "nginx-conf" 56 | status: {} 57 | --- 58 | apiVersion: v1 59 | kind: Service 60 | metadata: 61 | labels: 62 | app: nginx 63 | name: nginx 64 | spec: 65 | ports: 66 | - port: 80 67 | protocol: TCP 68 | targetPort: 80 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /iac/aws/sam/my-app/app.js: -------------------------------------------------------------------------------- 1 | const AWS = require("aws-sdk"); 2 | 3 | //All Cold Start Code of Lambda goes here 4 | const dynamodb = new AWS.DynamoDB.DocumentClient(); 5 | const ddbTable = process.env.RUNTIME_DDB_TABLE_NAME; 6 | 7 | //All Warm Start Code of Lambda goes withing LambdaHandler 8 | exports.lambdaHandler = async (event, context) => { 9 | let body; 10 | let statusCode = 200; 11 | const headers = { 12 | 'Content-Type': 'application/json', 13 | 'Access-Control-Allow-Headers': 'Content-Type', 14 | 'Access-Control-Allow-Origin': '*', 15 | 'Access-Control-Allow-Methods': 'GET' 16 | }; 17 | 18 | try { 19 | // Adding random data to DynamoDB table - Adding static data just for this Sample App otherwise data will be populated based on parsing the Request 20 | await dynamodb 21 | .put({ 22 | TableName: ddbTable, 23 | Item: { 24 | 'id': 'E1001', 25 | 'name': 'Vinod Kumar', 26 | 'company': 'My Company', 27 | 'email': 'vinod827@yahoo.com' 28 | } 29 | }) 30 | .promise(); 31 | 32 | // Retrieving data from DynamoDB table - Just from demo purpose only otherwise data will be fetched based on given employee id sent dynamically through headers 33 | body = await dynamodb.get({ TableName: ddbTable, Key: { id: 'E1001' }}).promise(); // To retrive 1 item 34 | 35 | } catch (err) { 36 | console.log(err); 37 | statusCode = 400; 38 | body = err.message; 39 | return err; 40 | 41 | } finally { 42 | body = JSON.stringify(body); 43 | 44 | } 45 | 46 | //return response 47 | return { 48 | statusCode, 49 | body, 50 | headers 51 | }; 52 | 53 | }; -------------------------------------------------------------------------------- /iac/k8s/cluster-gcp/install_worker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Source: http://kubernetes.io/docs/getting-started-guides/kubeadm/ 4 | 5 | ### setup terminal 6 | apt-get install -y bash-completion binutils 7 | echo 'colorscheme ron' >> ~/.vimrc 8 | echo 'set tabstop=2' >> ~/.vimrc 9 | echo 'set shiftwidth=2' >> ~/.vimrc 10 | echo 'set expandtab' >> ~/.vimrc 11 | echo 'source <(kubectl completion bash)' >> ~/.bashrc 12 | echo 'alias k=kubectl' >> ~/.bashrc 13 | echo 'alias c=clear' >> ~/.bashrc 14 | echo 'complete -F __start_kubectl k' >> ~/.bashrc 15 | sed -i '1s/^/force_color_prompt=yes\n/' ~/.bashrc 16 | 17 | 18 | ### install k8s and docker 19 | apt-get remove -y docker.io kubelet kubeadm kubectl kubernetes-cni 20 | apt-get autoremove -y 21 | apt-get install -y etcd-client vim build-essential 22 | 23 | systemctl daemon-reload 24 | curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - 25 | cat < /etc/apt/sources.list.d/kubernetes.list 26 | deb http://apt.kubernetes.io/ kubernetes-xenial main 27 | EOF 28 | KUBE_VERSION=1.21.0 29 | apt-get update 30 | apt-get install -y docker.io kubelet=${KUBE_VERSION}-00 kubeadm=${KUBE_VERSION}-00 kubectl=${KUBE_VERSION}-00 kubernetes-cni=0.8.7-00 31 | 32 | cat > /etc/docker/daemon.json < # Required. 7 | # AWS_SECRET_ACCESS_KEY: # Required. 8 | # AWS_SESSION_TOKEN: # Required when using temporary credentials. 9 | # --- 10 | # apiVersion: keda.sh/v1alpha1 11 | # kind: TriggerAuthentication 12 | # metadata: 13 | # name: keda-trigger-auth-aws-credentials 14 | # namespace: keda-test 15 | # spec: 16 | # secretTargetRef: 17 | # - parameter: awsAccessKeyID # Required. 18 | # name: test-secrets # Required. 19 | # key: AWS_ACCESS_KEY_ID # Required. 20 | # - parameter: awsSecretAccessKey # Required. 21 | # name: test-secrets # Required. 22 | # key: AWS_SECRET_ACCESS_KEY # Required. 23 | # - parameter: awsSessionToken # Required when using temporary credentials. 24 | # name: test-secrets # Required when using temporary credentials. 25 | # key: AWS_SESSION_TOKEN # Required when using temporary credentials. 26 | --- 27 | apiVersion: keda.sh/v1alpha1 28 | kind: ScaledObject 29 | metadata: 30 | name: aws-sqs-queue-scaledobject 31 | namespace: default 32 | spec: 33 | scaleTargetRef: 34 | name: my-nginx 35 | pollingInterval: 5 #Interval for polling 36 | cooldownPeriod: 10 37 | idleReplicaCount: 0 # When idle, scale-in to 0 pods 38 | minReplicaCount: 1 39 | maxReplicaCount: 3 40 | fallback: # Fallback strategy when metrics are unavailable for the apps 41 | failureThreshold: 5 #when metrics are unavailable, match the desired state of replicas -> 2 42 | replicas: 2 #Keep this desired state when metrics are unavailable 43 | triggers: 44 | - type: aws-sqs-queue 45 | authenticationRef: 46 | name: keda-trigger-auth-aws-credentials 47 | metadata: 48 | queueURL: https://sqs.us-east-2.amazonaws.com/711164302624/my-sqs-keda 49 | queueLength: "5" #batch size 50 | awsRegion: "us-east-2" 51 | #identityOwner: pod 52 | identityOwner: operator #when node role has required permission -------------------------------------------------------------------------------- /iac/demo/keda/003-sqs-scaler.yml: -------------------------------------------------------------------------------- 1 | # apiVersion: v1 2 | # kind: Secret 3 | # metadata: 4 | # name: test-secrets 5 | # data: 6 | # AWS_ACCESS_KEY_ID: # Required. 7 | # AWS_SECRET_ACCESS_KEY: # Required. 8 | # AWS_SESSION_TOKEN: # Required when using temporary credentials. 9 | # --- 10 | # apiVersion: keda.sh/v1alpha1 11 | # kind: TriggerAuthentication 12 | # metadata: 13 | # name: keda-trigger-auth-aws-credentials 14 | # namespace: keda-test 15 | # spec: 16 | # secretTargetRef: 17 | # - parameter: awsAccessKeyID # Required. 18 | # name: test-secrets # Required. 19 | # key: AWS_ACCESS_KEY_ID # Required. 20 | # - parameter: awsSecretAccessKey # Required. 21 | # name: test-secrets # Required. 22 | # key: AWS_SECRET_ACCESS_KEY # Required. 23 | # - parameter: awsSessionToken # Required when using temporary credentials. 24 | # name: test-secrets # Required when using temporary credentials. 25 | # key: AWS_SESSION_TOKEN # Required when using temporary credentials. 26 | --- 27 | apiVersion: keda.sh/v1alpha1 28 | kind: ScaledObject 29 | metadata: 30 | name: aws-sqs-queue-scaledobject 31 | namespace: default 32 | spec: 33 | scaleTargetRef: 34 | name: my-nginx 35 | pollingInterval: 5 #Interval for polling 36 | cooldownPeriod: 10 37 | idleReplicaCount: 0 # When idle, scale-in to 0 pods 38 | minReplicaCount: 1 39 | maxReplicaCount: 3 40 | fallback: # Fallback strategy when metrics are unavailable for the apps 41 | failureThreshold: 5 #when metrics are unavailable, match the desired state of replicas -> 2 42 | replicas: 2 #Keep this desired state when metrics are unavailable 43 | triggers: 44 | - type: aws-sqs-queue 45 | authenticationRef: 46 | name: keda-trigger-auth-aws-credentials 47 | metadata: 48 | queueURL: https://sqs.us-east-2.amazonaws.com/711164302624/my-sqs-keda 49 | queueLength: "5" #batch size 50 | awsRegion: "us-east-2" 51 | #identityOwner: pod 52 | identityOwner: operator #when node role has required permission -------------------------------------------------------------------------------- /iac/k8s/cluster-gcp/install_master.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Source: http://kubernetes.io/docs/getting-started-guides/kubeadm/ 4 | 5 | ### setup terminal 6 | apt-get install -y bash-completion binutils 7 | echo 'colorscheme ron' >> ~/.vimrc 8 | echo 'set tabstop=2' >> ~/.vimrc 9 | echo 'set shiftwidth=2' >> ~/.vimrc 10 | echo 'set expandtab' >> ~/.vimrc 11 | echo 'source <(kubectl completion bash)' >> ~/.bashrc 12 | echo 'alias k=kubectl' >> ~/.bashrc 13 | echo 'alias c=clear' >> ~/.bashrc 14 | echo 'complete -F __start_kubectl k' >> ~/.bashrc 15 | sed -i '1s/^/force_color_prompt=yes\n/' ~/.bashrc 16 | 17 | 18 | ### install k8s and docker 19 | apt-get remove -y docker.io kubelet kubeadm kubectl kubernetes-cni 20 | apt-get autoremove -y 21 | apt-get install -y etcd-client vim build-essential 22 | 23 | systemctl daemon-reload 24 | curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - 25 | cat < /etc/apt/sources.list.d/kubernetes.list 26 | deb http://apt.kubernetes.io/ kubernetes-xenial main 27 | EOF 28 | KUBE_VERSION=1.21.0 29 | apt-get update 30 | apt-get install -y docker.io kubelet=${KUBE_VERSION}-00 kubeadm=${KUBE_VERSION}-00 kubectl=${KUBE_VERSION}-00 kubernetes-cni=0.8.7-00 31 | 32 | cat > /etc/docker/daemon.json < # Required 44 | AWS_SECRET_ACCESS_KEY: <> # Required. 45 | --- 46 | apiVersion: keda.sh/v1alpha1 47 | kind: TriggerAuthentication 48 | metadata: 49 | name: keda-trigger-auth-aws 50 | namespace: keda-demo 51 | spec: 52 | secretTargetRef: 53 | - parameter: awsAccessKeyID # Required. 54 | name: test-secrets # Required. 55 | key: AWS_ACCESS_KEY_ID # Required. 56 | - parameter: awsSecretAccessKey # Required. 57 | name: test-secrets # Required. 58 | key: AWS_SECRET_ACCESS_KEY # Required. 59 | --- 60 | apiVersion: keda.sh/v1alpha1 61 | kind: ScaledObject 62 | metadata: 63 | name: my-app-scaledobject 64 | namespace: keda-demo 65 | #annotations: 66 | #autoscaling.keda.sh/paused-replicas: "0" # Optional. Use to pause autoscaling of objects 67 | #autoscaling.keda.sh/paused: "true" # Optional. Use to pause autoscaling of objects explicitly 68 | spec: 69 | scaleTargetRef: 70 | name: my-app-keda 71 | kind: Deployment 72 | pollingInterval: 5 # Optional: Set the polling interval for the scaler in seconds 73 | cooldownPeriod: 20 # Optional: Set the cooldown period for the scaler in seconds 74 | minReplicaCount: 0 # Set the minimum number of replicas 75 | maxReplicaCount: 10 # Set the maximum number of replicas 76 | #fallback: # Optional: Fallback strategy when metrics are unavailable for the apps 77 | #failureThreshold: 5 78 | #replicas: 1 # Keep this desired state when metrics are unavailable 79 | triggers: 80 | - type: aws-sqs-queue 81 | authenticationRef: 82 | name: keda-trigger-auth-aws 83 | metadata: 84 | queueURL: https://sqs.us-east-1.amazonaws.com/458419607076/MySQSQueue-ForKEDA-Demo 85 | queueLength: "5" # batch size 86 | awsRegion: us-east-1 87 | -------------------------------------------------------------------------------- /iac/k8s/eks-create/alb-ingress-controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: alb-ingress-controller 6 | name: alb-ingress-controller 7 | namespace: kube-system 8 | spec: 9 | selector: 10 | matchLabels: 11 | app.kubernetes.io/name: alb-ingress-controller 12 | template: 13 | metadata: 14 | labels: 15 | app.kubernetes.io/name: alb-ingress-controller 16 | spec: 17 | containers: 18 | - name: alb-ingress-controller 19 | args: 20 | # Limit the namespace where this ALB Ingress Controller deployment will 21 | # resolve ingress resources. If left commented, all namespaces are used. 22 | # - --watch-namespace=your-k8s-namespace 23 | 24 | # Setting the ingress-class flag below ensures that only ingress resources with the 25 | # annotation kubernetes.io/ingress.class: "alb" are respected by the controller. You may 26 | # choose any class you'd like for this controller to respect. 27 | - --ingress-class=alb 28 | 29 | # REQUIRED 30 | # Name of your cluster. Used when naming resources created 31 | # by the ALB Ingress Controller, providing distinction between 32 | # clusters. 33 | - --cluster-name=containerd-eks 34 | 35 | # AWS VPC ID this ingress controller will use to create AWS resources. 36 | # If unspecified, it will be discovered from ec2metadata. 37 | #- --aws-vpc-id=vpc-0a115322796facb38 38 | 39 | # AWS region this ingress controller will operate in. 40 | # If unspecified, it will be discovered from ec2metadata. 41 | # List of regions: http://docs.aws.amazon.com/general/latest/gr/rande.html#vpc_region 42 | #- --aws-region=us-east-1 43 | 44 | # Enables logging on all outbound requests sent to the AWS API. 45 | # If logging is desired, set to true. 46 | # - --aws-api-debug 47 | 48 | # Maximum number of times to retry the aws calls. 49 | # defaults to 10. 50 | # - --aws-max-retries=10 51 | env: 52 | # AWS key id for authenticating with the AWS API. 53 | # This is only here for examples. It's recommended you instead use 54 | # a project like kube2iam for granting access. 55 | # - name: AWS_ACCESS_KEY_ID 56 | # value: KEYVALUE 57 | 58 | # AWS key secret for authenticating with the AWS API. 59 | # This is only here for examples. It's recommended you instead use 60 | # a project like kube2iam for granting access. 61 | # - name: AWS_SECRET_ACCESS_KEY 62 | # value: SECRETVALUE 63 | # Repository location of the ALB Ingress Controller. 64 | image: docker.io/amazon/aws-alb-ingress-controller:v1.1.8 65 | serviceAccountName: alb-ingress-controller 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/d6a38ef88cf741f6a350e1fedf59311c)](https://app.codacy.com/gh/vinod827/cloudkit-lab?utm_source=github.com&utm_medium=referral&utm_content=vinod827/cloudkit-lab&utm_campaign=Badge_Grade_Settings) 3 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/665c8926c3374c3bb8c19f6932e5eee2)](https://app.codacy.com/gh/vinod827/cloudkit-lab?utm_source=github.com&utm_medium=referral&utm_content=vinod827/cloudkit-lab&utm_campaign=Badge_Grade_Settings) 4 | 5 | # cloudkit-lab 🐳🚀 6 | 7 | ## 🚀 Overview 8 | `cloudkit-lab` is a collection of Kubernetes configurations, Infrastructure as Code (IaC) templates, and deployment strategies designed to simplify cloud-native application management. It includes Helm charts, KEDA-based autoscaling, CI/CD configurations, and other resources to help developers and DevOps engineers manage Kubernetes workloads efficiently. 9 | 10 | ## 📌 Features 11 | - ⚙️ **Kubernetes manifests** for various workloads. 12 | - 📦 **Helm charts** and **Kustomize** configurations. 13 | - 🔄 **CI/CD automation** with GitHub Actions. 14 | - 🌍 **Infrastructure as Code (IaC)** using Terraform. 15 | - 📈 **KEDA-based event-driven scaling.** 16 | - 🔒 **Security policies** with Kyverno. 17 | - 🐳 **Docker and containerization** best practices. 18 | - 🖥️ **Sample applications** to demonstrate Kubernetes concepts. 19 | 20 | ## 📁 Repository Structure 21 | ```plaintext 22 | cloudkit-lab/ 23 | │-- .github/workflows/ # CI/CD workflows for GitHub Actions 24 | │-- iac/ # Infrastructure as Code resources 25 | │ ├── k8s/ # Kubernetes-specific configurations 26 | │ ├── terraform/ # Terraform modules 27 | │-- webapp/ # Sample application for deployment 28 | │-- Dockerfile # Containerization setup 29 | │-- allow.yaml # Security policy example 30 | │-- disallow.yaml # Security restriction policy 31 | ``` 32 | 33 | ## 🚀 Getting Started 34 | ### 🛠 Prerequisites 35 | Ensure you have the following installed: 36 | - 🐳 [Docker](https://www.docker.com/) 37 | - ☸️ [Kubernetes (kubectl)](https://kubernetes.io/docs/tasks/tools/) 38 | - ⛵ [Helm](https://helm.sh/) 39 | - 🌍 [Terraform](https://www.terraform.io/) 40 | - 📈 [KEDA](https://keda.sh/) 41 | - 🔒 [Kyverno](https://kyverno.io/) 42 | 43 | ### 🏗 Setup 44 | 1. Clone the repository: 45 | ```sh 46 | git clone https://github.com/vinod827/cloudkit-lab.git 47 | cd cloudkit-lab 48 | ``` 49 | 2. Deploy using Helm: 50 | ```sh 51 | helm install my-app ./helm-chart 52 | ``` 53 | 3. Apply Kubernetes manifests manually: 54 | ```sh 55 | kubectl apply -f iac/k8s/ 56 | ``` 57 | 58 | ## 🤝 Contributing 59 | Contributions are welcome! Please submit a pull request or create an issue for discussions. 60 | 61 | ## 📜 License 🏛️ 62 | This project is licensed under the MIT License. 63 | 64 | --- 65 | 66 | ### 👤 Author 67 | **Vinod Kumar Nair** 68 | 📧 vinod827@gmail.com 69 | 📍 [LinkedIn](https://www.linkedin.com/in/vinod827/) 70 | 71 | -------------------------------------------------------------------------------- /iac/demo/zerotrust/vault-s3-uploader.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: vault-s3-uploader-config 5 | namespace: vault 6 | data: 7 | VAULT_ADDR: "http://vault.vault.svc.cluster.local:8200" 8 | VAULT_ROLE: "dev-role" 9 | S3_BUCKET: "hashitalks2025-zerotrust" 10 | S3_REGION: "us-east-1" 11 | --- 12 | apiVersion: v1 13 | kind: Pod 14 | metadata: 15 | name: vault-s3-uploader 16 | namespace: vault 17 | labels: 18 | app: vault-s3-uploader 19 | spec: 20 | serviceAccountName: vault-sa # Bind to the service account 21 | imagePullSecrets: 22 | - name: ecr-secret # Reference the secret for pulling ECR images 23 | containers: 24 | - name: s3-uploader 25 | image: 730335385934.dkr.ecr.us-east-1.amazonaws.com/hashitalks2025:v1.0.0 26 | resources: 27 | limits: 28 | memory: "128Mi" 29 | cpu: "500m" 30 | requests: 31 | memory: "64Mi" 32 | cpu: "250m" 33 | env: 34 | - name: VAULT_ADDR 35 | valueFrom: 36 | configMapKeyRef: 37 | name: vault-s3-uploader-config 38 | key: VAULT_ADDR 39 | - name: VAULT_ROLE 40 | valueFrom: 41 | configMapKeyRef: 42 | name: vault-s3-uploader-config 43 | key: VAULT_ROLE 44 | - name: S3_BUCKET 45 | valueFrom: 46 | configMapKeyRef: 47 | name: vault-s3-uploader-config 48 | key: S3_BUCKET 49 | command: ["/bin/sh", "-c"] 50 | args: 51 | - | 52 | echo "Retrieving AWS credentials from Vault..." 53 | VAULT_PATH="aws/creds/${VAULT_ROLE}" 54 | 55 | # Get JWT token from Kubernetes service account 56 | K8S_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) 57 | 58 | # Authenticate with Vault using Kubernetes auth 59 | VAULT_RESPONSE=$(curl -s --request POST --data "{\"jwt\": \"${K8S_TOKEN}\", \"role\": \"${VAULT_ROLE}\"}" ${VAULT_ADDR}/v1/auth/kubernetes/login) 60 | VAULT_TOKEN=$(echo "$VAULT_RESPONSE" | jq -r '.auth.client_token') 61 | 62 | # Get temporary AWS credentials (IAM User) 63 | CREDS=$(curl -s --header "X-Vault-Token: ${VAULT_TOKEN}" ${VAULT_ADDR}/v1/${VAULT_PATH}) 64 | AWS_ACCESS_KEY_ID=$(echo "$CREDS" | jq -r '.data.access_key') 65 | AWS_SECRET_ACCESS_KEY=$(echo "$CREDS" | jq -r '.data.secret_key') 66 | 67 | if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then 68 | echo "Failed to retrieve AWS credentials from Vault. Exiting..." 69 | exit 1 70 | fi 71 | 72 | # Export credentials for AWS CLI 73 | export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY 74 | echo "AWS credentials retrieved successfully." 75 | sleep 4 76 | 77 | 78 | # Creating a dummy file 79 | echo "Hello everyone! Welcome to the HashiTalks 2025" > hashitalks.txt 80 | aws s3 cp hashitalks.txt s3://$S3_BUCKET/ 81 | 82 | echo "Upload completed!" 83 | volumeMounts: 84 | - name: sa-token 85 | mountPath: /var/run/secrets/kubernetes.io/serviceaccount 86 | readOnly: true 87 | volumes: 88 | - name: sa-token 89 | projected: 90 | sources: 91 | - serviceAccountToken: 92 | path: token 93 | expirationSeconds: 600 94 | audience: vault 95 | restartPolicy: Never 96 | -------------------------------------------------------------------------------- /iac/k8s/falco/README.md: -------------------------------------------------------------------------------- 1 | # 🔒 Falco Security Demo on Kubernetes 2 | 3 | This repository contains a demo setup for **[Falco](https://falco.org/)**, an open-source runtime security tool that detects unexpected behavior in containers and hosts. This guide includes installation, rule customization, Slack integration, and UI setup using **Helm** and **Falcosidekick**. 4 | 5 | --- 6 | 7 | ## 🚀 Prerequisites 8 | 9 | - Kubernetes cluster (local or cloud) 10 | - `kubectl` CLI configured 11 | - [Helm](https://helm.sh/) installed 12 | - Access to a Slack Webhook (optional for Slack alerting) 13 | 14 | --- 15 | 16 | ## 📦 Step-by-Step Setup 17 | 18 | ### 1. Add the Falco Helm Chart 19 | 20 | ```bash 21 | helm repo add falcosecurity https://falcosecurity.github.io/charts 22 | helm repo update 23 | ``` 24 | 25 | ### 2. Install Falco with Helm 26 | 27 | ```bash 28 | helm install --replace falco \ 29 | --namespace falco \ 30 | --create-namespace \ 31 | --set tty=true \ 32 | falcosecurity/falco 33 | ``` 34 | 35 | > This installs Falco in the `falco` namespace with basic settings. 36 | 37 | --- 38 | 39 | ### 3. Upgrade with Custom Rules and Enable Falcosidekick UI 40 | 41 | ```bash 42 | helm upgrade -n falco falco falcosecurity/falco \ 43 | -f custom_falco_rules.yaml \ 44 | --set falcosidekick.enabled=true \ 45 | --set falcosidekick.webui.enabled=true 46 | ``` 47 | 48 | > Ensure `custom_falco_rules.yaml` is in your working directory and includes your custom rules. 49 | 50 | --- 51 | 52 | ### 4. Access the Falcosidekick Web UI 53 | 54 | ```bash 55 | kubectl port-forward -n falco svc/falco-falcosidekick-ui 2802 56 | ``` 57 | 58 | > Access the UI at: `http://localhost:2802` 59 | 60 | --- 61 | 62 | ### 5. (Optional) Enable Slack Integration for Alerts 63 | 64 | ```bash 65 | helm upgrade -n falco falco falcosecurity/falco \ 66 | --reuse-values \ 67 | --set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/services/TXXXX/BXXXX/XXXX" \ 68 | --set falcosidekick.config.slack.outputformat="fields" \ 69 | --set falcosidekick.config.slack.minimumpriority="warning" 70 | ``` 71 | 72 | > Replace the webhook URL with your own Slack Incoming Webhook. 73 | 74 | --- 75 | 76 | ## 🔍 Useful Commands 77 | 78 | ### Check Falco Resources 79 | 80 | ```bash 81 | kubectl get po -n falco 82 | kubectl get cm -n falco 83 | ``` 84 | 85 | ### View Falco ConfigMap & Rules 86 | 87 | ```bash 88 | kubectl get cm falco -n falco -o yaml 89 | kubectl get cm falco-rules -n falco -o yaml 90 | ``` 91 | 92 | ### View Falco Logs for Critical Alerts 93 | 94 | ```bash 95 | kubectl logs -n falco -f | grep "Critical Container" 96 | ``` 97 | 98 | > Replace `` with your actual pod name. You can find it with `kubectl get po -n falco`. 99 | 100 | --- 101 | 102 | ## Demo Link 103 | https://drive.google.com/file/d/1Svv0lSg4_Xgf6cVzdXJeduvUmRqtkQRy/view?usp=sharing 104 | 105 | --- 106 | 107 | ## 🛡️ About Falco 108 | 109 | Falco lets you: 110 | 111 | - Detect container breakouts 112 | - Monitor system calls in real-time 113 | - Create custom detection rules 114 | - Trigger alerts to Slack, Webhooks, etc. 115 | 116 | Learn more at: [https://falco.org/docs](https://falco.org/docs) 117 | 118 | --- 119 | 120 | ## 📄 License 121 | 122 | This project follows the [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0). 123 | 124 | --- 125 | 126 | ## 🙌 Acknowledgements 127 | 128 | Thanks to the [Falco Security](https://github.com/falcosecurity) community and the maintainers of [Falcosidekick](https://github.com/falcosecurity/falcosidekick) for providing powerful tools to secure Kubernetes workloads. 129 | 130 | --- 131 | -------------------------------------------------------------------------------- /iac/aws/eks/amp/createIRSA-AMPQuery.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | CLUSTER_NAME=eks-amp-prometheus-grafana-demo #Replace this value by your Cluster name 3 | SERVICE_ACCOUNT_NAMESPACE=prometheus #Replace this value by your prometheus namespace 4 | AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) 5 | OIDC_PROVIDER=$(aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///") 6 | SERVICE_ACCOUNT_AMP_QUERY_NAME=amp-iamproxy-query-service-account 7 | SERVICE_ACCOUNT_IAM_AMP_QUERY_ROLE=amp-iamproxy-query-role 8 | SERVICE_ACCOUNT_IAM_AMP_QUERY_POLICY=AMPQueryPolicy 9 | # 10 | # Setup a trust policy designed for a specific combination of K8s service account and namespace to sign in from a Kubernetes cluster which hosts the OIDC Idp. 11 | # 12 | cat < TrustPolicy.json 13 | { 14 | "Version": "2012-10-17", 15 | "Statement": [ 16 | { 17 | "Effect": "Allow", 18 | "Principal": { 19 | "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER}" 20 | }, 21 | "Action": "sts:AssumeRoleWithWebIdentity", 22 | "Condition": { 23 | "StringEquals": { 24 | "${OIDC_PROVIDER}:sub": "system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_AMP_QUERY_NAME}" 25 | } 26 | } 27 | } 28 | ] 29 | } 30 | EOF 31 | # 32 | # Set up the permission policy that grants query permissions for all AMP workspaces 33 | # 34 | cat < PermissionPolicyQuery.json 35 | { 36 | "Version": "2012-10-17", 37 | "Statement": [ 38 | {"Effect": "Allow", 39 | "Action": [ 40 | "aps:QueryMetrics", 41 | "aps:GetSeries", 42 | "aps:GetLabels", 43 | "aps:GetMetricMetadata" 44 | ], 45 | "Resource": "*" 46 | } 47 | ] 48 | } 49 | EOF 50 | 51 | function getRoleArn() { 52 | OUTPUT=$(aws iam get-role --role-name $1 --query 'Role.Arn' --output text 2>&1) 53 | 54 | # Check for an expected exception 55 | if [[ $? -eq 0 ]]; then 56 | echo $OUTPUT 57 | elif [[ -n $(grep "NoSuchEntity" <<< $OUTPUT) ]]; then 58 | echo "" 59 | else 60 | >&2 echo $OUTPUT 61 | return 1 62 | fi 63 | } 64 | 65 | # 66 | # Create the IAM Role for query with the above trust policy 67 | # 68 | SERVICE_ACCOUNT_IAM_AMP_QUERY_ROLE_ARN=$(getRoleArn $SERVICE_ACCOUNT_IAM_AMP_QUERY_ROLE) 69 | if [ "$SERVICE_ACCOUNT_IAM_AMP_QUERY_ROLE_ARN" = "" ]; 70 | then 71 | # 72 | # Create the IAM role for service account 73 | # 74 | SERVICE_ACCOUNT_IAM_AMP_QUERY_ROLE_ARN=$(aws iam create-role \ 75 | --role-name $SERVICE_ACCOUNT_IAM_AMP_QUERY_ROLE \ 76 | --assume-role-policy-document file://TrustPolicy.json \ 77 | --query "Role.Arn" --output text) 78 | # 79 | # Create an IAM permission policy 80 | # 81 | SERVICE_ACCOUNT_IAM_AMP_QUERY_ARN=$(aws iam create-policy --policy-name $SERVICE_ACCOUNT_IAM_AMP_QUERY_POLICY \ 82 | --policy-document file://PermissionPolicyQuery.json \ 83 | --query 'Policy.Arn' --output text) 84 | # 85 | # Attach the required IAM policies to the IAM role create above 86 | # 87 | aws iam attach-role-policy \ 88 | --role-name $SERVICE_ACCOUNT_IAM_AMP_QUERY_ROLE \ 89 | --policy-arn $SERVICE_ACCOUNT_IAM_AMP_QUERY_ARN 90 | else 91 | echo "$SERVICE_ACCOUNT_IAM_AMP_QUERY_ROLE_ARN IAM role for query already exists" 92 | fi 93 | echo $SERVICE_ACCOUNT_IAM_AMP_QUERY_ROLE_ARN 94 | # 95 | # EKS cluster hosts an OIDC provider with a public discovery endpoint. 96 | # Associate this IdP with AWS IAM so that the latter can validate and accept the OIDC tokens issued by Kubernetes to service accounts. 97 | # Doing this with eksctl is the easier and best approach. 98 | # 99 | eksctl utils associate-iam-oidc-provider --cluster $CLUSTER_NAME --approve 100 | -------------------------------------------------------------------------------- /iac/aws/eks/amp/createIRSA-AMPIngest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | CLUSTER_NAME=eks-amp-prometheus-grafana-demo #Replace this value by your Cluster name 3 | SERVICE_ACCOUNT_NAMESPACE=prometheus #Replace this value by your prometheus namespace 4 | AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) 5 | OIDC_PROVIDER=$(aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///") 6 | SERVICE_ACCOUNT_AMP_INGEST_NAME=amp-iamproxy-ingest-service-account 7 | SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE=amp-iamproxy-ingest-role 8 | SERVICE_ACCOUNT_IAM_AMP_INGEST_POLICY=AMPIngestPolicy 9 | # 10 | # Set up a trust policy designed for a specific combination of K8s service account and namespace to sign in from a Kubernetes cluster which hosts the OIDC Idp. 11 | # 12 | cat < TrustPolicy.json 13 | { 14 | "Version": "2012-10-17", 15 | "Statement": [ 16 | { 17 | "Effect": "Allow", 18 | "Principal": { 19 | "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER}" 20 | }, 21 | "Action": "sts:AssumeRoleWithWebIdentity", 22 | "Condition": { 23 | "StringEquals": { 24 | "${OIDC_PROVIDER}:sub": "system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_AMP_INGEST_NAME}" 25 | } 26 | } 27 | } 28 | ] 29 | } 30 | EOF 31 | # 32 | # Set up the permission policy that grants ingest (remote write) permissions for all AMP workspaces 33 | # 34 | cat < PermissionPolicyIngest.json 35 | { 36 | "Version": "2012-10-17", 37 | "Statement": [ 38 | {"Effect": "Allow", 39 | "Action": [ 40 | "aps:RemoteWrite", 41 | "aps:GetSeries", 42 | "aps:GetLabels", 43 | "aps:GetMetricMetadata" 44 | ], 45 | "Resource": "*" 46 | } 47 | ] 48 | } 49 | EOF 50 | 51 | function getRoleArn() { 52 | OUTPUT=$(aws iam get-role --role-name $1 --query 'Role.Arn' --output text 2>&1) 53 | 54 | # Check for an expected exception 55 | if [[ $? -eq 0 ]]; then 56 | echo $OUTPUT 57 | elif [[ -n $(grep "NoSuchEntity" <<< $OUTPUT) ]]; then 58 | echo "" 59 | else 60 | >&2 echo $OUTPUT 61 | return 1 62 | fi 63 | } 64 | 65 | # 66 | # Create the IAM Role for ingest with the above trust policy 67 | # 68 | SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE_ARN=$(getRoleArn $SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE) 69 | if [ "$SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE_ARN" = "" ]; 70 | then 71 | # 72 | # Create the IAM role for service account 73 | # 74 | SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE_ARN=$(aws iam create-role \ 75 | --role-name $SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE \ 76 | --assume-role-policy-document file://TrustPolicy.json \ 77 | --query "Role.Arn" --output text) 78 | # 79 | # Create an IAM permission policy 80 | # 81 | SERVICE_ACCOUNT_IAM_AMP_INGEST_ARN=$(aws iam create-policy --policy-name $SERVICE_ACCOUNT_IAM_AMP_INGEST_POLICY \ 82 | --policy-document file://PermissionPolicyIngest.json \ 83 | --query 'Policy.Arn' --output text) 84 | # 85 | # Attach the required IAM policies to the IAM role created above 86 | # 87 | aws iam attach-role-policy \ 88 | --role-name $SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE \ 89 | --policy-arn $SERVICE_ACCOUNT_IAM_AMP_INGEST_ARN 90 | else 91 | echo "$SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE_ARN IAM role for ingest already exists" 92 | fi 93 | echo $SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE_ARN 94 | # 95 | # EKS cluster hosts an OIDC provider with a public discovery endpoint. 96 | # Associate this IdP with AWS IAM so that the latter can validate and accept the OIDC tokens issued by Kubernetes to service accounts. 97 | # Doing this with eksctl is the easier and best approach. 98 | # 99 | eksctl utils associate-iam-oidc-provider --cluster $CLUSTER_NAME --approve 100 | -------------------------------------------------------------------------------- /iac/k8s/fluentd/004-config-map.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | containers.conf: | 4 | 5 | @type tail 6 | @id in_tail_container_logs 7 | @label @containers 8 | path /var/log/containers/*.log 9 | pos_file /var/log/fluentd-containers.log.pos 10 | tag * 11 | read_from_head true 12 | 13 | @type json 14 | time_format %Y-%m-%dT%H:%M:%S.%NZ 15 | 16 | 17 | 18 | 48 | fluent.conf: | 49 | @include containers.conf 50 | @include systemd.conf 51 | 52 | 53 | @type null 54 | 55 | systemd.conf: | 56 | 57 | @type systemd 58 | @id in_systemd_kubelet 59 | @label @systemd 60 | filters [{ "_SYSTEMD_UNIT": "kubelet.service" }] 61 | 62 | field_map {"MESSAGE": "message", "_HOSTNAME": "hostname", "_SYSTEMD_UNIT": "systemd_unit"} 63 | field_map_strict true 64 | 65 | path /run/log/journal 66 | pos_file /var/log/fluentd-journald-kubelet.pos 67 | read_from_head true 68 | tag kubelet.service 69 | 70 | 71 | 72 | @type systemd 73 | @id in_systemd_kubeproxy 74 | @label @systemd 75 | filters [{ "_SYSTEMD_UNIT": "kubeproxy.service" }] 76 | 77 | field_map {"MESSAGE": "message", "_HOSTNAME": "hostname", "_SYSTEMD_UNIT": "systemd_unit"} 78 | field_map_strict true 79 | 80 | path /run/log/journal 81 | pos_file /var/log/fluentd-journald-kubeproxy.pos 82 | read_from_head true 83 | tag kubeproxy.service 84 | 85 | 86 | 87 | @type systemd 88 | @id in_systemd_docker 89 | @label @systemd 90 | filters [{ "_SYSTEMD_UNIT": "docker.service" }] 91 | 92 | field_map {"MESSAGE": "message", "_HOSTNAME": "hostname", "_SYSTEMD_UNIT": "systemd_unit"} 93 | field_map_strict true 94 | 95 | path /run/log/journal 96 | pos_file /var/log/fluentd-journald-docker.pos 97 | read_from_head true 98 | tag docker.service 99 | 100 | 101 | 126 | kind: ConfigMap 127 | metadata: 128 | labels: 129 | k8s-app: fluentd-cloudwatch 130 | name: fluentd-config 131 | namespace: kube-system -------------------------------------------------------------------------------- /iac/demo/textract/sqs_to_csv_lambda.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import csv 3 | import os 4 | import json 5 | from io import StringIO 6 | import datetime 7 | 8 | # Initialize clients 9 | s3_client = boto3.client('s3') 10 | sqs_client = boto3.client('sqs') 11 | 12 | # Environment variables 13 | CSV_S3_BUCKET = os.environ['CSV_S3_BUCKET'] 14 | CSV_S3_PREFIX = os.environ['CSV_S3_PREFIX'] 15 | 16 | def lambda_handler(event, context): 17 | try: 18 | for record in event['Records']: 19 | # Parse the SQS message 20 | message_body = json.loads(record['body']) 21 | print(f"Received message body: {json.dumps(message_body)}") # Log the message for inspection 22 | 23 | # Access the nested data within "Message" key 24 | message_data = json.loads(message_body['Message']) # This assumes the entire message is valid JSON 25 | 26 | # Extract required fields 27 | bucket = message_data.get('bucket') 28 | key = message_data.get('key') 29 | extracted_text = message_data.get('text') 30 | 31 | # Validate message structure (optional) 32 | if not all([bucket, key, extracted_text]): 33 | raise ValueError("Missing required fields in message body") 34 | 35 | 36 | # Prepare CSV data 37 | csv_buffer = StringIO() 38 | csv_writer = csv.writer(csv_buffer) 39 | csv_writer.writerow(['Extracted Text']) 40 | 41 | # Extract and filter text lines with confidence scores 42 | extracted_text_lines = extracted_text.splitlines() 43 | filtered_text = [] 44 | for line in extracted_text_lines: 45 | text, confidence = line.split(' (Confidence: ', 1) 46 | confidence = float(confidence.rstrip(')')) 47 | if confidence >= 70: 48 | filtered_text.append(f"{text.rstrip()} ({confidence:.2f})") 49 | 50 | # Join the filtered text lines into a single string 51 | final_text = '\n'.join(filtered_text) 52 | 53 | print('final_text::', final_text) 54 | 55 | # Write the final text to CSV 56 | csv_writer.writerow([final_text]) 57 | 58 | 59 | # # Extract and filter text lines with confidence scores 60 | # extracted_text_lines = extracted_text.splitlines() 61 | # filtered_text = [] 62 | # text_without_confidence = [] 63 | # for line in extracted_text_lines: 64 | # text, confidence = line.split(' (Confidence: ', 1) 65 | # confidence = float(confidence.rstrip(')')) 66 | # if confidence >= 70: 67 | # filtered_text.append(f"{text.rstrip()} ({confidence:.2f})") 68 | # text_without_confidence.append(text.rstrip()) # Extract only text 69 | 70 | # # Join the filtered text lines into a single string 71 | # final_text = '\n'.join(filtered_text) 72 | 73 | # # Write the final text to CSV, including extracted text without confidence 74 | # csv_writer.writerow([bucket, key, final_text, ', '.join(text_without_confidence)]) 75 | 76 | 77 | # Define the CSV file path and name 78 | timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") 79 | csv_filename = f"{CSV_S3_PREFIX}{key.replace('/', '_')}_{timestamp}.csv" 80 | 81 | # Upload CSV to S3 82 | s3_client.put_object( 83 | Bucket=CSV_S3_BUCKET, 84 | Key=csv_filename, 85 | Body=csv_buffer.getvalue() 86 | ) 87 | 88 | print(f"CSV file saved to S3: {csv_filename}") 89 | 90 | # Return a success response 91 | return { 92 | 'statusCode': 200, 93 | 'body': 'CSV files created and saved to S3' 94 | } 95 | 96 | except Exception as e: 97 | print(f"Error processing the SQS message: {str(e)}") 98 | return { 99 | 'statusCode': 500, 100 | 'body': f"Error processing file: {str(e)}" 101 | } 102 | -------------------------------------------------------------------------------- /iac/aws/sam/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=osx,node,linux,windows,sam 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | lerna-debug.log* 28 | 29 | # Diagnostic reports (https://nodejs.org/api/report.html) 30 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 31 | 32 | # Runtime data 33 | pids 34 | *.pid 35 | *.seed 36 | *.pid.lock 37 | 38 | # Directory for instrumented libs generated by jscoverage/JSCover 39 | lib-cov 40 | 41 | # Coverage directory used by tools like istanbul 42 | coverage 43 | *.lcov 44 | 45 | # nyc test coverage 46 | .nyc_output 47 | 48 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 49 | .grunt 50 | 51 | # Bower dependency directory (https://bower.io/) 52 | bower_components 53 | 54 | # node-waf configuration 55 | .lock-wscript 56 | 57 | # Compiled binary addons (https://nodejs.org/api/addons.html) 58 | build/Release 59 | 60 | # Dependency directories 61 | node_modules/ 62 | jspm_packages/ 63 | 64 | # TypeScript v1 declaration files 65 | typings/ 66 | 67 | # TypeScript cache 68 | *.tsbuildinfo 69 | 70 | # Optional npm cache directory 71 | .npm 72 | 73 | # Optional eslint cache 74 | .eslintcache 75 | 76 | # Optional stylelint cache 77 | .stylelintcache 78 | 79 | # Microbundle cache 80 | .rpt2_cache/ 81 | .rts2_cache_cjs/ 82 | .rts2_cache_es/ 83 | .rts2_cache_umd/ 84 | 85 | # Optional REPL history 86 | .node_repl_history 87 | 88 | # Output of 'npm pack' 89 | *.tgz 90 | 91 | # Yarn Integrity file 92 | .yarn-integrity 93 | 94 | # dotenv environment variables file 95 | .env 96 | .env.test 97 | .env*.local 98 | 99 | # parcel-bundler cache (https://parceljs.org/) 100 | .cache 101 | .parcel-cache 102 | 103 | # Next.js build output 104 | .next 105 | 106 | # Nuxt.js build / generate output 107 | .nuxt 108 | dist 109 | 110 | # Storybook build outputs 111 | .out 112 | .storybook-out 113 | storybook-static 114 | 115 | # rollup.js default build output 116 | dist/ 117 | 118 | # Gatsby files 119 | .cache/ 120 | # Comment in the public line in if your project uses Gatsby and not Next.js 121 | # https://nextjs.org/blog/next-9-1#public-directory-support 122 | # public 123 | 124 | # vuepress build output 125 | .vuepress/dist 126 | 127 | # Serverless directories 128 | .serverless/ 129 | 130 | # FuseBox cache 131 | .fusebox/ 132 | 133 | # DynamoDB Local files 134 | .dynamodb/ 135 | 136 | # TernJS port file 137 | .tern-port 138 | 139 | # Stores VSCode versions used for testing VSCode extensions 140 | .vscode-test 141 | 142 | # Temporary folders 143 | tmp/ 144 | temp/ 145 | 146 | ### OSX ### 147 | # General 148 | .DS_Store 149 | .AppleDouble 150 | .LSOverride 151 | 152 | # Icon must end with two \r 153 | Icon 154 | 155 | 156 | # Thumbnails 157 | ._* 158 | 159 | # Files that might appear in the root of a volume 160 | .DocumentRevisions-V100 161 | .fseventsd 162 | .Spotlight-V100 163 | .TemporaryItems 164 | .Trashes 165 | .VolumeIcon.icns 166 | .com.apple.timemachine.donotpresent 167 | 168 | # Directories potentially created on remote AFP share 169 | .AppleDB 170 | .AppleDesktop 171 | Network Trash Folder 172 | Temporary Items 173 | .apdisk 174 | 175 | ### SAM ### 176 | # Ignore build directories for the AWS Serverless Application Model (SAM) 177 | # Info: https://aws.amazon.com/serverless/sam/ 178 | # Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html 179 | 180 | **/.aws-sam 181 | 182 | ### Windows ### 183 | # Windows thumbnail cache files 184 | Thumbs.db 185 | Thumbs.db:encryptable 186 | ehthumbs.db 187 | ehthumbs_vista.db 188 | 189 | # Dump file 190 | *.stackdump 191 | 192 | # Folder config file 193 | [Dd]esktop.ini 194 | 195 | # Recycle Bin used on file shares 196 | $RECYCLE.BIN/ 197 | 198 | # Windows Installer files 199 | *.cab 200 | *.msi 201 | *.msix 202 | *.msm 203 | *.msp 204 | 205 | # Windows shortcuts 206 | *.lnk 207 | 208 | # End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam 209 | -------------------------------------------------------------------------------- /iac/k8s/eks-create/iam-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "acm:DescribeCertificate", 8 | "acm:ListCertificates", 9 | "acm:GetCertificate" 10 | ], 11 | "Resource": "*" 12 | }, 13 | { 14 | "Effect": "Allow", 15 | "Action": [ 16 | "ec2:AuthorizeSecurityGroupIngress", 17 | "ec2:CreateSecurityGroup", 18 | "ec2:CreateTags", 19 | "ec2:DeleteTags", 20 | "ec2:DeleteSecurityGroup", 21 | "ec2:DescribeAccountAttributes", 22 | "ec2:DescribeAddresses", 23 | "ec2:DescribeInstances", 24 | "ec2:DescribeInstanceStatus", 25 | "ec2:DescribeInternetGateways", 26 | "ec2:DescribeNetworkInterfaces", 27 | "ec2:DescribeSecurityGroups", 28 | "ec2:DescribeSubnets", 29 | "ec2:DescribeTags", 30 | "ec2:DescribeVpcs", 31 | "ec2:ModifyInstanceAttribute", 32 | "ec2:ModifyNetworkInterfaceAttribute", 33 | "ec2:RevokeSecurityGroupIngress" 34 | ], 35 | "Resource": "*" 36 | }, 37 | { 38 | "Effect": "Allow", 39 | "Action": [ 40 | "elasticloadbalancing:AddListenerCertificates", 41 | "elasticloadbalancing:AddTags", 42 | "elasticloadbalancing:CreateListener", 43 | "elasticloadbalancing:CreateLoadBalancer", 44 | "elasticloadbalancing:CreateRule", 45 | "elasticloadbalancing:CreateTargetGroup", 46 | "elasticloadbalancing:DeleteListener", 47 | "elasticloadbalancing:DeleteLoadBalancer", 48 | "elasticloadbalancing:DeleteRule", 49 | "elasticloadbalancing:DeleteTargetGroup", 50 | "elasticloadbalancing:DeregisterTargets", 51 | "elasticloadbalancing:DescribeListenerCertificates", 52 | "elasticloadbalancing:DescribeListeners", 53 | "elasticloadbalancing:DescribeLoadBalancers", 54 | "elasticloadbalancing:DescribeLoadBalancerAttributes", 55 | "elasticloadbalancing:DescribeRules", 56 | "elasticloadbalancing:DescribeSSLPolicies", 57 | "elasticloadbalancing:DescribeTags", 58 | "elasticloadbalancing:DescribeTargetGroups", 59 | "elasticloadbalancing:DescribeTargetGroupAttributes", 60 | "elasticloadbalancing:DescribeTargetHealth", 61 | "elasticloadbalancing:ModifyListener", 62 | "elasticloadbalancing:ModifyLoadBalancerAttributes", 63 | "elasticloadbalancing:ModifyRule", 64 | "elasticloadbalancing:ModifyTargetGroup", 65 | "elasticloadbalancing:ModifyTargetGroupAttributes", 66 | "elasticloadbalancing:RegisterTargets", 67 | "elasticloadbalancing:RemoveListenerCertificates", 68 | "elasticloadbalancing:RemoveTags", 69 | "elasticloadbalancing:SetIpAddressType", 70 | "elasticloadbalancing:SetSecurityGroups", 71 | "elasticloadbalancing:SetSubnets", 72 | "elasticloadbalancing:SetWebAcl" 73 | ], 74 | "Resource": "*" 75 | }, 76 | { 77 | "Effect": "Allow", 78 | "Action": [ 79 | "iam:CreateServiceLinkedRole", 80 | "iam:GetServerCertificate", 81 | "iam:ListServerCertificates" 82 | ], 83 | "Resource": "*" 84 | }, 85 | { 86 | "Effect": "Allow", 87 | "Action": [ 88 | "cognito-idp:DescribeUserPoolClient" 89 | ], 90 | "Resource": "*" 91 | }, 92 | { 93 | "Effect": "Allow", 94 | "Action": [ 95 | "waf-regional:GetWebACLForResource", 96 | "waf-regional:GetWebACL", 97 | "waf-regional:AssociateWebACL", 98 | "waf-regional:DisassociateWebACL" 99 | ], 100 | "Resource": "*" 101 | }, 102 | { 103 | "Effect": "Allow", 104 | "Action": [ 105 | "tag:GetResources", 106 | "tag:TagResources" 107 | ], 108 | "Resource": "*" 109 | }, 110 | { 111 | "Effect": "Allow", 112 | "Action": [ 113 | "waf:GetWebACL" 114 | ], 115 | "Resource": "*" 116 | }, 117 | { 118 | "Effect": "Allow", 119 | "Action": [ 120 | "wafv2:GetWebACL", 121 | "wafv2:GetWebACLForResource", 122 | "wafv2:AssociateWebACL", 123 | "wafv2:DisassociateWebACL" 124 | ], 125 | "Resource": "*" 126 | }, 127 | { 128 | "Effect": "Allow", 129 | "Action": [ 130 | "shield:DescribeProtection", 131 | "shield:GetSubscriptionState", 132 | "shield:DeleteProtection", 133 | "shield:CreateProtection", 134 | "shield:DescribeSubscription", 135 | "shield:ListProtections" 136 | ], 137 | "Resource": "*" 138 | } 139 | ] 140 | } 141 | -------------------------------------------------------------------------------- /iac/k8s/alb-demo/alb-ingress-iam-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "acm:DescribeCertificate", 8 | "acm:ListCertificates", 9 | "acm:GetCertificate" 10 | ], 11 | "Resource": "*" 12 | }, 13 | { 14 | "Effect": "Allow", 15 | "Action": [ 16 | "ec2:AuthorizeSecurityGroupIngress", 17 | "ec2:CreateSecurityGroup", 18 | "ec2:CreateTags", 19 | "ec2:DeleteTags", 20 | "ec2:DeleteSecurityGroup", 21 | "ec2:DescribeAccountAttributes", 22 | "ec2:DescribeAddresses", 23 | "ec2:DescribeInstances", 24 | "ec2:DescribeInstanceStatus", 25 | "ec2:DescribeInternetGateways", 26 | "ec2:DescribeNetworkInterfaces", 27 | "ec2:DescribeSecurityGroups", 28 | "ec2:DescribeSubnets", 29 | "ec2:DescribeTags", 30 | "ec2:DescribeVpcs", 31 | "ec2:ModifyInstanceAttribute", 32 | "ec2:ModifyNetworkInterfaceAttribute", 33 | "ec2:RevokeSecurityGroupIngress" 34 | ], 35 | "Resource": "*" 36 | }, 37 | { 38 | "Effect": "Allow", 39 | "Action": [ 40 | "elasticloadbalancing:AddListenerCertificates", 41 | "elasticloadbalancing:AddTags", 42 | "elasticloadbalancing:CreateListener", 43 | "elasticloadbalancing:CreateLoadBalancer", 44 | "elasticloadbalancing:CreateRule", 45 | "elasticloadbalancing:CreateTargetGroup", 46 | "elasticloadbalancing:DeleteListener", 47 | "elasticloadbalancing:DeleteLoadBalancer", 48 | "elasticloadbalancing:DeleteRule", 49 | "elasticloadbalancing:DeleteTargetGroup", 50 | "elasticloadbalancing:DeregisterTargets", 51 | "elasticloadbalancing:DescribeListenerCertificates", 52 | "elasticloadbalancing:DescribeListeners", 53 | "elasticloadbalancing:DescribeLoadBalancers", 54 | "elasticloadbalancing:DescribeLoadBalancerAttributes", 55 | "elasticloadbalancing:DescribeRules", 56 | "elasticloadbalancing:DescribeSSLPolicies", 57 | "elasticloadbalancing:DescribeTags", 58 | "elasticloadbalancing:DescribeTargetGroups", 59 | "elasticloadbalancing:DescribeTargetGroupAttributes", 60 | "elasticloadbalancing:DescribeTargetHealth", 61 | "elasticloadbalancing:ModifyListener", 62 | "elasticloadbalancing:ModifyLoadBalancerAttributes", 63 | "elasticloadbalancing:ModifyRule", 64 | "elasticloadbalancing:ModifyTargetGroup", 65 | "elasticloadbalancing:ModifyTargetGroupAttributes", 66 | "elasticloadbalancing:RegisterTargets", 67 | "elasticloadbalancing:RemoveListenerCertificates", 68 | "elasticloadbalancing:RemoveTags", 69 | "elasticloadbalancing:SetIpAddressType", 70 | "elasticloadbalancing:SetSecurityGroups", 71 | "elasticloadbalancing:SetSubnets", 72 | "elasticloadbalancing:SetWebAcl" 73 | ], 74 | "Resource": "*" 75 | }, 76 | { 77 | "Effect": "Allow", 78 | "Action": [ 79 | "iam:CreateServiceLinkedRole", 80 | "iam:GetServerCertificate", 81 | "iam:ListServerCertificates" 82 | ], 83 | "Resource": "*" 84 | }, 85 | { 86 | "Effect": "Allow", 87 | "Action": [ 88 | "cognito-idp:DescribeUserPoolClient" 89 | ], 90 | "Resource": "*" 91 | }, 92 | { 93 | "Effect": "Allow", 94 | "Action": [ 95 | "waf-regional:GetWebACLForResource", 96 | "waf-regional:GetWebACL", 97 | "waf-regional:AssociateWebACL", 98 | "waf-regional:DisassociateWebACL" 99 | ], 100 | "Resource": "*" 101 | }, 102 | { 103 | "Effect": "Allow", 104 | "Action": [ 105 | "tag:GetResources", 106 | "tag:TagResources" 107 | ], 108 | "Resource": "*" 109 | }, 110 | { 111 | "Effect": "Allow", 112 | "Action": [ 113 | "waf:GetWebACL" 114 | ], 115 | "Resource": "*" 116 | }, 117 | { 118 | "Effect": "Allow", 119 | "Action": [ 120 | "wafv2:GetWebACL", 121 | "wafv2:GetWebACLForResource", 122 | "wafv2:AssociateWebACL", 123 | "wafv2:DisassociateWebACL" 124 | ], 125 | "Resource": "*" 126 | }, 127 | { 128 | "Effect": "Allow", 129 | "Action": [ 130 | "shield:DescribeProtection", 131 | "shield:GetSubscriptionState", 132 | "shield:DeleteProtection", 133 | "shield:CreateProtection", 134 | "shield:DescribeSubscription", 135 | "shield:ListProtections" 136 | ], 137 | "Resource": "*" 138 | } 139 | ] 140 | } 141 | -------------------------------------------------------------------------------- /iac/demo/acd-srilanka2025/my_agent.py: -------------------------------------------------------------------------------- 1 | import os 2 | import boto3 3 | 4 | import logging 5 | from datetime import datetime 6 | 7 | # Setup 8 | logging.basicConfig(level=logging.INFO) 9 | logger = logging.getLogger("cloudcostadvisoragent") 10 | 11 | from datetime import datetime, timedelta 12 | from bedrock_agentcore.runtime import BedrockAgentCoreApp 13 | from strands import Agent, tool 14 | from strands.models import BedrockModel 15 | from bedrock_agentcore.memory import MemoryClient 16 | from botocore.exceptions import ClientError 17 | 18 | REGION = os.getenv("AWS_REGION", "us-east-1") 19 | MODEL_ID = os.getenv("BEDROCK_MODEL_ID", "meta.llama3-8b-instruct-v1:0") # Meta's Llama Model 20 | 21 | ACTOR_ID = "user_827" 22 | SESSION_ID = "personal_session_827" # Unique session identifier 23 | 24 | client = MemoryClient() 25 | 26 | # Initialize Memory Client 27 | client = MemoryClient(region_name=REGION) 28 | memory_name = "CloudCostAdvisorAgentMemory" 29 | 30 | try: 31 | # Create memory resource without strategies (thus only access to short-term memory) 32 | memory = client.create_memory_and_wait( 33 | name=memory_name, 34 | strategies=[], # No strategies for short-term memory 35 | description="Short-term memory for personal agent", 36 | event_expiry_days=7, # Retention period for short-term memory. This can be upto 365 days. 37 | ) 38 | memory_id = memory['id'] 39 | logger.info(f"Created memory: {memory_id}") 40 | except ClientError as e: 41 | logger.info(f"ERROR: {e}") 42 | if e.response['Error']['Code'] == 'ValidationException' and "already exists" in str(e): 43 | # If memory already exists, retrieve its ID 44 | memories = client.list_memories() 45 | memory_id = next((m['id'] for m in memories if m['id'].startswith(memory_name)), None) 46 | logger.info(f"Memory already exists. Using existing memory ID: {memory_id}") 47 | except Exception as e: 48 | # Show any errors during memory creation 49 | logger.error(f"ERROR: {e}") 50 | import traceback 51 | traceback.print_exc() 52 | # Cleanup on error - delete the memory if it was partially created 53 | if memory_id: 54 | try: 55 | client.delete_memory_and_wait(memory_id=memory_id) 56 | logger.info(f"Cleaned up memory: {memory_id}") 57 | except Exception as cleanup_error: 58 | logger.error(f"Failed to clean up memory: {cleanup_error}") 59 | 60 | # ------------------------------------------------------------------------------ 61 | # Initialize Bedrock AgentCore App 62 | # ------------------------------------------------------------------------------ 63 | app = BedrockAgentCoreApp() 64 | 65 | # Configure Bedrock model (AgentCore native) 66 | model = BedrockModel( 67 | model_id=MODEL_ID, 68 | streaming=False, 69 | region_name=REGION, 70 | ) 71 | 72 | SYSTEM_PROMPT = """ 73 | You are Cloud Cost Advisor Agent. 74 | Your job is to help users query AWS cost data and summarize it clearly. 75 | If a user asks about AWS cost (e.g., "last week EC2"), call the cost_explorer tool. 76 | """ 77 | 78 | 79 | # ------------------------------------------------------------------------------ 80 | # Custom Tool 81 | # ------------------------------------------------------------------------------ 82 | @tool 83 | def cost_explorer(service: str = "Amazon Elastic Compute Cloud - Compute", 84 | days: int = 7) -> dict: 85 | """ 86 | Fetch AWS cost data for a given service over the last N days. 87 | Default: EC2 over last 7 days. 88 | Returns structured JSON for LLM augmentation. 89 | """ 90 | end = datetime.utcnow().date() 91 | start = end - timedelta(days=days) 92 | 93 | response = ce.get_cost_and_usage( 94 | TimePeriod={ 95 | "Start": start.strftime("%Y-%m-%d"), 96 | "End": end.strftime("%Y-%m-%d") 97 | }, 98 | Granularity="DAILY", 99 | Metrics=["UnblendedCost"], 100 | Filter={ 101 | "Dimensions": { 102 | "Key": "SERVICE", 103 | "Values": [service] 104 | } 105 | } 106 | ) 107 | 108 | # Collect daily costs 109 | daily_costs = [] 110 | total = 0.0 111 | currency = "USD" 112 | for item in response["ResultsByTime"]: 113 | amount = float(item["Total"]["UnblendedCost"]["Amount"]) 114 | total += amount 115 | currency = item["Total"]["UnblendedCost"]["Unit"] 116 | daily_costs.append({ 117 | "date": item["TimePeriod"]["Start"], 118 | "amount": round(amount, 2) 119 | }) 120 | 121 | return { 122 | "service": service, 123 | "days": days, 124 | "total_cost": round(total, 2), 125 | "currency": currency, 126 | "daily_breakdown": daily_costs 127 | } 128 | 129 | agent = Agent( 130 | model=model, 131 | system_prompt=SYSTEM_PROMPT, 132 | #tools=[cost_explorer] 133 | ) 134 | 135 | # ------------------------------------------------------------------------------ 136 | # Initialize Cost Explorer Client 137 | # ------------------------------------------------------------------------------ 138 | ce = boto3.client("ce", region_name=REGION) 139 | 140 | @app.entrypoint 141 | def invoke(payload): 142 | """Main agent entrypoint""" 143 | user_message = payload.get("prompt", "Hello! How can I help you today?") 144 | result = agent(user_message) 145 | # return { 146 | # "response": result.message 147 | # } 148 | return result.message['content'][0]['text'] 149 | 150 | 151 | if __name__ == "__main__": 152 | app.run() -------------------------------------------------------------------------------- /iac/demo/textract/lambda_function.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | import os 4 | 5 | s3_client = boto3.client('s3') 6 | textract_client = boto3.client('textract') 7 | sns_client = boto3.client('sns') 8 | 9 | SNS_TOPIC_ARN = os.environ['SNS_TOPIC_ARN'] # Environment variable for SNS topic ARN 10 | 11 | SUPPORTED_IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg'] 12 | SUPPORTED_PDF_EXTENSION = '.pdf' 13 | 14 | def lambda_handler(event, context): 15 | try: 16 | print(f"Event: {json.dumps(event)}") 17 | 18 | # Get S3 bucket and object key from the S3 event 19 | bucket_name = event['Records'][0]['s3']['bucket']['name'] 20 | object_key = event['Records'][0]['s3']['object']['key'] 21 | 22 | print('bucket_name::', bucket_name, ' - object_key::', object_key) 23 | 24 | # Validate file extension 25 | if any(object_key.lower().endswith(ext) for ext in SUPPORTED_IMAGE_EXTENSIONS): 26 | # Process image files 27 | response = textract_client.detect_document_text( 28 | Document={'S3Object': {'Bucket': bucket_name, 'Name': object_key}} 29 | ) 30 | 31 | print('Textract detect_document_text response::', response) 32 | 33 | # Extract text blocks 34 | # text_blocks = [block['Text'] for block in response['Blocks'] if block['BlockType'] == 'LINE'] 35 | # extracted_text = '\n'.join(text_blocks) 36 | # print('extracted_text::', extracted_text) 37 | 38 | 39 | # Extract text blocks with confidence scores 40 | text_blocks_with_confidence = [] 41 | for block in response['Blocks']: 42 | if block['BlockType'] == 'LINE': 43 | text_blocks_with_confidence.append((block['Text'], block['Confidence'])) 44 | 45 | 46 | # Format the extracted text with confidence scores 47 | extracted_text_with_confidence = '\n'.join([f"{text} (Confidence: {confidence:.2f})" for text, confidence in text_blocks_with_confidence]) 48 | print('extracted_text_with_confidence::', extracted_text_with_confidence) 49 | 50 | 51 | # Send extracted text to SNS 52 | sns_client.publish( 53 | TopicArn=SNS_TOPIC_ARN, 54 | Message=json.dumps({ 55 | 'bucket': bucket_name, 56 | 'key': object_key, 57 | 'text': extracted_text_with_confidence 58 | }), 59 | Subject='Textract Extracted Text from Image' 60 | ) 61 | 62 | elif object_key.lower().endswith(SUPPORTED_PDF_EXTENSION): 63 | # Process PDF files 64 | try: 65 | response = textract_client.analyze_document( 66 | Document={'S3Object': {'Bucket': bucket_name, 'Name': object_key}}, 67 | FeatureTypes=['QUERIES'], 68 | QueriesConfig={ 69 | 'Queries': [ 70 | {'Text': 'What is the event name?', 'Alias': 'EventName'}, 71 | {'Text': 'What is the Location?', 'Alias': 'Location'} 72 | ] 73 | } 74 | ) 75 | 76 | print('Textract analyze_document response::', response) 77 | 78 | # # Extract key-value pairs from QUERY_RESULT blocks 79 | # query_results = { 80 | # block['QueryResult']['Alias']: block['Text'] 81 | # for block in response['Blocks'] 82 | # if block['BlockType'] == 'QUERY_RESULT' 83 | # } 84 | 85 | 86 | query_results = {} 87 | for block in response['Blocks']: 88 | if block['BlockType'] == 'QUERY_RESULT': 89 | for relationship in block['Relationships']: 90 | if relationship['Type'] == 'ANSWER': 91 | query_id = relationship['Ids'][0] 92 | for query_block in response['Blocks']: 93 | if query_block['Id'] == query_id: 94 | query_alias = query_block['Query']['Alias'] 95 | query_results[query_alias] = { 96 | 'Text': block['Text'], 97 | 'Confidence': block['Confidence'] 98 | } 99 | 100 | print('query_results::', query_results) 101 | 102 | 103 | # Send key-value pairs to SNS 104 | sns_client.publish( 105 | TopicArn=SNS_TOPIC_ARN, 106 | Message=json.dumps({ 107 | 'bucket': bucket_name, 108 | 'key': object_key, 109 | 'key_value_pairs': query_results 110 | }), 111 | Subject='Textract Extracted Key-Value Pairs from PDF' 112 | ) 113 | 114 | except Exception as e: 115 | print(f"Error processing file: {e}") 116 | 117 | else: 118 | raise ValueError(f"Unsupported file extension for file: {object_key}") 119 | 120 | return { 121 | 'statusCode': 200, 122 | 'body': json.dumps('File processed successfully and data sent to SNS') 123 | } 124 | 125 | except textract_client.exceptions.UnsupportedDocumentException as e: 126 | print(f"Unsupported document format: {e}") 127 | return { 128 | 'statusCode': 400, 129 | 'body': json.dumps('Unsupported document format') 130 | } 131 | except Exception as e: 132 | print(f"Error processing file: {e}") 133 | return { 134 | 'statusCode': 500, 135 | 'body': json.dumps(f"Error processing file: {str(e)}") 136 | } -------------------------------------------------------------------------------- /iac/demo/textract/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | profile = var.aws_profile 4 | } 5 | 6 | # S3 Bucket 7 | resource "aws_s3_bucket" "textract_bucket" { 8 | bucket_prefix = var.s3_bucket_name 9 | 10 | tags = { 11 | Name = "TextractBucket" 12 | } 13 | force_destroy = true 14 | } 15 | 16 | # SNS Topic 17 | resource "aws_sns_topic" "textract_topic" { 18 | name = "textract-sns-topic" 19 | 20 | tags = { 21 | Name = "TextractSNSTopic" 22 | } 23 | } 24 | 25 | # Lambda Function 26 | resource "aws_lambda_function" "textract_lambda" { 27 | filename = "lambda_function.zip" 28 | function_name = var.lambda_function_name 29 | role = aws_iam_role.lambda_role.arn 30 | handler = "lambda_function.lambda_handler" 31 | runtime = "python3.11" 32 | source_code_hash = filebase64sha256("lambda_function.zip") 33 | timeout = 60 34 | 35 | environment { 36 | variables = { 37 | SNS_TOPIC_ARN = aws_sns_topic.textract_topic.arn # Pass SNS Topic ARN as an environment variable 38 | } 39 | } 40 | } 41 | 42 | # S3 Bucket Notification to Lambda 43 | resource "aws_s3_bucket_notification" "bucket_notification" { 44 | bucket = aws_s3_bucket.textract_bucket.id 45 | 46 | lambda_function { 47 | lambda_function_arn = aws_lambda_function.textract_lambda.arn 48 | events = ["s3:ObjectCreated:*"] 49 | filter_suffix = ".pdf" 50 | } 51 | 52 | lambda_function { 53 | lambda_function_arn = aws_lambda_function.textract_lambda.arn 54 | events = ["s3:ObjectCreated:*"] 55 | filter_suffix = ".jpeg" 56 | } 57 | 58 | depends_on = [aws_lambda_permission.allow_s3] 59 | } 60 | 61 | # IAM Role for Lambda 62 | resource "aws_iam_role" "lambda_role" { 63 | name = "lambda-textract-role" 64 | 65 | assume_role_policy = jsonencode({ 66 | Version = "2012-10-17" 67 | Statement = [ 68 | { 69 | Action = "sts:AssumeRole" 70 | Effect = "Allow" 71 | Principal = { 72 | Service = "lambda.amazonaws.com" 73 | } 74 | } 75 | ] 76 | }) 77 | } 78 | 79 | # IAM Policy for Lambda to publish to SNS 80 | resource "aws_iam_role_policy" "lambda_policy" { 81 | name = "lambda-textract-policy" 82 | role = aws_iam_role.lambda_role.id 83 | policy = jsonencode({ 84 | Version = "2012-10-17" 85 | Statement = [ 86 | { 87 | Effect = "Allow" 88 | Action = ["s3:GetObject", "s3:PutObject"] 89 | Resource = "${aws_s3_bucket.textract_bucket.arn}/*" 90 | }, 91 | { 92 | Effect = "Allow" 93 | Action = ["sns:Publish"] 94 | Resource = aws_sns_topic.textract_topic.arn 95 | }, 96 | { 97 | Effect = "Allow" 98 | Action = ["textract:DetectDocumentText"] 99 | Resource = "*" 100 | }, 101 | { 102 | "Effect": "Allow", 103 | "Action": "textract:AnalyzeDocument", 104 | "Resource": "*" 105 | }, 106 | { 107 | Effect = "Allow" 108 | Action = [ 109 | "logs:CreateLogGroup", 110 | "logs:CreateLogStream", 111 | "logs:PutLogEvents" 112 | ] 113 | Resource = "*" 114 | }, 115 | { 116 | Effect = "Allow" 117 | Action = [ 118 | "sqs:ReceiveMessage", 119 | "sqs:DeleteMessage", 120 | "sqs:GetQueueAttributes" 121 | ] 122 | Resource = aws_sqs_queue.textract_queue.arn 123 | } 124 | ] 125 | }) 126 | } 127 | 128 | 129 | 130 | 131 | # Allow S3 to invoke Lambda 132 | resource "aws_lambda_permission" "allow_s3" { 133 | statement_id = "AllowS3Invoke" 134 | action = "lambda:InvokeFunction" 135 | function_name = aws_lambda_function.textract_lambda.function_name 136 | principal = "s3.amazonaws.com" 137 | source_arn = aws_s3_bucket.textract_bucket.arn 138 | } 139 | 140 | # SQS Queue 141 | resource "aws_sqs_queue" "textract_queue" { 142 | name = var.sqs_queue_name 143 | visibility_timeout_seconds = 60 144 | 145 | tags = { 146 | Name = "TextractQueue" 147 | } 148 | } 149 | 150 | # SQS Queue Policy to allow SNS to publish messages 151 | resource "aws_sqs_queue_policy" "sns_publish_policy" { 152 | queue_url = aws_sqs_queue.textract_queue.id 153 | policy = jsonencode({ 154 | Version = "2012-10-17" 155 | Statement = [ 156 | { 157 | Effect = "Allow" 158 | Principal = { 159 | Service = "sns.amazonaws.com" 160 | } 161 | Action = "sqs:SendMessage" 162 | Resource = aws_sqs_queue.textract_queue.arn 163 | Condition = { 164 | ArnEquals = { 165 | "aws:SourceArn" = aws_sns_topic.textract_topic.arn 166 | } 167 | } 168 | } 169 | ] 170 | }) 171 | } 172 | 173 | # SNS Subscription for SQS 174 | resource "aws_sns_topic_subscription" "sns_to_sqs" { 175 | topic_arn = aws_sns_topic.textract_topic.arn 176 | protocol = "sqs" 177 | endpoint = aws_sqs_queue.textract_queue.arn 178 | 179 | # Allow SNS to publish to SQS 180 | depends_on = [aws_sqs_queue_policy.sns_publish_policy] 181 | } 182 | 183 | resource "aws_lambda_function" "sqs_to_csv_lambda" { 184 | filename = "sqs_to_csv_lambda.zip" # Path to your Lambda zip file 185 | function_name = var.lambda_function_name_2 186 | role = aws_iam_role.lambda_role.arn # IAM role for Lambda 187 | handler = "sqs_to_csv_lambda.lambda_handler" # Lambda function handler 188 | runtime = "python3.11" # Lambda runtime 189 | source_code_hash = filebase64sha256("sqs_to_csv_lambda.zip") # Source code hash for validation 190 | timeout = 60 # Timeout in seconds 191 | 192 | environment { 193 | variables = { 194 | CSV_S3_BUCKET = aws_s3_bucket.textract_bucket.bucket # S3 bucket name 195 | CSV_S3_PREFIX = var.csv_s3_prefix # Prefix for CSV files 196 | } 197 | } 198 | } 199 | 200 | resource "aws_lambda_event_source_mapping" "sqs_trigger" { 201 | event_source_arn = aws_sqs_queue.textract_queue.arn 202 | function_name = aws_lambda_function.sqs_to_csv_lambda.arn 203 | batch_size = 1 204 | enabled = true 205 | } 206 | 207 | -------------------------------------------------------------------------------- /iac/aws/ecs/001-ecs-cluster-creation.yaml: -------------------------------------------------------------------------------- 1 | 2 | Description: > 3 | This template deploys an ECS cluster to the provided VPC and subnets 4 | using an Auto Scaling Group 5 | 6 | Parameters: 7 | EnvironmentName: 8 | Description: An environment name that will be prefixed to resource names 9 | Type: String 10 | 11 | InstanceType: 12 | Description: Which instance type should we use to build the ECS cluster? 13 | Type: String 14 | Default: t2.small 15 | AllowedValues: 16 | - t2.micro 17 | - t2.small 18 | - t2.medium 19 | - t2.large 20 | - t3a.xlarge 21 | ConstraintDescription: Please choose a valid instance type. 22 | 23 | ClusterSize: 24 | Description: Maximum number of instances that can be launched in your ECS cluster. 25 | Type: Number 26 | Default: 1 27 | 28 | VpcId: 29 | Description: Choose which VPC this ECS cluster should be deployed to 30 | Type: AWS::EC2::VPC::Id 31 | 32 | Subnets: 33 | Description: Choose which subnets this ECS cluster should be deployed to 34 | Type: List 35 | 36 | ExportPrefix: 37 | Type: String 38 | Default: sct 39 | Description: >- 40 | Prefix for the managed resources and cloudformation exports. 41 | Provide it in `namespace-environment` format, 42 | where namespace can be product UPI or leave blank for defaults. 43 | AllowedPattern: ^[a-z]+(-?[a-z0-9]+)*$ 44 | ConstraintDescription: >- 45 | Only hyphen (-) separated alphanumeric string is allowed. Should start with a letter. 46 | MinLength: 3 47 | MaxLength: 30 48 | 49 | ECSAMI: 50 | Description: ECS-Optimized AMI ID 51 | Type: AWS::SSM::Parameter::Value 52 | Default: /aws/service/ecs/optimized-ami/amazon-linux/recommended/image_id 53 | 54 | 55 | # ---------------------------------------------------------------------------- # 56 | # Conditions # 57 | # ---------------------------------------------------------------------------- # 58 | 59 | Conditions: 60 | HasNVirginiaRegion: !Equals [!Ref 'AWS::Region', 'us-east-1'] 61 | HasExportPrefix: !Not [!Equals [!Ref ExportPrefix, '']] 62 | 63 | Resources: 64 | ECSCluster: 65 | Type: AWS::ECS::Cluster 66 | Properties: 67 | ClusterName: !Sub ecs-cluster-${EnvironmentName} 68 | 69 | EcsSecurityGroup: 70 | Type: AWS::EC2::SecurityGroup 71 | Properties: 72 | GroupDescription: ECS Security Group 73 | VpcId: !Ref VpcId 74 | 75 | EcsSecurityGroupAllTrafficInbound: 76 | Type: AWS::EC2::SecurityGroupIngress 77 | Properties: 78 | GroupId: !Ref 'EcsSecurityGroup' 79 | IpProtocol: -1 80 | CidrIp: 0.0.0.0/0 81 | 82 | ECSAutoScalingGroup: 83 | DependsOn: ECSCluster 84 | Type: AWS::AutoScaling::AutoScalingGroup 85 | Properties: 86 | VPCZoneIdentifier: !Ref Subnets 87 | LaunchConfigurationName: !Ref ECSLaunchConfiguration 88 | MinSize: !Ref ClusterSize 89 | MaxSize: !Ref ClusterSize 90 | DesiredCapacity: !Ref ClusterSize 91 | Tags: 92 | - Key: Name 93 | Value: !Sub ${EnvironmentName} ECS host 94 | PropagateAtLaunch: true 95 | - Key: DELETE_ME 96 | Value: true 97 | PropagateAtLaunch: true 98 | CreationPolicy: 99 | ResourceSignal: 100 | Timeout: PT15M 101 | UpdatePolicy: 102 | AutoScalingRollingUpdate: 103 | MinInstancesInService: 1 104 | MaxBatchSize: 1 105 | PauseTime: PT15M 106 | SuspendProcesses: 107 | - HealthCheck 108 | - ReplaceUnhealthy 109 | - AZRebalance 110 | - AlarmNotification 111 | - ScheduledActions 112 | WaitOnResourceSignals: true 113 | 114 | ECSLaunchConfiguration: 115 | Type: AWS::AutoScaling::LaunchConfiguration 116 | Properties: 117 | ImageId: !Ref ECSAMI 118 | InstanceType: !Ref InstanceType 119 | SecurityGroups: [!Ref 'EcsSecurityGroup'] 120 | IamInstanceProfile: !Ref ECSInstanceProfile 121 | UserData: 122 | "Fn::Base64": !Sub | 123 | #!/bin/bash 124 | yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm 125 | yum install -y https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm 126 | yum install -y aws-cfn-bootstrap hibagent 127 | /opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource ECSLaunchConfiguration 128 | /opt/aws/bin/cfn-signal -e $? --region ${AWS::Region} --stack ${AWS::StackName} --resource ECSAutoScalingGroup 129 | /usr/bin/enable-ec2-spot-hibernation 130 | sudo sysctl -w vm.max_map_count=262144 131 | 132 | Metadata: 133 | AWS::CloudFormation::Init: 134 | config: 135 | packages: 136 | yum: 137 | collectd: [] 138 | 139 | commands: 140 | 01_add_instance_to_cluster: 141 | command: !Sub echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config 142 | 02_enable_cloudwatch_agent: 143 | command: !Sub /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c ssm:${ECSCloudWatchParameter} -s 144 | files: 145 | /etc/cfn/cfn-hup.conf: 146 | mode: 000400 147 | owner: root 148 | group: root 149 | content: !Sub | 150 | [main] 151 | stack=${AWS::StackId} 152 | region=${AWS::Region} 153 | 154 | /etc/cfn/hooks.d/cfn-auto-reloader.conf: 155 | content: !Sub | 156 | [cfn-auto-reloader-hook] 157 | triggers=post.update 158 | path=Resources.ECSLaunchConfiguration.Metadata.AWS::CloudFormation::Init 159 | action=/opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource ECSLaunchConfiguration 160 | 161 | services: 162 | sysvinit: 163 | cfn-hup: 164 | enabled: true 165 | ensureRunning: true 166 | files: 167 | - /etc/cfn/cfn-hup.conf 168 | - /etc/cfn/hooks.d/cfn-auto-reloader.conf 169 | 170 | # This IAM Role is attached to all of the ECS hosts. It is based on the default role 171 | # published here: 172 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html 173 | # 174 | # You can add other IAM policy statements here to allow access from your ECS hosts 175 | # to other AWS services. Please note that this role will be used by ALL containers 176 | # running on the ECS host. 177 | 178 | ECSRole: 179 | Type: AWS::IAM::Role 180 | Properties: 181 | Path: / 182 | RoleName: !Sub ${EnvironmentName}-ECSRole-${AWS::Region} 183 | AssumeRolePolicyDocument: | 184 | { 185 | "Statement": [{ 186 | "Action": "sts:AssumeRole", 187 | "Effect": "Allow", 188 | "Principal": { 189 | "Service": "ec2.amazonaws.com" 190 | } 191 | }] 192 | } 193 | ManagedPolicyArns: 194 | - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM 195 | - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy 196 | Policies: 197 | - PolicyName: ecs-service 198 | PolicyDocument: | 199 | { 200 | "Statement": [{ 201 | "Effect": "Allow", 202 | "Action": [ 203 | "ecs:CreateCluster", 204 | "ecs:DeregisterContainerInstance", 205 | "ecs:DiscoverPollEndpoint", 206 | "ecs:Poll", 207 | "ecs:RegisterContainerInstance", 208 | "ecs:StartTelemetrySession", 209 | "ecs:Submit*", 210 | "ecr:BatchCheckLayerAvailability", 211 | "ecr:BatchGetImage", 212 | "ecr:GetDownloadUrlForLayer", 213 | "ecr:GetAuthorizationToken" 214 | ], 215 | "Resource": "*" 216 | }] 217 | } 218 | 219 | ECSInstanceProfile: 220 | Type: AWS::IAM::InstanceProfile 221 | Properties: 222 | Path: / 223 | Roles: 224 | - !Ref ECSRole 225 | 226 | ECSServiceAutoScalingRole: 227 | Type: AWS::IAM::Role 228 | Properties: 229 | AssumeRolePolicyDocument: 230 | Version: "2012-10-17" 231 | Statement: 232 | Action: 233 | - "sts:AssumeRole" 234 | Effect: Allow 235 | Principal: 236 | Service: 237 | - application-autoscaling.amazonaws.com 238 | Path: / 239 | Policies: 240 | - PolicyName: ecs-service-autoscaling 241 | PolicyDocument: 242 | Statement: 243 | Effect: Allow 244 | Action: 245 | - application-autoscaling:* 246 | - cloudwatch:DescribeAlarms 247 | - cloudwatch:PutMetricAlarm 248 | - ecs:DescribeServices 249 | - ecs:UpdateService 250 | Resource: "*" 251 | 252 | ECSCloudWatchParameter: 253 | Type: AWS::SSM::Parameter 254 | Properties: 255 | Description: ECS 256 | Name: !Sub "AmazonCloudWatch-${ECSCluster}-ECS" 257 | Type: String 258 | Value: !Sub | 259 | { 260 | "logs": { 261 | "force_flush_interval": 5, 262 | "logs_collected": { 263 | "files": { 264 | "collect_list": [ 265 | { 266 | "file_path": "/var/log/messages", 267 | "log_group_name": "${ECSCluster}-/var/log/messages", 268 | "log_stream_name": "{instance_id}", 269 | "timestamp_format": "%b %d %H:%M:%S" 270 | }, 271 | { 272 | "file_path": "/var/log/dmesg", 273 | "log_group_name": "${ECSCluster}-/var/log/dmesg", 274 | "log_stream_name": "{instance_id}" 275 | }, 276 | { 277 | "file_path": "/var/log/docker", 278 | "log_group_name": "${ECSCluster}-/var/log/docker", 279 | "log_stream_name": "{instance_id}", 280 | "timestamp_format": "%Y-%m-%dT%H:%M:%S.%f" 281 | }, 282 | { 283 | "file_path": "/var/log/ecs/ecs-init.log", 284 | "log_group_name": "${ECSCluster}-/var/log/ecs/ecs-init.log", 285 | "log_stream_name": "{instance_id}", 286 | "timestamp_format": "%Y-%m-%dT%H:%M:%SZ" 287 | }, 288 | { 289 | "file_path": "/var/log/ecs/ecs-agent.log.*", 290 | "log_group_name": "${ECSCluster}-/var/log/ecs/ecs-agent.log", 291 | "log_stream_name": "{instance_id}", 292 | "timestamp_format": "%Y-%m-%dT%H:%M:%SZ" 293 | }, 294 | { 295 | "file_path": "/var/log/ecs/audit.log", 296 | "log_group_name": "${ECSCluster}-/var/log/ecs/audit.log", 297 | "log_stream_name": "{instance_id}", 298 | "timestamp_format": "%Y-%m-%dT%H:%M:%SZ" 299 | } 300 | ] 301 | } 302 | } 303 | }, 304 | "metrics": { 305 | "append_dimensions": { 306 | "AutoScalingGroupName": "${!aws:AutoScalingGroupName}", 307 | "InstanceId": "${!aws:InstanceId}", 308 | "InstanceType": "${!aws:InstanceType}" 309 | }, 310 | "metrics_collected": { 311 | "collectd": { 312 | "metrics_aggregation_interval": 60 313 | }, 314 | "disk": { 315 | "measurement": [ 316 | "used_percent" 317 | ], 318 | "metrics_collection_interval": 60, 319 | "resources": [ 320 | "/" 321 | ] 322 | }, 323 | "mem": { 324 | "measurement": [ 325 | "mem_used_percent" 326 | ], 327 | "metrics_collection_interval": 60 328 | }, 329 | "statsd": { 330 | "metrics_aggregation_interval": 60, 331 | "metrics_collection_interval": 10, 332 | "service_address": ":8125" 333 | } 334 | } 335 | } 336 | } 337 | 338 | Outputs: 339 | VPC: 340 | Description: VPC Id for the ECS Cluster 341 | Value: !Ref VpcId 342 | Export: 343 | Name: !Sub 344 | - '${ExportPrefix_}:${AWS::Region}:${AWS::AccountId}:Vpc:Id' 345 | - ExportPrefix_: !If 346 | - HasExportPrefix 347 | - !Ref ExportPrefix 348 | - 'sct' 349 | VPCPrivateSubnets: 350 | Description: VPC Subnets Available to attach to transit gateway 351 | Value: !Join 352 | - ',' 353 | - !Ref Subnets 354 | Export: 355 | Name: !Sub 356 | - '${ExportPrefix_}:${AWS::Region}:${AWS::AccountId}:Vpc:Private1Subnets' 357 | - ExportPrefix_: !If 358 | - HasExportPrefix 359 | - !Ref ExportPrefix 360 | - 'sct' 361 | ECSCluster: 362 | Description: A reference to the ECS cluster 363 | Value: !Ref ECSCluster 364 | Export: 365 | Name: !Sub 366 | - '${ExportPrefix_}:${AWS::Region}:${AWS::AccountId}:EcsCluster' 367 | - ExportPrefix_: !If 368 | - HasExportPrefix 369 | - !Ref ExportPrefix 370 | - 'sct' 371 | EcsSecurityGroup: 372 | Description: A reference to the ECS cluster security group 373 | Value: !Ref EcsSecurityGroup 374 | Export: 375 | Name: !Sub 376 | - '${ExportPrefix_}:${AWS::Region}:${AWS::AccountId}:EcsSecurityGroup' 377 | - ExportPrefix_: !If 378 | - HasExportPrefix 379 | - !Ref ExportPrefix 380 | - 'sct' 381 | ECSServiceAutoScalingRole: 382 | Description: A reference to ECS service auto scaling role 383 | Value: !GetAtt ECSServiceAutoScalingRole.Arn 384 | Export: 385 | Name: !Sub 386 | - '${ExportPrefix_}:${AWS::Region}:${AWS::AccountId}:EcsServiceAutoScalingRole' 387 | - ExportPrefix_: !If 388 | - HasExportPrefix 389 | - !Ref ExportPrefix 390 | - 'sct' 391 | ECSAutoScalingGroupName: 392 | Description: A reference to ECS AutoScaling Group Name 393 | Value: !Ref ECSAutoScalingGroup 394 | Export: 395 | Name: !Sub 396 | - '${ExportPrefix_}:${AWS::Region}:${AWS::AccountId}:EcsAutoScalingGroupName' 397 | - ExportPrefix_: !If 398 | - HasExportPrefix 399 | - !Ref ExportPrefix 400 | - 'sct' -------------------------------------------------------------------------------- /iac/aws/sam/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: > 4 | AWS Serverless Application Model app with GitHub Actions 5 | Sample application 6 | 7 | Globals: 8 | Function: 9 | Timeout: !Ref DefaultLambdaTimeout # Limit 900 seconds (15 minutes) 10 | MemorySize: !Ref DefaultLambdaMemory # Limit 128 MB to 3,008 MB, in 64 MB increments 11 | 12 | Api: 13 | Cors: 14 | AllowMethods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'" 15 | AllowHeaders: "'Content-Type,X-Amz-Date,X-Amz-Security-Token,Authorization,X-Api-Key,X-Requested-With,Accept,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Access-Control-Allow-Headers'" 16 | AllowOrigin: "'*'" 17 | 18 | Parameters: 19 | Environment: 20 | Type: String 21 | Default: dev 22 | Description: Deployment Environment 23 | AllowedValues: 24 | - sbx 25 | - dev 26 | - qa 27 | - stage 28 | - prod 29 | ExportPrefix: 30 | Type: String 31 | Default: 'myapp' 32 | Description: >- 33 | Prefix for the managed resources and cloudformation exports. 34 | Provide it in `namespace-environment` format, 35 | where namespace can be product UPI or leave blank for defaults. 36 | AllowedPattern: ^[a-zA-Z]+(-?[a-zA-Z0-9]+)*$ 37 | ConstraintDescription: >- 38 | Only hyphen (-) separated alphanumeric string is allowed. Should start with a letter. 39 | MinLength: 3 40 | MaxLength: 30 41 | BillingMode: 42 | AllowedValues: 43 | - PAY_PER_REQUEST 44 | - PROVISIONED 45 | Default: 'PROVISIONED' 46 | Description: >- 47 | Specify how you are charged for read and write throughput and how you manage capacity. 48 | Set to PROVISIONED for predictable workloads and PAY_PER_REQUEST for unpredictable workloads. 49 | PAY_PER_REQUEST sets the billing mode to On-Demand Mode 50 | Type: String 51 | DefaultLambdaTimeout: 52 | Default: 3 53 | MinValue: 3 54 | MaxValue: 900 55 | Description: >- 56 | The amount of time that Lambda allows a function to run before stopping it. 57 | The default is 3 seconds. 58 | The maximum allowed value is 900 seconds. 59 | Type: Number 60 | DefaultLambdaMemory: 61 | Default: 128 62 | MinValue: 128 63 | MaxValue: 3008 64 | Description: >- 65 | The amount of memory that your function has access to. 66 | Increasing the function's memory also increases its CPU allocation. 67 | The default value is 128 MB. The value must be a multiple of 64 MB. 68 | Type: Number 69 | TTLAttribute: 70 | Default: None 71 | Description: >- 72 | Attribute name for time to live. Provide None or leave blank for if not required. 73 | Type: String 74 | CDNHostedZoneId: 75 | Default: 'Z2FDTNDATAQYW2' 76 | Description: >- 77 | CloudFront hosted zone id. 78 | Always Static Value. 79 | Refer AWS Documentation here 80 | https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget.html#cfn-route53-aliastarget-hostedzoneid 81 | Type: String 82 | DomainName: 83 | Default: 'acloudtiger.com' 84 | Description: >- 85 | Registered domain name with Top Level Domain as .com 86 | Type: String 87 | 88 | 89 | # ---------------------------------------------------------------------------- # 90 | # Mappings # 91 | # ---------------------------------------------------------------------------- # 92 | 93 | Mappings: 94 | Environment: 95 | FullForm: 96 | sbx: sandbox 97 | dev: development 98 | stage: staging 99 | qa: qa 100 | prod: production 101 | 102 | 103 | 104 | # ---------------------------------------------------------------------------- # 105 | # Conditions # 106 | # ---------------------------------------------------------------------------- # 107 | 108 | Conditions: 109 | HasExportPrefix: !Not [!Equals [!Ref ExportPrefix, '']] 110 | HasNoTTLAttribute: !Or [!Equals [!Ref TTLAttribute, ''], !Equals [!Ref TTLAttribute, 'None']] 111 | HasTTLAttribute: !Not [!Condition HasNoTTLAttribute] 112 | 113 | # ---------------------------------------------------------------------------- # 114 | # Resources # 115 | # ---------------------------------------------------------------------------- # 116 | 117 | Resources: 118 | # ---------------------------------------------------------------------------- # 119 | # MyApp Lambda Execution Role for reading writing to DynamoDB, CloudWatch, etc # 120 | # ---------------------------------------------------------------------------- # 121 | MyAppLambdaExecutionRole: 122 | Type: AWS::IAM::Role 123 | Properties: 124 | AssumeRolePolicyDocument: 125 | Statement: 126 | - Action: 127 | - "sts:AssumeRole" 128 | Effect: Allow 129 | Principal: 130 | Service: 131 | - lambda.amazonaws.com 132 | Version: 2012-10-17 133 | Path: / 134 | ManagedPolicyArns: 135 | - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 136 | - arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess 137 | - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole 138 | Policies: 139 | - PolicyName: !Sub 140 | - "${ExportPrefix_}MyAppGetLambdaPolicy" 141 | - ExportPrefix_: !If 142 | - HasExportPrefix 143 | - !Join ['-', [!Ref ExportPrefix, !Ref Environment, '']] 144 | - !Join ['-', [!Select [0, !Split ["-", !Ref "AWS::StackName"]], !Ref Environment, '']] 145 | PolicyDocument: 146 | Version: 2012-10-17 147 | Statement: 148 | - Sid: MyAppLambdaExecutionAndXRayTracing 149 | Effect: Allow 150 | Action: 151 | - cloudwatch:PutMetricData 152 | - dynamodb:UpdateItem 153 | - dynamodb:ConditionCheckItem 154 | - dynamodb:Scan 155 | - dynamodb:BatchWriteItem 156 | - dynamodb:PutItem 157 | - dynamodb:GetItem 158 | - dynamodb:DescribeTable 159 | - dynamodb:Query 160 | - dynamodb:BatchGetItem 161 | - dynamodb:DeleteItem 162 | - s3:* 163 | - lambda:* 164 | Resource: "*" 165 | 166 | # ---------------------------------------------------------------------------- # 167 | # MyApp Lambda Function # 168 | # ---------------------------------------------------------------------------- # 169 | MyAppFunction: 170 | Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction 171 | Properties: 172 | CodeUri: my-app/ 173 | Handler: app.lambdaHandler 174 | Runtime: nodejs14.x 175 | MemorySize: !Ref DefaultLambdaMemory 176 | Timeout: !Ref DefaultLambdaTimeout 177 | Role: !GetAtt MyAppLambdaExecutionRole.Arn 178 | Environment: 179 | Variables: 180 | RUNTIME_DDB_TABLE_NAME: !Ref DynamoDBTable 181 | Architectures: 182 | - x86_64 183 | Events: 184 | MyApp: 185 | Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api 186 | Properties: 187 | Path: /hello 188 | Method: get 189 | 190 | 191 | # ---------------------------------------------------------------------------- # 192 | # DynamoDB Table # 193 | # ---------------------------------------------------------------------------- # 194 | DynamoDBTable: 195 | Type: AWS::DynamoDB::Table 196 | Properties: 197 | AttributeDefinitions: 198 | - AttributeName: id 199 | AttributeType: S 200 | KeySchema: 201 | - AttributeName: id 202 | KeyType: HASH 203 | ProvisionedThroughput: 204 | ReadCapacityUnits: 5 205 | WriteCapacityUnits: 5 206 | Tags: # Optional 207 | - Key: Namespace 208 | Value: '@MyApp' 209 | - Key: Name 210 | Value: !Sub 211 | - '${ExportPrefix_}DynamoDBTable' 212 | - ExportPrefix_: !If 213 | - HasExportPrefix 214 | - !Join ['-', [!Ref ExportPrefix, !Ref Environment, '']] 215 | - !Join ['-', [!Select [0, !Split ["-", !Ref "AWS::StackName"]], !Ref Environment, '']] 216 | - Key: Environment 217 | Value: !FindInMap [Environment, FullForm, !Ref Environment] 218 | TimeToLiveSpecification: !If 219 | - HasTTLAttribute 220 | - AttributeName: !Ref TTLAttribute 221 | Enabled: true 222 | - !Ref 'AWS::NoValue' 223 | 224 | # ---------------------------------------------------------------------------- # 225 | # S3 bucket for static website hosting # 226 | # ---------------------------------------------------------------------------- # 227 | FEStaticWebsiteS3Bucket: 228 | Type: AWS::S3::Bucket 229 | Properties: 230 | BucketName: acloudtiger.com 231 | Tags: # Optional 232 | - Key: Namespace 233 | Value: '@MyApp' 234 | - Key: Name 235 | Value: !Sub 236 | - '${ExportPrefix_}FEStaticWebsiteS3Bucket' 237 | - ExportPrefix_: !If 238 | - HasExportPrefix 239 | - !Join ['-', [!Ref ExportPrefix, !Ref Environment, '']] 240 | - !Join ['-', [!Select [0, !Split ["-", !Ref "AWS::StackName"]], !Ref Environment, '']] 241 | - Key: Environment 242 | Value: !FindInMap [Environment, FullForm, !Ref Environment] 243 | 244 | S3BucketPolicy: 245 | Type: AWS::S3::BucketPolicy 246 | Properties: 247 | Bucket: !Ref FEStaticWebsiteS3Bucket 248 | PolicyDocument: 249 | Statement: 250 | - Effect: Allow 251 | Action: 's3:GetObject' 252 | Resource: 253 | - !Sub "arn:aws:s3:::${FEStaticWebsiteS3Bucket}/*" 254 | Principal: 255 | AWS: !Sub "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${MyCDNOAI}" 256 | 257 | 258 | # ---------------------------------------------------------------------------- # 259 | # CDN and its distribution # 260 | # ---------------------------------------------------------------------------- # 261 | MyCDNOAI: 262 | Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity' 263 | Properties: 264 | CloudFrontOriginAccessIdentityConfig: 265 | Comment: 'Serverless website OA' 266 | 267 | MyCDN: 268 | Type: 'AWS::CloudFront::Distribution' 269 | Properties: 270 | Tags: # Optional 271 | - Key: Namespace 272 | Value: '@MyApp' 273 | - Key: Name 274 | Value: !Sub 275 | - '${ExportPrefix_}MyCDN' 276 | - ExportPrefix_: !If 277 | - HasExportPrefix 278 | - !Join ['-', [!Ref ExportPrefix, !Ref Environment, '']] 279 | - !Join ['-', [!Select [0, !Split ["-", !Ref "AWS::StackName"]], !Ref Environment, '']] 280 | - Key: Environment 281 | Value: !FindInMap [Environment, FullForm, !Ref Environment] 282 | DistributionConfig: 283 | Comment: "CDN configuration for s3 static website" 284 | ViewerCertificate: 285 | AcmCertificateArn: arn:aws:acm:us-east-1:195725532069:certificate/742155d5-0040-4a85-a24d-ac248d93324e #Cert for my personal domain acloudtiger.com 286 | MinimumProtocolVersion: TLSv1.1_2016 287 | SslSupportMethod: sni-only 288 | DefaultRootObject: 'index.html' 289 | Aliases: 290 | - !Ref DomainName 291 | #- !Sub '*.${DomainName}' 292 | - !Sub 'www.${DomainName}' 293 | Enabled: true 294 | HttpVersion: http2 295 | IPV6Enabled: true 296 | Origins: 297 | - Id: my-s3-static-website 298 | DomainName: !GetAtt FEStaticWebsiteS3Bucket.DomainName 299 | S3OriginConfig: 300 | OriginAccessIdentity: 301 | Fn::Sub: 'origin-access-identity/cloudfront/${MyCDNOAI}' 302 | DefaultCacheBehavior: 303 | Compress: 'true' 304 | AllowedMethods: 305 | - DELETE 306 | - GET 307 | - HEAD 308 | - OPTIONS 309 | - PATCH 310 | - POST 311 | - PUT 312 | CachedMethods: 313 | - GET 314 | - HEAD 315 | - OPTIONS 316 | ForwardedValues: 317 | QueryString: false 318 | TargetOriginId: my-s3-static-website 319 | ViewerProtocolPolicy : redirect-to-https 320 | 321 | # ---------------------------------------------------------------------------- # 322 | # Zone file for TLD - Public Hosted Zone # 323 | # ---------------------------------------------------------------------------- # 324 | 325 | # PublicHostedZoneForZoneFile: 326 | # Type: AWS::Route53::HostedZone 327 | # Properties: 328 | # HostedZoneConfig: 329 | # Comment: Hosted zone acloudtiger.com 330 | # Name: !Ref DomainName 331 | 332 | # ---------------------------------------------------------------------------- # 333 | # Record sets for Public Hosted Zone # 334 | # ---------------------------------------------------------------------------- # 335 | # DNSAliasRecordForIPV4: 336 | # Type: AWS::Route53::RecordSet 337 | # DependsOn: MyCDN 338 | # Properties: 339 | # HostedZoneId: !Ref PublicHostedZoneForZoneFile 340 | # Name: !Ref DomainName 341 | # Type: A 342 | # AliasTarget: 343 | # DNSName: !GetAtt MyCDN.DomainName 344 | # HostedZoneId: !Ref CDNHostedZoneId 345 | 346 | # DNSAliasRecordForIPV6: 347 | # Type: AWS::Route53::RecordSet 348 | # DependsOn: MyCDN 349 | # Properties: 350 | # HostedZoneId: !Ref PublicHostedZoneForZoneFile 351 | # Name: !Ref DomainName 352 | # Type: AAAA 353 | # AliasTarget: 354 | # DNSName: !GetAtt MyCDN.DomainName 355 | # HostedZoneId: !Ref CDNHostedZoneId 356 | 357 | # DNSIP4RecordForMyApp: 358 | # Type: AWS::Route53::RecordSet 359 | # DependsOn: MyCDN 360 | # Properties: 361 | # HostedZoneId: !Ref PublicHostedZoneForZoneFile 362 | # Name: www.acloudtiger.com 363 | # Type: A 364 | # AliasTarget: 365 | # DNSName: !GetAtt MyCDN.DomainName 366 | # HostedZoneId: !Ref CDNHostedZoneId 367 | 368 | # DNSIPV6RecordForMyApp: 369 | # Type: AWS::Route53::RecordSet 370 | # DependsOn: MyCDN 371 | # Properties: 372 | # HostedZoneId: !Ref PublicHostedZoneForZoneFile 373 | # Name: www.acloudtiger.com 374 | # Type: AAAA 375 | # AliasTarget: 376 | # DNSName: !GetAtt MyCDN.DomainName 377 | # HostedZoneId: !Ref CDNHostedZoneId 378 | 379 | # ---------------------------------------------------------------------------- # 380 | # Outputs # 381 | # ---------------------------------------------------------------------------- # 382 | Outputs: 383 | MyAppApi: 384 | Description: "API Gateway endpoint URL for Prod stage for this function" 385 | Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" 386 | 387 | MyAppFunction: 388 | Description: "Lambda Function ARN" 389 | Value: !GetAtt MyAppFunction.Arn 390 | 391 | # MyAppFunctionIamRole: 392 | # Description: "Implicit IAM Role created for function" 393 | # Value: !GetAtt MyAppFunctionRole.Arn 394 | 395 | DynamoDBTable: 396 | Description: Dynamodb table 397 | Export: 398 | Name: !Sub 399 | - ${ExportPrefix_}:${AWS::Region}:myapp:DynamoDBTable 400 | - ExportPrefix_: !If 401 | - HasExportPrefix 402 | - !Join ['-', [!Ref ExportPrefix, !Ref Environment]] 403 | - !Join ['-', [!Select [0, !Split ["-", !Ref "AWS::StackName"]], !Ref Environment]] 404 | Value: !Ref DynamoDBTable 405 | 406 | DynamoDBTableArn: 407 | Description: Dynamodb table Arn 408 | Export: 409 | Name: !Sub 410 | - ${ExportPrefix_}:${AWS::Region}:myapp:DynamoDBTable:Arn 411 | - ExportPrefix_: !If 412 | - HasExportPrefix 413 | - !Join ['-', [!Ref ExportPrefix, !Ref Environment]] 414 | - !Join ['-', [!Select [0, !Split ["-", !Ref "AWS::StackName"]], !Ref Environment]] 415 | Value: !GetAtt DynamoDBTable.Arn 416 | -------------------------------------------------------------------------------- /iac/aws/terraform/creating-custom-vpc/terraform.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "1.7.0", 4 | "serial": 12, 5 | "lineage": "69ea60e4-e317-8e43-6a2c-43fc92d249d3", 6 | "outputs": {}, 7 | "resources": [ 8 | { 9 | "mode": "managed", 10 | "type": "aws_eip", 11 | "name": "nat", 12 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 13 | "instances": [ 14 | { 15 | "schema_version": 0, 16 | "attributes": { 17 | "address": null, 18 | "allocation_id": "eipalloc-0dec4e7657e6f0c73", 19 | "arn": "arn:aws:ec2:us-east-1:458419607076:elastic-ip/eipalloc-0dec4e7657e6f0c73", 20 | "associate_with_private_ip": null, 21 | "association_id": "", 22 | "carrier_ip": "", 23 | "customer_owned_ip": "", 24 | "customer_owned_ipv4_pool": "", 25 | "domain": "vpc", 26 | "id": "eipalloc-0dec4e7657e6f0c73", 27 | "instance": "", 28 | "network_border_group": "us-east-1", 29 | "network_interface": "", 30 | "private_dns": null, 31 | "private_ip": "", 32 | "ptr_record": "", 33 | "public_dns": "ec2-34-239-9-241.compute-1.amazonaws.com", 34 | "public_ip": "34.239.9.241", 35 | "public_ipv4_pool": "amazon", 36 | "tags": { 37 | "Name": "EIP", 38 | "Owner": "Vinod" 39 | }, 40 | "tags_all": { 41 | "Name": "EIP", 42 | "Owner": "Vinod" 43 | }, 44 | "timeouts": null, 45 | "vpc": true 46 | }, 47 | "sensitive_attributes": [], 48 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==" 49 | } 50 | ] 51 | }, 52 | { 53 | "mode": "managed", 54 | "type": "aws_internet_gateway", 55 | "name": "igw", 56 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 57 | "instances": [ 58 | { 59 | "schema_version": 0, 60 | "attributes": { 61 | "arn": "arn:aws:ec2:us-east-1:458419607076:internet-gateway/igw-088310bb91dd11a7d", 62 | "id": "igw-088310bb91dd11a7d", 63 | "owner_id": "458419607076", 64 | "tags": { 65 | "Name": "IGW", 66 | "owner": "vinod" 67 | }, 68 | "tags_all": { 69 | "Name": "IGW", 70 | "owner": "vinod" 71 | }, 72 | "timeouts": null, 73 | "vpc_id": "vpc-0f75836ee7c86c9aa" 74 | }, 75 | "sensitive_attributes": [], 76 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH19", 77 | "dependencies": [ 78 | "aws_vpc.mycustomvpc" 79 | ] 80 | } 81 | ] 82 | }, 83 | { 84 | "mode": "managed", 85 | "type": "aws_nat_gateway", 86 | "name": "nat", 87 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 88 | "instances": [ 89 | { 90 | "schema_version": 0, 91 | "attributes": { 92 | "allocation_id": "eipalloc-0dec4e7657e6f0c73", 93 | "association_id": "eipassoc-06e6ec3f3093f4c88", 94 | "connectivity_type": "public", 95 | "id": "nat-0018a52afaafb9f96", 96 | "network_interface_id": "eni-06120f8892dde34cf", 97 | "private_ip": "10.0.3.98", 98 | "public_ip": "34.239.9.241", 99 | "secondary_allocation_ids": null, 100 | "secondary_private_ip_address_count": 0, 101 | "secondary_private_ip_addresses": [], 102 | "subnet_id": "subnet-0fa01d974a4407e26", 103 | "tags": { 104 | "Name": "NAT Gateway", 105 | "Owner": "Vinod" 106 | }, 107 | "tags_all": { 108 | "Name": "NAT Gateway", 109 | "Owner": "Vinod" 110 | }, 111 | "timeouts": null 112 | }, 113 | "sensitive_attributes": [], 114 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTgwMDAwMDAwMDAwMCwidXBkYXRlIjo2MDAwMDAwMDAwMDB9fQ==", 115 | "dependencies": [ 116 | "aws_eip.nat", 117 | "aws_internet_gateway.igw", 118 | "aws_subnet.public-us-east-1a", 119 | "aws_vpc.mycustomvpc" 120 | ] 121 | } 122 | ] 123 | }, 124 | { 125 | "mode": "managed", 126 | "type": "aws_route_table", 127 | "name": "privateroute", 128 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 129 | "instances": [ 130 | { 131 | "schema_version": 0, 132 | "attributes": { 133 | "arn": "arn:aws:ec2:us-east-1:458419607076:route-table/rtb-05f23dd2cd83ea85a", 134 | "id": "rtb-05f23dd2cd83ea85a", 135 | "owner_id": "458419607076", 136 | "propagating_vgws": [], 137 | "route": [ 138 | { 139 | "carrier_gateway_id": "", 140 | "cidr_block": "0.0.0.0/0", 141 | "core_network_arn": "", 142 | "destination_prefix_list_id": "", 143 | "egress_only_gateway_id": "", 144 | "gateway_id": "", 145 | "ipv6_cidr_block": "", 146 | "local_gateway_id": "", 147 | "nat_gateway_id": "nat-0018a52afaafb9f96", 148 | "network_interface_id": "", 149 | "transit_gateway_id": "", 150 | "vpc_endpoint_id": "", 151 | "vpc_peering_connection_id": "" 152 | } 153 | ], 154 | "tags": { 155 | "Name": "private" 156 | }, 157 | "tags_all": { 158 | "Name": "private" 159 | }, 160 | "timeouts": null, 161 | "vpc_id": "vpc-0f75836ee7c86c9aa" 162 | }, 163 | "sensitive_attributes": [], 164 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", 165 | "dependencies": [ 166 | "aws_eip.nat", 167 | "aws_internet_gateway.igw", 168 | "aws_nat_gateway.nat", 169 | "aws_subnet.public-us-east-1a", 170 | "aws_vpc.mycustomvpc" 171 | ] 172 | } 173 | ] 174 | }, 175 | { 176 | "mode": "managed", 177 | "type": "aws_route_table", 178 | "name": "publicroute", 179 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 180 | "instances": [ 181 | { 182 | "schema_version": 0, 183 | "attributes": { 184 | "arn": "arn:aws:ec2:us-east-1:458419607076:route-table/rtb-03ab2291f0acd446b", 185 | "id": "rtb-03ab2291f0acd446b", 186 | "owner_id": "458419607076", 187 | "propagating_vgws": [], 188 | "route": [ 189 | { 190 | "carrier_gateway_id": "", 191 | "cidr_block": "0.0.0.0/0", 192 | "core_network_arn": "", 193 | "destination_prefix_list_id": "", 194 | "egress_only_gateway_id": "", 195 | "gateway_id": "igw-088310bb91dd11a7d", 196 | "ipv6_cidr_block": "", 197 | "local_gateway_id": "", 198 | "nat_gateway_id": "", 199 | "network_interface_id": "", 200 | "transit_gateway_id": "", 201 | "vpc_endpoint_id": "", 202 | "vpc_peering_connection_id": "" 203 | } 204 | ], 205 | "tags": { 206 | "Name": "public" 207 | }, 208 | "tags_all": { 209 | "Name": "public" 210 | }, 211 | "timeouts": null, 212 | "vpc_id": "vpc-0f75836ee7c86c9aa" 213 | }, 214 | "sensitive_attributes": [], 215 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", 216 | "dependencies": [ 217 | "aws_internet_gateway.igw", 218 | "aws_vpc.mycustomvpc" 219 | ] 220 | } 221 | ] 222 | }, 223 | { 224 | "mode": "managed", 225 | "type": "aws_route_table_association", 226 | "name": "privateassociation_a", 227 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 228 | "instances": [ 229 | { 230 | "schema_version": 0, 231 | "attributes": { 232 | "gateway_id": "", 233 | "id": "rtbassoc-04dfc37a17834fe61", 234 | "route_table_id": "rtb-05f23dd2cd83ea85a", 235 | "subnet_id": "subnet-03bca1e66e34483ac", 236 | "timeouts": null 237 | }, 238 | "sensitive_attributes": [], 239 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", 240 | "dependencies": [ 241 | "aws_eip.nat", 242 | "aws_internet_gateway.igw", 243 | "aws_nat_gateway.nat", 244 | "aws_route_table.privateroute", 245 | "aws_subnet.private-us-east-1a", 246 | "aws_subnet.public-us-east-1a", 247 | "aws_vpc.mycustomvpc" 248 | ] 249 | } 250 | ] 251 | }, 252 | { 253 | "mode": "managed", 254 | "type": "aws_route_table_association", 255 | "name": "privateassociation_b", 256 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 257 | "instances": [ 258 | { 259 | "schema_version": 0, 260 | "attributes": { 261 | "gateway_id": "", 262 | "id": "rtbassoc-07529e65f3f89e88a", 263 | "route_table_id": "rtb-05f23dd2cd83ea85a", 264 | "subnet_id": "subnet-03a9d74e9339d2434", 265 | "timeouts": null 266 | }, 267 | "sensitive_attributes": [], 268 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", 269 | "dependencies": [ 270 | "aws_eip.nat", 271 | "aws_internet_gateway.igw", 272 | "aws_nat_gateway.nat", 273 | "aws_route_table.privateroute", 274 | "aws_subnet.private-us-east-1b", 275 | "aws_subnet.public-us-east-1a", 276 | "aws_vpc.mycustomvpc" 277 | ] 278 | } 279 | ] 280 | }, 281 | { 282 | "mode": "managed", 283 | "type": "aws_route_table_association", 284 | "name": "publicassociation_a", 285 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 286 | "instances": [ 287 | { 288 | "schema_version": 0, 289 | "attributes": { 290 | "gateway_id": "", 291 | "id": "rtbassoc-0870ae7bc90384420", 292 | "route_table_id": "rtb-03ab2291f0acd446b", 293 | "subnet_id": "subnet-0fa01d974a4407e26", 294 | "timeouts": null 295 | }, 296 | "sensitive_attributes": [], 297 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", 298 | "dependencies": [ 299 | "aws_internet_gateway.igw", 300 | "aws_route_table.publicroute", 301 | "aws_subnet.public-us-east-1a", 302 | "aws_vpc.mycustomvpc" 303 | ] 304 | } 305 | ] 306 | }, 307 | { 308 | "mode": "managed", 309 | "type": "aws_route_table_association", 310 | "name": "publicassociation_b", 311 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 312 | "instances": [ 313 | { 314 | "schema_version": 0, 315 | "attributes": { 316 | "gateway_id": "", 317 | "id": "rtbassoc-0de18ab0cf25e5c3a", 318 | "route_table_id": "rtb-03ab2291f0acd446b", 319 | "subnet_id": "subnet-0bd01bd3dcc7c2b28", 320 | "timeouts": null 321 | }, 322 | "sensitive_attributes": [], 323 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", 324 | "dependencies": [ 325 | "aws_internet_gateway.igw", 326 | "aws_route_table.publicroute", 327 | "aws_subnet.public-us-east-1b", 328 | "aws_vpc.mycustomvpc" 329 | ] 330 | } 331 | ] 332 | }, 333 | { 334 | "mode": "managed", 335 | "type": "aws_subnet", 336 | "name": "private-us-east-1a", 337 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 338 | "instances": [ 339 | { 340 | "schema_version": 1, 341 | "attributes": { 342 | "arn": "arn:aws:ec2:us-east-1:458419607076:subnet/subnet-03bca1e66e34483ac", 343 | "assign_ipv6_address_on_creation": false, 344 | "availability_zone": "us-east-1a", 345 | "availability_zone_id": "use1-az4", 346 | "cidr_block": "10.0.1.0/24", 347 | "customer_owned_ipv4_pool": "", 348 | "enable_dns64": false, 349 | "enable_lni_at_device_index": 0, 350 | "enable_resource_name_dns_a_record_on_launch": false, 351 | "enable_resource_name_dns_aaaa_record_on_launch": false, 352 | "id": "subnet-03bca1e66e34483ac", 353 | "ipv6_cidr_block": "", 354 | "ipv6_cidr_block_association_id": "", 355 | "ipv6_native": false, 356 | "map_customer_owned_ip_on_launch": false, 357 | "map_public_ip_on_launch": false, 358 | "outpost_arn": "", 359 | "owner_id": "458419607076", 360 | "private_dns_hostname_type_on_launch": "ip-name", 361 | "tags": { 362 | "Name": "Private Subnet", 363 | "subnet": "private-us-east-1a" 364 | }, 365 | "tags_all": { 366 | "Name": "Private Subnet", 367 | "subnet": "private-us-east-1a" 368 | }, 369 | "timeouts": null, 370 | "vpc_id": "vpc-0f75836ee7c86c9aa" 371 | }, 372 | "sensitive_attributes": [], 373 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", 374 | "dependencies": [ 375 | "aws_vpc.mycustomvpc" 376 | ] 377 | } 378 | ] 379 | }, 380 | { 381 | "mode": "managed", 382 | "type": "aws_subnet", 383 | "name": "private-us-east-1b", 384 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 385 | "instances": [ 386 | { 387 | "schema_version": 1, 388 | "attributes": { 389 | "arn": "arn:aws:ec2:us-east-1:458419607076:subnet/subnet-03a9d74e9339d2434", 390 | "assign_ipv6_address_on_creation": false, 391 | "availability_zone": "us-east-1b", 392 | "availability_zone_id": "use1-az6", 393 | "cidr_block": "10.0.2.0/24", 394 | "customer_owned_ipv4_pool": "", 395 | "enable_dns64": false, 396 | "enable_lni_at_device_index": 0, 397 | "enable_resource_name_dns_a_record_on_launch": false, 398 | "enable_resource_name_dns_aaaa_record_on_launch": false, 399 | "id": "subnet-03a9d74e9339d2434", 400 | "ipv6_cidr_block": "", 401 | "ipv6_cidr_block_association_id": "", 402 | "ipv6_native": false, 403 | "map_customer_owned_ip_on_launch": false, 404 | "map_public_ip_on_launch": false, 405 | "outpost_arn": "", 406 | "owner_id": "458419607076", 407 | "private_dns_hostname_type_on_launch": "ip-name", 408 | "tags": { 409 | "Name": "Private Subnet", 410 | "subnet": "private-us-east-1b" 411 | }, 412 | "tags_all": { 413 | "Name": "Private Subnet", 414 | "subnet": "private-us-east-1b" 415 | }, 416 | "timeouts": null, 417 | "vpc_id": "vpc-0f75836ee7c86c9aa" 418 | }, 419 | "sensitive_attributes": [], 420 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", 421 | "dependencies": [ 422 | "aws_vpc.mycustomvpc" 423 | ] 424 | } 425 | ] 426 | }, 427 | { 428 | "mode": "managed", 429 | "type": "aws_subnet", 430 | "name": "public-us-east-1a", 431 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 432 | "instances": [ 433 | { 434 | "schema_version": 1, 435 | "attributes": { 436 | "arn": "arn:aws:ec2:us-east-1:458419607076:subnet/subnet-0fa01d974a4407e26", 437 | "assign_ipv6_address_on_creation": false, 438 | "availability_zone": "us-east-1a", 439 | "availability_zone_id": "use1-az4", 440 | "cidr_block": "10.0.3.0/24", 441 | "customer_owned_ipv4_pool": "", 442 | "enable_dns64": false, 443 | "enable_lni_at_device_index": 0, 444 | "enable_resource_name_dns_a_record_on_launch": false, 445 | "enable_resource_name_dns_aaaa_record_on_launch": false, 446 | "id": "subnet-0fa01d974a4407e26", 447 | "ipv6_cidr_block": "", 448 | "ipv6_cidr_block_association_id": "", 449 | "ipv6_native": false, 450 | "map_customer_owned_ip_on_launch": false, 451 | "map_public_ip_on_launch": true, 452 | "outpost_arn": "", 453 | "owner_id": "458419607076", 454 | "private_dns_hostname_type_on_launch": "ip-name", 455 | "tags": { 456 | "Name": "Public Subnet", 457 | "subnet": "public-us-east-1a" 458 | }, 459 | "tags_all": { 460 | "Name": "Public Subnet", 461 | "subnet": "public-us-east-1a" 462 | }, 463 | "timeouts": null, 464 | "vpc_id": "vpc-0f75836ee7c86c9aa" 465 | }, 466 | "sensitive_attributes": [], 467 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", 468 | "dependencies": [ 469 | "aws_vpc.mycustomvpc" 470 | ] 471 | } 472 | ] 473 | }, 474 | { 475 | "mode": "managed", 476 | "type": "aws_subnet", 477 | "name": "public-us-east-1b", 478 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 479 | "instances": [ 480 | { 481 | "schema_version": 1, 482 | "attributes": { 483 | "arn": "arn:aws:ec2:us-east-1:458419607076:subnet/subnet-0bd01bd3dcc7c2b28", 484 | "assign_ipv6_address_on_creation": false, 485 | "availability_zone": "us-east-1b", 486 | "availability_zone_id": "use1-az6", 487 | "cidr_block": "10.0.4.0/24", 488 | "customer_owned_ipv4_pool": "", 489 | "enable_dns64": false, 490 | "enable_lni_at_device_index": 0, 491 | "enable_resource_name_dns_a_record_on_launch": false, 492 | "enable_resource_name_dns_aaaa_record_on_launch": false, 493 | "id": "subnet-0bd01bd3dcc7c2b28", 494 | "ipv6_cidr_block": "", 495 | "ipv6_cidr_block_association_id": "", 496 | "ipv6_native": false, 497 | "map_customer_owned_ip_on_launch": false, 498 | "map_public_ip_on_launch": true, 499 | "outpost_arn": "", 500 | "owner_id": "458419607076", 501 | "private_dns_hostname_type_on_launch": "ip-name", 502 | "tags": { 503 | "Name": "Public Subnet", 504 | "subnet": "public-us-east-1b" 505 | }, 506 | "tags_all": { 507 | "Name": "Public Subnet", 508 | "subnet": "public-us-east-1b" 509 | }, 510 | "timeouts": null, 511 | "vpc_id": "vpc-0f75836ee7c86c9aa" 512 | }, 513 | "sensitive_attributes": [], 514 | "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", 515 | "dependencies": [ 516 | "aws_vpc.mycustomvpc" 517 | ] 518 | } 519 | ] 520 | }, 521 | { 522 | "mode": "managed", 523 | "type": "aws_vpc", 524 | "name": "mycustomvpc", 525 | "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", 526 | "instances": [ 527 | { 528 | "schema_version": 1, 529 | "attributes": { 530 | "arn": "arn:aws:ec2:us-east-1:458419607076:vpc/vpc-0f75836ee7c86c9aa", 531 | "assign_generated_ipv6_cidr_block": false, 532 | "cidr_block": "10.0.0.0/16", 533 | "default_network_acl_id": "acl-0d154a4490e56ad7e", 534 | "default_route_table_id": "rtb-0239fb174935a2e25", 535 | "default_security_group_id": "sg-05e5a80f7c4662f97", 536 | "dhcp_options_id": "dopt-077b1d9ea0f5fb912", 537 | "enable_dns_hostnames": true, 538 | "enable_dns_support": true, 539 | "enable_network_address_usage_metrics": false, 540 | "id": "vpc-0f75836ee7c86c9aa", 541 | "instance_tenancy": "default", 542 | "ipv4_ipam_pool_id": null, 543 | "ipv4_netmask_length": null, 544 | "ipv6_association_id": "", 545 | "ipv6_cidr_block": "", 546 | "ipv6_cidr_block_network_border_group": "", 547 | "ipv6_ipam_pool_id": "", 548 | "ipv6_netmask_length": 0, 549 | "main_route_table_id": "rtb-0239fb174935a2e25", 550 | "owner_id": "458419607076", 551 | "tags": { 552 | "Name": "my custom VPC", 553 | "owner": "vinod" 554 | }, 555 | "tags_all": { 556 | "Name": "my custom VPC", 557 | "owner": "vinod" 558 | } 559 | }, 560 | "sensitive_attributes": [], 561 | "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" 562 | } 563 | ] 564 | } 565 | ], 566 | "check_results": null 567 | } 568 | --------------------------------------------------------------------------------