- name: execute a powershell cmdlet
  win_shell: Write-Output "hello from Ansible"
  register: shellout

- name: validate result
  assert:
    that:
    - shellout is successful
    - shellout is changed
    - shellout.cmd == 'Write-Output "hello from Ansible"'
    - shellout.delta is match('^\d:(\d){2}:(\d){2}.(\d){6}$')
    - shellout.end is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
    - shellout.rc == 0
    - shellout.start is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
    # assertion disabled since it does not pass on Windows Server 2016
    # - shellout.stderr == ""
    - shellout.stdout == "hello from Ansible\r\n"
    - shellout.stdout_lines == ["hello from Ansible"]

- name: execute a powershell cmdlet with multi-line output that uses environment with embedded quotes
  win_shell: Write-Output "hello from Ansible"; Write-Output "another line"; Write-Output "yet another line"; Write-Output "envvar was $env:taskvar"
  environment:
    taskvar: "o'doyle rules"
  register: shellout

- name: validate result
  assert:
    that:
    - shellout is successful
    - shellout is changed
    - shellout.cmd == 'Write-Output "hello from Ansible"; Write-Output "another line"; Write-Output "yet another line"; Write-Output "envvar was $env:taskvar"'
    - shellout.delta is match('^\d:(\d){2}:(\d){2}.(\d){6}$')
    - shellout.end is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
    - shellout.rc == 0
    - shellout.start is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
    # assertion disabled since it does not pass on Windows Server 2016
    # - shellout.stderr == ""
    - shellout.stdout == "hello from Ansible\r\nanother line\r\nyet another line\r\nenvvar was o'doyle rules\r\n"
    - shellout.stdout_lines == ["hello from Ansible","another line", "yet another line", "envvar was o'doyle rules"]

- name: execute something nonexistent
  win_shell: bogus_command1234
  register: shellout
  ignore_errors: true

- name: validate result
  assert:
    that:
    - shellout is failed
    - shellout.failed == true # check the failure key explicitly, since failed does magic with RC
    - shellout is changed
    - shellout.cmd == 'bogus_command1234'
    - shellout.delta is match('^\d:(\d){2}:(\d){2}.(\d){6}$')
    - shellout.end is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
    - shellout.rc == 1
    - shellout.start is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
    - shellout.stderr is search('not recognized')
    - shellout.stdout == ""
    - shellout.stdout_lines == []

- name: execute something with error output
  win_shell: Write-Error "it broke"; Write-Output "some output"
  register: shellout

- name: validate result
  assert:
    that:
    - shellout is successful
    - shellout is changed
    - shellout.cmd == 'Write-Error "it broke"; Write-Output "some output"'
    - shellout.delta is match('^\d:(\d){2}:(\d){2}.(\d){6}$')
    - shellout.end is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
    - shellout.rc == 0
    - shellout.start is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
    - shellout.stderr is search('it broke')
    - shellout.stdout == "some output\r\n"
    - shellout.stdout_lines == ["some output"]

- name: ensure test file is absent
  win_file:
    path: c:\testfile.txt
    state: absent

- name: run with creates, should create
  win_shell: echo $null >> c:\testfile.txt
  args:
    creates: c:\testfile.txt
  register: shellout

- name: validate result
  assert:
    that:
    - shellout is successful
    - shellout is changed

- name: run again with creates, should skip
  win_shell: echo $null >> c:\testfile.txt
  args:
    creates: c:\testfile.txt
  register: shellout

- name: validate result
  assert:
    that:
    - shellout is skipped
    - shellout.msg is search('exists')

- name: get path of pagefile
  win_shell: |
    $pagefile = $null
    $cs = Get-CimInstance -ClassName Win32_ComputerSystem
    if ($cs.AutomaticManagedPagefile) {
        $pagefile = "$($env:SystemRoot.Substring(0, 1)):\pagefile.sys"
    } else {
        $pf = Get-CimInstance -ClassName Win32_PageFileSetting
        if ($pf -ne $null) {
            $pagefile = $pf[0].Name
        }
    }
    $pagefile
  register: pagefile_path

- name: test creates with hidden system file, should skip
  win_shell: echo test
  args:
    creates: '{{pagefile_path.stdout_lines[0]}}'
  register: shellout
  when: pagefile_path.stdout_lines|count != 0

- name: validate result
  assert:
    that:
    - shellout is skipped
    - shellout.msg is search('exists')
  when: pagefile_path.stdout_lines|count != 0

- name: ensure testfile is still present
  win_stat:
    path: c:\testfile.txt
  register: statout

- name: validate result
  assert:
    that:
    - statout.stat.exists == true

# https://github.com/ansible/ansible/issues/37967
- name: test creates with file in missing directory
  win_shell: echo hi
  args:
    creates: c:\fakefolder\fakefolder2\fakefile.txt
  register: shellout

- name: validate result
  assert:
    that:
    - shellout.skipped is not defined
    - shellout.changed

- name: run with removes, should remove
  win_shell: Remove-Item c:\testfile.txt
  args:
    removes: c:\testfile.txt
  register: shellout

- name: validate result
  assert:
    that:
    - shellout is successful
    - shellout is changed

- name: run again with removes, should skip
  win_shell: echo $null >> c:\testfile.txt
  args:
    removes: c:\testfile.txt
  register: shellout

- name: validate result
  assert:
    that:
    - shellout is skipped
    - shellout.msg is search('does not exist')

- name: run something with known nonzero exit code
  win_shell: exit 254
  register: shellout
  ignore_errors: true

- name: validate result
  assert:
    that:
    - shellout is failed
    - shellout.failed == True # check the failure key explicitly, since failed does magic with RC
    - shellout.rc == 254

- name: run something via cmd that will fail in powershell
  win_shell: echo line1 & echo.line2
  args:
    executable: cmd
  register: shellout

- name: validate result
  assert:
    that:
    - shellout is successful
    - shellout is changed
    - shellout.rc == 0
    - shellout.stdout == "line1 \r\nline2\r\n"
    - shellout.stdout_lines == ["line1 ", "line2"]
    - shellout.stderr == ""

- name: test with job to ensure that preamble-free InputEncoding is working
  win_shell: Start-Job { echo yo } | Receive-Job -Wait
  register: shellout

- name: check job result
  assert:
    that:
    - shellout is successful
    - shellout.stdout_lines[0] == 'yo'

- name: interleave large writes between stdout/stderr (check for buffer consumption deadlock)
  win_shell: $ba = New-Object byte[] 4096; (New-Object System.Random 32).NextBytes($ba); $text = [Convert]::ToBase64String($ba); Write-Output startout; Write-Error starterror; Write-Error $text; Write-Output $text; Write-Error $text; Write-Output $text; Write-Error $text; Write-Output $text; Write-Output doneout Write-Error doneerror
  register: shellout

- name: ensure that the entirety of both streams were read
  assert:
    that:
    - shellout.stdout is search("startout")
    - shellout.stdout is search("doneout")
    - shellout.stderr is search("starterror")
    - shellout.stderr is search("doneerror")

- name: run stdin test
  win_shell: '$string = [Console]::In.ReadToEnd(); Write-Output $string.Trim()'
  args:
    stdin: some input
  register: shellout

- name: assert run stdin test
  assert:
    that:
    - shellout is changed
    - shellout.rc == 0
    - shellout.stderr == ""
    - shellout.stdout == "some input\r\n"

- name: echo some non ascii characters
  win_shell: Write-Host über den Fußgängerübergang gehen
  register: nonascii_output

- name: assert echo some non ascii characters
  assert:
    that:
    - nonascii_output is changed
    - nonascii_output.rc == 0
    - nonascii_output.stdout_lines|count == 1
    - nonascii_output.stdout_lines[0] == 'über den Fußgängerübergang gehen'
    - nonascii_output.stderr == ''

- name: execute powershell without no_profile
  win_shell: '[System.Environment]::CommandLine'
  register: no_profile

- name: assert execute powershell with no_profile
  assert:
    that:
    - no_profile is successful
    - no_profile is changed
    - no_profile.cmd == "[System.Environment]::CommandLine"
    - no_profile.rc == 0
    - no_profile.stdout is match ('^"powershell.exe" -noninteractive -encodedcommand')

- name: execute powershell with no_profile
  win_shell: '[System.Environment]::CommandLine'
  args:
    no_profile: yes
  register: no_profile

- name: assert execute powershell with no_profile
  assert:
    that:
    - no_profile is successful
    - no_profile is changed
    - no_profile.cmd == "[System.Environment]::CommandLine"
    - no_profile.rc == 0
    - no_profile.stdout is match ('^"powershell.exe" -noprofile -noninteractive -encodedcommand')

- name: create symbolic link with space in the path
  win_command: cmd.exe /c mklink /d "C:\ansible test link" C:\Windows\System32\WindowsPowerShell\v1.0
  args:
    creates: C:\ansible test link

- block:
  - name: run with space in the executable path
    win_shell: '[System.Environment]::CommandLine'
    args:
      executable: C:\ansible test link\powershell
    register: space_exe

  - debug:
      var: space_exe.stdout|trim

  - name: assert run with space in the executable path
    assert:
      that:
      - space_exe is successful
      - space_exe is changed
      - space_exe.cmd == '[System.Environment]::CommandLine'
      - space_exe.rc == 0
      - space_exe.stdout|trim == '"C:\\ansible test link\\powershell.exe" /c [System.Environment]::CommandLine'
  always:
  - name: remove test symbolic link
    win_file:
      path: C:\ansible test link
      state: absent
