How to get all remote computer's usernames via Powershell? - windows

Is there any way to get list of all local user accounts on a remote computer via Powershell?

You can get this with a WMI-query.
function Get-LocalUser ($Computername = $env:COMPUTERNAME) {
Get-WmiObject -Query "Select * from Win32_UserAccount Where LocalAccount = 'True'" -ComputerName $ComputerName |
Select-Object -ExpandProperty Name
}
Get-LocalUser -ComputerName "TestPC1"

Related

How to get Windows Activation status on all PCs in a specific OU in Active Directory using PowerShell?

I am trying to scan computers in a specific OU in my AD to get the activation status of Windows.
I keep getting
Test-Connection : Testing connection to computer 'CN=PCNAME,OU=MY-OU' failed: No such host is known
although when i try to test the command against a single remote PC, it works fine getting the output
I tries getting all hosts using
$Hosts = Get-ADComputer -Filter \* -SearchBase "OU=MY-OU"
and then ran a for loop to test the connection of each host and using
foreach ($PC in $Hosts) {
if (Test-Connection $PC -Count 1) {
$License = Get-CimInstance SoftwareLicensingProduct -Filter "Name like 'Windows%'" -ComputerName $PC |where { $_.PartialProductKey } | select Description, LicenseStatus
$csv = [PSCustomObject]#{
License = $License
Computername = $PC
}
}
}
Write-Output $csv
Currently your $PC value is like this
$PC = "CN=server1,OU=OU1,OU=OU2,OU=OU3,DC=domain,DC=org"
in order to get computer name, split the string like below and use that value for test-connection
$CN = $PC.Split(',')[0].Split('=')[1]
$domainName = "CN=server1,OU=OU1,OU=OU2,OU=OU3,DC=domain,DC=org"
$CN = $domainName.Split(',')[0].Split('=')[1]
$CN
Edited: There are multiple properties in $Hosts, so instead of splitting distinguished name, use Select-object to get dns hostname.
I do not have an environment to test this code.. so please try yourself.
$Hosts = Get-ADComputer -Filter \* -SearchBase "OU=MY-OU" | Select-Object dnsHostName
$csv = foreach ($dnsHostName in $Hosts) {
Write-Output $dnsHostName
if (Test-Connection $dnsHostName -Count 1) {
$License = Get-CimInstance SoftwareLicensingProduct -Filter "Name like 'Windows%'" -ComputerName $dnsHostName | where { $_.PartialProductKey } | select Description, LicenseStatus
[PSCustomObject]#{
License = $License
Computername = $dnsHostName
}
}
}
$csv | Export-csv -Path C:\temp\output.csv -NoTypeInformation

Change Windows service with PowerShell script

I wrote a PowerShell script to change the login user for a service on my remote VMs. It works when I execute it. However, when I send it to my coworkers, the script appears that it ran without errors and they checked it still listed as "local account'.
$account = Read-Host "Please admin account Name"
$password = Read-Host -AsSecureString "Please enter your password"
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
$service = Read-Host "Enter service name"
$computers = Get-Content -Path ".\servers.txt"
foreach ($computer in $computers) {
$svc = gwmi Win32_Service -ComputerName $computer -Filter "name='$service'"
$svc.StopService()
$svc.Change($null,$null,$null,$null,$null,$null,$account,$password,$null,$null,$null)
$svc.StartService()
}
Write-Host $service "is now running as" $account
Read-Host
I would move this block
foreach ($computer in $computers) {
$svc = gwmi Win32_Service -ComputerName $computer -Filter "name='$service'"
$svc.StopService()
$svc.Change($null,$null,$null,$null,$null,$null,$account,$password,$null,$null,$null)
$svc.StartService()
}
into a invoke-command block. Cmd-lets using the -Computer parameter implement the remote actions in a proprietary way, while invoke-command uses WSMAN (-> more standardized way).
Try this:
foreach ($computer in $computers) {
# $computer can be either an IP-address or a FQDN e.g. computer.mydomain
invoke-command -computer $computer -credential (get-credential) -scripblock {
$svc = gwmi Win32_Service -ComputerName $computer -Filter "name='$service'"
$svc.StopService()
$svc.Change($null,$null,$null,$null,$null,$null,$account,$password,$null,$null,$null)
$svc.StartService()
}
}
With that proposal all actions are performed on the remote machine. In contrary to the first attempt. The first attempt "fetches" the remote objects to you local machine (objects are converted), than you locally perform some actions on the converted object (-> changed properties are send back to the remote).
If your computer is not in the same domain as the remote ones, you've to add your remote targets to your local trusted host list. This link describes how to update your trusted hosts list.
You should also check if Powershell remoting is active on your targets, also described in this link. If your target OS is WIN Server 2012 R2 Powershell remoting is active per default.

How will I save my credentials every loop in Powershell

Is there a way in saving my credentials so it will not keep asking every loop? Like, isolating it outside the foreach loop. I tried removing -credentials and add it outside but it gives me an error of unauthorized access. I can't seem to figure it out. Sorry if this question seem to be stupid, I'm new to Powershell.
Import-Module ActiveDirectory
$ServerList = Get-Content "C:\servers.txt"
foreach($ServerName in $ServerList){
"$ServerName"
"=========="
Get-WmiObject -Class Win32_UserAccount -Computer $ServerName -Filter "LocalAccount='True'" -credential CORPORATE\usmenm03adm| Select Name
" "
}
You can define the credential outside the loop.
I edited your script like below
Import-Module ActiveDirectory
$ServerList = Get-Content "C:\servers.txt"
$cred=Get-Credential
foreach($ServerName in $ServerList){
"$ServerName"
"=========="
Get-WmiObject -Class Win32_UserAccount -Computer $ServerName -Filter "LocalAccount='True'" -credential $cred | Select Name
" "
}
Yes, read the credentials once with Get-Credential and store in a variable:
Import-Module ActiveDirectory
$ServerList = Get-Content "C:\servers.txt"
$MyCredential = Get-Credential -UserName "CORPORATE\usmenm03adm" -Message "Enter WMI credentials"
foreach ($ServerName in $ServerList) {
"$ServerName"
"=========="
Get-WmiObject -Class Win32_UserAccount -Computer $ServerName -Filter "LocalAccount='True'" -Credential $MyCredential | Select-Object Name
" "
}

Obtain a list of non-admin file shares from multiple Windows servers

GOAL: Obtain a CSV file with the following information:
Computer Name
Share Name
Share Path
Share Description
for all non-admin (type 0) SMB shares on all servers from a list (txt file).
INITIAL CODE:
param (
[Parameter(Mandatory=$true,Position=0)]
[ValidateNotNullOrEmpty()]
[String]
$path
)
$computers = Get-Content $path
$shareInfo = #()
ForEach ($computer in $computers) {
$shares = gwmi -Computer $computer -Class Win32_Share -filter "Type = 0" | Select Name,Path,Description
$shares | % {
$ShareName = $_.Name
$Props = [ordered]#{
Computer = $computer
ShareName = $_.Name
Path = $shares.Path
Description = $shares.Description
}
}
$ShareInfo += New-Object -TypeName PSObject -Property $Props
}
$shareInfo | Export-CSV -Path .\shares.csv -NoType
CODE OUTPUT:
"Computer","ShareName","Path","Description"
"SERVER1","SHARE1","System.Object[]","System.Object[]"
"SERVER2","SHARE12","System.Object[]","System.Object[]"
"SERVER3","SHARE3","System.Object[]","System.Object[]"
PROBLEM:
While the code provides output for each server, it seems to not include all shares from the servers. Furthermore, the Path and Description fields are not populated with good information.
ADDITIONAL INFO:
The code:
$shares = gwmi -Computer $computer -Class Win32_Share -filter "Type = 0" | Select Name,Path,Description
Produces good information as below:
Name Path Description
---- ---- -----------
print$ C:\WINDOWS\system32\spool\drivers Printer Drivers
Share D:\Share
SHARE2 D:\SHARE2
Software C:\Software The Software
$shares | % {
$ShareName = $_.Name
$Props = [ordered]#{
Computer = $computer
ShareName = $_.Name
Path = $shares.Path
Description = $shares.Description
}
}
You're using $shares instead of $_ for the Path and Description properties, so each of these properties is assigned a list of the values of the respective property of each element of the $shares collection.
Also, why are you building custom objects in the first place when you just need to filter the WMI query results? The computer name can be obtained from the __SERVER (or PSMachineName) property. Plus, type 0 means a shared disk drive, not an administrative share. You need to filter the latter by other criteria (usually description and/or share name).
$filter = "Type = 0 And Description != 'Default Share' And " +
"Name != 'ADMIN$' And Name != 'IPC$'"
$computers |
ForEach-Object { Get-WmiObject -Computer $_ -Class Win32_Share -Filter $filter } |
Select-Object #{n='Computer';e={$_.__SERVER}}, Name, Path, Description |
Export-Csv -Path .\shares.csv -NoType

Check Win32_group membership with powershell

I want to know if a user whom username is delivered is member of a group whom groupname is delivered.
$u = Get-WmiObject -Class Win32_UserAccount -Filter "Name='$username'"
$g = Get-WmiObject -Class Win32_Group -Filter "Name='$groupname'"
So I get two object with the property SID.
How can I check that user $u is member of group $g?
You can do this with an Associators query (example). Which are notoriously slow but do work.
$u = Get-WmiObject -Class Win32_UserAccount -Filter "Name='user'"
$group = Get-WmiObject -Class Win32_Group -Filter "Name='group'" | Select-Object -ExpandProperty Caption
$u | foreach {
$query = “Associators Of {Win32_UserAccount.Domain='” `
+ $_.Domain + “',Name='” + $_.Name `
+ “'} WHERE AssocClass=Win32_GroupUser”
$memberOf = Get-WmiObject -Query $query |
select -ExpandProperty Caption
If($memberOf -contains $group){
Write-Host "$($_.Name) is a member of $group"
} Else {
Write-Host "$($_.Name) is not a member of $group"
}
}
Get the use you are looking for and group your are checking to see if the user is a member of. While u$ should be only one user it is still a collection with one member. Pipe it into a ForEach-Object and build the Associators query. Execute the query and return all the group captions ( domain\groupname). Since $memberof is an array we can use -contains to see if the group you are looking for is there.
Alternatively
You could use the AD cmdlets if you have access to them and run the following
(Get-ADUser $user -Properties memberof | Select-Object -ExpandProperty memberof) -contains (Get-ADGroup -Identity $group)
The above will return True or False. You can install Ad cmdlets by using import-module activedirectory
Continued Testing
OpenLDAP should support this from what I gather and it's much faster then the previous WMI.
$search = [adsisearcher]"(&(objectcategory=user)(Name=userFullName))"
$userLDAP = $search.FindOne().Path
$userMembers = ([ADSI]$userLDAP).memberof
$search = [adsisearcher]"(&(objectcategory=group)(Name=groupname))"
$group = ($search.FindOne().Path) -replace "LDAP://"
$userMembers -contains $group
Sorry as I do not have access to OpenLDAP for testing. Do a search for a user and get the MemberOf as $userMembers. Then get the group into $group. Needed to remove the LDAP prefix from the string. Then just do another -Contains again.

Resources