Returning Data from a PS script

719 views
Skip to first unread message

Ansible User

unread,
Feb 17, 2017, 1:13:09 PM2/17/17
to Ansible Project
Hello all, 

I am having an issue to where I am executing a powershell script on a group of hosts and cannot get the data to be returned in the way that I want it so that I can iterate over it further down in my playbook. I am pretty sure that I need to build the JSON object in powershell before I return it, but it's just not working. Please feel free to ask me for any clarification and I will be glad to try and provide any info needed.

J Hawkesworth

unread,
Feb 18, 2017, 2:16:05 AM2/18/17
to Ansible Project
Best way to so this is to write a module. This isn't as hard as it might sound. Have a look at win_environment.ps1 it's only about 30 lines of code. First bit checks the module parameters and sets up a $results variable, then the module does its thing and either returns a failure or adds info the the $results variable and completes, passing the results back to the ansible controller where they can be registered and used later in your playbook.

If you still want to write a script, most likely thing that might be messing things up is the fact that you need to be aware that PowerShell has the concept of an output pipe which can contain objects of varying types, and by default many PowerShell built in functions (also called CmdLets) default to sending objects to the output pipe. That's why sometimes you will see

| Out-Null

on the ends of some lines of the module code. Thus throws away unwanted objects and lets you control what is in the output pipe, so that only the things you need are converted into json to be returned to the ansible controller.

Hope this helps,

Jon

Ansible User

unread,
Feb 20, 2017, 9:30:42 AM2/20/17
to Ansible Project
Before you had sent the message back I think we got started along the right path there. I'm sure there are some things we aren't doing correctly, but would appreciate your input to help with this. I think we might have a working prototype soon, and once we get it going, then I think it could potentially be extended to become an extras module. Maybe that's just me being hopeful, but I do think it would be extendable in that manner.

Ansible User

unread,
Feb 20, 2017, 12:57:58 PM2/20/17
to Ansible Project
Maybe I was a tad over zealous in my last post. Basically what I am trying to do is gather a bunch of entries about multiple files such that I get the following information returned so that I can then iterate over it and call a different module using the "path" and gather the information using win_file_version.

 {
                "name": "Create USB Recovery",
                "path": "C:\\Windows\\System32\\RecoveryDrive.exe"
            },
            {
                "name": "Task Manager",
                "path": "C:\\WINDOWS\\system32\\taskmgr.exe"
},

I have looked at the win_environment.ps1 file, but I don't see that it adds multiple entries to the returned data. I know I am doing a pisspoor excuse explaining what I am trying to do, but any help could be appreciated. 

Ansible User

unread,
Feb 20, 2017, 4:26:15 PM2/20/17
to Ansible Project
So here is the script that I am trying to do. rough form of it is

query installed programs
get display name and target_exe
add to variable to be returned by module
iterate over returned paths and do win_file_version on target_exe

Rough code as of now is...

#!powershell
# WANT_JSON
# POWERSHELL_COMMON

$path = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs"
$items = Get-ChildItem -Recurse -Path $path -Include *.lnk
$Shell = New-Object -ComObject WScript.Shell
$result = @{
  changed = $false
  installed_progs = @{}
}
$intalled_programs = New-Object System.Collections.Arraylist

foreach ($item in $items) {

   $object = new-object psobject @{
      name = $item.BaseName.tolower()
      path = $Shell.CreateShortcut($item).targetpath.tolower()
   }

   if ($object.path.endswith('exe')) {
      $result.installed_progs.add($item.BaseName.toLower(), $Shell.CreateShortcut($item).targetpath.tolower())
    }
}

[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
Exit-Json $result



Ansible User

unread,
Feb 20, 2017, 4:28:52 PM2/20/17
to Ansible Project
Didn't state explicitly, but the code above is not working and is the Module that I am trying to get integrated.

Paul Markham

unread,
Feb 20, 2017, 5:12:13 PM2/20/17
to Ansible Project
I don't know anything about Power Shell, but to return variables to Ansible, you need to return them in JSON format under the 'ansible_facts' key. See http://docs.ansible.com/ansible/dev_guide/developing_modules_general.html#module-provided-facts

Ansible User

unread,
Feb 22, 2017, 8:53:32 AM2/22/17
to Ansible Project
So here's the script and data as I have them and while not 100% correct with the output it's close enough until I can figure it out properly.

#!powershell
# WANT_JSON
$path = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs"
$items = Get-ChildItem -Recurse -Path $path -Include *.lnk
$Shell = New-Object -ComObject WScript.Shell
$result = @{
  changed = $false
  installed_progs = @()
}

foreach ($item in $items) {

   $object = new-object psobject @{
      name = $item.BaseName.tolower()
      path = $Shell.CreateShortcut($item).targetpath.tolower()
   }

   if ($object.path.endswith('exe') -and $object.path -like "*Program*") {
      $result.installed_progs+=$object
   }
}

[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null

echo $result | convertto-json -compress -depth 99

and the play that I run to get the data out as I am doing it via module

---
- name: Get Installed Programs with Versions
  hosts: testing
#  no_log: true
  gather_facts: FALSE
  tasks:
    - name: Get Installed Applications
      action: getInstalledPrograms
      register: applications
    - debug: var=applications

    - name: Check Application Version
      win_file_version:
        path: "{{ item.path }}"
      no_log: false
      register: exe_file_version
      with_items:
        - "{{ applications.installed_progs }}"
    - debug: var="{{ item }}"
      no_log: false
      with_items: "{{ exe_file_version.results }}"

and the output that I have at the moment

ok: [REMOVED] => {
    "applications": {
        "changed": false,
        "installed_progs": [
            {
                "name": "7-zip file manager",
                "path": "c:\\program files\\7-zip\\7zfm.exe"
            },
     }
}




Ansible User

unread,
Feb 22, 2017, 8:56:58 AM2/22/17
to Ansible Project
and the final output of the bottom part of the play to output the gathered data

ok: [REMOVED] => (item={u'_ansible_parsed': True, u'changed': False, u'_ansible_no_log': False, u'_ansible_item_result': True, u'win_file_version': {u'file_private_part': u'0', u'file_build_part': u'0', u'file_version': u'2, 3, 0, 0', u'product_version': u'2.3 beta 2', u'path': u'c:\\program files\\teracopy\\teracopy.exe', u'file_major_part': u'2', u'file_minor_part': u'3'}, u'item': {u'path': u'c:\\program files\\teracopy\\teracopy.exe', u'name': u'teracopy'}, u'invocation': {u'module_name': u'win_file_version'}}) => {
    "<type 'dict'>": "VARIABLE IS NOT DEFINED!",
    "item": {
        "changed": false,
        "invocation": {
            "module_name": "win_file_version"
        },
        "item": {
            "name": "teracopy",
            "path": "c:\\program files\\teracopy\\teracopy.exe"
        },
        "win_file_version": {
            "file_build_part": "0",
            "file_major_part": "2",
            "file_minor_part": "3",
            "file_private_part": "0",
            "file_version": "2, 3, 0, 0",
            "path": "c:\\program files\\teracopy\\teracopy.exe",
            "product_version": "2.3 beta 2"
        }
    }
}



Paul Markham

unread,
Feb 22, 2017, 4:06:02 PM2/22/17
to Ansible Project
You need to return your variables under the "ansible_facts" key. Something like:

{
   
"changed": false,
   
"ansible_facts": {
       
"applications": {
           
"installed_progs": [
                   
{
                       
"name": "7-zip file manager",
                       
"path": "c:\\program files\\7-zip\\7zfm.exe"
                   
},
       
}
   
}
}


J Hawkesworth

unread,
Feb 25, 2017, 1:15:40 PM2/25/17
to Ansible Project
I know i suggested writing a module, but one thing you can do which might be close to what you are trying is using a script to add custom facts to your systems. There's a nice write up of doing this here. http://hindenes.com/trondsworking/2016/11/05/using-ansible-as-a-software-inventory-db-for-your-windows-nodes/
This might be easier as you don't have to create an entire module.
As you have seen, you can return data from a script, but by extending facts, it is more straightforward to use the extra info you have gathered later in your playbook.

Hope this helps,

Jon

Ansible User

unread,
Feb 26, 2017, 4:43:05 AM2/26/17
to Ansible Project
That's the road I have been digging in to. Been busy with some life things for the past few days. I am going to be able to double down and be able to properly flesh this out. Realistically it should be easy enough(... I hope). Will post back when I can.
Reply all
Reply to author
Forward
0 new messages