Kiarash Zamanifar
Kiarash Zamanifar

Reputation: 677

how to extend windows path variable using ansible

Using win_environment, it is possible to add/remove environment variables to a windows host. But to modify variables that are already there, win_environment does not seem to be useful as u can't read old value to modify and update a variable. right?

Upvotes: 6

Views: 10668

Answers (6)

nitzmahone
nitzmahone

Reputation: 13940

EDIT: Since Ansible 2.3, the win_path module does all the heavy lifting for you. Just give it a list of items that should be present in the path and it'll make sure they're present and in the relative order you specified.

(if you're still using an ancient version of Ansible, the following is still the way to go)

To get this to work sanely, you'll want to combine with a replace and search filter to only make the change if the value you want isn't in there. For instance (this is for Ansible 1.9):

  - raw: echo %PATH%
    register: path_out

  - win_environment: 
      name: path
      value: "{{ path_out.stdout | regex_replace('[\r\n]*', '') + ';C:\\\\newpath' }}"
      state: present
      level: machine
    when: not (path_out.stdout | search("(?i)c:\\\\newpath"))

This is a lot harder than it should be- I've got half a mind to hack up a win_path module for 2.0 to make it easier...

For 2.0, raw runs under Powershell, so you'd want Get-Item env:PATH instead.

Upvotes: 5

Allen Fisher
Allen Fisher

Reputation: 697

Casey's solution is pretty close. The only problem is that [Environment]::SetEnvironmentVariable adds a newline at the end of the PATH. So when you add to it, it puts all your new values on another line making the PATH not work. Here's what I did, and it works pretty well.

Just needed to add a split on newlines... then the system PATH variable gets set correctly.

It's a combination of Casey's solution and Chris Hillery's:

in a file called extend-path.yml:

 ---
 - name: Get current machine PATH.
   raw: "$([Environment]::GetEnvironmentVariables(\"Machine\").Path -split '\r\n')"
   register: path_out

- name: Print Out PATH
  debug:
  msg: "PATH: {{ path_out }}"

- name: "Add {{ item }} to PATH."
  raw: SETX /M PATH "$($([Environment]::GetEnvironmentVariables("Machine").Path -split '\r\n'));{{ item }}"
  when: path_out.stdout.find(item) == -1
  changed_when: true

Then to call it, in your playbook:

- name: Update system PATH
  include: tasks/win_system_path.yml
  with_items:
    - C:\Program Files\Git\bin

Upvotes: 0

Casey
Casey

Reputation: 195

Here is an example that sets msbuild to the machine path. You could add more items if needed. It's important that you only retrieve the Machine path before then modifying the machine path. If you just call $ENV:PATH, you will get the machine path combined with the user path. If you use that to set the machine path, then you are copying all your user path values to the machine path which I'm assuming is not what you want.

- name: Get System PATH
  raw: '[Environment]::GetEnvironmentVariables("Machine").Path'
  register: path_out

- name: Modify System PATH
  raw: SETX /M PATH "$([Environment]::GetEnvironmentVariables("Machine").Path | Out-String);{{ item }}"
  when: path_out.stdout.find(item) == -1
  with_items: 
    - 'C:\Program Files (x86)\MSBuild\14.0\Bin'

Upvotes: 1

mdorenka
mdorenka

Reputation: 1

You can use Powershell for adding a string to the Path. The code below adds a given path to the PATH variable while ensuring path isn't modified if the given path is already existent in PATH.

$env = [Environment]::GetEnvironmentVariable('path','machine') -split ';'
$msdeploypath = 'C:\Program Files\IIS\Microsoft Web Deploy'

if ($env -notcontains $msdeploypath) {
    $env += $msdeploypath
    [Environment]::SetEnvironmentVariable('path', ($env -join ';'), 'machine')
    Write-Host "changed"
}

In Ansible 2 you can also use the raw module for that as it uses Powershell

- name: Set Path
  raw: $env = [Environment]::GetEnvironmentVariable('path','machine') -split ';' ; $msdeploypath = 'C:\Program Files\IIS\Microsoft Web Deploy' ; if ($env -notcontains $msdeploypath) { $env += $msdeploypath ; [Environment]::SetEnvironmentVariable('path', ($env -join ';'), 'machine') ; Write-Host "changed" } 
  register: pathchange
  changed_when: pathchange.stdout.find('changed') != -1

Upvotes: 0

Chris Hillery
Chris Hillery

Reputation: 441

I just spent some hours fighting with Ansible, Jinja2, and JSON backslash hell and finally found a generic solution for this - ie, one that lets you add ANY directory to the system path, and won't add the same path twice. I adapted Devis' solution but made both the SETX command and the when: clause accept (the same) {{item}}, so it could be parameterized. Here's what I came up with.

Save this as extend-path.yml:

---
- name: Get current machine PATH.
  raw: $ENV:PATH
  register: path_out

- name: "Add {{ item }} to PATH."
  raw: SETX /M PATH "$ENV:PATH;{{ item }}"
  when: "not (path_out.stdout | urlencode | search( '{{ item | urlencode }}' ) )"
  changed_when: true

And then, for example, in your playbook.yml:

---
tasks:
  - name: Add tools to PATH.
    include: extend-path.yml
    with_items:
      - C:\bin
      - C:\Program Files (x86)\CMake\bin
      - C:\Program Files\git\cmd

(As you see, I actually lost the backslash war and decided to bypass it entirely by using urlencode.)

Upvotes: 3

Devis Caliò
Devis Caliò

Reputation: 23

Try this with Ansible 2.0

- name: Get actual PATH
  raw: $ENV:PATH
  register: path_out
  tags: path

- name: Add Notepad++ to PATH
  raw: SETX /M PATH "$ENV:PATH;C:\Program Files (x86)\Notepad++"
  when: path_out.stdout.find('Notepad') == -1
  tags: path

Upvotes: 2

Related Questions