This seems like a very easy thing to do; however, I don't know anything about WMI. How would I go about determining if a directory is shared over the network on one of my servers (using PowerShell)? Also, is there a quick primer that I can read to get aquainted to WMI? I really want to be able to use it when scripting, but most resources don't show any examples with PowerShell.
Currently, I'm running a check to see if the "Server" service is running; and another check to see if a specific group has permissions on the directory.
$serversrvc = Get-Service -Name "Server"
$dirpath = "C:\My\Path\"
$pathacl = (Get-Acl -path $dirpath).Access
$group = "Domain\User"
# Test if the "Server" service is running
If ($serversrvc.Status -eq "Running")
{
echo '"Server" service is running.'
}
Else
{
echo '"Server" is NOT running.'
}
# Test if the $dirpath exists
If (Test-Path $dirpath)
{
echo "$dirpath exists."
}
Else
{
echo "$dirpath does not exist."
}
# Test if $group has permssions on $dirpath
foreach ($id in $pathacl)
{
If ($id.IdentityReference -eq $group)
{
echo "$group has access to $dirpath"
}
}
The Win32_Share WMI class returns all instances of shared folders on a computer. This will give you all shared folders on the local machine:
Get-WmiObject -Class Win32_Share
Notice the Path property, you can use the Filter parameter (equivalent to the WQL WHERE clause) to get back shares witha certain local path but you'd also need to double each slash (slash is a :
Get-WmiObject -Class Win32_Share -ComputerName PC1 -Filter "Path='C:\\Windows'"
If you get back a result the folder is shared, otherwise it isn't. Notice the ComputerName parameter, you can execute the wmi command against remote computers.
To get back a Boolean result, true if the folder is shared and false otherwise, cast the command to bool:
[bool](Get-WmiObject -Class Win32_Share -ComputerName PC1 -Filter "Path='C:\\Windows'")
You can use Get-WmiObject Win32_Share to enumerate the shares on a computer. You could also use net share but then you'd have to parse the output and why bother with that when you have the WMI output. :-)
Related
I'm brand new to PS scripting, so bear with me :)
I'm trying to create a PS script that will write the Win10 activation code to a file then copy that file to a central repo to then manually activate.
I'm creating a PS script and trying to run
cscript.exe c:\windows\system32\slmgr.vbs -dti >
$SourceDir\$env:computername.txt
$SourceDir = \\computer01\c$\temp
I need to run it from one computer, remotely connecting to every computer on the network, creating the computername.txt file then copying that file back to a central repository for all the files.
What I have so far:
$s1=New-PSSession -ComputerName computer01 -Credential $AdminCred
Test-Connection -ComputerName computer01
$id='\\computer01\windows\system32'
$SourceDir='\\computer01\c$\temp'
md $SourceDir
$GetActID=cscript.exe $id\slmgr.vbs -dti >
$SourceDir\$env:computername.txt
Invoke-Command -Session $s1 -ScriptBlock { $Using:GetActID }
Then I call a batch file that copies the computername.txt file from the computer01 over to a repository where they are going to sit.
I FINALLY got it working correctly except for the name of the file isn't naming it to the computer01, it's naming it with the hostname of the computer I'm running it from, therefore the filenames are identical. I had the naming piece working, but I had to change the way I was remoting into the computer and now it's not naming correctly.
Any idea on how I could get it to name the file to be related to the remote computer?
**I'm still working on the whole piece of the puzzle where it goes back to an excel sheet pulled from AD and pulls the host names from that sheet to connect to each machine, I believe I'll be adding a ForEach syntax in there somehow for that.
Although not sure how you are getting the list of "every computer on the network", chances are you are doing this using
# get a list of all AD computers (their names only)
$computers = (Get-ADComputer -Filter *).Name
Then I think you don't need to have every computer save the file on its own disk and later copy these files to a central share.
Instead, just capture the info in a variable and after the loop write the file to the central share as structured CSV file combining all computernames and install id's so you can open in Excel.
Using the array of computernames from above, iterate through them
$result = $computers | ForEach-Object {
# test if the computer can be reached
if (Test-Connection -ComputerName $_ -Count 1 -Quiet) {
$installId = Invoke-Command -ComputerName $_ -ScriptBlock {
cscript.exe //nologo "$env:SystemRoot\System32\slmgr.vbs" -dti
}
# $installId is returned as array !
# output an object with two properties
[PsCustomObject]#{
Computer = $_
InstallId = $installId[0] -replace '\D' # remove everything non-numeric
}
}
else {
Write-Warning "Computer $_ is not responding"
}
}
# now you can display the result on screen
$result | Format-Table -AutoSize
# or by means of the GridView if you prefer
$result | Out-GridView -Title 'Computer InstallIds'
# and save the results in your central share as structured CSV file
$result | Export-Csv -Path '\\server\share\restofpath\ComputerInstallIds.csv' -NoTypeInformation
You may have to append -Credential $adminCreds to the Invoke-Command call to make sure you have permissions to have each machine run that piece of code in the scriptblock. The easiest way of obtaining that credential is to start off with $adminCreds = Get-Credential -Message "Please enter administrator credentials"
There has to be a better way
$server = (Get-ADComputer -Filter * -Properties *).name
foreach ($s in $server)
{
Get-WmiObject Win32_Service -filter 'STARTNAME LIKE "%serviceaccount%"' -computername $s
}
I want to search all servers on the domain for a service account. The above kind of does what I'm looking for but it doesnt return what server the services account was found on. Thanks in advance.
here's what i meant about using Get-Member to find the object properties that would give you the info you want. [grin]
this could be sped up considerably by giving the G-WO call a list of systems. i wasn't ready to code that just now. lazy ... [blush]
what it does ...
sets the account to look for
i only have the LocalSystem and NetworkService accounts listed on my services. [grin]
sets the computer list to search
you will likely use Get-ADComputer. make sure to either use the property name in the loop OR to make your query return only the actual name value.
i only have one system, so my list is 3 different ways to get to the same computer.
loops thru the systems
call G-WO to get the service[s] that use the target account
builds a [PSCustomObect] with the wanted properties
sends that to the $Result collection
shows that on screen
the code ...
$TargetAccount = 'LocalSystem'
$ComputerList = #(
'LocalHost'
'127.0.0.1'
$env:COMPUTERNAME
)
$Result = foreach ($CL_Item in $ComputerList)
{
# i didn't want a gazillion services, so this uses array notation to grab the 1st item
# if you want all the items, remove the trailing "[0]"
$GWMI_Result = #(Get-WmiObject -Class Win32_Service -Filter "STARTNAME LIKE '%$TargetAccount%'" -ComputerName $CL_Item)[0]
[PSCustomObject]#{
ComputerName = $GWMI_Result.SystemName
AccountName = $GWMI_Result.StartName
ServiceName = $GWMI_Result.Name
}
}
$Result
output ...
ComputerName AccountName ServiceName
------------ ----------- -----------
MySysName LocalSystem AMD External Events Utility
MySysName LocalSystem AMD External Events Utility
MySysName LocalSystem AMD External Events Utility
I have this script that I am trying to run, that I hope will back up DNS zones. I am attempting to export this information into a csv file using the export-csv powershell cmdlet. Finally, I use use the dnscmd.exe command to export zones information into a text file and store them in the defined location.
# Get Name of the server with env variable
$DNSSERVER=get-content env:computername
#—Define folder where to store backup —–#
$BkfFolder=”c:\windows\system32\dns\backup”
#—Define file name where to store Dns Settings
$StrFile=Join-Path $BkfFolder “input.csv”
#—-Check if folder exists. if exists, delete contents–#
if (-not(test-path $BkfFolder)) {
new-item $BkfFolder -Type Directory | Out-Null
} else {
Remove-Item $BkfFolder”\*” -recurse
}
#—- GET DNS SETTINGS USING WMI OBJECT ——–#
#– Line wrapped should be only one line –#
$List = get-WmiObject -ComputerName $DNSSERVER
-Namespace root\MicrosoftDNS -Class MicrosoftDNS_Zone
#—-Export information into input.csv file —#
#– Line wrapped should be only one line –#
$list | Select Name,ZoneType,AllowUpdate,#{Name=”MasterServers”;Expression={$_.MasterServers}},
DsIntegrated | Export-csv $strFile -NoTypeInformation
#— Call Dnscmd.exe to export dns zones
$list | foreach {
$path=”backup\”+$_.name
$cmd=”dnscmd {0} /ZoneExport {1} {2}” -f $DNSSERVER,$_.Name,$path
Invoke-Expression $cmd
}
# End of Script
#——————————————————————————————-#
When I run the script, I get the following message:
I am not exactly sure what this message is saying. I tried inputting my computer name, but that does not work either.
Any help is appreciated!
From line 2:
$DNSSERVER=get-content env:computername
should be:
$DNSSERVER = $Env:Computername
The error is in this line:
$List = get-WmiObject -ComputerName $DNSSERVER -Namespace root\MicrosoftDNS -Class MicrosoftDNS_Zone
Make sure that it's on the same line instead of separate lines, It is requesting the class for the gwmi command, but since it is in another line it's not taking it. Because the class does exist in here so the issue should be in that particular line.
Another point it is looking for the DNS class so only would work if the windows servers have DNS feature or role installed on it.
I am writing a powershell script to be deployed by SCCM via a package. The aim of this is to remove an account with a specific name then write to a file stating if the account exists or not. The code is below:
$Computer = hostname
foreach ($C in $Computer) {
if (Test-Connection $C -Quiet) {
Write-Verbose "$C > Online"
$Users = Get-WMIObject Win32_UserAccount -Filter "LocalAccount=True" -ComputerName $C
if ($Users.Name -contains 'test') {
Add-Content \\SERVERNAME\SHARENAME.$\$computer-found_$(get-date -Format yyyymmdd_hhmmtt).txt "User 'test' found, Disable 'test' found"
net user test /active:no }
else {
Add-Content \\SERVERNAME\SHARENAME.$\$computer-notfound_$(get-date -Format yyyymmdd_hhmmtt).txt "User 'test' not found"
}
}
else {
Write-Verbose "$C > Offline"
}
}
I have also tried replace Write-Verbose with Write-Host and Add-Content with Out-File but the problem I having is that no content / file is created when I use the full network path or share e.g. \\SERVERNAME\SHARENAME.$ the path identified has all the correct permissions and is being ran locally using the System account.
I wanted to see if the issue occured when writing the file locatlly consequently this does not happen when written to C:\Temp\
Does anyone have any ideas on to solve this.
I don't think that local system account has access to a network resource. I'm not sure if you have ever configured it or not. And what the command you used to run the command
Here I post a working way of doing this using Configuration Manager deployment after testing in my lab.
Basically I created a package with source files
and created a task sequence with single "Run Command Line" step.
The reason I use a task sequence is because I want to use an account to access the txt file on the network, which I can configure within a task sequence. I don't think Local System Account have such permission.
The script (DeactivateTest.ps1) I use as below just like what you provided and changed a little on the logic:
$Computer = hostname
foreach ($C in $Computer) {
if (Test-Connection $C -Quiet) {
Write-host "$C > Online"
$Users = Get-WMIObject Win32_UserAccount -Filter "LocalAccount=True" -ComputerName $C
$result=0
Foreach($user in $Users){
if ($User.Name -like '*test*') {
$username = $user.Name
"`n$(get-date -Format yyyymmdd_hhmmtt) User $username found ON $C, Disable 'test'" | Add-Content \\cas\resource\Result.txt
net user $username /active:no
$result+=1
}}
if($result =0){
"`n$(get-date -Format yyyymmdd_hhmmtt) User 'test' not found ON $C" | Add-Content \\cas\resource\Result.txt}
}
else {
"`n$C is Offline" | Add-Content \\cas\resource\Result.txt
}
}
The script query local account and disable accounts which have words "Test" in the name. If you don't like this logic, you can change :).
\\cas\resource\Result.txt is a txt file on the network share. Clients will write result to this txt file.
The command in the task sequence is (it's a x64 machine):
PowerShell.exe -ExecutionPolicy Bypass -File ".\DeactiveTest.ps1"
The output is like:
I may get downvoted for this as my answer isn't technically directly answering your question, it is, however, intended to try and point you in what may be a more logical direction. All apologies if I offend anyone, but here it is:
Why not just disable the user using Group Policy? If you really want to know where the user is/isn't disabled then you could just use hardware inventory for that, but GP really is the best way to enforce this kind of setting.
I am an InfoSec admin with an okay amount of PowerShell experience. I'll keep it short and sweet:
([WMI] "\\$comp\root\CIMV2:CIM_DataFile.Name='$path'").Version)
I use this for calling file versions instead of using get-item VersionInfo.ProductVersion, since this does not always return an accurate value. It works well. However, when $path is equal to something like this:
C:\Windows\System32\Macromed\Flash\Flash*.ocx
The query doesn't work because the file is not found. I imagine this is due to the single quotes around the variable ignoring the wildcard.
I will admit that I did find a work around to my problem here (the answer posted by JPBlanc):
Powershell get-item VersionInfo.ProductVersion incorrect / different than WMI
However, I want to know if it is possible for me to use a wildcard with my existing script.
You can't pass a wildcard directly, but you can query the filesystem with that wildcard and then loop through the results. In both cases here, I'm assuming that you're doing this remotely.
$FlashFiles = invoke-command -computername $comp {Get-ChildItem C:\Windows\System32\Macromed\Flash\Flash*.ocx;};
foreach ($File in $FlashFiles) {
write-output "$($File.Fullname): $(([WMI] "\\$comp\root\CIMV2:CIM_DataFile.Name='$($File.FullName)'").Version)"
}
Or do it with a single pipeline:
invoke-command -computername $comp {Get-ChildItem C:\Windows\System32\Macromed\Flash\Flash*.ocx||foreach-object {write-output "$($_.Fullname): $(([WMI] "\\$comp\root\CIMV2:CIM_DataFile.Name='$($_.FullName)'").Version)"};
You can make the latter even faster by running the WMI query local to the remote computer (you could do it with the first too, but it's not as pretty)
invoke-command -computername $comp {Get-ChildItem C:\Windows\System32\Macromed\Flash\Flash*.ocx|foreach-object {write-output "$($_.Fullname): $(([WMI] "\\.\root\CIMV2:CIM_DataFile.Name='$($_.FullName)'").Version)"}};
The Name property of a CIM_DataFile can't contain wildcards. I don't believe any of them can.
However, you can specify the Drive, Path, and Extension to get a list:
Get-WmiObject -ComputerName $comp -Class CIM_DataFile -Filter "Drive='C:' AND Path='\\Windows\\System32\\Macromed\\Flash\\' AND Extension='ocx'"
The syntax of Path is a bit flaky. You need the trailing backslashes, for example.
You can also pipe to Where-Object for further filtering:
Get-WmiObject -ComputerName $comp -Class CIM_DataFile -Filter "Drive='C:' AND Path='\\Windows\\System32\\Macromed\\Flash\\' AND Extension='ocx'" |`
Where-Object { $_.FileName -like 'Flash*' } |`
ForEach-Object { $_.Name; $_.Version }