Configuration as Code for TeamCity Using Terraform
Tags: teamcity, devops, infrastructureascode, terraform
Published: 2024-02-15 11:06:05

<!-- wp:paragraph -->
<p>In DevOps, development and infrastructure tasks are split. Developers and build engineers handle project configuration and scripting, while DevOps engineers on the infrastructure side manage things like TeamCity instances, database maintenance, and permissions.&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Although <a href="https://www.jetbrains.com/teamcity/features/configuration-as-code/">the Kotlin DSL</a> can address CI/CD needs at the project level, it lacks system administration capabilities. It is incapable of setting universal server configurations and managing users, which poses challenges for DevOps engineers, especially those from a systems administration background.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To overcome this, TeamCity has introduced the <a href="https://github.com/JetBrains/terraform-provider-teamcity">Terraform Provider for TeamCity</a> on the basis of this widely adopted infrastructure provisioning tool. The Terraform Provider uses a language familiar to DevOps professionals and streamlines global server setup, user management, permissions, and project initiation.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><img width="624" height="361" src="https://lh7-us.googleusercontent.com/gdQNRKSlYbrFhp2YkUnF7woP1iNAQ0oWcMdFnxySYA8Q3kPdyuU10B8LudKp_-eUkWRTqw3gjrgpvLAOBvmWCVVNf_hhVCzqdHwM5oOZGBQnEM6riZm2Nx7h4veOi7C3Wh0xDO4UmD_KFz3tsQC0gzA"></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em>Source: <a href="https://www.jetbrains.com/lp/devecosystem-2023/devops/#devops_infr_provisioning_devops">Developer Ecosystem Report 2023</a></em></p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Terraform Provider in action: Setting up OAuth for GitHub accounts</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Let’s see how we can use the new Terraform Provider for TeamCity. In the first example, we’ll set up OAuth for GitHub accounts. Here, we need to change the server URL and then add a GitHub App authentication model.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444452,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/TeamCity-and-Terraform-Provider_1-2-1.png" alt="" class="wp-image-444452"/></figure>
<!-- /wp:image -->

<!-- wp:image {"id":444463,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/TeamCity-and-Terraform-Provider_2-1.png" alt="" class="wp-image-444463"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>In Terraform, we can describe our web forms as a set of resources. For the global settings, we can change the server URL. All of the other settings are left as default.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444474,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/Terraform-Provider-for-TeamCity_3-1.png" alt="" class="wp-image-444474"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Then, we add a module for authentication as shown below.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444485,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/Terraform-Provider-for-TeamCity-authentication-1.png" alt="" class="wp-image-444485"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>We also need to add a connection with our custom GitHub App into our root project.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444519,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/Terraform-Provider-for-TeamCity-GH-connection-2-1.png" alt="" class="wp-image-444519"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>We can also configure SMTP settings for notifications, add license keys, and set up cleaning.&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Terraform is a command line tool. Here, we can type in <code>terraform apply</code>, and it will evaluate our configuration files and declare which changes will be made in our infrastructure.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444530,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/terraform-provider-for-teamcity-apply-command.png" alt="" class="wp-image-444530"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Once the changes are applied, we can log in to TeamCity with a new external account.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Creating accounts and granting permissions in advance</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In TeamCity, you can create custom roles and grant only certain permissions to certain roles, following the principle of least privilege. Let’s see how to do so using Terraform Provider.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Let’s create a new custom role, a project, and a new group, and assign project permissions to the group.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444552,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/Terraform-Provider-for-TeamCity-custom-role-2-1.png" alt="" class="wp-image-444552"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>When the user connects to TeamCity for the first time, they’ll immediately have access to their projects and custom permissions.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The project that we created is empty. However, when you start developing a new product or microservice, it’s great to provide the team with a working example. Here, we have a repo in GitHub with a basic Kotlin DSL script.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444563,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/github-repo-terraform-provider.png" alt="" class="wp-image-444563"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Now, let’s register it in TeamCity. We’ll create a project, and within it, register an SSH key and create a VCS root using SSH authentication.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444574,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/terraform-provider-for-teamcity-ssh-key.png" alt="" class="wp-image-444574"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>We can also enable versioned settings there to ensure our Kotlin DSL script is applied.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444586,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/Terraform-Provider-for-TeamCity-versioned-settings-1.png" alt="" class="wp-image-444586"/></figure>
<!-- /wp:image -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Managing GitHub resources via Terraform</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Terraform doesn’t just refer to GitHub resources but actually manages them. To demonstrate how this feature works, let's create a new repo by cloning it from a template.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the code, we declare a new GitHub repository and refer to our template as a base for cloning.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>This integration can also work the other way around. Namely, Terraform can also reflect changes in your actual infrastructure. Now, let’s see how we can create a repo manually.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444597,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/terraform-provider-for-teamcity-clone-repo.png" alt="" class="wp-image-444597"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Here, we’ve listed all of the repos in their organization and ensured that each of them has a corresponding set of TeamCity resources.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444620,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/Terraform-Provider-for-TeamCity-each-key-2-1.png" alt="" class="wp-image-444620"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>As we can see, Terraform was able to find a new repo that we recently created.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444631,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/terraform-provider-for-teamcity-picked-up-repo-2800x1546.png" alt="" class="wp-image-444631"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>We can also add a set of TeamCity objects, and they will appear automatically in the corresponding TeamCity project.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Importing existing resources</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>For those who have been working with TeamCity for a while, there is a way to import existing configurations and settings into Terraform without recreating everything from scratch. Here’s an example of how we can customize existing settings.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Here, we’ve specified the ID of a TeamCity object and the ID of a resultant Terraform resource:</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444642,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/terraform-provider-for-teamcity-ID-specified-2800x1552.png" alt="" class="wp-image-444642"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Now, we can tell Terraform to go into an actual TeamCity instance, read its state, and generate configuration files.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444653,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/terraform-provider-import-existing-role.png" alt="" class="wp-image-444653"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Terraform will then fetch the list of permissions from TeamCity.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":444664,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://blog.jetbrains.com/wp-content/uploads/2024/02/terraform-provider-fetched-roles-from-teamcity.png" alt="" class="wp-image-444664"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>We can then go ahead and add a new permission in Terraform. It will then automatically appear in TeamCity.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Learn more about Terraform Provider for TeamCity</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>You can find the <a href="https://registry.terraform.io/providers/JetBrains/teamcity/latest/docs">Terraform Provider for TeamCity</a> in the HashiCorp Terraform registry. Please refer to <a href="https://registry.terraform.io/providers/JetBrains/teamcity/latest/docs">the documentation </a>for more resources and details.&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Also, make sure to check out the project’s <a href="https://github.com/JetBrains/terraform-provider-teamcity">GitHub repository</a>.&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>If you have any questions, feel free to <a href="https://www.jetbrains.com/teamcity/get-in-touch/">reach out to us</a> – we’ll be happy to help.</p>
<!-- /wp:paragraph -->