1 #!powershell
2 
3 #AnsibleRequires -CSharpUtil Ansible.Basic
4 #AnsibleRequires -CSharpUtil Ansible.Become
5 
6 $module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
7 
Assert-Equalsnull8 Function Assert-Equals {
9     param(
10         [Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
11         [Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
12     )
13 
14     $matched = $false
15     if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) {
16         $Actual.Count | Assert-Equals -Expected $Expected.Count
17         for ($i = 0; $i -lt $Actual.Count; $i++) {
18             $actual_value = $Actual[$i]
19             $expected_value = $Expected[$i]
20             Assert-Equals -Actual $actual_value -Expected $expected_value
21         }
22         $matched = $true
23     } else {
24         $matched = $Actual -ceq $Expected
25     }
26 
27     if (-not $matched) {
28         if ($Actual -is [PSObject]) {
29             $Actual = $Actual.ToString()
30         }
31 
32         $call_stack = (Get-PSCallStack)[1]
33         $module.Result.test = $test
34         $module.Result.actual = $Actual
35         $module.Result.expected = $Expected
36         $module.Result.line = $call_stack.ScriptLineNumber
37         $module.Result.method = $call_stack.Position.Text
38         $module.FailJson("AssertionError: actual != expected")
39     }
40 }
41 
42 # Would be great to move win_whomai out into it's own module util and share the
43 # code here, for now just rely on a cut down version
44 $test_whoami = {
45     Add-Type -TypeDefinition @'
46 using Microsoft.Win32.SafeHandles;
47 using System;
48 using System.Runtime.ConstrainedExecution;
49 using System.Runtime.InteropServices;
50 using System.Security.Principal;
51 using System.Text;
52 
53 namespace Ansible
54 {
55     internal class NativeHelpers
56     {
57         [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
58         public struct LSA_UNICODE_STRING
59         {
60             public UInt16 Length;
61             public UInt16 MaximumLength;
62             public IntPtr Buffer;
63 
64             public override string ToString()
65             {
66                 return Marshal.PtrToStringUni(Buffer, Length / sizeof(char));
67             }
68         }
69 
70         [StructLayout(LayoutKind.Sequential)]
71         public struct LUID
72         {
73             public UInt32 LowPart;
74             public Int32 HighPart;
75 
76             public static explicit operator UInt64(LUID l)
77             {
78                 return (UInt64)((UInt64)l.HighPart << 32) | (UInt64)l.LowPart;
79             }
80         }
81 
82         [StructLayout(LayoutKind.Sequential)]
83         public struct SECURITY_LOGON_SESSION_DATA
84         {
85             public UInt32 Size;
86             public LUID LogonId;
87             public LSA_UNICODE_STRING UserName;
88             public LSA_UNICODE_STRING LogonDomain;
89             public LSA_UNICODE_STRING AuthenticationPackage;
90             public SECURITY_LOGON_TYPE LogonType;
91         }
92 
93         [StructLayout(LayoutKind.Sequential)]
94         public struct SID_AND_ATTRIBUTES
95         {
96             public IntPtr Sid;
97             public int Attributes;
98         }
99 
100         [StructLayout(LayoutKind.Sequential)]
101         public struct TOKEN_MANDATORY_LABEL
102         {
103             public SID_AND_ATTRIBUTES Label;
104         }
105 
106         [StructLayout(LayoutKind.Sequential)]
107         public struct TOKEN_SOURCE
108         {
109             [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public char[] SourceName;
110             public LUID SourceIdentifier;
111         }
112 
113         [StructLayout(LayoutKind.Sequential)]
114         public struct TOKEN_STATISTICS
115         {
116             public LUID TokenId;
117             public LUID AuthenticationId;
118         }
119 
120         [StructLayout(LayoutKind.Sequential)]
121         public struct TOKEN_USER
122         {
123             public SID_AND_ATTRIBUTES User;
124         }
125 
126         public enum SECURITY_LOGON_TYPE
127         {
128             System = 0, // Used only by the Sytem account
129             Interactive = 2,
130             Network,
131             Batch,
132             Service,
133             Proxy,
134             Unlock,
135             NetworkCleartext,
136             NewCredentials,
137             RemoteInteractive,
138             CachedInteractive,
139             CachedRemoteInteractive,
140             CachedUnlock
141         }
142 
143         public enum TokenInformationClass
144         {
145             TokenUser = 1,
146             TokenSource = 7,
147             TokenStatistics = 10,
148             TokenIntegrityLevel = 25,
149         }
150     }
151 
152     internal class NativeMethods
153     {
154         [DllImport("kernel32.dll", SetLastError = true)]
155         public static extern bool CloseHandle(
156             IntPtr hObject);
157 
158         [DllImport("kernel32.dll")]
159         public static extern SafeNativeHandle GetCurrentProcess();
160 
161         [DllImport("userenv.dll", SetLastError = true)]
162         public static extern bool GetProfileType(
163             out UInt32 dwFlags);
164 
165         [DllImport("advapi32.dll", SetLastError = true)]
166         public static extern bool GetTokenInformation(
167             SafeNativeHandle TokenHandle,
168             NativeHelpers.TokenInformationClass TokenInformationClass,
169             SafeMemoryBuffer TokenInformation,
170             UInt32 TokenInformationLength,
171             out UInt32 ReturnLength);
172 
173         [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
174         public static extern bool LookupAccountSid(
175             string lpSystemName,
176             IntPtr Sid,
177             StringBuilder lpName,
178             ref UInt32 cchName,
179             StringBuilder ReferencedDomainName,
180             ref UInt32 cchReferencedDomainName,
181             out UInt32 peUse);
182 
183         [DllImport("secur32.dll", SetLastError = true)]
184         public static extern UInt32 LsaEnumerateLogonSessions(
185             out UInt32 LogonSessionCount,
186             out SafeLsaMemoryBuffer LogonSessionList);
187 
188         [DllImport("secur32.dll", SetLastError = true)]
189         public static extern UInt32 LsaFreeReturnBuffer(
190             IntPtr Buffer);
191 
192         [DllImport("secur32.dll", SetLastError = true)]
193         public static extern UInt32 LsaGetLogonSessionData(
194             IntPtr LogonId,
195             out SafeLsaMemoryBuffer ppLogonSessionData);
196 
197         [DllImport("advapi32.dll")]
198         public static extern UInt32 LsaNtStatusToWinError(
199             UInt32 Status);
200 
201         [DllImport("advapi32.dll", SetLastError = true)]
202         public static extern bool OpenProcessToken(
203             SafeNativeHandle ProcessHandle,
204             TokenAccessLevels DesiredAccess,
205             out SafeNativeHandle TokenHandle);
206     }
207 
208     internal class SafeLsaMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid
209     {
210         public SafeLsaMemoryBuffer() : base(true) { }
211 
212         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
213         protected override bool ReleaseHandle()
214         {
215             UInt32 res = NativeMethods.LsaFreeReturnBuffer(handle);
216             return res == 0;
217         }
218     }
219 
220     internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid
221     {
222         public SafeMemoryBuffer() : base(true) { }
223         public SafeMemoryBuffer(int cb) : base(true)
224         {
225             base.SetHandle(Marshal.AllocHGlobal(cb));
226         }
227         public SafeMemoryBuffer(IntPtr handle) : base(true)
228         {
229             base.SetHandle(handle);
230         }
231 
232         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
233         protected override bool ReleaseHandle()
234         {
235             Marshal.FreeHGlobal(handle);
236             return true;
237         }
238     }
239 
240     internal class SafeNativeHandle : SafeHandleZeroOrMinusOneIsInvalid
241     {
242         public SafeNativeHandle() : base(true) { }
243         public SafeNativeHandle(IntPtr handle) : base(true) { this.handle = handle; }
244 
245         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
246         protected override bool ReleaseHandle()
247         {
248             return NativeMethods.CloseHandle(handle);
249         }
250     }
251 
252     public class Win32Exception : System.ComponentModel.Win32Exception
253     {
254         private string _msg;
255 
256         public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { }
257         public Win32Exception(int errorCode, string message) : base(errorCode)
258         {
259             _msg = String.Format("{0} ({1}, Win32ErrorCode {2})", message, base.Message, errorCode);
260         }
261 
262         public override string Message { get { return _msg; } }
263         public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
264     }
265 
266     public class Logon
267     {
268         public string AuthenticationPackage { get; internal set; }
269         public string LogonType { get; internal set; }
270         public string MandatoryLabelName { get; internal set; }
271         public SecurityIdentifier MandatoryLabelSid { get; internal set; }
272         public bool ProfileLoaded { get; internal set; }
273         public string SourceName { get; internal set; }
274         public string UserName { get; internal set; }
275         public SecurityIdentifier UserSid { get; internal set; }
276 
277         public Logon()
278         {
279             using (SafeNativeHandle process = NativeMethods.GetCurrentProcess())
280             {
281                 TokenAccessLevels dwAccess = TokenAccessLevels.Query | TokenAccessLevels.QuerySource;
282 
283                 SafeNativeHandle hToken;
284                 NativeMethods.OpenProcessToken(process, dwAccess, out hToken);
285                 using (hToken)
286                 {
287                     SetLogonSessionData(hToken);
288                     SetTokenMandatoryLabel(hToken);
289                     SetTokenSource(hToken);
290                     SetTokenUser(hToken);
291                 }
292             }
293             SetProfileLoaded();
294         }
295 
296         private void SetLogonSessionData(SafeNativeHandle hToken)
297         {
298             NativeHelpers.TokenInformationClass tokenClass = NativeHelpers.TokenInformationClass.TokenStatistics;
299             UInt32 returnLength;
300             NativeMethods.GetTokenInformation(hToken, tokenClass, new SafeMemoryBuffer(IntPtr.Zero), 0, out returnLength);
301 
302             UInt64 tokenLuidId;
303             using (SafeMemoryBuffer infoPtr = new SafeMemoryBuffer((int)returnLength))
304             {
305                 if (!NativeMethods.GetTokenInformation(hToken, tokenClass, infoPtr, returnLength, out returnLength))
306                     throw new Win32Exception("GetTokenInformation(TokenStatistics) failed");
307 
308                 NativeHelpers.TOKEN_STATISTICS stats = (NativeHelpers.TOKEN_STATISTICS)Marshal.PtrToStructure(
309                     infoPtr.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_STATISTICS));
310                 tokenLuidId = (UInt64)stats.AuthenticationId;
311             }
312 
313             UInt32 sessionCount;
314             SafeLsaMemoryBuffer sessionPtr;
315             UInt32 res = NativeMethods.LsaEnumerateLogonSessions(out sessionCount, out sessionPtr);
316             if (res != 0)
317                 throw new Win32Exception((int)NativeMethods.LsaNtStatusToWinError(res), "LsaEnumerateLogonSession() failed");
318             using (sessionPtr)
319             {
320                 IntPtr currentSession = sessionPtr.DangerousGetHandle();
321                 for (UInt32 i = 0; i < sessionCount; i++)
322                 {
323                     SafeLsaMemoryBuffer sessionDataPtr;
324                     res = NativeMethods.LsaGetLogonSessionData(currentSession, out sessionDataPtr);
325                     if (res != 0)
326                     {
327                         currentSession = IntPtr.Add(currentSession, Marshal.SizeOf(typeof(NativeHelpers.LUID)));
328                         continue;
329                     }
330                     using (sessionDataPtr)
331                     {
332                         NativeHelpers.SECURITY_LOGON_SESSION_DATA sessionData = (NativeHelpers.SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(
333                             sessionDataPtr.DangerousGetHandle(), typeof(NativeHelpers.SECURITY_LOGON_SESSION_DATA));
334                         UInt64 sessionId = (UInt64)sessionData.LogonId;
335                         if (sessionId == tokenLuidId)
336                         {
337                             AuthenticationPackage = sessionData.AuthenticationPackage.ToString();
338                             LogonType = sessionData.LogonType.ToString();
339                             break;
340                         }
341                     }
342 
343                     currentSession = IntPtr.Add(currentSession, Marshal.SizeOf(typeof(NativeHelpers.LUID)));
344                 }
345             }
346         }
347 
348         private void SetTokenMandatoryLabel(SafeNativeHandle hToken)
349         {
350             NativeHelpers.TokenInformationClass tokenClass = NativeHelpers.TokenInformationClass.TokenIntegrityLevel;
351             UInt32 returnLength;
352             NativeMethods.GetTokenInformation(hToken, tokenClass, new SafeMemoryBuffer(IntPtr.Zero), 0, out returnLength);
353             using (SafeMemoryBuffer infoPtr = new SafeMemoryBuffer((int)returnLength))
354             {
355                 if (!NativeMethods.GetTokenInformation(hToken, tokenClass, infoPtr, returnLength, out returnLength))
356                     throw new Win32Exception("GetTokenInformation(TokenIntegrityLevel) failed");
357                 NativeHelpers.TOKEN_MANDATORY_LABEL label = (NativeHelpers.TOKEN_MANDATORY_LABEL)Marshal.PtrToStructure(
358                     infoPtr.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_MANDATORY_LABEL));
359                 MandatoryLabelName = LookupSidName(label.Label.Sid);
360                 MandatoryLabelSid = new SecurityIdentifier(label.Label.Sid);
361             }
362         }
363 
364         private void SetTokenSource(SafeNativeHandle hToken)
365         {
366             NativeHelpers.TokenInformationClass tokenClass = NativeHelpers.TokenInformationClass.TokenSource;
367             UInt32 returnLength;
368             NativeMethods.GetTokenInformation(hToken, tokenClass, new SafeMemoryBuffer(IntPtr.Zero), 0, out returnLength);
369             using (SafeMemoryBuffer infoPtr = new SafeMemoryBuffer((int)returnLength))
370             {
371                 if (!NativeMethods.GetTokenInformation(hToken, tokenClass, infoPtr, returnLength, out returnLength))
372                     throw new Win32Exception("GetTokenInformation(TokenSource) failed");
373                 NativeHelpers.TOKEN_SOURCE source = (NativeHelpers.TOKEN_SOURCE)Marshal.PtrToStructure(
374                     infoPtr.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_SOURCE));
375                 SourceName = new string(source.SourceName).Replace('\0', ' ').TrimEnd();
376             }
377         }
378 
379         private void SetTokenUser(SafeNativeHandle hToken)
380         {
381             NativeHelpers.TokenInformationClass tokenClass = NativeHelpers.TokenInformationClass.TokenUser;
382             UInt32 returnLength;
383             NativeMethods.GetTokenInformation(hToken, tokenClass, new SafeMemoryBuffer(IntPtr.Zero), 0, out returnLength);
384             using (SafeMemoryBuffer infoPtr = new SafeMemoryBuffer((int)returnLength))
385             {
386                 if (!NativeMethods.GetTokenInformation(hToken, tokenClass, infoPtr, returnLength, out returnLength))
387                     throw new Win32Exception("GetTokenInformation(TokenSource) failed");
388                 NativeHelpers.TOKEN_USER user = (NativeHelpers.TOKEN_USER)Marshal.PtrToStructure(
389                     infoPtr.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_USER));
390                 UserName = LookupSidName(user.User.Sid);
391                 UserSid = new SecurityIdentifier(user.User.Sid);
392             }
393         }
394 
395         private void SetProfileLoaded()
396         {
397             UInt32 flags;
398             ProfileLoaded = NativeMethods.GetProfileType(out flags);
399         }
400 
401         private static string LookupSidName(IntPtr pSid)
402         {
403             StringBuilder name = new StringBuilder(0);
404             StringBuilder domain = new StringBuilder(0);
405             UInt32 nameLength = 0;
406             UInt32 domainLength = 0;
407             UInt32 peUse;
408             NativeMethods.LookupAccountSid(null, pSid, name, ref nameLength, domain, ref domainLength, out peUse);
409             name.EnsureCapacity((int)nameLength);
410             domain.EnsureCapacity((int)domainLength);
411 
412             if (!NativeMethods.LookupAccountSid(null, pSid, name, ref nameLength, domain, ref domainLength, out peUse))
413                 throw new Win32Exception("LookupAccountSid() failed");
414 
415             return String.Format("{0}\\{1}", domain.ToString(), name.ToString());
416         }
417     }
418 }
419 '@
420     $logon = New-Object -TypeName Ansible.Logon
421     ConvertTo-Json -InputObject $logon
422 }.ToString()
423 
424 $current_user_raw = [Ansible.Process.ProcessUtil]::CreateProcess($null, "powershell.exe -NoProfile -", $null, $null, $test_whoami + "`r`n")
425 $current_user = ConvertFrom-Json -InputObject $current_user_raw.StandardOut
426 
427 $adsi = [ADSI]"WinNT://$env:COMPUTERNAME"
428 
429 $standard_user = "become_standard"
430 $admin_user = "become_admin"
431 $become_pass = "password123!$([System.IO.Path]::GetRandomFileName())"
432 $medium_integrity_sid = "S-1-16-8192"
433 $high_integrity_sid = "S-1-16-12288"
434 $system_integrity_sid = "S-1-16-16384"
435 
436 $tests = @{
437     "Runas standard user" = {
438         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass,
439             "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
440         $actual.StandardError | Assert-Equals -Expected ""
441         $actual.ExitCode | Assert-Equals -Expected 0
442 
443         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
444         $stdout.LogonType | Assert-Equals -Expected "Interactive"
445         $stdout.ProfileLoaded | Assert-Equals -Expected $true
446         $stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
447         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
448     }
449 
450     "Runas admin user" = {
451         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass,
452             "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
453         $actual.StandardError | Assert-Equals -Expected ""
454         $actual.ExitCode | Assert-Equals -Expected 0
455         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
456         $stdout.LogonType | Assert-Equals -Expected "Interactive"
457         $stdout.ProfileLoaded | Assert-Equals -Expected $true
458         $stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
459         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
460     }
461 
462     "Runas SYSTEM" = {
463         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null,
464             "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
465         $actual.StandardError | Assert-Equals -Expected ""
466         $actual.ExitCode | Assert-Equals -Expected 0
467 
468         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
469         $stdout.LogonType | Assert-Equals -Expected "System"
470         $stdout.ProfileLoaded | Assert-Equals -Expected $true
471         $stdout.UserSid.Value | Assert-Equals -Expected "S-1-5-18"
472         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $system_integrity_sid
473 
474         $with_domain = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NT AUTHORITY\System", $null, "whoami.exe")
475         $with_domain.StandardOut | Assert-Equals -Expected "nt authority\system`r`n"
476     }
477 
478     "Runas LocalService" = {
479         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("LocalService", $null,
480             "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
481         $actual.StandardError | Assert-Equals -Expected ""
482         $actual.ExitCode | Assert-Equals -Expected 0
483 
484         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
485         $stdout.LogonType | Assert-Equals -Expected "Service"
486         $stdout.ProfileLoaded | Assert-Equals -Expected $true
487         $stdout.UserSid.Value | Assert-Equals -Expected "S-1-5-19"
488         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $system_integrity_sid
489 
490         $with_domain = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NT AUTHORITY\LocalService", $null, "whoami.exe")
491         $with_domain.StandardOut | Assert-Equals -Expected "nt authority\local service`r`n"
492     }
493 
494     "Runas NetworkService" = {
495         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NetworkService", $null,
496             "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
497         $actual.StandardError | Assert-Equals -Expected ""
498         $actual.ExitCode | Assert-Equals -Expected 0
499 
500         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
501         $stdout.LogonType | Assert-Equals -Expected "Service"
502         $stdout.ProfileLoaded | Assert-Equals -Expected $true
503         $stdout.UserSid.Value | Assert-Equals -Expected "S-1-5-20"
504         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $system_integrity_sid
505 
506         $with_domain = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NT AUTHORITY\NetworkService", $null, "whoami.exe")
507         $with_domain.StandardOut | Assert-Equals -Expected "nt authority\network service`r`n"
508     }
509 
510     "Runas without working dir set" = {
511         $expected = "$env:SystemRoot\system32`r`n"
512         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null,
513             'powershell.exe $pwd.Path', $null, $null, "")
514         $actual.StandardOut | Assert-Equals -Expected $expected
515         $actual.StandardError | Assert-Equals -Expected ""
516         $actual.ExitCode | Assert-Equals -Expected 0
517     }
518 
519     "Runas with working dir set" = {
520         $expected = "$env:SystemRoot`r`n"
521         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null,
522             'powershell.exe $pwd.Path', $env:SystemRoot, $null, "")
523         $actual.StandardOut | Assert-Equals -Expected $expected
524         $actual.StandardError | Assert-Equals -Expected ""
525         $actual.ExitCode | Assert-Equals -Expected 0
526     }
527 
528     "Runas without environment set" = {
529         $expected = "Windows_NT`r`n"
530         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null,
531             'powershell.exe $env:TEST; $env:OS', $null, $null, "")
532         $actual.StandardOut | Assert-Equals -Expected $expected
533         $actual.StandardError | Assert-Equals -Expected ""
534         $actual.ExitCode | Assert-Equals -Expected 0
535     }
536 
537     "Runas with environment set" = {
538         $env_vars = @{
539             TEST = "tesTing"
540             TEST2 = "Testing 2"
541         }
542         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
543             'cmd.exe /c set', $null, $env_vars, "")
544         ("TEST=tesTing" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
545         ("TEST2=Testing 2" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
546         ("OS=Windows_NT" -cnotin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
547         $actual.StandardError | Assert-Equals -Expected ""
548         $actual.ExitCode | Assert-Equals -Expected 0
549     }
550 
551     "Runas with string stdin" = {
552         $expected = "input value`r`n`r`n"
553         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
554             'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, "input value")
555         $actual.StandardOut | Assert-Equals -Expected $expected
556         $actual.StandardError | Assert-Equals -Expected ""
557         $actual.ExitCode | Assert-Equals -Expected 0
558     }
559 
560     "Runas with string stdin and newline" = {
561         $expected = "input value`r`n`r`n"
562         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
563             'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, "input value`r`n")
564         $actual.StandardOut | Assert-Equals -Expected $expected
565         $actual.StandardError | Assert-Equals -Expected ""
566         $actual.ExitCode | Assert-Equals -Expected 0
567     }
568 
569     "Runas with byte stdin" = {
570         $expected = "input value`r`n"
571         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
572             'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value"))
573         $actual.StandardOut | Assert-Equals -Expected $expected
574         $actual.StandardError | Assert-Equals -Expected ""
575         $actual.ExitCode | Assert-Equals -Expected 0
576     }
577 
578     "Missing executable" = {
579         $failed = $false
580         try {
581             [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, "fake.exe")
582         } catch {
583             $failed = $true
584             $_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "Ansible.Process.Win32Exception"
585             $expected = 'Exception calling "CreateProcessAsUser" with "3" argument(s): "CreateProcessWithTokenW() failed '
586             $expected += '(The system cannot find the file specified, Win32ErrorCode 2)"'
587             $_.Exception.Message | Assert-Equals -Expected $expected
588         }
589         $failed | Assert-Equals -Expected $true
590     }
591 
592     "CreateProcessAsUser with lpApplicationName" = {
593         $expected = "abc`r`n"
594         $full_path = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe"
595         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $full_path,
596             "Write-Output 'abc'", $null, $null, "")
597         $actual.StandardOut | Assert-Equals -Expected $expected
598         $actual.StandardError | Assert-Equals -Expected ""
599         $actual.ExitCode | Assert-Equals -Expected 0
600 
601         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $full_path,
602             "powershell.exe Write-Output 'abc'", $null, $null, "")
603         $actual.StandardOut | Assert-Equals -Expected $expected
604         $actual.StandardError | Assert-Equals -Expected ""
605         $actual.ExitCode | Assert-Equals -Expected 0
606     }
607 
608     "CreateProcessAsUser with stderr" = {
609         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $null,
610             "powershell.exe [System.Console]::Error.WriteLine('hi')", $null, $null, "")
611         $actual.StandardOut | Assert-Equals -Expected ""
612         $actual.StandardError | Assert-Equals -Expected "hi`r`n"
613         $actual.ExitCode | Assert-Equals -Expected 0
614     }
615 
616     "CreateProcessAsUser with exit code" = {
617         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $null,
618             "powershell.exe exit 10", $null, $null, "")
619         $actual.StandardOut | Assert-Equals -Expected ""
620         $actual.StandardError | Assert-Equals -Expected ""
621         $actual.ExitCode | Assert-Equals -Expected 10
622     }
623 
624     "Local account with computer name" = {
625         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("$env:COMPUTERNAME\$standard_user", $become_pass,
626             "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
627         $actual.StandardError | Assert-Equals -Expected ""
628         $actual.ExitCode | Assert-Equals -Expected 0
629 
630         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
631         $stdout.LogonType | Assert-Equals -Expected "Interactive"
632         $stdout.ProfileLoaded | Assert-Equals -Expected $true
633         $stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
634         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
635     }
636 
637     "Local account with computer as period" = {
638         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser(".\$standard_user", $become_pass,
639             "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
640         $actual.StandardError | Assert-Equals -Expected ""
641         $actual.ExitCode | Assert-Equals -Expected 0
642 
643         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
644         $stdout.LogonType | Assert-Equals -Expected "Interactive"
645         $stdout.ProfileLoaded | Assert-Equals -Expected $true
646         $stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
647         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
648     }
649 
650     "Local account with invalid password" = {
651         $failed = $false
652         try {
653             [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, "incorrect", "powershell.exe Write-Output abc")
654         } catch {
655             $failed = $true
656             $_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "Ansible.AccessToken.Win32Exception"
657             # Server 2008 has a slightly different error msg, just assert we get the error 1326
658             ($_.Exception.Message.Contains("Win32ErrorCode 1326")) | Assert-Equals -Expected $true
659         }
660         $failed | Assert-Equals -Expected $true
661     }
662 
663     "Invalid account" = {
664         $failed = $false
665         try {
666             [Ansible.Become.BecomeUtil]::CreateProcessAsUser("incorrect", "incorrect", "powershell.exe Write-Output abc")
667         } catch {
668             $failed = $true
669             $_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "System.Security.Principal.IdentityNotMappedException"
670             $expected = 'Exception calling "CreateProcessAsUser" with "3" argument(s): "Some or all '
671             $expected += 'identity references could not be translated."'
672             $_.Exception.Message | Assert-Equals -Expected $expected
673         }
674         $failed | Assert-Equals -Expected $true
675     }
676 
677     "Interactive logon with standard" = {
678         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
679             "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
680         $actual.StandardError | Assert-Equals -Expected ""
681         $actual.ExitCode | Assert-Equals -Expected 0
682 
683         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
684         $stdout.LogonType | Assert-Equals -Expected "Interactive"
685         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
686         $stdout.ProfileLoaded | Assert-Equals -Expected $true
687         $stdout.SourceName | Assert-Equals -Expected "Advapi"
688         $stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
689     }
690 
691     "Batch logon with standard" = {
692         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
693             "Batch", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
694         $actual.StandardError | Assert-Equals -Expected ""
695         $actual.ExitCode | Assert-Equals -Expected 0
696 
697         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
698         $stdout.LogonType | Assert-Equals -Expected "Batch"
699         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
700         $stdout.ProfileLoaded | Assert-Equals -Expected $true
701         $stdout.SourceName | Assert-Equals -Expected "Advapi"
702         $stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
703     }
704 
705     "Network logon with standard" = {
706         # Server 2008 will not work with become to Network or Network Credentials
707         if ([System.Environment]::OSVersion.Version -lt [Version]"6.1") {
708             continue
709         }
710         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
711             "Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
712         $actual.StandardError | Assert-Equals -Expected ""
713         $actual.ExitCode | Assert-Equals -Expected 0
714 
715         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
716         $stdout.LogonType | Assert-Equals -Expected "Network"
717         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
718         $stdout.ProfileLoaded | Assert-Equals -Expected $true
719         $stdout.SourceName | Assert-Equals -Expected "Advapi"
720         $stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
721     }
722 
723     "Network with cleartext logon with standard" = {
724         # Server 2008 will not work with become to Network or Network Cleartext
725         if ([System.Environment]::OSVersion.Version -lt [Version]"6.1") {
726             continue
727         }
728         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
729             "NetworkCleartext", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
730         $actual.StandardError | Assert-Equals -Expected ""
731         $actual.ExitCode | Assert-Equals -Expected 0
732 
733         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
734         $stdout.LogonType | Assert-Equals -Expected "NetworkCleartext"
735         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
736         $stdout.ProfileLoaded | Assert-Equals -Expected $true
737         $stdout.SourceName | Assert-Equals -Expected "Advapi"
738         $stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
739     }
740 
741     "Logon without password with standard" = {
742         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, [NullString]::Value, "WithProfile",
743             "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
744         $actual.StandardError | Assert-Equals -Expected ""
745         $actual.ExitCode | Assert-Equals -Expected 0
746 
747         # Too unstable, there might be another process still lingering which causes become to steal instead of using
748         # S4U. Just don't check the type and source to verify we can become without a password
749         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
750         # $stdout.LogonType | Assert-Equals -Expected "Batch"
751         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
752         $stdout.ProfileLoaded | Assert-Equals -Expected $true
753         # $stdout.SourceName | Assert-Equals -Expected "ansible"
754         $stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
755     }
756 
757     "Logon without password and network type with standard" = {
758         # Server 2008 will not work with become to Network or Network Cleartext
759         if ([System.Environment]::OSVersion.Version -lt [Version]"6.1") {
760             continue
761         }
762         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, [NullString]::Value, "WithProfile",
763             "Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
764         $actual.StandardError | Assert-Equals -Expected ""
765         $actual.ExitCode | Assert-Equals -Expected 0
766 
767         # Too unstable, there might be another process still lingering which causes become to steal instead of using
768         # S4U. Just don't check the type and source to verify we can become without a password
769         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
770         # $stdout.LogonType | Assert-Equals -Expected "Network"
771         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
772         $stdout.ProfileLoaded | Assert-Equals -Expected $true
773         # $stdout.SourceName | Assert-Equals -Expected "ansible"
774         $stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
775     }
776 
777     "Interactive logon with admin" = {
778         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
779             "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
780         $actual.StandardError | Assert-Equals -Expected ""
781         $actual.ExitCode | Assert-Equals -Expected 0
782 
783         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
784         $stdout.LogonType | Assert-Equals -Expected "Interactive"
785         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
786         $stdout.ProfileLoaded | Assert-Equals -Expected $true
787         $stdout.SourceName | Assert-Equals -Expected "Advapi"
788         $stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
789     }
790 
791     "Batch logon with admin" = {
792         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
793             "Batch", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
794         $actual.StandardError | Assert-Equals -Expected ""
795         $actual.ExitCode | Assert-Equals -Expected 0
796 
797         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
798         $stdout.LogonType | Assert-Equals -Expected "Batch"
799         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
800         $stdout.ProfileLoaded | Assert-Equals -Expected $true
801         $stdout.SourceName | Assert-Equals -Expected "Advapi"
802         $stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
803     }
804 
805     "Network logon with admin" = {
806         # Server 2008 will not work with become to Network or Network Credentials
807         if ([System.Environment]::OSVersion.Version -lt [Version]"6.1") {
808             continue
809         }
810         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
811             "Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
812         $actual.StandardError | Assert-Equals -Expected ""
813         $actual.ExitCode | Assert-Equals -Expected 0
814 
815         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
816         $stdout.LogonType | Assert-Equals -Expected "Network"
817         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
818         $stdout.ProfileLoaded | Assert-Equals -Expected $true
819         $stdout.SourceName | Assert-Equals -Expected "Advapi"
820         $stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
821     }
822 
823     "Network with cleartext logon with admin" = {
824         # Server 2008 will not work with become to Network or Network Credentials
825         if ([System.Environment]::OSVersion.Version -lt [Version]"6.1") {
826             continue
827         }
828         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
829             "NetworkCleartext", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
830         $actual.StandardError | Assert-Equals -Expected ""
831         $actual.ExitCode | Assert-Equals -Expected 0
832 
833         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
834         $stdout.LogonType | Assert-Equals -Expected "NetworkCleartext"
835         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
836         $stdout.ProfileLoaded | Assert-Equals -Expected $true
837         $stdout.SourceName | Assert-Equals -Expected "Advapi"
838         $stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
839     }
840 
841     "Fail to logon with null or empty password" = {
842         $failed = $false
843         try {
844             # Having $null or an empty string means we are trying to become a user with a blank password and not
845             # become without setting the password. This is confusing as $null gets converted to "" and we need to
846             # use [NullString]::Value instead if we want that behaviour. This just tests to see that an empty
847             # string won't go the S4U route.
848             [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $null, "WithProfile",
849                     "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
850         } catch {
851             $failed = $true
852             $_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "Ansible.AccessToken.Win32Exception"
853             # Server 2008 has a slightly different error msg, just assert we get the error 1326
854             ($_.Exception.Message.Contains("Win32ErrorCode 1326")) | Assert-Equals -Expected $true
855         }
856         $failed | Assert-Equals -Expected $true
857     }
858 
859     "Logon without password with admin" = {
860         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, [NullString]::Value, "WithProfile",
861             "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
862         $actual.StandardError | Assert-Equals -Expected ""
863         $actual.ExitCode | Assert-Equals -Expected 0
864 
865         # Too unstable, there might be another process still lingering which causes become to steal instead of using
866         # S4U. Just don't check the type and source to verify we can become without a password
867         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
868         # $stdout.LogonType | Assert-Equals -Expected "Batch"
869         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
870         $stdout.ProfileLoaded | Assert-Equals -Expected $true
871         # $stdout.SourceName | Assert-Equals -Expected "ansible"
872         $stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
873     }
874 
875     "Logon without password and network type with admin" = {
876         # become network doesn't work on Server 2008
877         if ([System.Environment]::OSVersion.Version -lt [Version]"6.1") {
878             continue
879         }
880         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, [NullString]::Value, "WithProfile",
881             "Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
882         $actual.StandardError | Assert-Equals -Expected ""
883         $actual.ExitCode | Assert-Equals -Expected 0
884 
885         # Too unstable, there might be another process still lingering which causes become to steal instead of using
886         # S4U. Just don't check the type and source to verify we can become without a password
887         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
888         # $stdout.LogonType | Assert-Equals -Expected "Network"
889         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
890         $stdout.ProfileLoaded | Assert-Equals -Expected $true
891         # $stdout.SourceName | Assert-Equals -Expected "ansible"
892         $stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
893     }
894 
895     "Logon without profile with admin" = {
896         # Server 2008 and 2008 R2 does not support running without the profile being set
897         if ([System.Environment]::OSVersion.Version -lt [Version]"6.2") {
898             continue
899         }
900 
901         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0,
902             "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
903         $actual.StandardError | Assert-Equals -Expected ""
904         $actual.ExitCode | Assert-Equals -Expected 0
905 
906         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
907         $stdout.LogonType | Assert-Equals -Expected "Interactive"
908         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
909         $stdout.ProfileLoaded | Assert-Equals -Expected $false
910         $stdout.SourceName | Assert-Equals -Expected "Advapi"
911         $stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
912     }
913 
914     "Logon with network credentials and no profile" = {
915         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("fakeuser", "fakepassword", "NetcredentialsOnly",
916             "NewCredentials", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
917         $actual.StandardError | Assert-Equals -Expected ""
918         $actual.ExitCode | Assert-Equals -Expected 0
919 
920         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
921         $stdout.LogonType | Assert-Equals -Expected "NewCredentials"
922         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $current_user.MandatoryLabelSid.Value
923 
924         # while we didn't set WithProfile, the new process is based on the current process
925         $stdout.ProfileLoaded | Assert-Equals -Expected $current_user.ProfileLoaded
926         $stdout.SourceName | Assert-Equals -Expected "Advapi"
927         $stdout.UserSid.Value | Assert-Equals -Expected $current_user.UserSid.Value
928     }
929 
930     "Logon with network credentials and with profile" = {
931         $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("fakeuser", "fakepassword", "NetcredentialsOnly, WithProfile",
932             "NewCredentials", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
933         $actual.StandardError | Assert-Equals -Expected ""
934         $actual.ExitCode | Assert-Equals -Expected 0
935 
936         $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
937         $stdout.LogonType | Assert-Equals -Expected "NewCredentials"
938         $stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $current_user.MandatoryLabelSid.Value
939         $stdout.ProfileLoaded | Assert-Equals -Expected $current_user.ProfileLoaded
940         $stdout.SourceName | Assert-Equals -Expected "Advapi"
941         $stdout.UserSid.Value | Assert-Equals -Expected $current_user.UserSid.Value
942     }
943 }
944 
945 try {
946     $tmp_dir = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName())
947     New-Item -Path $tmp_dir -ItemType Directory > $null
948     $acl = Get-Acl -Path $tmp_dir
949     $ace = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList @(
950         New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList ([System.Security.Principal.WellKnownSidType]::WorldSid, $null)
951         [System.Security.AccessControl.FileSystemRights]::FullControl,
952         [System.Security.AccessControl.InheritanceFlags]"ContainerInherit, ObjectInherit",
953         [System.Security.AccessControl.PropagationFlags]::None,
954         [System.Security.AccessControl.AccessControlType]::Allow
955     )
956     $acl.AddAccessRule($ace)
957     Set-Acl -Path $tmp_dir -AclObject $acl
958 
959     $tmp_script = Join-Path -Path $tmp_dir -ChildPath "whoami.ps1"
960     Set-Content -LiteralPath $tmp_script -Value $test_whoami
961 
962     foreach ($user in $standard_user, $admin_user) {
963         $user_obj = $adsi.Children | Where-Object { $_.SchemaClassName -eq "User" -and $_.Name -eq $user }
964         if ($null -eq $user_obj) {
965             $user_obj = $adsi.Create("User", $user)
966             $user_obj.SetPassword($become_pass)
967             $user_obj.SetInfo()
968         } else {
969             $user_obj.SetPassword($become_pass)
970         }
971         $user_obj.RefreshCache()
972 
973         if ($user -eq $standard_user) {
974             $standard_user_sid = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @($user_obj.ObjectSid.Value, 0)).Value
975             $group = [System.Security.Principal.WellKnownSidType]::BuiltinUsersSid
976         } else {
977             $admin_user_sid = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @($user_obj.ObjectSid.Value, 0)).Value
978             $group = [System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid
979         }
980         $group = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $group, $null).Value
981         [string[]]$current_groups = $user_obj.Groups() | ForEach-Object {
982             New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @($_.GetType().InvokeMember("objectSID", "GetProperty", $null, $_, $null), 0)
983         }
984         if ($current_groups -notcontains $group) {
985             $group_obj = $adsi.Children | Where-Object {
986                 if ($_.SchemaClassName -eq "Group") {
987                     $group_sid = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @($_.objectSID.Value, 0)
988                     $group_sid -eq $group
989                 }
990             }
991             $group_obj.Add($user_obj.Path)
992         }
993     }
994     foreach ($test_impl in $tests.GetEnumerator()) {
995         $test = $test_impl.Key
996         &$test_impl.Value
997     }
998 } finally {
999     Remove-Item -LiteralPath $tmp_dir -Force -Recurse
1000     foreach ($user in $standard_user, $admin_user) {
1001         $user_obj = $adsi.Children | Where-Object { $_.SchemaClassName -eq "User" -and $_.Name -eq $user }
1002         $adsi.Delete("User", $user_obj.Name.Value)
1003     }
1004 }
1005 
1006 
1007 $module.Result.data = "success"
1008 $module.ExitJson()
1009 
1010