functions/File.ps1
# # todo fix all parameterset etc. like in all other functions # # todo use for find-directory # todo use profile expansion # allow searching for multiple files, e.g. *.tmp within %temp% # todo problem with environment variables and expansion # use placeholder for gwmi win32_userprofile -filter 'special = false' | select localpath for finding userprofiles #Get-WmiObject win32_userprofile | Select-Object -ExpandProperty localpath) Function Find-File() { [CmdletBinding(SupportsShouldProcess=$True,DefaultParameterSetName='ExternComputerName')] param ( [Parameter(ParameterSetName='CredentialWinRMComputerName',Position=0)] [Parameter(ParameterSetName='CredentialWMIComputerName',Position=0)] [Parameter(ParameterSetName='ExternComputerName', Position=0)] [ValidateNotNullOrEmpty ()] [string[]] $ComputerName, [Parameter(ParameterSetName='CredentialWinRMComputerList',Mandatory=$true)] [Parameter(ParameterSetName='CredentialWMIComputerList',Mandatory=$true)] [Parameter(ParameterSetName='ExternComputerList', Mandatory=$true)] [ValidateNotNullOrEmpty ()] [string] $ComputerList, [Parameter(ParameterSetName='SessionWMI',Mandatory=$true,Position=0)] [Parameter(ParameterSetName='SessionWinRM',Mandatory=$true,Position=0)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Runspaces.PSSession[]] $Session=$Null, [Parameter(ParameterSetName='CredentialWinRMComputerList',Mandatory=$false)] [Parameter(ParameterSetName='CredentialWMIComputerList',Mandatory=$false)] [Parameter(ParameterSetName='CredentialWinRMComputerName',Mandatory=$false)] [Parameter(ParameterSetName='CredentialWMIComputerName',Mandatory=$false)] [System.Management.Automation.PSCredential] $Credential=$Null, [Parameter(ParameterSetName='ExternComputerList')] [Parameter(ParameterSetName='ExternComputerName')] [ValidateNotNullOrEmpty ()] [Switch] $UseExternal, [Parameter(ParameterSetName='ExternComputerList',Mandatory=$false)] [Parameter(ParameterSetName='ExternComputerName',Mandatory=$false)] [string] $BinPath = $(Join-Path -Path $ModuleRoot -ChildPath "\bin"), [Parameter(ParameterSetName='CredentialWinRMComputerList')] [Parameter(ParameterSetName='CredentialWinRMComputerName')] [Parameter(ParameterSetName='SessionWinRM')] [Switch] $UseWinRM, [Parameter(ParameterSetName='CredentialWMIComputerList')] [Parameter(ParameterSetName='CredentialWMIComputerName')] [Parameter(ParameterSetName='SessionWMI')] [ValidateNotNullOrEmpty ()] [Switch] $UseWMI, [Parameter(ParameterSetName='ExternComputerList')] [Parameter(ParameterSetName='ExternComputerName')] [switch] $NoRemoteRegistry, [string] $FilePathStatic, [string] $FilePathVariable, [string] $FileName, [Switch] $NoRegex ) Write-Verbose "Entering $($MyInvocation.MyCommand)" $returnobject = @() $Function = $MyInvocation.MyCommand $Method = $PSCmdlet.ParameterSetName $Arguments = "StaticFilePath: $FilePathStatic, VariableFilePath: $FilePathVariable, FileName: $FileName" if ($PSBoundParameters.ContainsKey('whatif') -and $PSBoundParameters['whatif']) { $WhatIfPassed = $true } else { $WhatIfPassed = $false } Write-Verbose "whatif: $WhatIfPassed" if ($PSBoundParameters.ContainsKey('NoRemoteRegistry') -and $PSBoundParameters['NoRemoteRegistry']) { $NoRemoteRegistry = $true } else { $NoRemoteRegistry = $false } Write-Verbose "NoRemoteRegistry $NoRemoteRegistry" if (!$FilePathStatic) { $Reason = 'You have to provide a file path (-FilePathStatic)' $Status = "fail" $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments } elseif (!$FileName) { $Reason = 'You have to provide a filename (-FileName)' $Status = "fail" $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments } else { $targets = Get-Target -ComputerList:$(if ($ComputerList){$ComputerList}) -ComputerName:$(if ($ComputerName){$ComputerName}) foreach ($target in $targets) { # todo fixme check online stastus of target # fixme $variable script local -> prefix use $ret = "" if ($UseWMI) { # use find file first -> regex search if ($pscmdlet.ShouldProcess($target, "Find file `"$FileName`"")) { # check if regex is used $Drive = $FilePathStatic[0..1]-join'' $FilePathStaticInput = $FilePathStatic $FilePathStatic = $FilePathStatic[2..($FilePathStatic.length-1)]-join'' $FilePathStatic = $FilePathStatic.replace("\", "\\") #$VariableFilePath = $VariableFilePath.replace("\", "\\") #$VariableFileName = $VariableFileName.replace("\", "\\") #$StaticFileName = $StaticFileName.replace("\", "\\") # todo fix fixme ComputerName and Credential add below to wmi # $ret = Get-WmiObject -Class CIM_Datafile -Filter $Filter -Credential $Credential -ComputerName $target -ea Stop # todo fixme allow to use StaticFilePath and VariableFileName if (!$NoRegex) { # read directories without regex $Filter = "Drive = '{0}' AND Path = '{1}'" -f $Drive, $FilePathStatic Write-Verbose $Filter try { #$dirs = (Get-WmiObject Win32_Directory -filter $Filter).getrelated('win32_directory') $dirs = (Get-WmiObject Win32_Directory -filter $Filter).getrelated() # todo use this approach multiple times and build the dir tree } catch { #no subdirs Write-Verbose "no directories match" } if ($dirs) { Write-Verbose "found subdirs for given static directory" $DirMatches = ($dirs | select -unique ) $DirMatches = $DirMatches | ? { !$_.path.startswith('\') } try { $DirMatches = $DirMatches | ? { $_.path[($StaticFilePathInput.length - 1)..($_.path.length - 1)]-join'' -match $VariableFilePath } } catch { Write-Verbose "Error in regex match - match filepath" } } if ($DirMatches) { Write-Verbose "variable dir matches found" foreach ($dir in $DirMatches) { Write-Verbose "dir: $($dir.path)" $dir = $dir.path[2..($dir.path.length-1)]-join'' if (!$dir.endswith('\')) { $dir += '\' } $dirtmp = $dir $dir = $dir.replace("\", "\\") # StaticFileName vs VariableFileName handling different #$VariableFileName = ([Management.Automation.WildcardPattern]$VariableFileName).ToWql() #$Filter = "drive = '$Drive' AND path = '$dir' AND filename LIKE '$VariableFileName'" $Filter = "drive = '$Drive' AND path = '$dir'" Write-Verbose $Filter $files = Get-WmiObject CIM_Datafile -filter $Filter Write-Verbose "names" #$files.name if ($files) { #$_.path[($StaticFilePathInput.length - 1)..($_.path.length - 1)]-join'' -match $VariableFilePath } try { $filesfound = $files | ? { $_.name[($dirtmp.length - 1)..($_.name.length - 1)]-join'' -match $VariableFileName } if ($filesfound) { Write-Verbose "matched filenames" } #$filesfound.name $ret = $filesfound } catch { Write-Verbose "error in regex - filename matching" } } } #dirmatches VariableFilePath } else { Write-Verbose "no matches for path" } } # Regex search else { # no regex search #$Filter = "drive = '$Drive' AND path = '$StaticFilePath' AND filename = '$StaticFileName'" $Filter = "name = '$Drive$StaticFilePath'" Write-Verbose $Filter #Write-Verbose $Filter $ret = Get-WmiObject CIM_Datafile -filter $Filter # } if ($ret) { Write-Verbose "File found: $($ret.name)" $Status = "pass" $Reason = "$($ret.name -join " ; ")" } else { Write-Verbose "file not found" $Status = "fail" $Reason = "File not found" } } # https://www.petri.com/using-powershell-and-wmi-to-find-folders-by-file-type } #wmi elseif ($UseWinRM) { $Status = "fail" $Reason = "method not implemented yet" } elseif ($UseExternal) { Write-Verbose "Using ExternalTools / Sysinternals" Write-Verbose "BinPath: $BinPath" throw "not implemented" if (!(Test-Path -Path "$BinPath\psexec.exe")) { $Status = "fail" $Reason = "Binary psexec not found." } else { # RemoteRegistry is needed for PsExec try { if (!$NoRemoteRegistry) { # Enable RemoteRegistry for psexec $err = Enable-RemoteRegistry -method external -ComputerName $target $returnobject += $err $srr = Start-Service -ComputerName $target -method external -Name "RemoteRegistry" $returnobject += $srr $RemoteRegistryStarted = ($srr.status -match "pass") } else { # assume RemoteRegistry is already started $RemoteRegistryStarted = $true } } catch { Write-Verbose "Error while enabling and starting RemoteRegistry" $Reason = "Error while enabling RemoteRegistry" $Status = "fail" } if ($pscmdlet.ShouldProcess($target, "$Function `"$FilePath\$FileName`"")) { if ($RemoteRegistryStarted) { # Output of schtasks is written in OS language # therefore we use XML output which is not written in OS language $pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = "$BinPath\psexec.exe" $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false # todo #$pinfo.Arguments = "\\$target -accepteula -nobanner cmd /C dir `"$FilePath\$FileName`"" $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null # todo fix timeout $retP = $p.WaitForExit(2000) $stdout = $p.StandardOutput.ReadToEnd() $stderr = $p.StandardError.ReadToEnd() if ($p.ExitCode -eq 0) { Write-Verbose "Successfully executed PsExec on $target" } else { $Status = "fail" $Reason = "Error while running PsExec on $target" Write-Verbose "stdout" Write-Verbose $stdout Write-Verbose "stderr" Write-Verbose $stderr } if ($stdout -and ($p.ExitCode -eq 0)) { # valid response - response from PsExec $Status = "todo" $Reason = "not implemented yet" } } #RemoteRegistryStarted = true else { $Status = "fail" $Reason = "Error while enabling RemoteRegistry" } } #whatif else { $Status = "pass" $Reason = "Not executed - started with -WhatIf" } try { if (!$NoRemoteRegistry -and $RemoteRegistryStarted -or $WhatIfPassed) { Write-Verbose "Cleanup RemoteRegistry" $srr = Stop-Service -ComputerName $target -method external -Name "RemoteRegistry" $returnobject += $srr $drr = Disable-RemoteRegistry -method external -ComputerName $target $returnobject += $drr } } catch { Write-Verbose "Error while disabling RemoteRegistry" $Reason = "Error while disabling RemoteRegistry" $Status = "fail" } } #binary found } #UseExternal $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments -ComputerName $target } #foreach target } #parameters are correct, process targets if (!$WhatIfPassed) { $returnobject } Write-Verbose "Leaving $($MyInvocation.MyCommand)" } #Remove-File # todo Invoke-WmiMethod -Path "CIM_DataFile.Name='C:\scripts\test.txt'" -Name Rename -ArgumentList "C:\scripts\test_bu.txt" Function Remove-File() { [CmdletBinding(SupportsShouldProcess=$True,DefaultParameterSetName='ExternComputerName')] param ( [Parameter(ParameterSetName='CredentialWinRMComputerName',Position=0)] [Parameter(ParameterSetName='CredentialWMIComputerName',Position=0)] [Parameter(ParameterSetName='ExternComputerName', Position=0)] [ValidateNotNullOrEmpty ()] [string[]] $ComputerName="empty", [Parameter(ParameterSetName='CredentialWinRMComputerList',Mandatory=$true)] [Parameter(ParameterSetName='CredentialWMIComputerList',Mandatory=$true)] [Parameter(ParameterSetName='ExternComputerList', Mandatory=$true)] [ValidateNotNullOrEmpty ()] [string] $ComputerList = "empty", [Parameter(ParameterSetName='SessionWMI',Mandatory=$true,Position=0)] [Parameter(ParameterSetName='SessionWinRM',Mandatory=$true,Position=0)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Runspaces.PSSession[]] $Session=$Null, [Parameter(ParameterSetName='CredentialWinRMComputerList',Mandatory=$false)] [Parameter(ParameterSetName='CredentialWMIComputerList',Mandatory=$false)] [Parameter(ParameterSetName='CredentialWinRMComputerName',Mandatory=$false)] [Parameter(ParameterSetName='CredentialWMIComputerName',Mandatory=$false)] [System.Management.Automation.PSCredential] $Credential=$Null, [Parameter(ParameterSetName='ExternComputerList')] [Parameter(ParameterSetName='ExternComputerName')] [ValidateNotNullOrEmpty ()] [Switch] $UseExternal, [Parameter(ParameterSetName='ExternComputerList',Mandatory=$false)] [Parameter(ParameterSetName='ExternComputerName',Mandatory=$false)] [string] $BinPath = $(Join-Path -Path $ModuleRoot -ChildPath "\bin"), [Parameter(ParameterSetName='CredentialWinRMComputerList')] [Parameter(ParameterSetName='CredentialWinRMComputerName')] [Parameter(ParameterSetName='SessionWinRM')] [Switch] $UseWinRM, [Parameter(ParameterSetName='CredentialWMIComputerList')] [Parameter(ParameterSetName='CredentialWMIComputerName')] [Parameter(ParameterSetName='SessionWMI')] [ValidateNotNullOrEmpty ()] [Switch] $UseWMI, [Parameter(ParameterSetName='ExternComputerList')] [Parameter(ParameterSetName='ExternComputerName')] [switch] $NoRemoteRegistry, [string] $Name ) Write-Verbose "Entering $($MyInvocation.MyCommand)" throw "not implemented" $returnobject = @() $Function = $MyInvocation.MyCommand Write-Verbose "Entering $Function" $Arguments = "Filepath $FileName" Write-Verbose "Arguments $Arguments" if ($PSBoundParameters.ContainsKey('whatif') -and $PSBoundParameters['whatif']) { $WhatIfPassed = $true } else { $WhatIfPassed = $false } Write-Verbose "whatif: $WhatIfPassed" if ($PSBoundParameters.ContainsKey('NoRemoteRegistry') -and $PSBoundParameters['NoRemoteRegistry']) { $NoRemoteRegistry = $true } else { $NoRemoteRegistry = $false } Write-Verbose "NoRemoteRegistry $NoRemoteRegistry" if (!$FileName) { $Reason = 'You have to provide a file path (-FileName)' $Status = "fail" $returnobject += New-PowerSponseObject -Function $Action -Status $Status -Reason $Reason -Arguments $Arguments } else { $targets = Get-Target $PSCmdlet.ParameterSetName $ComputerName $ComputerList [regex]$RegexWildcards = "\*|%|_|\?" foreach ($target in $targets) { if ($UseWMI) { # use find file first -> regex search if ($pscmdlet.ShouldProcess($target, "Find file `"$FileName`"")) { $Filter = "Name = '$("$FileName".replace("\", "\\"))'" # check if regex is used if ($Filter -match $RegexWildcards) { $Filter = ($Filter -replace "="," LIKE ") $Filter = ([Management.Automation.WildcardPattern]$Filter).ToWql() Write-Verbose `"$Filter`" } $ret = Get-WmiObject -Class CIM_Datafile -Filter $Filter -Credential $Credential -ComputerName $target -ea Stop if ($ret) { Write-Verbose "File found: $($ret.name)" } else { Write-Verbose "file not found" } } # https://www.petri.com/using-powershell-and-wmi-to-find-folders-by-file-type if ($pscmdlet.ShouldProcess($target, "Remove file `"$FileName`"")) { #enable #$del = $ret.Delete() if($del -and $del.returnvalue -eq 0) { $Status = "pass" $Reason = "File removed" } elseif($del -and $del.returnvalue -eq 2) { $Status = "fail" $Reason = "Access denied" } else { $Status = "fail" $Reason = "File not removed" } } } #wmi elseif ($UseWinRM) { $Status = "fail" $Reason = "method not implemented yet" } elseif ($UseExternal) { Write-Verbose "Using ExternalTools / Sysinternals" Write-Verbose "BinPath: $BinPath" if (!(Test-Path -Path "$BinPath\psexec.exe")) { $Status = "fail" $Reason = "Binary psexec not found." } else { # RemoteRegistry is needed for PsExec try { if (!$NoRemoteRegistry) { # Enable RemoteRegistry for psexec $err = Enable-RemoteRegistry -method external -ComputerName $target $returnobject += $err $srr = Start-Service -ComputerName $target -method external -Name "RemoteRegistry" $returnobject += $srr $RemoteRegistryStarted = ($srr.status -match "pass") } else { # assume RemoteRegistry is already started $RemoteRegistryStarted = $true } } catch { Write-Verbose "Error while enabling and starting RemoteRegistry" $Reason = "Error while enabling RemoteRegistry" $Status = "fail" } if ($pscmdlet.ShouldProcess($target, "$Action `"$FilePath\$FileName`"")) { if ($RemoteRegistryStarted -or $WhatIfPassed) { # Output of schtasks is written in OS language # therefore we use XML output which is not written in OS language $pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = "$BinPath\psexec.exe" $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false # todo #$pinfo.Arguments = "\\$target -accepteula -nobanner cmd /C rm `"$FilePath\$FileName`"" $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null # todo fix timeout $retP = $p.WaitForExit(2000) $stdout = $p.StandardOutput.ReadToEnd() $stderr = $p.StandardError.ReadToEnd() if ($p.ExitCode -eq 0) { Write-Verbose "Successfully executed PsExec on $target" } else { $Status = "fail" $Reason = "Error while running PsExec on $target" Write-Verbose "stdout" Write-Verbose $stdout Write-Verbose "stderr" Write-Verbose $stderr } if ($stdout -and ($p.ExitCode -eq 0)) { # valid response - response from PsExec $Status = "todo" $Reason = "not implemented yet" } } #RemoteRegistryStarted = true else { $Status = "fail" $Reason = "Error while enabling RemoteRegistry" } } #whatif else { $Status = "pass" $Reason = "Not executed - started with -WhatIf" } try { if (!$NoRemoteRegistry -and $RemoteRegistryStarted -or $WhatIfPassed) { Write-Verbose "Cleanup RemoteRegistry" $srr = Stop-Service -ComputerName $target -method external -Name "RemoteRegistry" $returnobject += $srr $drr = Disable-RemoteRegistry -method external -ComputerName $target $returnobject += $drr } } catch { Write-Verbose "Error while disabling RemoteRegistry" $Reason = "Error while disabling RemoteRegistry" $Status = "fail" } } #binary found } #UseExternal $returnobject += New-PowerSponseObject -Function $Action -Status $Status -Reason $Reason -Arguments $Arguments -ComputerName $target } #foreach target } #parameters are correct, process targets if (!$WhatIfPassed) { $returnobject } Write-Verbose "Leaving $($MyInvocation.MyCommand)" } #Remove-File Function Rename-File() { } # todo see Find-File Function Find-Directory() { } Function Rename-Directory() { } Function Remove-Directory() { } function Get-FileHandle() { [CmdletBinding(SupportsShouldProcess=$True,DefaultParameterSetName='ByProcessName')] param ( [Parameter(ParameterSetName='ByProcessPid')] [Parameter(ParameterSetName='ByProcessName')] [string[]] $ComputerName, [Parameter(ParameterSetName='ByProcessPid')] [Parameter(ParameterSetName='ByProcessName')] [string] $ComputerList, [Parameter(ParameterSetName='ByProcessPid')] [Parameter(ParameterSetName='ByProcessName')] [ValidateSet("external")] [string] $Method = "external", [Parameter(ParameterSetName='ByProcessPid')] [Parameter(ParameterSetName='ByProcessName')] [string] $BinPath = $(Join-Path -Path $ModuleRoot -ChildPath "\bin"), [Parameter(ParameterSetName='ByProcessPid')] [Parameter(ParameterSetName='ByProcessName')] [System.Management.Automation.Runspaces.PSSession[]] $Session=$Null, [Parameter(ParameterSetName='ByProcessPid')] [Parameter(ParameterSetName='ByProcessName')] [System.Management.Automation.PSCredential] $Credential=$Null, [boolean] $OnlineCheck = $true, [Parameter(ParameterSetName='ByProcessName')] [string] $ProcessName, [Parameter(ParameterSetName='ByProcessPid')] [int] $ProcessPid, [Switch] $HandlesByProcessName ) $Action = $MyInvocation.MyCommand Write-Verbose "Entering $Action" $returnobject = @() $Arguments = "ProcessName: $ProcessName, ProcessPid: $ProcessPid" Write-Progress -Activity "Running $Action" -Status "Initializing..." if (!$ProcessName -and !$ProcessPid) { $Status = "fail" $Reason = "specify ProcessName or ProcessPid" $returnobject += New-PowerSponseObject -Function $Action -Status $Status -Reason $Reason -Arguments $Arguments } # if all parameters are correctly supplied else { # build target list based on parameters $targets = Get-Target -ComputerList:$(if ($ComputerList){$ComputerList}) -ComputerName:$(if ($ComputerName){$ComputerName}) # process every target foreach ($target in $targets) { Write-Progress -Activity "Running $Action" -Status "Checking connection to $target..." if ($OnlineCheck -and !(Test-Connection $target -Quiet -Count 1)) { Write-Verbose "$target is offline" $Status = "fail" $Reason = "offline" $returnobject += New-PowerSponseObject -Function $Action -Status $Status -Reason $Reason -Arguments $Arguments -ComputerName $target } else { if (($target -eq "localhost") -and $Credential) { $Status = "fail" $Reason = "localhost and WMI and credential not working" $returnobject += New-PowerSponseObject -Function $Action -Status $Status -Reason $Reason -Arguments $Arguments -ComputerName $target Continue } # todo # enable RemoteRegistry # start RemoteRegistry if ($ProcessName) { $ret = Get-Process -ProcessName:$ProcessName -OnlyProcessName:$(if ($HandlesByProcessName){$HandlesByProcessName}) -OnlyPid:$(if (!$HandlesByProcessName) {!$HandlesByProcessName}) -ComputerName $target -Credential $Credential -OnlineCheck:$false | select -ExpandProperty reason } else { $ret = Get-Process -ProcessPid:$ProcessPid -OnlyProcessName:$(if ($HandlesByProcessName){$HandlesByProcessName}) -OnlyPid:$(if (!$HandlesByProcessName) {!$HandlesByProcessName}) -ComputerName $target -Credential $Credential -OnlineCheck:$false | select -ExpandProperty reason } if ($ret) { foreach ($pid in $ret) { Write-Progress -Activity "Running $Action" -Status "Collecting handles for $pid on $target..." Write-Verbose "Processing Pid $pid" if ($target -match "localhost") { $handles = Start-Process handle.exe -CommandLine "-nobanner -accepteula -p $pid" } else { $handles = Start-Process handle.exe -CommandLine "-nobanner -accepteula \\$target -p $pid" } if ($handles.ExitCode -eq 0) { $status = "pass" $reason = $handles.stdout } else { $status = "fail" $reason = $handles.stderr } } } else { $Status = "fail" $Reason = "error running handle.exe on $target" } $returnobject += New-PowerSponseObject -Function $Action -Status $Status -Reason $Reason -Arguments $Arguments -ComputerName $target # todo # stop RemoteRegistry # disable RemoteRegistry } } } $returnobject Write-Verbose "Leaving $($MyInvocation.MyCommand)" } |