Screenshot of Pulumi Code
Screen­shot of Pulumi Code

In this blog post, we’ll explore how to use Pulumi and the Ter­ra­form Nexus Pro­vi­der to con­fi­gure and manage Sona­type Nexus, a powerful tool for mana­ging and gover­ning the flow of soft­ware com­pon­ents throug­hout the deve­lo­p­ment life­cy­cle. We’ll take a step-by-step approach, cove­ring ever­y­thing from instal­ling and con­fi­gu­ring Pulumi to defi­ning and deploy­ing Nexus resour­ces. By the end of this post, you’ll have a solid under­stan­ding of how to leverage Infra­struc­ture as Code (IaC) to auto­mate your Nexus configuration.

Tools

Ter­ra­form Nexus Provider:

The first com­mit of the Synvert Dat­adri­vers Nexus Pro­vi­der for Ter­ra­form was pushed on Janu­ary, 10th 2020. What was meant to be a purely inter­nal pro­ject is now used by many indi­vi­du­als glo­bally, expec­ting to reach two mil­lion down­loads in 2025. The pro­vi­der is a plugin which enables us to describe the desi­red con­fi­gu­ra­tion of our Nexus in Terraform’s native lan­guage cal­led Hash­i­corp Con­fi­gu­ra­tion Lan­guage (HCL). Wit­hout it, Ter­ra­form would not know how to map Create, Read, Update and Delete ope­ra­ti­ons (CRUD) to the Nexus API.

Ter­ra­form / OpenTofu:

For quite some time, Ter­ra­form was seen as the de facto indus­try stan­dard for decla­ra­tive con­fi­gu­ra­tion and infra­struc­ture manage­ment.
Open­Tofu is a com­mu­nity pro­ject based on the ori­gi­nal Ter­ra­form code­base that was forked prior to Hash­i­corps announce­ment to adopt the BSL license.

Pulumi:

Like Ter­ra­form, Pulumi is a Soft­ware to describe and manage IaC. What’s uni­que to Pulumi, is that you can choose the pro­gramming lan­guage of your choice for this task and not have to bother with HCL.

Pulumi’s Any Ter­ra­form Provider:

In August 2024 Pulumi announ­ced a new fea­ture which enables it’s users to use any Ter­ra­form or Open­Tofu Pro­vi­der wit­hout the neces­sity to bridge a Ter­ra­form Pro­vi­der to Pulumi in order to make use of it.

How To

Setup Nexus

If you don’t have a run­ning Nexus instal­la­tion at hand, which you can use to go through the next steps, you can use our deve­lo­p­ment setup from this link.
This will setup Nexus in your Docker envi­ron­ment, thus you need to have Docker and Docker Com­pose run­ning and configured.

git clone https://github.com/datadrivers/terraform-provider-nexus.git
cd terraform-provider-nexus/scripts
source .env
./start-services.sh
# It might take a minute or two to setup Nexus
# Use curl to check if the setup is completed
curl localhost:8081
Execute start-services.sh and wait for Nexus to be up and running
Exe­cute start-services.sh and wait for Nexus to be up and running

Install Pulumi

Pulumi is available for Linux, macOS and Win­dows and can be instal­led by down­loa­ding the respec­tive binary from the Pulumi web­site or by using your favo­rite package mana­ger. If you are on macOS and have brew instal­led, instal­ling Pulumi is as simple as:

brew install pulumi

Create a working direc­tory and setup a Pulumi project

Once you have Pulumi instal­led, open a ter­mi­nal and exe­cute the fol­lo­wing com­mands to setup the basic scaffold:

mkdir nexus-pulumi
cd nexus-pulumi
pulumi new python -g
  • mkdir crea­tes a new direc­tory and cd swit­ches into it,
  • The new sub­com­mand ins­tructs Pulumi to setup a new pro­ject in the cur­rent working direc­tory sup­port­ing the Python pro­gramming lan­guage and the -g flag tells Pulumi to only gene­rate the scaf­fold, wit­hout a Pulumi stack or the instal­la­tion of any depen­den­cies, as this will be done in the fol­lo­wing steps. The com­mand prompts for three inputs:
    - Pro­ject name,
    - Pro­ject descrip­tion and
    - Tool­chain for the depen­dency manage­ment. (Sel­ect pip in case you are unsure)
Example output of the pulumi new command
Exam­ple out­put of the pulumi new command

Con­fi­gure Pulumi to use the Any Ter­ra­form Pro­vi­der with the Nexus Provider

pulumi package add terraform-provider datadrivers/nexus
pulumi install
pulumi login --local
pulumi stack init
pulumi config set nexus:url "http://127.0.0.1:8081"

This is where the magic hap­pens. The first com­mand does a cou­ple things: First it ins­tructs Pulumi to set up a new vir­tua­lenv for Python and then it down­loads the Any Ter­ra­form Pro­vi­der from the Pulumi Regis­try. This Pro­vi­der is then con­fi­gu­red to be used with the Ter­ra­form Nexus Pro­vi­der. This Con­fi­gu­ra­tion results in a Nexus SDK which is loca­ted under­neath the “sdks” fol­der in the cur­rent working direc­tory.
In order to create Resour­ces in your Nexus instal­la­tion you need to point the Nexus Pro­vi­der to cor­rect URL, this can be achie­ved using the con­fig sub­com­mand as shown in the last com­mand above.

Create Nexus Resour­ces using Pulumi

Depen­ding on the lan­guage that has been sel­ec­ted with the pulumi new com­mand, Pulumi also crea­tes a main source file on your behalf. In case of Python it is cal­led __main__.py. This file can be fur­ther modi­fied to create a Nexus Blobs­tore and a Yum Repo­si­tory like so:

"""A Python Pulumi program"""

import pulumi
import pulumi_nexus as nexus

yum_blob_store = nexus.BlobstoreFile(
"yum_blob_store",
path="/yum_blob_store",
)

yum_repo = nexus.RepositoryYumHosted(
"yum_repo",
name="YumRepository",
storage={
"blob_store_name": yum_blob_store.name,
"strict_content_type_validation": True
},
)

We can break this file up into two parts.

  1. Depen­dency Management:
import pulumi
import pulumi_nexus as nexus

The first line imports gene­ral Pulumi depen­den­cies and the second line crea­tes a new Python object named nexus that holds all the methods that we pro­grammed into the Nexus Ter­ra­form Provider.

2. Nexus Configuration:

yum_blob_store = nexus.BlobstoreFile(
"yum_blob_store",
path="/yum_blob_store",
)

yum_repo = nexus.RepositoryYumHosted(
"yum_repo",
name="YumRepository",
storage={
"blob_store_name": yum_blob_store.name,
"strict_content_type_validation": True
},
)

Among others the nexus object con­ta­ins two methods:

  • Blobs­to­re­File and
  • Repo­si­to­ry­Y­um­Hos­ted

Using this methods, we first define a Blobs­tore named “yum_blob_store” and then a Yum Repo­si­tory named “Yum­Re­po­si­tory”.
You can now save this file and exe­cute the command:

pulumi up

You should get an out­put simi­lar to the following:

Output of pulumi up command
Out­put of pulumi up command

If you sel­ect yes, Pulumi checks your code and crea­tes the descri­bed Resour­ces respec­ting the con­fi­gu­red order. What that means, is that the blobs­tore is crea­ted before the Yum Repo­si­tory. You should see some­thing simi­lar to this:

Final Output of pulumi up command
Final Out­put of pulumi up command

Finally, loo­king at the Nexus Web-UI (http://127.0.0.1:8081), we can see that a new Repo­si­tory has been created.

New Nexus Yum Repository has been created by Pulumi
New Nexus Yum Repo­si­tory has been crea­ted by Pulumi

Final words of caution

At the time of wri­ting (Febru­ary, 21st 2025) the Any Ter­ra­form Pro­vi­der is still in public beta.

A com­plete life­cy­cle manage­ment of resour­ces should also include the des­truc­tion of such resour­ces. This is what the pulumi des­troy com­mand is for. But for unknown reasons the des­truc­tion of the resour­ces fails:

Pulumi fails to destroy the Resources.
Pulumi fails to des­troy the Resources.

This excep­tion does not occur when using the Nexus Ter­ra­form Pro­vi­der with Ter­ra­form, thus I suspect it to be somehow rela­ted to the Any Ter­ra­form Pro­vi­der by Pulumi and the way it is bridging the CRUD ope­ra­ti­ons from Python to Golang. This is a known issue, which should have been resol­ved with v0.7.0. But I still run in to this behavior.

Nevert­hel­ess, this looks like a very pro­mi­sing fea­ture as it enables deve­lo­p­ment teams to setup their own infra­struc­ture using the pro­gramming lan­guages they are most fami­liar with.