]> insang Git - wevape-lu1_pos.git/blob
32438d2f8d7645cee29fd132e9554d634a128d42
[wevape-lu1_pos.git] /
1 data LocalizedData
2 {
3     # culture="en-US"
4     ConvertFrom-StringData @'
5 FileNotFound=File not found in the environment path.
6 AbsolutePathOrFileName=Absolute path or file name expected.
7 InvalidArgument=Invalid argument: '{0}' with value: '{1}'.
8 InvalidArgumentAndMessage={0} {1}
9 ProcessStarted=Process matching path '{0}' started
10 ProcessesStopped=Proceses matching path '{0}' with Ids '({1})' stopped.
11 ProcessAlreadyStarted=Process matching path '{0}' found running and no action required.
12 ProcessAlreadyStopped=Process matching path '{0}' not found running and no action required.
13 ErrorStopping=Failure stopping processes matching path '{0}' with IDs '({1})'. Message: {2}.
14 ErrorStarting=Failure starting process matching path '{0}'. Message: {1}.
15 StartingProcessWhatif=Start-Process
16 ProcessNotFound=Process matching path '{0}' not found
17 PathShouldBeAbsolute=The path should be absolute
18 PathShouldExist=The path should exist
19 ParameterShouldNotBeSpecified=Parameter {0} should not be specified.
20 FailureWaitingForProcessesToStart=Failed to wait for processes to start
21 FailureWaitingForProcessesToStop=Failed to wait for processes to stop
22 ErrorParametersNotSupportedWithCredential=Can't specify StandardOutputPath, StandardInputPath or WorkingDirectory when trying to run a process under a user context.
23 VerboseInProcessHandle=In process handle {0}
24 ErrorRunAsCredentialParameterNotSupported= The PsDscRunAsCredential parameter is not supported by the Process resource. To start the process with user '{0}', add the Credential parameter.
25 ErrorCredentialParameterNotSupportedWithRunAsCredential= The PsDscRunAsCredential parameter is not supported by the Process resource, and cannot be used with the Credential parameter. To start the process with user '{0}', use only the Credential parameter, not the PsDscRunAsCredential parameter.
26 '@
27 }
28
29 Import-LocalizedData  LocalizedData -filename MSFT_ProcessResource.strings.psd1
30
31 Import-Module "$PSScriptRoot\..\RunAsHelper.psm1"
32
33 function ExtractArguments($functionBoundParameters,[string[]]$argumentNames,[string[]]$newArgumentNames)
34 {
35     $returnValue=@{}
36     for($i=0;$i -lt $argumentNames.Count;$i++)
37     {
38         $argumentName=$argumentNames[$i]
39
40         if($newArgumentNames -eq $null)
41         {   
42             $newArgumentName=$argumentName
43         }
44         else
45         {
46             $newArgumentName=$newArgumentNames[$i]
47         }
48
49         if($functionBoundParameters.ContainsKey($argumentName))
50         {
51             $null=$returnValue.Add($newArgumentName,$functionBoundParameters[$argumentName])
52         }
53     }
54
55     return $returnValue
56 }
57
58 function IsRunFromLocalSystemUser()
59 {
60     (New-Object Security.Principal.WindowsPrincipal ( [Security.Principal.WindowsIdentity]::GetCurrent())).Identity.IsSystem
61 }
62
63 function Get-TargetResource
64 {
65     param
66     (
67         [parameter(Mandatory = $true)]
68         [ValidateNotNullOrEmpty()]
69         [System.String]
70         $Path,
71
72         [parameter(Mandatory = $true)]
73         [AllowEmptyString()]
74         [System.String]
75         $Arguments,
76
77         [ValidateNotNullOrEmpty()]
78         [System.Management.Automation.PSCredential]
79         $Credential
80     )
81     
82     $Path=(ResolvePath $Path)
83     $PSBoundParameters["Path"] = $Path
84     $getArguments = ExtractArguments $PSBoundParameters ("Path","Arguments","Credential")
85     $processes = @(GetWin32_Process @getArguments)
86
87     if($processes.Count -eq 0)
88     {
89         return @{
90             Path=$Path
91             Arguments=$Arguments
92             Ensure='Absent'
93         }
94     }
95
96     foreach($process in $processes)
97     {
98         # in case the process was killed between GetWin32_Process and this point, we should
99         # ignore errors which will generate empty entries in the return
100         $gpsProcess = (get-process -id $process.ProcessId -ErrorAction Ignore)
101
102         @{
103             Path=$process.Path
104             Arguments=(GetProcessArgumentsFromCommandLine $process.CommandLine)
105             PagedMemorySize=$gpsProcess.PagedMemorySize64
106             NonPagedMemorySize=$gpsProcess.NonpagedSystemMemorySize64
107             VirtualMemorySize=$gpsProcess.VirtualMemorySize64
108             HandleCount=$gpsProcess.HandleCount
109             Ensure='Present'
110             ProcessId=$process.ProcessId
111         }
112     }
113 }
114
115
116 function Set-TargetResource
117 {
118     [CmdletBinding(SupportsShouldProcess=$true)]
119     param
120     (
121         [parameter(Mandatory = $true)]
122         [ValidateNotNullOrEmpty()]
123         [System.String]
124         $Path,
125
126         [parameter(Mandatory = $true)]
127         [AllowEmptyString()]
128         [System.String]
129         $Arguments,
130
131         [ValidateNotNullOrEmpty()]
132         [System.Management.Automation.PSCredential]
133         $Credential,
134
135         [System.String]
136         [ValidateSet("Present", "Absent")]
137         $Ensure="Present",
138
139         [System.String]
140         $StandardOutputPath,
141
142         [System.String]
143         $StandardErrorPath,
144
145         [System.String]
146         $StandardInputPath,
147
148         [System.String]
149         $WorkingDirectory
150     )
151
152     $Path=ResolvePath $Path
153     $PSBoundParameters["Path"] = $Path
154     $getArguments = ExtractArguments $PSBoundParameters ("Path","Arguments","Credential")
155     $processes = @(GetWin32_Process @getArguments)
156
157     if($Ensure -eq 'Absent')
158     {
159         "StandardOutputPath","StandardErrorPath","StandardInputPath","WorkingDirectory" | AssertParameterIsNotSpecified $PSBoundParameters
160
161         if ($processes.Count -gt 0)
162         {
163            $processIds=$processes.ProcessId
164
165            $err=Stop-Process -Id $processIds -force 2>&1
166            
167            if($err -eq $null)
168            {
169                Write-Log ($LocalizedData.ProcessesStopped -f $Path,($processIds -join ","))
170            }
171            else
172            {
173                Write-Log ($LocalizedData.ErrorStopping -f $Path,($processIds -join ","),($err | out-string))
174                throw $err
175            }
176
177            # Before returning from Set-TargetResource we have to ensure a subsequent Test-TargetResource is going to work
178            if (!(WaitForProcessCount @getArguments -waitCount 0))
179            {
180                 $message = $LocalizedData.ErrorStopping -f $Path,($processIds -join ","),$LocalizedData.FailureWaitingForProcessesToStop
181                 Write-Log $message
182                 ThrowInvalidArgumentError "FailureWaitingForProcessesToStop" $message
183            }
184         }
185         else
186         {
187             Write-Log ($LocalizedData.ProcessAlreadyStopped -f $Path)
188         }
189     }
190     else
191     {
192         "StandardInputPath","WorkingDirectory" |  AssertAbsolutePath $PSBoundParameters -Exist
193         "StandardOutputPath","StandardErrorPath" | AssertAbsolutePath $PSBoundParameters
194
195         if ($processes.Count -eq 0)
196         {
197             $startArguments = ExtractArguments $PSBoundParameters `
198                  ("Path",     "Arguments",    "Credential", "StandardOutputPath",     "StandardErrorPath",     "StandardInputPath", "WorkingDirectory") `
199                  ("FilePath", "ArgumentList", "Credential",  "RedirectStandardOutput", "RedirectStandardError", "RedirectStandardInput", "WorkingDirectory")
200
201             if([string]::IsNullOrEmpty($Arguments))
202             {
203                 $null=$startArguments.Remove("ArgumentList")
204             }
205
206             if($PSCmdlet.ShouldProcess($Path,$LocalizedData.StartingProcessWhatif))
207             {
208                 #
209                 # Start-Process calls .net Process.Start()
210                 # If -Credential is present Process.Start() uses win32 api CreateProcessWithLogonW http://msdn.microsoft.com/en-us/library/0w4h05yb(v=vs.110).aspx
211                 # CreateProcessWithLogonW cannot be called as LocalSystem user.
212                 # Details http://msdn.microsoft.com/en-us/library/windows/desktop/ms682431(v=vs.85).aspx (section Remarks/Windows XP with SP2 and Windows Server 2003)
213                 #
214                 # In this case we call another api.
215                 #
216                 if($PSBoundParameters.ContainsKey("Credential") -and (IsRunFromLocalSystemUser))
217                 {
218                     if($PSBoundParameters.ContainsKey("StandardOutputPath") -or $PSBoundParameters.ContainsKey("StandardInputPath") -or $PSBoundParameters.ContainsKey("WorkingDirectory"))
219                     {
220                         $exception = New-Object System.ArgumentException $LocalizedData.ErrorParametersNotSupportedWithCredential
221                         $err = New-Object System.Management.Automation.ErrorRecord $exception, "InvalidCombinationOfArguments", InvalidArgument, $null
222                     }
223                     else 
224                     {
225                         $Domain, $UserName = Get-DomainAndUserName $Credential
226                         try
227                         {
228                             #
229                             # Internally we use win32 api LogonUser() with dwLogonType == LOGON32_LOGON_NETWORK_CLEARTEXT. 
230                             # It grants process ability for second-hop.
231                             #
232                             Import-DscNativeMethods
233                             [PSDesiredStateConfiguration.NativeMethods]::CreateProcessAsUser( "$Path $Arguments", $Domain, $UserName, $Credential.Password, $false, [ref] $null )
234                         }
235                         catch
236                         {
237                             throw  New-Object System.Management.Automation.ErrorRecord $_.Exception, "Win32Exception", OperationStopped, $null
238                         }
239                     }
240                 }
241                 else
242                 {
243                     $err=Start-Process @startArguments 2>&1
244                 }
245                 if($err -eq $null)
246                 {
247                     Write-Log ($LocalizedData.ProcessStarted -f $Path)
248                 }
249                 else
250                 {
251                     Write-Log ($LocalizedData.ErrorStarting -f $Path,($err | Out-String))
252                     throw $err
253                 }
254
255                 # Before returning from Set-TargetResource we have to ensure a subsequent Test-TargetResource is going to work
256                 if (!(WaitForProcessCount @getArguments -waitCount 1))
257                 {
258                     $message = $LocalizedData.ErrorStarting -f $Path,$LocalizedData.FailureWaitingForProcessesToStart
259                     Write-Log $message
260                     ThrowInvalidArgumentError "FailureWaitingForProcessesToStart" $message
261                 }
262             }
263         }
264         else
265         {
266             Write-Log ($LocalizedData.ProcessAlreadyStarted -f $Path)
267         }
268     }
269 }
270
271 function Test-TargetResource
272 {
273     param
274     (
275         [parameter(Mandatory = $true)]
276         [ValidateNotNullOrEmpty()]
277         [System.String]
278         $Path,
279
280         [parameter(Mandatory = $true)]
281         [AllowEmptyString()]
282         [System.String]
283         $Arguments,
284
285         [ValidateNotNullOrEmpty()]
286         [System.Management.Automation.PSCredential]
287         $Credential,
288
289         [System.String]
290         [ValidateSet("Present", "Absent")]
291         $Ensure="Present",
292
293         [System.String]
294         $StandardOutputPath,
295
296         [System.String]
297         $StandardErrorPath,
298
299         [System.String]
300         $StandardInputPath,
301
302         [System.String]
303         $WorkingDirectory
304     )
305
306     if($PsDscContext.RunAsUser)
307     {
308             if($PSBoundParameters.ContainsKey("Credential"))
309             {
310                 $exception = New-Object System.ArgumentException ($LocalizedData.ErrorCredentialParameterNotSupportedWithRunAsCredential -f $PsDscContext.RunAsUser)
311             $err = New-Object System.Management.Automation.ErrorRecord $exception, "InvalidArgument", InvalidArgument, $null
312             }
313             else
314             {
315                 $exception = New-Object System.ArgumentException ($LocalizedData.ErrorRunAsCredentialParameterNotSupported -f $PsDscContext.RunAsUser)
316             $err = New-Object System.Management.Automation.ErrorRecord $exception, "InvalidCombinationOfArguments", InvalidArgument, $null
317             }
318
319             Write-Log ($LocalizedData.ErrorStarting -f $Path,($err | Out-String))
320         throw $err
321     }    
322
323     $Path=ResolvePath $Path
324     $PSBoundParameters["Path"] = $Path
325     $getArguments = ExtractArguments $PSBoundParameters ("Path","Arguments","Credential")
326     $processes = @(GetWin32_Process @getArguments)
327
328
329     if($Ensure -eq 'Absent')
330     {
331         return ($processes.Count -eq 0)
332     }
333     else
334     {
335         return ($processes.Count -gt 0)
336     }
337 }
338
339 function GetWin32ProcessOwner
340 {
341     param
342     (
343         [parameter(Mandatory = $true)]
344         [ValidateNotNull()]
345         $process
346     )
347
348     # if the process was killed by the time this is called, GetOwner 
349     # will throw a WMIMethodException "Not found"
350     try
351     {
352         $owner = Invoke-CimMethod -InputObject $process -MethodName GetOwner
353     }
354     catch
355     {
356     }
357     
358     if($owner.Domain -ne $null)
359     {
360         return $owner.Domain + "\" + $owner.User
361     }
362     else                
363     {
364         return $owner.User
365     }
366 }
367
368 function WaitForProcessCount
369 {
370     [CmdletBinding(SupportsShouldProcess=$true)]
371     param
372     (
373         [parameter(Mandatory = $true)]
374         [ValidateNotNullOrEmpty()]
375         [System.String]
376         $Path,
377
378         [System.String]
379         $Arguments,
380
381         [ValidateNotNullOrEmpty()]
382         [System.Management.Automation.PSCredential]
383         $Credential,
384
385         [parameter(Mandatory=$true)]
386         $waitCount
387     )
388
389     $start = [DateTime]::Now
390     do
391     {
392         $getArguments = ExtractArguments $PSBoundParameters ("Path","Arguments","Credential")
393         $value = @(GetWin32_Process @getArguments).Count -eq $waitCount
394     } while(!$value -and ([DateTime]::Now - $start).TotalMilliseconds -lt 2000)
395     
396     return $value
397 }
398
399 function GetWin32_Process
400 {
401     [CmdletBinding(SupportsShouldProcess=$true)]
402     param
403     (
404         
405         [parameter(Mandatory = $true)]
406         [ValidateNotNullOrEmpty()]
407         [System.String]
408         $Path,
409
410         [System.String]
411         $Arguments,
412
413         [ValidateNotNullOrEmpty()]
414         [System.Management.Automation.PSCredential]
415         $Credential,
416
417         $useWmiObjectCount=8
418     )
419
420
421
422     $fileName = [io.path]::GetFileNameWithoutExtension($Path)
423
424     $gpsProcesses = @(get-process -Name $fileName -ErrorAction SilentlyContinue)
425     
426     if($gpsProcesses.Count -ge $useWmiObjectCount)
427     {
428         # if there are many processes it is faster to perform a Get-WmiObject
429         # in order to get Win32_Process objects for all processes
430         $Path=WQLEscape $Path
431         $filter = "ExecutablePath = '$Path'"
432         $processes = Get-CimInstance Win32_Process -Filter $filter
433     }
434     else
435     {
436         # if there are few processes, building a Win32_Process for
437         # each matching result of get-process is faster
438         $processes = foreach($gpsProcess in $gpsProcesses)
439         {
440             if(!($gpsProcess.Path -ieq $Path))
441             {
442                 continue
443             }
444
445             try
446             {
447                 Write-Verbose ($LocalizedData.VerboseInProcessHandle -f $gpsProcess.Id)
448                 Get-CimInstance Win32_Process -Filter "ProcessId=$($gpsProcess.Id)"
449             }
450             catch
451             {
452                 #ignore if could not retrieve process
453             }
454         }
455     }
456
457     if($PSBoundParameters.ContainsKey('Credential'))
458     {
459         $Domain, $UserName = Get-DomainAndUserName $Credential
460         # Since there are credentials we need to call the GetOwner method in each process to search for matches
461         $processes = $processes | where { (GetWin32ProcessOwner $_) -eq "$Domain\$UserName" }
462
463     }
464
465     if($Arguments -eq $null) {$Arguments = ""}
466     $processes = $processes | where { (GetProcessArgumentsFromCommandLine $_.CommandLine) -eq $Arguments }
467
468     return $processes
469 }
470
471 <#
472 .Synopsis
473    Strips the Arguments part of a commandLine. In "c:\temp\a.exe X Y Z" the Arguments part is "X Y Z".
474 #>
475 function GetProcessArgumentsFromCommandLine
476 {
477     param
478     (
479         [System.String]
480         $commandLine
481     )
482
483     if($commandLine -eq $null)
484     {
485         return ""
486     }
487     
488     $commandLine=$commandLine.Trim()
489
490     if($commandLine.Length -eq 0)
491     {
492         return ""
493     }
494
495     if($commandLine[0] -eq '"')
496     {
497         $charToLookfor=[char]'"'
498     }
499     else
500     {
501         $charToLookfor=[char]' '
502     }
503
504     $endofCommand=$commandLine.IndexOf($charToLookfor ,1)
505     if($endofCommand -eq -1)
506     {
507         return ""
508     }
509
510     return $commandLine.Substring($endofCommand+1).Trim()
511 }
512
513 <#
514 .Synopsis
515    Escapes a string to be used in a WQL filter as the one passed to get-wmiobject
516 #>
517 function WQLEscape
518 {
519     param
520     (
521         
522         [parameter(Mandatory = $true)]
523         [ValidateNotNullOrEmpty()]
524         [System.String]
525         $query
526     )
527
528     return $query.Replace("\","\\").Replace('"','\"').Replace("'","\'")
529 }
530
531 function ThrowInvalidArgumentError
532 {
533     [CmdletBinding()]
534     param
535     (
536         
537         [parameter(Mandatory = $true)]
538         [ValidateNotNullOrEmpty()]
539         [System.String]
540         $errorId,
541
542         [parameter(Mandatory = $true)]
543         [ValidateNotNullOrEmpty()]
544         [System.String]
545         $errorMessage
546     )
547
548     $errorCategory=[System.Management.Automation.ErrorCategory]::InvalidArgument
549     $exception = New-Object System.ArgumentException $errorMessage;
550     $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $null
551     throw $errorRecord
552 }
553
554 function ResolvePath
555 {
556     [CmdletBinding()]
557     param
558     (
559         [parameter(Mandatory = $true)]
560         [ValidateNotNullOrEmpty()]
561         [System.String]
562         $Path
563     )
564
565     $Path = [Environment]::ExpandEnvironmentVariables($Path)
566
567     if(IsRootedPath $Path)
568     {
569         if(!(Test-Path $Path -PathType Leaf))
570         {
571             ThrowInvalidArgumentError "CannotFindRootedPath" ($LocalizedData.InvalidArgumentAndMessage -f ($LocalizedData.InvalidArgument -f "Path",$Path), $LocalizedData.FileNotFound)
572         }
573
574         return $Path
575     }
576
577     if([string]::IsNullOrEmpty($env:Path))
578     {
579         ThrowInvalidArgumentError "EmptyEnvironmentPath" ($LocalizedData.InvalidArgumentAndMessage -f ($LocalizedData.InvalidArgument -f "Path",$Path), $LocalizedData.FileNotFound)
580     }
581
582     # This will block relative paths. The statement is only true id $Path contains a plain file name.
583     # Checking a relative path against segments of the $env:Path does not make sense
584     if((Split-Path $Path -Leaf) -ne $Path)
585     {
586         ThrowInvalidArgumentError "NotAbsolutePathOrFileName" ($LocalizedData.InvalidArgumentAndMessage -f ($LocalizedData.InvalidArgument -f "Path",$Path), $LocalizedData.AbsolutePathOrFileName)
587     }
588
589     foreach($rawSegment in $env:Path.Split(";"))
590     {
591         $segment = [Environment]::ExpandEnvironmentVariables($rawSegment)
592
593         # if an exception causes $segmentedRooted not to be set, we will consider it $false
594         $segmentRooted = $false
595         try
596         {
597             # If the whole path passed through [IO.Path]::IsPathRooted with no exceptions, it does not have
598             # invalid characters, so segment has no invalid characters and will not throw as well 
599             $segmentRooted=[IO.Path]::IsPathRooted($segment)
600         }
601         catch {}
602         
603         if(!$segmentRooted)
604         {
605             continue
606         }
607
608         $candidate = join-path $segment $Path
609         
610         if(Test-Path $candidate -PathType Leaf)
611         {
612             return $candidate
613         }
614     }
615
616     ThrowInvalidArgumentError "CannotFindRelativePath" ($LocalizedData.InvalidArgumentAndMessage -f ($LocalizedData.InvalidArgument -f "Path",$Path), $LocalizedData.FileNotFound)
617 }
618
619
620 function AssertAbsolutePath
621 {
622     [CmdletBinding()]
623     param
624     (
625         $ParentBoundParameters,
626
627         [System.String]
628         [Parameter (ValueFromPipeline=$true)]
629         $ParameterName,
630
631         [switch]
632         $Exist
633     )
634
635     Process
636     {
637         if(!$ParentBoundParameters.ContainsKey($ParameterName)) 
638         {
639             return
640         }
641
642         $path=$ParentBoundParameters[$ParameterName]
643         
644         if(!(IsRootedPath $Path))
645         {
646             ThrowInvalidArgumentError "PathShouldBeAbsolute" ($LocalizedData.InvalidArgumentAndMessage -f ($LocalizedData.InvalidArgument -f $ParameterName,$Path), 
647                 $LocalizedData.PathShouldBeAbsolute)
648         }
649
650         if(!$Exist.IsPresent)
651         {
652             return
653         }
654
655         if(!(Test-Path $Path))
656         {
657             ThrowInvalidArgumentError "PathShouldExist" ($LocalizedData.InvalidArgumentAndMessage -f ($LocalizedData.InvalidArgument -f $ParameterName,$Path), 
658                 $LocalizedData.PathShouldExist)
659         }
660     }
661 }
662
663 function AssertParameterIsNotSpecified
664 {
665     [CmdletBinding()]
666     param
667     (
668         $ParentBoundParameters,
669
670         [System.String]
671         [Parameter (ValueFromPipeline=$true)]
672         $ParameterName
673     )
674
675     Process
676     {
677         if($ParentBoundParameters.ContainsKey($ParameterName)) 
678         {
679             ThrowInvalidArgumentError "ParameterShouldNotBeSpecified" ($LocalizedData.ParameterShouldNotBeSpecified -f $ParameterName)
680         }
681     }
682 }
683
684 function IsRootedPath
685 {
686     param
687     (
688         [parameter(Mandatory = $true)]
689         [ValidateNotNullOrEmpty()]
690         [System.String]
691         $Path
692     )
693
694     try
695     {
696         return [IO.Path]::IsPathRooted($Path)
697     }
698     catch
699     {
700         # if the Path has invalid characters like >, <, etc, we cannot determine if it is rooted so we do not go on
701         ThrowInvalidArgumentError "CannotGetIsPathRooted" ($LocalizedData.InvalidArgumentAndMessage -f ($LocalizedData.InvalidArgument -f "Path",$Path), $_.Exception.Message)
702     }
703 }
704
705 function Write-Log
706 {
707     [CmdletBinding(SupportsShouldProcess=$true)]
708     param
709     (    
710         [parameter(Mandatory = $true)]
711         [ValidateNotNullOrEmpty()]
712         [System.String]
713         $Message
714     )
715
716     if ($PSCmdlet.ShouldProcess($Message, $null, $null))
717     {
718         Write-Verbose $Message        
719     }  
720 }  
721
722 Export-ModuleMember -function Get-TargetResource, Set-TargetResource, Test-TargetResource