{"id":19392843,"url":"https://github.com/ruanbekker/aws-codepipeline-codedeploy-fastapi","last_synced_at":"2025-04-24T02:30:43.920Z","repository":{"id":44388570,"uuid":"467520707","full_name":"ruanbekker/aws-codepipeline-codedeploy-fastapi","owner":"ruanbekker","description":"CodeCommit, CodePipeline and CodeDeploy Demo using a FastAPI Service","archived":false,"fork":false,"pushed_at":"2022-03-17T06:50:24.000Z","size":4203,"stargazers_count":2,"open_issues_count":0,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-07-30T17:34:53.490Z","etag":null,"topics":["aws","cicd","codedeploy","devops","fastapi","python"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ruanbekker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-08T13:21:15.000Z","updated_at":"2024-04-27T10:14:25.000Z","dependencies_parsed_at":"2022-07-14T15:31:12.801Z","dependency_job_id":null,"html_url":"https://github.com/ruanbekker/aws-codepipeline-codedeploy-fastapi","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Faws-codepipeline-codedeploy-fastapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Faws-codepipeline-codedeploy-fastapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Faws-codepipeline-codedeploy-fastapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Faws-codepipeline-codedeploy-fastapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ruanbekker","download_url":"https://codeload.github.com/ruanbekker/aws-codepipeline-codedeploy-fastapi/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223941329,"owners_count":17228999,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","cicd","codedeploy","devops","fastapi","python"],"created_at":"2024-11-10T10:32:28.052Z","updated_at":"2024-11-10T10:33:49.013Z","avatar_url":"https://github.com/ruanbekker.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CodeDeploy Demo\n\nThis is a example on using CodePipeline with CodeDeploy for EC2 deployments.\n\n## About\n\nWe will manually create IAM Roles, CodePipeline, CodeDeploy and setup our EC2 instance for deployment of our Python FastAPI application.\n\nThe source code for this example will reside on CodeCommit which is an assumption that already exists.\n\n## IAM\n\nThe following can be referenced to create the CodeDeploy Service IAM Role and the EC2 IAM Instance Profile:\n\n- [IAM Service Role](https://docs.aws.amazon.com/codedeploy/latest/userguide/getting-started-create-service-role.html)\n- [IAM Instance Profile](https://docs.aws.amazon.com/codedeploy/latest/userguide/getting-started-create-iam-instance-profile.html)\n\n### Service Role\n\nIn short, to create the service role name `CodeDeployServiceRole`:\n\n```bash\naws iam create-role --role-name CodeDeployServiceRole --assume-role-policy-document file://aws/codedeploy_trust.json\n```\n\nFor EC2/On-Premise compute platform, we will need to attach the AWSCodeDeploy service role:\n\n```bash\naws iam attach-role-policy --role-name CodeDeployServiceRole --policy-arn arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole\n```\n\n### Instance Profile\n\nTo create the EC2 Instance Profile, create the Role `CodeDeployDemo-EC2-Instance-Profile`:\n\n```bash\naws iam create-role --role-name CodeDeployDemo-EC2-Instance-Profile --assume-role-policy-document file://aws/codedeploy_ec2_trust.json\n```\n\nAdd the IAM Policy to the Role:\n\n```bash\naws iam put-role-policy --role-name CodeDeployDemo-EC2-Instance-Profile --policy-name CodeDeployDemo-EC2-Permissions --policy-document file://aws/codedeploy_ec2_permissions.json\n```\n\nAttach the SSM Managed policy to the Role if you are using SSM to install the CodeDeploy Agent:\n\n```bash\naws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore --role-name CodeDeployDemo-EC2-Instance-Profile\n```\n\nCreate the instance profile, then add the role to the instance profile:\n\n```bash\naws iam create-instance-profile --instance-profile-name CodeDeployDemo-EC2-Instance-Profile\naws iam add-role-to-instance-profile --instance-profile-name CodeDeployDemo-EC2-Instance-Profile --role-name CodeDeployDemo-EC2-Instance-Profile\n```\n\n## EC2 Instance\n\nCreate a EC2 instance, and on the instance creation page, ensure to select the `CodeDeployDemo-EC2-Instance-Profile` IAM Role for the EC2 instance.\n\nFor this scenario we will be installing the codedeploy agent manually, but you can use AWS Systems Manager to install it.\n\n### CodeDeploy Agent Installation\n\nInstall Dependencies on the EC2 instance:\n\n```bash\nsudo apt update\nsudo apt install wget ruby-full -y\n```\n\nInstallation of the CodeDeploy Agent:\n\n```bash\nexport cd_region=eu-west-1\nexport cd_bucket_name=\"aws-codedeploy-${cd_region}\"\n\nwget \"https://${cd_bucket_name}.s3.${cd_region}.amazonaws.com/latest/install\"\nchmod +x ./install\nsudo ./install auto \u003e /tmp/logfile\n```\n\nEnsure the codedeploy agent is running:\n\n```bash\nsudo service codedeploy-agent status\n```\n\nNote: Whenever changes are made to the IAM Role or Permissions, the codedeploy agent needs to be restarted with:\n\n```bash\nsudo service codedeploy-agent restart\n```\n\nWe will be referencing the EC2 instances Tags from CodeDeploy, so head over to EC2 and focus on the Tags, which we will use these values later:\n\n![image](images/157020942-5509a962-9516-48b6-857f-47dccfc536de.png)\n\n\n## Create CodeDeploy Application\n\nFrom CodeDeploy, select Applications:\n\n![image](images/157012357-9038a3ee-b9fc-4b12-9d95-198307bc6532.png)\n\nName the application and the compute platform, which in this case will be EC2/On-Prem:\n\n![image](images/157012506-d5f5422e-63f6-4838-8f7d-25aaf5d8cfe0.png)\n\nNext you should be directed to the \"Deployment groups\" page:\n\n![image](images/157012798-29aefd70-649f-4342-94ee-c3dce32480be.png)\n\nWhere we will create a deployment group, for this use-case we will be using a \"In-place\" deployment typem which updates the instance with the latest revision of the deployment:\n\n![image](images/157171373-e53fd82f-d4f1-439f-8c7f-6aa5b47d81da.png)\n\nThen on \"Environment configuration\" select \"Amazon EC2 instances\", then select the Key/Value for the Tags:\n\n![image](images/157021698-024301b6-b5c5-4323-9c43-d65d48c12f54.png)\n\nWe have bootstrapped the AWS CodeDeploy Agent with a AMI, but it is recommended to use AWS Systems Manager, for this use-case, we will not be installing the AWS CodeDeploy Agent since it's already installed:\n\n![image](images/157021931-736aacb1-9558-401e-9577-9b2c67c59c0c.png)\n\nFor \"Deployment settings\" we will use OneAtATime so that only one instance gets changed at a time:\n\n![image](images/157022195-1cf4e730-75c9-41c8-a7c0-490cd3c211a2.png)\n\nBecause this example uses a \"In-place\" deployment, we won't be using a Load Balancer:\n\n![image](images/157022861-623a2d7b-46b4-442a-8046-6d2d5a00ae66.png)\n\nUnder the advanced settings, select \"Roll back when a deployment fails\" under \"Rollbacks\" and set the \"Deployment group tags\" to \"ManagedBy\" to \"codedeploy\":\n\n![image](images/157023135-5bea94c3-dea0-44ff-9a7e-a46fc9f4f0c7.png)\n\nThen select \"Create deployment group\", then you will be directed to the \"Applications\" screen:\n\n![image](images/157171612-540ca8cc-ccbf-42e3-85c7-8c495fd29021.png)\n\nFrom here we can create a manual deployment, but we will skip this for now. \n\n## CodePipeline\n\nCreate a CodePipeline:\n\n![image](images/157023980-073de118-87af-4cb3-bf1c-55228f5260b1.png)\n\nAdd the \"source\" stage to codecommit, select our repository and the branch that we are targeting:\n\n![image](images/157024131-a7e45b46-3668-4ddd-83e9-731af91494aa.png)\n\nFor this scenario, skip the build stage, and select AWS CodeDeploy as the \"Deploy\" stage, set the \"Application name\" to the application we defined in CodeBuild and select the \"Deployment group\" as the deployment group we defined in our CodeDeploy application:\n\n![image](images/157025102-f6c3c062-6635-48e8-9dc2-0d87dcd06ee6.png)\n\nOn the \"Review\" screen, note that the \"OutputArtifactFormat\" has \"CODE_ZIP\":\n\n![image](images/157025868-09046efc-cfe6-4514-b8f1-d1ef62d87518.png)\n\nThe reason for that is that CodePipeline zip's the revision and uploads it on S3, during the CodeDeploy [lifecycle event hooks](https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html#reference-appspec-file-structure-hooks-availability), the lifecycle event `DownloadBundle` which happens right after `ApplicationStop` downloads the zip and installs it to the `destination` specified in the `files` section of the `appspec.yml`: \n\n![image](images/157172274-bc75c113-3a6f-4882-9b62-25e244ac7661.png)\n\nThen select \"Create pipeline\", note that when you create the pipeline it will trigger its first run, and in my case it will fail as the branch that I selected does not exist yet.\n\nMake a commit to that branch, and if the pipeline is successful, it should look like the following:\n\n![image](images/157172519-620bb346-6086-4285-ac44-bfb20ed97b89.png)\n\nWe can view the deployment:\n\n![image](images/157172655-a36bfdfe-898c-4187-baa4-7340a7eecba1.png)\n\nAnd when we view the events:\n\n![image](images/157172767-0ba05d28-0e28-4d92-a21b-90d4073f492f.png)\n\nIf any of the events failed, it will look like this:\n\n![image](images/157172938-6393d745-cffb-413f-91a0-7eec1f84b2b4.png)\n\nAnd you can view the event:\n\n![image](images/157173013-481c6ea2-4c48-4c41-ba56-0d0a46aad479.png)\n\nWhen we select the `ScriptFailed` hyperlink we can view the stderr of the script:\n\n![image](images/157173136-dd997696-26bd-484f-8c26-73e10ed5ee88.png)\n\n## CodeDeploy Environment Variables\n\nThe following environment variables are accessible using the codedeploy agent:\n\n- `LIFECYCLE_EVENT`: This variable contains the name of the lifecycle event associated with the script.\n- `DEPLOYMENT_ID`:  This variables contains the deployment ID of the current deployment.\n- `APPLICATION_NAME`:  This variable contains the name of the application being deployed. This is the name the user sets in the console or AWS CLI.\n- `DEPLOYMENT_GROUP_NAME`:  This variable contains the name of the deployment group. A deployment group is a set of instances associated with an application that you target for a deployment.\n- `DEPLOYMENT_GROUP_ID`: This variable contains the ID of the deployment group in AWS CodeDeploy that corresponds to the current deployment\n\nWhen dumping them to a file, the will look more or less like the following:\n\n```bash\nLIFECYCLE_EVENT=BeforeInstall\nDEPLOYMENT_ID=d-MS07UR13G\nAPPLICATION_NAME=devops-python-service\nDEPLOYMENT_GROUP_NAME=devops-python-service-dg\nDEPLOYMENT_GROUP_ID=6859c2df-1fdb-4591-94f9-7acf02eba479\n```\n\n- [Source](https://aws.amazon.com/blogs/devops/using-codedeploy-environment-variables/)\n\n## CodeDeploy Runtime\n\nWhen a deployment occurs, the home for the deployment root will be at this location by default:\n\n- `/opt/codedeploy-agent/deployment-root/`\n\nUnder the `deployment-root` directory you will find the `DEPLOYMENT_GROUP_ID` and under that you will find the `DEPLOYMENT_ID` directories:\n\n```bash\n/opt/codedeploy-agent/deployment-root/6859c2df-1fdb-4591-94f9-7acf02eba479/\n├── d-4AG56713G\n│   ├── bundle.tar\n│   ├── deployment-archive\n│   │   ├── Dockerfile\n│   │   ├── Makefile\n│   │   ├── README.md\n│   │   ├── appspec.yml\n│   │   ├── configs\n│   │   │   ├── hypercorn.toml\n│   │   │   ├── python-app.service\n│   │   │   └── sample.env\n│   │   ├── dependencies\n│   │   │   └── requirements.pip\n│   │   ├── docker-compose.yml\n│   │   ├── scripts\n│   │   │   ├── after_install.sh\n│   │   │   ├── before_install.sh\n│   │   │   ├── start_server.sh\n│   │   │   ├── stop_server.sh\n│   │   │   └── validate_service.sh\n│   │   ├── src\n│   │   │   ├── config.py\n│   │   │   ├── main.py\n│   │   │   ├── models.py\n│   │   │   └── tests\n│   │   │       ├── __init__.py\n│   │   │       └── test_main.py\n│   │   └── trigger\n    └── logs\n        └── scripts.log\n\n```\n\nAs you can see we have multiple `DEPLOYMENT_ID`'s, as each deployment has it's unique DEPLOYMENT_ID:\n\n```bash\n$ ls /opt/codedeploy-agent/deployment-root/6859c2df-1fdb-4591-94f9-7acf02eba479/\nd-4AG56713G  d-MS07UR13G  d-MUISVJ03G  d-OTDLJNZ2G  d-QZD1YU03G\n```\n\nThe source code for the deployment has been extracted under:\n\n```bash\n$ ls /opt/codedeploy-agent/deployment-root/${DEPLOYMENT_GROUP_ID}/${DEPLOYMENT_ID}/deployment-archive/\nDockerfile  Makefile  README.md  appspec.yml  configs  dependencies  docker-compose.yml  scripts  src  trigger\n```\n\nThe logs can be found under:\n\n```bash\n$ ls /opt/codedeploy-agent/deployment-root/${DEPLOYMENT_GROUP_ID}/${DEPLOYMENT_ID}/logs/\nscripts.log\n```\n\nThe install step (which happens after before_install), extracts the deployment archive to: `/home/snake/_target` which is set as the destination on the `appspec.yml` and since the source is `/` it means everything will be included from the source repo. The archive is also extracted to the following directory: `/opt/codedeploy-agent/deployment-root/6859c2df-1fdb-4591-94f9-7acf02eba479/d-JYMPSK23G/deployment-archive`\n\n## Example Deploy\n\n### Deployment Target\n\nWhen we commit a test file, foobarfile:\n\n```bash\n$ echo 1 \u003e foobarfile\n$ git add foobarfile\n$ git commit -m \"add foobarfile\" \u0026\u0026 git push origin master\n```\n\nIn our appspec.yml, we defined the target of of our deployment in the /home/snake/_target directory:\n\n```yaml\nversion: 0.0\nos: linux\nfiles:\n  - source: /\n    destination: /home/snake/_target\n```\n\nOn our deployment target (EC2), if we search for the foobarfile, we will find it in:\n\n- `/home/snake/_target/foobarfile`\n- `/opt/codedeploy-agent/deployment-root/${DEPLOYMENT_GROUP_ID}/${DEPLOYMENT_ID}/deployment-archive/foobarfile`\n\n```bash\n$ find / -name foobarfile -type f\n/opt/codedeploy-agent/deployment-root/6859c2df-1fdb-4591-94f9-7acf02eba479/d-GBGEACQ8G/deployment-archive/foobarfile\n/home/snake/_target/foobarfile\n```\n\nIf we list the DEPLOYMENT_GROUP_ID we will find the deployment revisions (not limited to 2 revisions):\n\n```bash\n$ ls -latr /opt/codedeploy-agent/deployment-root/6859c2df-1fdb-4591-94f9-7acf02eba479/\ntotal 25\ndrwxr-xr-x 6 root root 4096 Mar  7 16:16 ..\ndrwxr-xr-x 4 root root 4096 Mar 16 09:05 d-810J8UP8G\ndrwxr-xr-x 4 root root 4096 Mar 16 09:40 .\ndrwxr-xr-x 4 root root 4096 Mar 16 09:40 d-GBGEACQ8G\n```\n\nIn this case when we look at the previous revision, we will note that the foobarfile is not present:\n\n```bash\n$ ls /opt/codedeploy-agent/deployment-root/6859c2df-1fdb-4591-94f9-7acf02eba479/d-810J8UP8G/deployment-archive/ | grep foobarfile | wc -l\n0\n```\n\n### Deployment Logs\n\nThere’s two locations for logs, we have the combined view, and per deployment log:\n\n- Combined: `/opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log`\n- Per-Deployment: `/opt/codedeploy-agent/deployment-root/6859c2df-1fdb-4591-94f9-7acf02eba479/d-GBGEACQ8G/logs/scripts.log`\n\nThe combined view:\n\n```bash\n$ cat /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log | tail -n3\n[2022-03-16 09:40:52.800] [d-GBGEACQ8G][stdout]{\"status\":\"ok\"}[2022-03-16T09:03] application passing health checks\n[2022-03-16 09:40:52.800] [d-GBGEACQ8G][stderr]+ echo d-GBGEACQ8G\n[2022-03-16 09:40:52.800] [d-GBGEACQ8G][stderr]+ echo d-GBGEACQ8G\n```\n\nThe per-deployment view:\n\n```bash\n$ cat /opt/codedeploy-agent/deployment-root/6859c2df-1fdb-4591-94f9-7acf02eba479/d-GBGEACQ8G/logs/scripts.log | tail -n3\n2022-03-16 09:40:52 [stdout]{\"status\":\"ok\"}[2022-03-16T09:03] application passing health checks\n2022-03-16 09:40:52 [stderr]+ echo d-GBGEACQ8G\n2022-03-16 09:40:52 [stderr]+ echo d-GBGEACQ8G\n```\n\n## Resources\n\n- https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html#appspec-hooks-server\n- https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file.html\n- https://docs.aws.amazon.com/codedeploy/latest/userguide/codedeploy-agent-operations-install-ubuntu.html\n- https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html\n- https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-files.html","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruanbekker%2Faws-codepipeline-codedeploy-fastapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruanbekker%2Faws-codepipeline-codedeploy-fastapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruanbekker%2Faws-codepipeline-codedeploy-fastapi/lists"}