Powershell: How to find out which running services aren't part of OS and non-MS - powershell

Is there a way to find out using Powershell which running services are non-native to Windows ? In other words, services that aren't part Windows OS and are non-Microsoft. I want to find out all the services that were installed by our vendors.
Finding out running services is easy:
Get-Service | Where-Object {$_.Status -eq "Running"}

You can't do with it Get-Service alone, because even though the service-info objects it outputs have a .BinaryPathName property, that property is available in PowerShell Core only and it is not necessarily the true service binary, which is often a DLL hosted by the generic svchost.exe service host.
To find the binary file path in all cases, you must (also) query the service definitions in the registry. Once you have the file path, you can use Get-Item and the .VersionInfo property of the file-info objects returned to extract information such as the product and company name.
The Get-ServiceFileInfo function at the bottom does just that; it allows you to run commands such as:
# Return information about all services that aren't part of Windows.
# (May still include Microsoft services).
Get-ServiceFileInfo | Where ProductName -ne 'Microsoft® Windows® Operating System'
Get-ServiceFileInfo source code (PSv5+, but could be adapted to work with lower versions):
# Note: While it is possible to run without elevated privileges,
# not all file information is retrievable then.
#requires -runAsAdministrator
function Get-ServiceFileInfo {
Set-StrictMode -Version 1
Get-Service | ForEach-Object {
# PowerShell Core only:
# Get the service binary path, which may or may not be the true service
# executable.
$binaryPath = $_.BinaryPathName
# Windows PowerShell:
# We fall back on trying to obtain the "ImagePath" value from the registry.
# Note: Even in PowerShell Core there appear to be services that Get-Service fails
# to query even when running as admin, such as "SshdBroker"
# (a non-terminating error is issued).
# Reading from the registry is needed in that case too,
# but, only succeeds when running as admin.
if (-not $binaryPath) {
$binaryPath = try { Get-ItemPropertyValue -EA Ignore "HKLM:\SYSTEM\CurrentControlSet\Services\$($_.Name)" ImagePath } catch { }
}
# Test for svchost.exe, which indicates the need to look for the service-specific DLL path elsewhere.
if ($binaryPath -like '*\svchost.exe *') {
# Get the actual binary (DLL) from the registry, subkey "Parameters", value "ServiceDLL"
# NOTE: Some services exist in *2* (multiple?) incarnations, as "<name>"" and "<name>_<num>"
# Only the "<name>" incarnation has the "ServiceDLL" value, so we fall back on that.
foreach ($keyName in $_.Name, ($_.Name -split '_')[0]) {
# NOTE: Most DLL-based services store the "ServiceDLL" value in the "Parameters" subkey, but
# some have it in the service's root key itself.
foreach ($subKeyName in "$keyName\Parameters", $keyName) {
$binaryPath = try { Get-ItemPropertyValue -EA Ignore "HKLM:\SYSTEM\CurrentControlSet\Services\$subKeyName" ServiceDLL } catch { }
if ($binaryPath) { break }
}
}
}
# Sanitize the path:
# * Some values have enclosing "...", so we strip them,
# * others have arguments, so we only take the first token.
$binaryPath = if ($binaryPath.StartsWith('"')) {
($binaryPath -split '"')[1]
} else {
# The path / command line isn't or doesn't start with a double-quoted token, which
# can mean one of two things:
# * It is a command line based on an unquoted executable, possibly with arguments.
# * It is a service DLL path - possibly with spaces in the (expanded) path.
if (Test-Path -LiteralPath $binaryPath -Type Leaf) {
$binaryPath # Value as a whole is a file path
} else {
(-split $binaryPath)[0] # Value is a command line, extract executable
}
}
$FileVersionInfo = if ($binaryPath) { (Get-Item -LiteralPath $binaryPath).VersionInfo }
# Construct the output object.
[pscustomobject] #{
Name = $_.Name
BinaryPath = if ($binaryPath) { $binaryPath } else { '(n/a)'; Write-Error "Failed to determine binary path for service '$($_.Name)'. Try running as admin." }
ProductName = $FileVersionInfo.ProductName
FileDescription = $FileVersionInfo.FileDescription
CompanyName = $FileVersionInfo.CompanyName
}
}
}

Related

While Loop with Break Statement in PowerShell [duplicate]

I am trying to build my own script to check some Windows services (status and start mode) and I am facing an issue on the IF ...
For example even if the service is "Running", it will never run the code inside the IF...
let me share my code below (I am a newbie on powershell so be gentle xD)
For info, I will do more actions inside the IF and ELSE, it is just for the example.
# import computers list, 1 by line
$Computers = get-content .\computers.txt
# define variable of services we want to check
$ServiceNetbios = "netbt"
# define variable to ask credentials
$Cred = Get-Credential
# declare Function to open a session a remote computer
Function EndPSS { Get-PSSession | Remove-PSSession }
EndPSS
########################################################
# BEGINNING OF SCRIPT #
# by xxx #
# 2022-02-03 #
########################################################
# loop for each computer imported from the file
foreach ($computer in $computers) {
# show name of computer in progress
$computer
# connect remotely to the computer
$session = New-PSSession -ComputerName $computer -Credential $Cred
# check Netbios service
$StatusServiceNetbios = Invoke-Command -Session $session -ScriptBlock { Get-Service -Name $Using:ServiceNetbios | select -property * }
# Check Netbios service started or not
write-host $StatusServiceNetbios.Status
if ($StatusServiceNetbios.Status -eq 'Running')
{
Write-host "IF Running"
}
else
{
write-host "IF NOT Running"
}
EndPSS
}
and what return my script :
computername
Running (<= the variable $StatusServiceNetbios.Status )
IF NOT Running (<= the ELSE action)
Thanks you in advance for your help,
this drive me crazy and maybe this is very simple...
To complement Cpt.Whale's helpful answer, this is likely to be caused by the serialization and deserialization done by Invoke-Command:
using namespace System.Management.Automation
$service = Get-Service netbt
$afterInvokeCmd = [PSSerializer]::Deserialize(([PSSerializer]::Serialize($service)))
$service.Status -eq 'Running' # => True
$afterInvokeCmd.Status -eq 'Running' # => False
$afterInvokeCmd.Status.Value -eq 'Running' # => True
$afterInvokeCmd.Status.ToString() -eq 'Running' # => True
To put some context to my answer, this is a nice quote from about_Remote_Output that can better explain why and what is happening:
Because most live Microsoft .NET Framework objects (such as the objects that PowerShell cmdlets return) cannot be transmitted over the network, the live objects are "serialized". In other words, the live objects are converted into XML representations of the object and its properties. Then, the XML-based serialized object is transmitted across the network.
On the local computer, PowerShell receives the XML-based serialized object and "deserializes" it by converting the XML-based object into a standard .NET Framework object.
However, the deserialized object is not a live object. It is a snapshot of the object at the time that it was serialized, and it includes properties but no methods.
This is probably because of the way powershell creates service objects - (Get-Service netbt).Status has a child property named Value:
$StatusServiceNetbios.Status
Value
-----
Running
# so Status is never -eq to 'Running':
$StatusServiceNetbios.Status -eq 'Running'
False
# use the Value property in your If statement instead:
$StatusServiceNetbios.Status.Value -eq 'Running'
True

How to load variables automatically in PowerShell for every session? [duplicate]

On Windows, not counting ISE or x86, there are four (4) profile scripts.
AllUsersAllHosts # C:\Program Files\PowerShell\6\profile.ps1
AllUsersCurrentHost # C:\Program Files\PowerShell\6\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts # C:\Users\lit\Documents\PowerShell\profile.ps1
CurrentUserCurrentHost # C:\Users\lit\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
On Linux with pwsh 6.2.0 I can find only two locations.
CurrentUserAllHosts # ~/.config/powershell/Microsoft.PowerShell_profile.ps1
CurrentUserCurrentHost # ~/.config/powershell/profile.ps1
Are there any "AllUsers" profile scripts on Linux? If so, where are they?
tl;dr (also applies to Windows):
The conceptual about_Profiles help topic describes PowerShell's profiles (initialization files).
The automatic $PROFILE variable contains a string that is the path of the initialization file for the current user and the current PowerShell host environment (typically, the terminal a.k.a console).
Additional profile files are defined - along the dimensions of (a) all-users vs. current-user and (b) all host environments vs. the current one - which are exposed via properties that the $PROFILE string variable is decorated with, which makes them nontrivial to discover - see below.
None of the profile files exist by default, and in some case even their parent directories may not; the bottom section of this answer shows programmatic on-demand creation and updating of the $PROFILE file.
Olaf provided the crucial pointer in comment:
$PROFILE | select * # short for: $profile | Select-Object -Property *
shows all profile file locations, whether or not the individual profile files exist.
E.g., on my Ubuntu machine with PowerShell installed in /home/jdoe/.powershell, I get:
AllUsersAllHosts : /home/jdoe/.powershell/profile.ps1
AllUsersCurrentHost : /home/jdoe/.powershell/Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts : /home/jdoe/.config/powershell/profile.ps1
CurrentUserCurrentHost : /home/jdoe/.config/powershell/Microsoft.PowerShell_profile.ps1
Length : 62
Note the presence of the [string] type's native Length property, which you could omit if you used $PROFILE | select *host* instead.
That you can get the profile locations that way is not obvious, given that $PROFILE is a string variable (type [string]).
PowerShell decorates that [string] instance with NoteProperty members reflecting all profile locations, which is why select (Select-Object) is able to extract them.
Outputting just $PROFILE - i.e. the string value - yields /home/jdoe/.config/powershell/Microsoft.PowerShell_profile.ps1, i.e. the same path as its CurrentUserCurrentHost property, i.e. the path of the user-specific profile file specific to the current PowerShell host environment (typically, the terminal aka console).[1]
You can verify the presence of these properties with reflection as follows, (which reveals their values too):
$PROFILE | Get-Member -Type NoteProperty
This means that you can also use regular property access and tab completion to retrieve individual profile locations; e.g.:
# Use tab-completion to find a specific profile location.
# Expands to .Length first, then cycles through the profile-location properties.
$profile.<tab>
# Open the all-users, all-hosts profiles for editing.
# Note: Only works if the file already exists.
# Also, you must typically run as admin to modify all-user profiles.
Invoke-Item $profile.AllUsersAllHosts
Convenience functions for getting profile locations and opening profiles for editing:
The code below defines:
Get-Profile enumerates profiles, showing their location and whether they exist on a given machine.
Edit-Profile opens profile(s) for editing (use -Force to create them on demand); note that modifying all-user profiles typically requires running as admin.
function Get-Profile {
<#
.SYNOPSIS
Gets the location of PowerShell profile files and shows whether they exist.
#>
[CmdletBinding(PositionalBinding=$false)]
param (
[Parameter(Position=0)]
[ValidateSet('AllUsersAllHosts', 'AllUsersCurrentHost', 'CurrentUserAllHosts', 'CurrentUserCurrentHost')]
[string[]] $Scope
)
if (-not $Scope) {
$Scope = 'AllUsersAllHosts', 'AllUsersCurrentHost', 'CurrentUserAllHosts', 'CurrentUserCurrentHost'
}
foreach ($thisScope in $Scope) {
[pscustomobject] #{
Scope = $thisScope
FilePath = $PROFILE.$thisScope
Exists = (Test-Path -PathType Leaf -LiteralPath $PROFILE.$thisScope)
}
}
}
function Edit-Profile {
<#
.SYNOPSIS
Opens PowerShell profile files for editing. Add -Force to create them on demand.
#>
[CmdletBinding(PositionalBinding=$false, DefaultParameterSetName='Select')]
param (
[Parameter(Position=0, ValueFromPipelineByPropertyName, ParameterSetName='Select')]
[ValidateSet('AllUsersAllHosts', 'AllUsersCurrentHost', 'CurrentUserAllHosts', 'CurrentUserCurrentHost')]
[string[]] $Scope = 'CurrentUserCurrentHost'
,
[Parameter(ParameterSetName='All')]
[switch] $All
,
[switch] $Force
)
begin {
$scopes = New-Object Collections.Generic.List[string]
if ($All) {
$scopes = 'AllUsersAllHosts', 'AllUsersCurrentHost', 'CurrentUserAllHosts', 'CurrentUserCurrentHost'
}
}
process {
if (-not $All) { $scopes.Add($Scope) }
}
end {
$filePaths = foreach ($sc in $scopes) { $PROFILE.$sc }
$extantFilePaths = foreach ($filePath in $filePaths) {
if (-not (Test-Path -LiteralPath $filePath)) {
if ($Force) {
if ((New-Item -Force -Type Directory -Path (Split-Path -LiteralPath $filePath)) -and (New-Item -Force -Type File -Path $filePath)) {
$filePath
}
} else {
Write-Verbose "Skipping nonexistent profile: $filePath"
}
} else {
$filePath
}
}
if ($extantFilePaths.Count) {
Write-Verbose "Opening for editing: $extantFilePaths"
Invoke-Item -LiteralPath $extantFilePaths
} else {
Write-Warning "The implied or specified profile file(s) do not exist yet. To force their creation, pass -Force."
}
}
}
[1] PowerShell considers the current-user, current-host profile the profile of interest, which is why $PROFILE's string value contains that value. Note that in order to decorate a [string] instance with note properties, Add-Member alone is not enough; you must use the following idiom: $decoratedString = $string | Add-Member -PassThru propName propValue - see the Add-Member help topic.

Windows version of Linux's 'which'? [duplicate]

How do I ask PowerShell where something is?
For instance, "which notepad" and it returns the directory where the notepad.exe is run from according to the current paths.
The very first alias I made once I started customizing my profile in PowerShell was 'which'.
New-Alias which get-command
To add this to your profile, type this:
"`nNew-Alias which get-command" | add-content $profile
The `n at the start of the last line is to ensure it will start as a new line.
Here is an actual *nix equivalent, i.e. it gives *nix-style output.
Get-Command <your command> | Select-Object -ExpandProperty Definition
Just replace with whatever you're looking for.
PS C:\> Get-Command notepad.exe | Select-Object -ExpandProperty Definition
C:\Windows\system32\notepad.exe
When you add it to your profile, you will want to use a function rather than an alias because you can't use aliases with pipes:
function which($name)
{
Get-Command $name | Select-Object -ExpandProperty Definition
}
Now, when you reload your profile you can do this:
PS C:\> which notepad
C:\Windows\system32\notepad.exe
I usually just type:
gcm notepad
or
gcm note*
gcm is the default alias for Get-Command.
On my system, gcm note* outputs:
[27] » gcm note*
CommandType Name Definition
----------- ---- ----------
Application notepad.exe C:\WINDOWS\notepad.exe
Application notepad.exe C:\WINDOWS\system32\notepad.exe
Application Notepad2.exe C:\Utils\Notepad2.exe
Application Notepad2.ini C:\Utils\Notepad2.ini
You get the directory and the command that matches what you're looking for.
Try this example:
(Get-Command notepad.exe).Path
My proposition for the Which function:
function which($cmd) { get-command $cmd | % { $_.Path } }
PS C:\> which devcon
C:\local\code\bin\devcon.exe
A quick-and-dirty match to Unix which is
New-Alias which where.exe
But it returns multiple lines if they exist so then it becomes
function which {where.exe command | select -first 1}
I like Get-Command | Format-List, or shorter, using aliases for the two and only for powershell.exe:
gcm powershell | fl
You can find aliases like this:
alias -definition Format-List
Tab completion works with gcm.
To have tab list all options at once:
set-psreadlineoption -editmode emacs
This seems to do what you want (I found it on http://huddledmasses.org/powershell-find-path/):
Function Find-Path($Path, [switch]$All = $false, [Microsoft.PowerShell.Commands.TestPathType]$type = "Any")
## You could comment out the function stuff and use it as a script instead, with this line:
#param($Path, [switch]$All = $false, [Microsoft.PowerShell.Commands.TestPathType]$type = "Any")
if($(Test-Path $Path -Type $type)) {
return $path
} else {
[string[]]$paths = #($pwd);
$paths += "$pwd;$env:path".split(";")
$paths = Join-Path $paths $(Split-Path $Path -leaf) | ? { Test-Path $_ -Type $type }
if($paths.Length -gt 0) {
if($All) {
return $paths;
} else {
return $paths[0]
}
}
}
throw "Couldn't find a matching path of type $type"
}
Set-Alias find Find-Path
Check this PowerShell Which.
The code provided there suggests this:
($Env:Path).Split(";") | Get-ChildItem -filter notepad.exe
Try the where command on Windows 2003 or later (or Windows 2000/XP if you've installed a Resource Kit).
BTW, this received more answers in other questions:
Is there an equivalent of 'which' on Windows?
PowerShell equivalent to Unix which command?
If you want a comamnd that both accepts input from pipeline or as paramater, you should try this:
function which($name) {
if ($name) { $input = $name }
Get-Command $input | Select-Object -ExpandProperty Path
}
copy-paste the command to your profile (notepad $profile).
Examples:
❯ echo clang.exe | which
C:\Program Files\LLVM\bin\clang.exe
❯ which clang.exe
C:\Program Files\LLVM\bin\clang.exe
I have this which advanced function in my PowerShell profile:
function which {
<#
.SYNOPSIS
Identifies the source of a PowerShell command.
.DESCRIPTION
Identifies the source of a PowerShell command. External commands (Applications) are identified by the path to the executable
(which must be in the system PATH); cmdlets and functions are identified as such and the name of the module they are defined in
provided; aliases are expanded and the source of the alias definition is returned.
.INPUTS
No inputs; you cannot pipe data to this function.
.OUTPUTS
.PARAMETER Name
The name of the command to be identified.
.EXAMPLE
PS C:\Users\Smith\Documents> which Get-Command
Get-Command: Cmdlet in module Microsoft.PowerShell.Core
(Identifies type and source of command)
.EXAMPLE
PS C:\Users\Smith\Documents> which notepad
C:\WINDOWS\SYSTEM32\notepad.exe
(Indicates the full path of the executable)
#>
param(
[String]$name
)
$cmd = Get-Command $name
$redirect = $null
switch ($cmd.CommandType) {
"Alias" { "{0}: Alias for ({1})" -f $cmd.Name, (. { which $cmd.Definition } ) }
"Application" { $cmd.Source }
"Cmdlet" { "{0}: {1} {2}" -f $cmd.Name, $cmd.CommandType, (. { if ($cmd.Source.Length) { "in module {0}" -f $cmd.Source} else { "from unspecified source" } } ) }
"Function" { "{0}: {1} {2}" -f $cmd.Name, $cmd.CommandType, (. { if ($cmd.Source.Length) { "in module {0}" -f $cmd.Source} else { "from unspecified source" } } ) }
"Workflow" { "{0}: {1} {2}" -f $cmd.Name, $cmd.CommandType, (. { if ($cmd.Source.Length) { "in module {0}" -f $cmd.Source} else { "from unspecified source" } } ) }
"ExternalScript" { $cmd.Source }
default { $cmd }
}
}
Use:
function Which([string] $cmd) {
$path = (($Env:Path).Split(";") | Select -uniq | Where { $_.Length } | Where { Test-Path $_ } | Get-ChildItem -filter $cmd).FullName
if ($path) { $path.ToString() }
}
# Check if Chocolatey is installed
if (Which('cinst.bat')) {
Write-Host "yes"
} else {
Write-Host "no"
}
Or this version, calling the original where command.
This version also works better, because it is not limited to bat files:
function which([string] $cmd) {
$where = iex $(Join-Path $env:SystemRoot "System32\where.exe $cmd 2>&1")
$first = $($where -split '[\r\n]')
if ($first.getType().BaseType.Name -eq 'Array') {
$first = $first[0]
}
if (Test-Path $first) {
$first
}
}
# Check if Curl is installed
if (which('curl')) {
echo 'yes'
} else {
echo 'no'
}
You can install the which command from https://goprogram.co.uk/software/commands, along with all of the other UNIX commands.
If you have scoop you can install a direct clone of which:
scoop install which
which notepad
There also always the option of using which. there are actually three ways to access which from Windows powershell, the first (not necessarily the best) wsl -e which command (this requires installation of windows subsystem for Linux and a running distro). B. gnuwin32 which is a port of several gnu binaries in .exe format as standle alone bundled lanunchers option three, install msys2 (cross compiler platform) if you go where it installed in /usr/bin you'll find many many gnu utils that are more up-to-date. most of them work as stand alone exe and can be copied from the bin folder to your home drive somewhere amd added to your PATH.
There also always the option of using which. there are actually three ways to access which from Windows powershell
The first, (though not the best) is wsl(windows subsystem for linux)
wsl -e which command
This requires installation of windows subsystem for Linux and a running distro.
Next is gnuwin32 which is a port of several gnu binaries in .exe format as standle alone bundled lanunchers
Third, install msys2 (cross compiler platform) if you go where it installed in /usr/bin you'll find many many gnu utils that are more up-to-date. most of them work as stand alone exe and can be copied from the bin folder to your home drive somewhere amd added to your PATH.

How to register the result of a PowerShell expression in a variable for a DSC?

I am trying to configure an Azure VM with Azure Automation DSC. One of the resources I want to set is DNS on the client workstation with xDnsServerAddress from xNetworking module.
The problem is that it requires an interface alias and interface aliases change on Azure VMs vary depending on deployment (mainly VMs seem to get either Ethernet or Ethernet 2).
I can query the interface name locally using the following cmdlet expression:
$Interface=Get-NetAdapter|Where Name -Like "Ethernet*"|Select-Object -First 1
$InterfaceAlias=$($Interface.Name)
I don't know, however, how to use it inside the DSC.
My DSC configuration is as below (only the relevant part):
Configuration MyDscConfig
{
Import-DscResource -ModuleName xNetworking
# place-1
Node $AllNodes.where{$_.Role -eq "Workstation"}.NodeName
{
# place-2
xDnsServerAddress DnsServerAddressSetToDc1
{
Address = '10.0.0.4'
InterfaceAlias = $InterfaceAlias
AddressFamily = 'IPv4'
Validate = $true
}
}
}
The problem is that if I place the cmdlet expression either in place-1 or place-2 the compilation job fails with:
The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: The term 'Get-NetAdapter' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
I assume it tries to execute Get-NetAdapter on the pull server, but I may be misinterpreting the error message.
How can I register the result of the cmdlet expression on a destination machine and register it in $InterfaceAlias variable for the xDnsServerAddress resource?
You currently cannot perform a query keep the results of the operation and use it to declare the next state (See notes at the end of the answer.)
You can work around this limitation using the documented workaround/solution from xNetworking, which will find an active ethernet adapter named Ethernet1 if it does not, it will find the first active ethernet adapter and make sure it is named Ethernet1. Then, use a resource to set the DSC server address on Ethernet1.
This is investigational, names and parameters are subject to change. The DSC team is investigating a better way to do this.
Configuration SetDns
{
param
(
[string[]]$NodeName = 'localhost'
)
Import-DSCResource -ModuleName xNetworking
Node $NodeName
{
script NetAdapterName
{
GetScript = {
Import-module xNetworking
$getResult = Get-xNetworkAdapterName -Name 'Ethernet1'
return #{
result = $getResult
}
}
TestScript = {
Import-module xNetworking
Test-xNetworkAdapterName -Name 'Ethernet1'
}
SetScript = {
Import-module xNetworking
Set-xNetworkAdapterName -Name 'Ethernet1' -IgnoreMultipleMatchingAdapters
}
}
xDnsServerAddress DnsServerAddress
{
Address = '10.0.0.4'
InterfaceAlias = 'Ethernet1'
AddressFamily = 'IPv4'
DependsOn = #('[Script]NetAdapterName')
}
}
}
Notes:
There is a question in the comments. The summary of the question is if querying turns the declarative paradigm into an imperative paradigm.
Answer:
I don't believe querying turns it into an imperative paradigm, but you
currently cannot perform a query keep the results of the operation and
use it to declare the next state.
This currently forces us into something a little further away from
declarative for the problem that I would like. My personal opinion is that we
should work with what we have and write resources that query and set a
known state. Then, use the known state through the rest of the
configuration (a form a declarative-relative, per your terminology).
The DSC team has this similar UserVoice
suggestion
we are using to track this request. Please upvote it if you think this
is a useful feature.
It seems the DSC Node Configuration (which is a MOF file) must have all the values set at the time of compilation.
As a workaround I decided to use a PowerShell script resource instead of xDnsServerAddress (some values below are hardcoded to match the example in the question):
Script DnsServerAddressSetToDc1
{
GetScript = {
Return #{
Result = [string](get-DnsClientServerAddress -InterfaceAlias (Get-NetAdapter|Where Name -Like "Ethernet*"|Select-Object -First 1).Name -AddressFamily IPv4).ServerAddresses
}
}
TestScript = {
if (([string](get-DnsClientServerAddress -InterfaceAlias (Get-NetAdapter|Where Name -Like "Ethernet*"|Select-Object -First 1).Name -AddressFamily IPv4).ServerAddresses) -eq '10.0.0.4') {
Write-Verbose "DNS server set"
Return $true
} Else {
Write-Verbose "DNS Server not set"
Return $false
}
}
SetScript = {
Set-DnsClientServerAddress `
-InterfaceAlias (Get-NetAdapter|Where Name -Like "Ethernet*"|Select-Object -First 1).Name `
-ServerAddresses 10.0.0.4 `
-Validate `
-ErrorAction Stop
}
}

PowerShell script to check an application that's locking a file?

Using in PowerShell, how can I check if an application is locking a file?
I like to check which process/application is using the file, so that I can close it.
You can do this with the SysInternals tool handle.exe. Try something like this:
PS> $handleOut = handle
PS> foreach ($line in $handleOut) {
if ($line -match '\S+\spid:') {
$exe = $line
}
elseif ($line -match 'C:\\Windows\\Fonts\\segoeui\.ttf') {
"$exe - $line"
}
}
MSASCui.exe pid: 5608 ACME\hillr - 568: File (---) C:\Windows\Fonts\segoeui.ttf
...
This could help you: Use PowerShell to find out which process locks a file. It parses the System.Diagnostics.ProcessModuleCollection Modules property of each process and it looks for the file path of the locked file:
$lockedFile="C:\Windows\System32\wshtcpip.dll"
Get-Process | foreach{$processVar = $_;$_.Modules | foreach{if($_.FileName -eq $lockedFile){$processVar.Name + " PID:" + $processVar.id}}}
You should be able to use the openfiles command from either the regular command line or from PowerShell.
The openfiles built-in tool can be used for file shares or for local files. For local files, you must turn on the tool and restart the machine (again, just for first time use). I believe the command to turn this feature on is:
openfiles /local on
For example (works on Windows Vista x64):
openfiles /query | find "chrome.exe"
That successfully returns file handles associated with Chrome. You can also pass in a file name to see the process currently accessing that file.
You can find a solution using Sysinternal's Handle utility.
I had to modify the code (slightly) to work with PowerShell 2.0:
#/* http://jdhitsolutions.com/blog/powershell/3744/friday-fun-find-file-locking-process-with-powershell/ */
Function Get-LockingProcess {
[cmdletbinding()]
Param(
[Parameter(Position=0, Mandatory=$True,
HelpMessage="What is the path or filename? You can enter a partial name without wildcards")]
[Alias("name")]
[ValidateNotNullorEmpty()]
[string]$Path
)
# Define the path to Handle.exe
# //$Handle = "G:\Sysinternals\handle.exe"
$Handle = "C:\tmp\handle.exe"
# //[regex]$matchPattern = "(?<Name>\w+\.\w+)\s+pid:\s+(?<PID>\b(\d+)\b)\s+type:\s+(?<Type>\w+)\s+\w+:\s+(?<Path>.*)"
# //[regex]$matchPattern = "(?<Name>\w+\.\w+)\s+pid:\s+(?<PID>\d+)\s+type:\s+(?<Type>\w+)\s+\w+:\s+(?<Path>.*)"
# (?m) for multiline matching.
# It must be . (not \.) for user group.
[regex]$matchPattern = "(?m)^(?<Name>\w+\.\w+)\s+pid:\s+(?<PID>\d+)\s+type:\s+(?<Type>\w+)\s+(?<User>.+)\s+\w+:\s+(?<Path>.*)$"
# skip processing banner
$data = &$handle -u $path -nobanner
# join output for multi-line matching
$data = $data -join "`n"
$MyMatches = $matchPattern.Matches( $data )
# //if ($MyMatches.value) {
if ($MyMatches.count) {
$MyMatches | foreach {
[pscustomobject]#{
FullName = $_.groups["Name"].value
Name = $_.groups["Name"].value.split(".")[0]
ID = $_.groups["PID"].value
Type = $_.groups["Type"].value
User = $_.groups["User"].value.trim()
Path = $_.groups["Path"].value
toString = "pid: $($_.groups["PID"].value), user: $($_.groups["User"].value), image: $($_.groups["Name"].value)"
} #hashtable
} #foreach
} #if data
else {
Write-Warning "No matching handles found"
}
} #end function
Example:
PS C:\tmp> . .\Get-LockingProcess.ps1
PS C:\tmp> Get-LockingProcess C:\tmp\foo.txt
Name Value
---- -----
ID 2140
FullName WINWORD.EXE
toString pid: 2140, user: J17\Administrator, image: WINWORD.EXE
Path C:\tmp\foo.txt
Type File
User J17\Administrator
Name WINWORD
PS C:\tmp>
I was looking for a solution to this as well and hit some hiccups.
Didn't want to use an external app
Open Files requires the local ON attribute which meant systems had to be configured to use it before execution.
After extensive searching I found.
https://github.com/pldmgg/misc-powershell/blob/master/MyFunctions/PowerShellCore_Compatible/Get-FileLockProcess.ps1
Thanks to Paul DiMaggio
This seems to be pure powershell and .net / C#
You can find for your path on handle.exe.
I've used PowerShell but you can do with another command line tool.
With administrative privileges:
handle.exe -a | Select-String "<INSERT_PATH_PART>" -context 0,100
Down the lines and search for "Thread: ...", you should see there the name of the process using your path.
Posted a PowerShell module in PsGallery to discover & kill processes that have open handles to a file or folder.
It exposes functions to: 1) find the locking process, and 2) kill the locking process.
The module automatically downloads handle.exe on first usage.
Find-LockingProcess()
Retrieves process information that has a file handle open to the specified path.
Example: Find-LockingProcess -Path $Env:LOCALAPPDATA
Example: Find-LockingProcess -Path $Env:LOCALAPPDATA | Get-Process
Stop-LockingProcess()
Kills all processes that have a file handle open to the specified path.
Example: Stop-LockingProcess -Path $Home\Documents
PsGallery Link: https://www.powershellgallery.com/packages/LockingProcessKiller
To install run:
Install-Module -Name LockingProcessKiller
I like what the command prompt (CMD) has, and it can be used in PowerShell as well:
tasklist /m <dllName>
Just note that you can't enter the full path of the DLL file. Just the name is good enough.
I've seen a nice solution at Locked file detection that uses only PowerShell and .NET framework classes:
function TestFileLock {
## Attempts to open a file and trap the resulting error if the file is already open/locked
param ([string]$filePath )
$filelocked = $false
$fileInfo = New-Object System.IO.FileInfo $filePath
trap {
Set-Variable -name filelocked -value $true -scope 1
continue
}
$fileStream = $fileInfo.Open( [System.IO.FileMode]::OpenOrCreate,[System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None )
if ($fileStream) {
$fileStream.Close()
}
$obj = New-Object Object
$obj | Add-Member Noteproperty FilePath -value $filePath
$obj | Add-Member Noteproperty IsLocked -value $filelocked
$obj
}
If you modify the above function slightly like below it will return True or False
(you will need to execute with full admin rights)
e.g. Usage:
PS> TestFileLock "c:\pagefile.sys"
function TestFileLock {
## Attempts to open a file and trap the resulting error if the file is already open/locked
param ([string]$filePath )
$filelocked = $false
$fileInfo = New-Object System.IO.FileInfo $filePath
trap {
Set-Variable -name Filelocked -value $true -scope 1
continue
}
$fileStream = $fileInfo.Open( [System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None )
if ($fileStream) {
$fileStream.Close()
}
$filelocked
}