Using Terraspace with Azure DevOps Pipeline

Hello,

Thank you for the great tool, and sorry if this question has been asked before.

Have you had any experience/instructions in installing/running Terraspace with Azure DevOps pipeline?

I am trying to run Terraspace from an Azure DevOps pipeline (using Microsoft Build Agents - with vmImage: ‘ubuntu-latest’), which has ruby installed by default by the looks of it. I.e. I just have a basic bash task that does

# Install Terraspace
sudo gem install terraspace
sudo gem install bundler

# Create a new project
terraspace new project infra --plugin aws --examples
cd infra
terraspace up demo

Now, when the pipeline runs, it installs Terraspace and creates the project OK. But it errors when doing the terraspace up demo (the below is an output from the pipeline).

=> Creating new project called infra.
      create  infra
      create  infra/.gitignore
      create  infra/Gemfile
      create  infra/README.md
      create  infra/Terrafile
      create  infra/config/app.rb
       exist  infra
      create  infra/config/terraform/backend.tf
      create  infra/config/terraform/provider.tf
=> Creating test for new module: example
      create  infra/app/modules/example
      create  infra/app/modules/example/main.tf
      create  infra/app/modules/example/outputs.tf
      create  infra/app/modules/example/variables.tf
=> Creating new stack called demo.
      create  infra/app/stacks/demo
      create  infra/app/stacks/demo/main.tf
      create  infra/app/stacks/demo/outputs.tf
      create  infra/app/stacks/demo/variables.tf
=> Installing dependencies with: bundle install
Fetching gem metadata from https://rubygems.org/........
Resolving dependencies......
Following files may not be writable, so sudo is needed:
  /var/lib/gems/2.7.0
  /var/lib/gems/2.7.0/build_info
  /var/lib/gems/2.7.0/cache
  /var/lib/gems/2.7.0/doc
  /var/lib/gems/2.7.0/extensions
  /var/lib/gems/2.7.0/gems
  /var/lib/gems/2.7.0/specifications
Fetching rake 13.0.6
Installing rake 13.0.6
Using concurrent-ruby 1.1.9
Using zeitwerk 2.4.2
Fetching minitest 5.14.4
Using public_suffix 4.0.6
Using aws-eventstream 1.2.0
Fetching aws-partitions 1.516.0
Installing minitest 5.14.4
Installing aws-partitions 1.516.0
Using jmespath 1.4.0
Using memoist 0.16.2
Fetching multipart-post 2.1.1
Fetching racc 1.5.2
Installing multipart-post 2.1.1
Installing racc 1.5.2 with native extensions
Using azure_info 0.1.2
Using unf_ext 0.0.8
Using timeliness 0.3.10
Using bundler 2.2.29
Using text-table 1.2.4
Using declarative 0.0.20
Using deep_merge 1.2.1
Using diff-lcs 1.4.4
Using digest-crc 0.6.4
Using rainbow 3.0.0
Using eventmachine 1.2.7
Using google-protobuf 3.18.1 (x86_64-linux)
Using jwt 2.3.0
Using multi_json 1.15.0
Using os 1.1.1
Using httpclient 2.8.3
Fetching mini_mime 1.1.2
Installing mini_mime 1.1.2
Using trailblazer-option 0.1.1
Using uber 0.1.0
Using retriable 3.1.2
Using rexml 3.2.5
Fetching webrick 1.7.0
Installing webrick 1.7.0
Using google-cloud-errors 1.2.0
Using graph 2.10.0
Using tilt 2.0.10
Using rspec-support 3.10.2
Using rubyzip 2.3.2
Using thor 1.1.0
Using tty-tree 0.4.0
Using i18n 1.8.10
Using tzinfo 2.0.4
Using addressable 2.8.0
Using aws-sigv4 1.4.0
Using gcp_data 0.2.0
**Fetching faraday 0.17.4**
**Installing faraday 0.17.4**
Using unf 0.1.4
Using dsl_evaluator 0.1.3
Using eventmachine-tail 0.6.5
Using googleapis-common-protos-types 1.2.0
Using rhcl 0.1.0
Using representable 3.1.1
Using activesupport 6.1.4.1
Using aws-sdk-core 3.121.2
Using rspec-core 3.10.1
Using rspec-expectations 3.10.1
Using rspec-mocks 3.10.2
Fetching faraday_middleware 0.14.0
Installing faraday_middleware 0.14.0
Using ms_rest 0.7.6
Using signet 0.16.0
Using google-cloud-env 1.5.0
Using domain_name 0.5.20190701
Using grpc 1.41.0 (x86_64-linux)
Using hcl_parser 0.1.0
Using aws-sdk-dynamodb 1.64.0
Fetching aws-sdk-kms 1.50.0
Installing aws-sdk-kms 1.50.0
Using aws-sdk-secretsmanager 1.51.0
Using aws-sdk-ssm 1.120.0
Using aws_data 0.1.1
Using cli-format 0.2.0
Using render_me_pretty 0.8.3
Using rspec 3.10.0
Using http-cookie 1.0.4
Using googleapis-common-protos 1.3.12
Fetching googleauth 0.17.1
Installing googleauth 0.17.1
Using google-cloud-core 1.6.0
Fetching aws-sdk-s3 1.104.0
Installing aws-sdk-s3 1.104.0
Using faraday-cookie_jar 0.0.7
Using grpc-google-iam-v1 1.0.0
Using rspec-terraspace 0.3.0
Fetching gapic-common 0.3.4
Installing gapic-common 0.3.4
Using google-apis-core 0.4.1
Using s3-secure 0.5.1
Using ms_rest_azure 0.12.0
Fetching google-cloud-secret_manager-v1 0.8.0
Installing google-cloud-secret_manager-v1 0.8.0
Fetching google-cloud-secret_manager-v1beta1 0.8.0
Using google-apis-iamcredentials_v1 0.7.0
Using google-apis-storage_v1 0.8.0
Using terraspace_plugin_aws 0.3.0
Using azure_mgmt_resources 0.18.2
Using azure_mgmt_storage 0.23.0
Fetching nokogiri 1.12.5 (x86_64-linux)
Installing google-cloud-secret_manager-v1beta1 0.8.0
Using google-cloud-storage 1.34.1
Using google-cloud-secret_manager 1.1.2
Using terraspace_plugin_google 0.3.0
Installing nokogiri 1.12.5 (x86_64-linux)
Using terraspace-bundler 0.4.2
Fetching azure-core 0.1.15
Installing azure-core 0.1.15
Fetching azure-storage-common 1.1.0
Installing azure-storage-common 1.1.0
Fetching azure-storage-blob 1.1.0
Installing azure-storage-blob 1.1.0
Using terraspace_plugin_azurerm 0.3.1
Using terraspace 0.6.17
Bundle complete! 3 Gemfile dependencies, 101 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
================================================================
Congrats! You have successfully created a terraspace project.
Check out the created files. Adjust to the examples and then deploy with:

    cd infra
    terraspace up demo -y   # to deploy
    terraspace down demo -y # to destroy

More info: https://terraspace.cloud/
**You have already activated faraday 1.8.0, but your Gemfile requires faraday 0.17.4. Prepending `bundle exec` to your command may solve this.**
**You have already activated faraday 1.8.0, but your Gemfile requires faraday 0.17.4. Prepending `bundle exec` to your command may solve this.**

I.e. It errors because of faraday. But if you were to look at the logs, faraday was installed as part of the bundler, and it is erroring not because it isn’t installed - rather, because another version has been activated.

I have tried removing faraday by doing “sudo gem uninstall faraday -v 1.8.0” (before the installation of Terraspace), but it failed saying

**ERROR:  While executing gem ... (Gem::DependencyRemovalException)**
**    Uninstallation aborted due to dependent gem(s)**

Do you have any suggestions on how I would work around this issue? The use of “bundle exec” was suggested as a workaround, but I suppose that would be built into Terraspace?

Looking forward to hearing back some suggestions

Many thanks,
James

Haven’t messed around with Azure pipelines. Maybe someone else here has though.

This seems like a general error though that, like you mentioned, “bundle exec” will probably resolve.

I don’t like having to remember to pretend “bundle exec” either. You can write a shim wrapper.

It’s a workaround but sometimes must be done.

Though, don’t fully understand why it needs to be done since Terraspace does a “bundle exec” internally rather early on:

It might be system specific or the use of sudo. Unsure.

Sudo runs under a bare minimal shell. The normal shell might have some different settings from the sudo shell that affect how bundler resolves dependencies. Have had better luck using rbenv or rvm to install Ruby so the current user has writable accessible to the install and the environment is the same, not a sudo environment. It just what have found to work better for me.

Hi Tung,

Thank you for getting back to me with the suggestion to use the shim wrapper.

I think it might be some quirks with the way the Microsoft hosted agent works… But I’ve followed your suggestion and have been trying to make it work with the shim wrapper, but it looks like I might be doing something wrong (because it’s still not working)… What I’ve done:

# Install Terraspace
sudo gem install terraspace
sudo gem install bundler

# Create shim wrapper
mkdir -p ~/bin
terraspace new shim --path ~/bin/terraspace
export PATH="~/bin:$PATH"

# Create a new project
terraspace new project infra --plugin aws --examples
cd infra
terraspace up demo

So,

  • ~/bin/terraspace was created successfully
  • ~/bin was added to PATH
  • which terraspace returns: /usr/local/bin/terraspace

But, when terraspace up demo was run, it still errored with:
You have already activated faraday 1.8.0, but your Gemfile requires faraday 0.17.4. Prepending bundle exec to your command may solve this.

Have I used the shim wrapper correctly?

Many thanks,
James

Note: Found a subtle bug with terraspace new shim. The bug is that the shim would not be generated if you were not in a git repo with a .git folder. Fixed here:

Though it doesn’t seem like it should have affected what you’re doing within the Azure DevOps pipeline. Unsure if the pipeline cwd includes a .git folder.

RE:

  • ~/bin was added to PATH
  • which terraspace returns: /usr/local/bin/terraspace

Guessing ~ is /usr/local for your particular setup.

It seems like you have done everything right.

I also added a note to the docs as well as the output of the generator, something like this:

Note, the shim wrapper contains starter code. Though it should generally work for most systems, it might require adjustments depending on your system.

It should really work though because the shim wrapper is pretty simple and generalized. Generated one and checked it out. Here’s what it is:

/path/to/shim/terraspace

#!/bin/bash

if [ -f config/app.rb ]; then
  exec bundle exec terraspace "$@"
else
  exec terraspace "$@"
fi

Maybe manually generate your own shim wrapper and add some debugging echo statements to see if that can help figure out what’s going on. Commands like this should produce the script:

cat << 'EOF' > /usr/local/bin/terraspace
#!/bin/bash
if [ -f config/app.rb ]; then
  echo "config/app.rb found running terraspace with bundle exec"
  exec bundle exec terraspace "$@"
else
  echo "config/app.rb not found running terraspace without bundle exec"
  exec terraspace "$@"
fi
EOF

chmod +x /usr/local/bin/terraspace
type terraspace # to double check the wrapper is in the PATH

Hi Tung,

Thanks again for persevering with me on this :smiley:

~ in the Microsoft build agent works out to be /home/vsts/.
i.e. Running terraspace new shim --path ~/bin/terraspace gives
create /home/vsts/bin/terraspace
chmod /home/vsts/bin/terraspace

and with content
#!/bin/bash

if [ -f config/app.rb ]; then
  exec bundle exec terraspace "$@"
else
  exec terraspace "$@"
fi

(the above is before your new changes to the terraspace new shim)

I tried to generate the shim wrapper to /usr/local/bin/terraspace, but the Microsoft build agent is not allowing me to overwrite it (with and without doing sudo). i.e.
/home/vsts/work/_temp/f30bfc69-a0de-485e-8d24-4ea2a61cdb51.sh: line 5: /usr/local/bin/terraspace: Permission denied

I can however, like above, generate it to ~/bin/terraspace, and although type terraspace gives
terraspace is /home/vsts/bin/terraspace

However, when the following is run

# Create a new project
terraspace new project infra --plugin aws --examples
cd infra
terraspace up demo

It isn’t outputting any of the echo commands in the if/else condition in the shim wrapper. i.e. I’m basically getting the same error as the one in my original post.

Cheers,
James

Update:

Because I wasn’t seeing the echo output in the commands, it made me double check if the correct terraspace command was being called. And it turns out that because I was creating the shim wrapper as another bash task (and the create new project as another bash task), the export PATH="~/bin:$PATH" wasn’t being carried across the different tasks. I.e. On the task where the shim wrapper was created, type terraspace returns
terraspace is /home/vsts/bin/terraspace (after the export PATH command).

But on the (new) task where it creates the project type terraspace returns (before the export PATH command)
terraspace is /usr/local/bin/terraspace

and only after doing another export PATH command, type terraspace returns
terraspace is /home/vsts/bin/terraspace

However, the strange thing is… When it is running terraspace new project infra --plugin aws --examples, it seems to go into an infinite loop of (I added the extra messaging in the if/else condition)

config/app.rb not found running terraspace without bundle exec
exec terraspace new project infra --plugin aws --examples
config/app.rb not found running terraspace without bundle exec
exec terraspace new project infra --plugin aws --examples

So, my thought is that it is always calling itself within the shim wrapper (because terraspace is referencing itself)? Maybe I shouldn’t be exporting the shim wrapper to the path, but rather, calling using the full path to the shim wrapper when calling terraspace? E.g.
instead of doing

export PATH="~/bin:$PATH"
terraspace new project infra --plugin aws --examples
cd infra
terraspace up demo

maybe I should be doing

~/bin/terraspace new project infra --plugin aws --examples   #Don't really need to call the shim wrapper here
cd infra
~/bin/terraspace up demo

Which seems to work :smiley:

TL ;DR:
This should resolve the issue

# Install Terraspace
sudo gem install terraspace
sudo gem install bundler

# Create shim wrapper
mkdir -p ~/bin
terraspace new shim --path ~/bin/terraspace
# There is no need to add the shim wrapper to the path here

# Create a new project
# There is no need to add the shim wrapper to the path
terraspace new project infra --plugin aws --examples
cd infra
~/bin/terraspace up demo #Call terraspace with the shim wrapper

Thanks again for your help and guidance Tung :fist_right:

Many thanks,
James

1 Like