Files
hackanooga.com/content/post/2024-03-11-automating-ci-cd-with-teamcity-ansible.md
2025-02-19 16:36:29 -05:00

197 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
author: mikeconrad
categories:
- Ansible
- Automation
- CI/CD
- TeamCity
dark_fusion_page_sidebar:
- sidebar-1
dark_fusion_site_layout:
- ""
date: "2024-03-11T09:37:47Z"
guid: https://wordpress.hackanooga.com/?p=393
id: 393
tags:
- Blog Post
title: Automating CI/CD with TeamCity and Ansible
url: /automating-ci-cd-with-teamcity-ansible/
---
In part one of this series we are going to explore a CI/CD option you may not be familiar with but should definitely be on your radar. I used Jetbrains TeamCity for several months at my last company and really enjoyed my time with it. A couple of the things I like most about it are:
- Ability to declare global variables and have them be passed down to all projects
- Ability to declare variables that are made up of other variables
I like to use private or self hosted Docker registries for a lot of my projects and one of the pain points I have had with some other solutions (well mostly Bitbucket) is that they dont integrate well with these private registries and when I run into a situation where I am pushing an image to or pulling an image from a private registry it gets a little messy. TeamCity is nice in that I can add a connection to my private registry in my root project and them simply add that as a build feature to any projects that may need it. Essentially, now I only have one place where I have to keep those credentials and manage that connection.
Another reason I love it is the fact that you can create really powerful build templates that you can reuse. This became very powerful when we were trying to standardize our build processes. For example, most of the apps we build are `.NET` backends and `React` frontends. We built docker images for every project and pushed them to our private registry. TeamCity gave us the ability to standardize the naming convention and really streamline the build process. Enough about that though, the rest of this series will assume that you are using TeamCity. This post will focus on getting up and running using Ansible.
---
## Installation and Setup
For this I will assume that you already have Ansible on your machine and that you will be installing TeamCity locally. You can simply follow along with the installation guide [here](https://www.jetbrains.com/help/teamcity/install-teamcity-server-on-linux-or-macos.html#Example%3A+Installation+using+Ubuntu+Linux). We will be creating an Ansible playbook based on the following steps. If you just want the finished code, you can find it on my Gitea instance [here](https://git.hackanooga.com/mikeconrad/teamcity-ansible-scripts.git):
#### Step 1 : Create project and initial playbook
To get started go ahead and create a new directory to hold our configuration:
```
mkdir ~/projects/teamcity-configuration-ansible
touch install-teamcity-server.yml
```
Now open up `install-teamcity-server.yml` and add a task to install Java 17 as it is a prerequisite. You will need sudo for this task. \*\*\*As of this writing TeamCity does not support Java 18 or 19. If you try to install one of these you will get an error when trying to start TeamCity.
```
---
- name: Install Teamcity
hosts: localhost
become: true
become_user: sudo
# Add some variables to make our lives easier
vars:
java_version: "17"
teamcity:
installation_path: /opt/TeamCity
version: "2023.11.4"
tasks:
- name: Install Java
ansible.builtin.apt:
name: openjdk-{{ java_version }}-jre-headless
update_cache: yes
state: latest
install_recommends: no
```
The next step is to create a dedicated user account. Add the following task to `install-teamcity-server.yml`
```
- name: Add Teamcity User
ansible.builtin.user:
name: teamcity
```
Next we will need to download the latest version of TeamCity. 2023.11.4 is the latest as of this writing. Add the following task to your `install-teamcity-server.yml`
```
- name: Download TeamCity Server
ansible.builtin.get_url:
url: https://download.jetbrains.com/teamcity/TeamCity-{{teamcity.version}}.tar.gz
dest: /opt/TeamCity-{{teamcity.version}}.tar.gz
mode: '0770'
```
Now to install TeamCity Server add the following:
```
- name: Install TeamCity Server
ansible.builtin.shell: |
tar xfz /opt/TeamCity-{{teamcity.version}}.tar.gz
rm -rf /opt/TeamCity-{{teamcity.version}}.tar.gz
args:
chdir: /opt
```
Now that we have everything set up and installed we want to make sure that our new `teamcity` user has access to everything they need to get up and running. We will add the following lines:
```
- name: Update permissions
ansible.builtin.shell: chown -R teamcity:teamcity /opt/TeamCity
```
This gives us a pretty nice setup. We have TeamCity server installed with a dedicated user account. The last thing we will do is create a `systemd` service so that we can easily start/stop the server. For this we will need to add a few things.
1. A service file that tells our system how to manage TeamCity
2. A j2 template file that is used to create this service file
3. A handler that tells the system to run `systemctl daemon-reload` once the service has been installed.
Go ahead and create a new templates folder with the following `teamcity.service.j2` file
```
[Unit]
Description=JetBrains TeamCity
Requires=network.target
After=syslog.target network.target
[Service]
Type=forking
ExecStart={{teamcity.installation_path}}/bin/runAll.sh start
ExecStop={{teamcity.installation_path}}/bin/runAll.sh stop
User=teamcity
PIDFile={{teamcity.installation_path}}/teamcity.pid
Environment="TEAMCITY_PID_FILE_PATH={{teamcity.installation_path}}/teamcity.pid"
[Install]
WantedBy=multi-user.target
```
Your project should now look like the following:
```
$: ~/projects/teamcity-ansible-terraform
.
├── install-teamcity-server.yml
└── templates
└── teamcity.service.j2
1 directory, 2 files
```
Thats it! Now you should have a fully automated installed of TeamCity Server ready to be deployed wherever you need it. Here is the final playbook file, also you can find the most up to date version in my [repo](https://git.hackanooga.com/mikeconrad/teamcity-ansible-scripts.git):
```
---
- name: Install Teamcity
hosts: localhost
become: true
become_method: sudo
vars:
java_version: "17"
teamcity:
installation_path: /opt/TeamCity
version: "2023.11.4"
tasks:
- name: Install Java
ansible.builtin.apt:
name: openjdk-{{ java_version }}-jdk # This is important because TeamCity will fail to start if we try to use 18 or 19
update_cache: yes
state: latest
install_recommends: no
- name: Add TeamCity User
ansible.builtin.user:
name: teamcity
- name: Download TeamCity Server
ansible.builtin.get_url:
url: https://download.jetbrains.com/teamcity/TeamCity-{{teamcity.version}}.tar.gz
dest: /opt/TeamCity-{{teamcity.version}}.tar.gz
mode: '0770'
- name: Install TeamCity Server
ansible.builtin.shell: |
tar xfz /opt/TeamCity-{{teamcity.version}}.tar.gz
rm -rf /opt/TeamCity-{{teamcity.version}}.tar.gz
args:
chdir: /opt
- name: Update permissions
ansible.builtin.shell: chown -R teamcity:teamcity /opt/TeamCity
- name: TeamCity | Create environment file
template: src=teamcity.service.j2 dest=/etc/systemd/system/teamcityserver.service
notify:
- reload systemctl
- name: TeamCity | Start teamcity
service: name=teamcityserver.service state=started enabled=yes
# Trigger a reload of systemctl after the service file has been created.
handlers:
- name: reload systemctl
command: systemctl daemon-reload
```