Repository: ansible/ansible

Issue:
win_lineinfile: windows paths have \r and \n stripped
##### ISSUE TYPE
 Bug Report

##### COMPONENT NAME
win_lineinfile

##### ANSIBLE VERSION
```
ansible 2.4.1.0
  config file = None
  configured module search path = [u'/none/of/your/business', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.14 (default, Sep 23 2017, 22:06:14) [GCC 7.2.0]
```

##### CONFIGURATION
<!---
ANSIBLE_NOCOWS(env: ANSIBLE_NOCOWS) = True
-->

##### OS / ENVIRONMENT
<!---
control node: ubuntu 17.10 and centos 7.3, client: windows 2016
-->

##### SUMMARY
this:
https://github.com/ansible/ansible/blob/e76e8aed902f44b71ec52dacc0d2a4e2ffe5bbd3/lib/ansible/modules/windows/win_lineinfile.ps1#L348

butchers any windows path and also makes regexes fail and destroys idempotency

##### STEPS TO REPRODUCE
use win_lineinefile to add a windows path containing \r or \n

```yaml
- win_lineinfile:
    path: C:\temp\testfile.txt
    line: 'C:\path\returning\from\nowhere'
```
OR
```yaml
- win_lineinfile:
    path: C:\temp\testfile.txt
    line: "C:\\path\\returning\\from\\nowhere"
```

##### EXPECTED RESULTS
file C:\temp\testfile.tx contains C:\path\returning\from\nowhere

##### ACTUAL RESULTS
file C:\temp\testfile.tx contains C:\patheturning\fromowhere

Current workaround is to use forward slashes, but that requires forward slash compatibility in any program that uses this file
win_lineinfile: windows paths have \r and \n stripped
##### ISSUE TYPE
 Bug Report

##### COMPONENT NAME
win_lineinfile

##### ANSIBLE VERSION
```
ansible 2.4.1.0
  config file = None
  configured module search path = [u'/none/of/your/business', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.14 (default, Sep 23 2017, 22:06:14) [GCC 7.2.0]
```

##### CONFIGURATION
<!---
ANSIBLE_NOCOWS(env: ANSIBLE_NOCOWS) = True
-->

##### OS / ENVIRONMENT
<!---
control node: ubuntu 17.10 and centos 7.3, client: windows 2016
-->

##### SUMMARY
this:
https://github.com/ansible/ansible/blob/e76e8aed902f44b71ec52dacc0d2a4e2ffe5bbd3/lib/ansible/modules/windows/win_lineinfile.ps1#L348

butchers any windows path and also makes regexes fail and destroys idempotency

##### STEPS TO REPRODUCE
use win_lineinefile to add a windows path containing \r or \n

```yaml
- win_lineinfile:
    path: C:\temp\testfile.txt
    line: 'C:\path\returning\from\nowhere'
```
OR
```yaml
- win_lineinfile:
    path: C:\temp\testfile.txt
    line: "C:\\path\\returning\\from\\nowhere"
```

##### EXPECTED RESULTS
file C:\temp\testfile.tx contains C:\path\returning\from\nowhere

##### ACTUAL RESULTS
file C:\temp\testfile.tx contains C:\patheturning\fromowhere

Current workaround is to use forward slashes, but that requires forward slash compatibility in any program that uses this file


Discussion:
Files identified in the description:
* [lib/ansible/modules/windows/win_lineinfile.ps1](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/windows/win_lineinfile.ps1)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @brianlloyd @dagwieers @jborean93 @jhawkesworth
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
* [lib/ansible/modules/windows/win_lineinfile.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/windows/win_lineinfile.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @nitzmahone
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
* [lib/ansible/modules/windows/win_lineinfile.ps1](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/windows/win_lineinfile.ps1)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
I think there are two ways of approaching this.

1.  Always treat line as literal. Multiple lines don't fit into the purpose of the module. I did test the Linux version of this module however, and it does allow line breaks....so this may not be an acceptable answer.
2. Remove this validation block. If people want to insert line breaks they should do it as a PowerShell native/acceptable format.

https://github.com/ansible/ansible/blob/e76e8aed902f44b71ec52dacc0d2a4e2ffe5bbd3/lib/ansible/modules/windows/win_lineinfile.ps1#L347-L352

In addition, lines 350-351 don't make sense as they are converting what is an attempt to escape the line break and print it literally, into a line break. I can't see a good reason for doing this conversion.

I'll bring it up in the WWG today.
Files identified in the description:
* [lib/ansible/modules/windows/win_lineinfile.ps1](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/windows/win_lineinfile.ps1)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @brianlloyd @dagwieers @jborean93 @jhawkesworth
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
* [lib/ansible/modules/windows/win_lineinfile.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/windows/win_lineinfile.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @nitzmahone
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
* [lib/ansible/modules/windows/win_lineinfile.ps1](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/windows/win_lineinfile.ps1)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
I think there are two ways of approaching this.

1.  Always treat line as literal. Multiple lines don't fit into the purpose of the module. I did test the Linux version of this module however, and it does allow line breaks....so this may not be an acceptable answer.
2. Remove this validation block. If people want to insert line breaks they should do it as a PowerShell native/acceptable format.

https://github.com/ansible/ansible/blob/e76e8aed902f44b71ec52dacc0d2a4e2ffe5bbd3/lib/ansible/modules/windows/win_lineinfile.ps1#L347-L352

In addition, lines 350-351 don't make sense as they are converting what is an attempt to escape the line break and print it literally, into a line break. I can't see a good reason for doing this conversion.

I'll bring it up in the WWG today.

Resolving patch:
diff --git a/lib/ansible/modules/windows/win_lineinfile.py b/lib/ansible/modules/windows/win_lineinfile.py
--- a/lib/ansible/modules/windows/win_lineinfile.py
+++ b/lib/ansible/modules/windows/win_lineinfile.py
@@ -1,20 +1,7 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
-#
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
 
 ANSIBLE_METADATA = {'metadata_version': '1.1',
                     'status': ['preview'],
@@ -56,6 +43,8 @@
     description:
       - Required for C(state=present). The line to insert/replace into the file. If C(backrefs) is set, may contain backreferences that will get
         expanded with the C(regexp) capture groups if the regexp matches.
+      - Be aware that the line is processed first on the controller and thus is dependent on yaml quoting rules. Any double quoted line
+        will have control characters, such as '\r\n', expanded. To print such characters literally, use single or no quotes.
   backrefs:
     required: false
     default: "no"
@@ -123,6 +112,11 @@
 
 EXAMPLES = r'''
 # Before 2.3, option 'dest', 'destfile' or 'name' was used instead of 'path'
+- name: insert path without converting \r\n
+  win_lineinfile:
+    path: c:\file.txt
+    line: c:\return\new
+
 - win_lineinfile:
     path: C:\temp\example.conf
     regexp: '^name='


Test patch:
diff --git a/test/integration/targets/win_lineinfile/files/test_linebreak.txt b/test/integration/targets/win_lineinfile/files/test_linebreak.txt
new file mode 100644
diff --git a/test/integration/targets/win_lineinfile/tasks/main.yml b/test/integration/targets/win_lineinfile/tasks/main.yml
--- a/test/integration/targets/win_lineinfile/tasks/main.yml
+++ b/test/integration/targets/win_lineinfile/tasks/main.yml
@@ -638,3 +638,66 @@
   assert:
     that:
     - "result.stat.checksum == '66a72e71f42c4775f4326da95cfe82c8830e5022'"
+
+#########################################################################
+# issue #33858
+# \r\n causes line break instead of printing literally which breaks paths.
+
+- name: create testing file
+  win_copy:
+    src: test_linebreak.txt
+    dest: "{{win_output_dir}}/test_linebreak.txt"
+
+- name: stat the test file
+  win_stat:
+    path: "{{win_output_dir}}/test_linebreak.txt"
+  register: result
+
+# (Get-FileHash -path C:\ansible\test\integration\targets\win_lineinfile\files\test_linebreak.txt -Algorithm sha1).hash.tolower()
+- name: check win_stat file result
+  assert:
+    that:
+      - result.stat.exists
+      - not result.stat.isdir
+      - result.stat.checksum == 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
+      - result is not failed
+      - result is not changed
+
+- name: insert path c:\return\new to test file
+  win_lineinfile:
+    dest: "{{win_output_dir}}/test_linebreak.txt"
+    line: c:\return\new
+  register: result_literal
+
+- name: insert path "c:\return\new" to test file, will cause line breaks
+  win_lineinfile:
+    dest: "{{win_output_dir}}/test_linebreak.txt"
+    line: "c:\return\new"
+  register: result_expand
+
+- name: assert that the lines were inserted
+  assert:
+    that:
+    - result_literal.changed == true
+    - result_literal.msg == 'line added'
+    - result_expand.changed == true
+    - result_expand.msg == 'line added'
+
+- name: stat the test file
+  win_stat:
+    path: "{{win_output_dir}}/test_linebreak.txt"
+  register: result
+
+- debug:
+    var: result
+    verbosity: 1
+
+# expect that the file looks like this:
+# c:\return\new
+# c:
+# eturn
+# ew #or c:eturnew  on windows
+- name: assert that one line is literal and the other has breaks
+  assert:
+    that:
+    - result.stat.checksum == 'd2dfd11bc70526ff13a91153c76a7ae5595a845b'