PowerShell to find computers in the domain, user is logged on - powershell

I am fairly new to PowerShell. I have this script working for computer names on the domain, thanks to #Adam Bertram https://4sysops.com/archives/how-to-find-a-logged-in-user-remotely-using-powershell/.
I understand it and it works fine on my domain i.e. I can query the computer name and it returns a list of logged users. I have difficulties altering the code so it could return a list of computer names for a given user, instead. I believe the issue could be with the correct Win32 class.
function Get-LoggedOnUser
{
[CmdletBinding()]
param
(
[Parameter()]
[ValidateScript({ Test-Connection -ComputerName $_ -Quiet -Count 1 })]
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($comp in $ComputerName)
{
$output = #{ 'ComputerName' = $comp }
$output.UserName = (Get-WmiObject -Class win32_computersystem -ComputerName $comp).UserName
[PSCustomObject]$output
}
}
thanks
Tomasz

Try this:
function Get-LoggedOnUser
{
[CmdletBinding()]
param
(
[ Parameter() ]
$UserName
)
Get-WmiObject -Class win32_computersystem | Where-Object { $_.Username -like "*$UserName*" } | Select-Object Name
}
Get-LoggedOnUser -UserName "vish"

Related

How do you make a function that only takes strings, take variables in Powershell?

I want to run a function in Powershell called Get-OSArchitecture which tells me whether a computer has a 32bit or 64bit system when you give it a domain name. However, it only accepts strings such as "SALES-DENNY" and not variables with stored strings such as $string1. I've played around with something called Out-String but this function is really stubborn with getting strings and nothing to do with variables.
The following code is for getting the global Get-OSArchitecture function:
function global:Get-OSArchitecture {
#Requires -Version 2.0
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$false,
Position=1,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[String[]]$ComputerName = $env:COMPUTERNAME
)#End Param
Begin
{
Write-Verbose "Retrieving Computer Info . . ."
}
Process
{
$ComputerName | foreach {
$ErrorActionPreference = 0
$Computer = $_
$Windir,$OSArchitecture,$OSVersion = Get-WmiObject -class Win32_OperatingSystem -ComputerName $_ |
foreach {$_.WindowsDirectory,$_.OSArchitecture,$_.Version}
$SysDrive = ($Windir -split ":")[0] + "$"
# $OSVersion[0]
# $OSArchitecture is only suppored on OSVersion -ge 6
# I was going to test for that, however now I just test if $OSArchitecture -eq $True
Write-Verbose "Operating System version on $Computer is: $OSVersion"
if ($OSArchitecture)
{
New-Object PSObject -Property #{
Hostname=$Computer
OSArchitecture=$OSArchitecture
SysDrive=$SysDrive
OSVersion=$OSVersion
WinDir=$WinDir
}
}
else
{
# check the program files directory
write-verbose "System Drive on $Computer is: $SysDrive"
$x64 = "\\$Computer\" + $SysDrive + "\Program Files (x86)"
if (test-path ("\\$Computer\" + $SysDrive))
{
if (test-path $x64)
{
New-Object PSObject -Property #{
Hostname=$Computer
OSArchitecture="64-bit"
SysDrive=$SysDrive
OSVersion=$OSVersion
WinDir=$WinDir
}
}
elseif (!(test-path $x64))
{
New-Object PSObject -Property #{
Hostname=$Computer
OSArchitecture="32-bit"
SysDrive=$SysDrive
OSVersion=$OSVersion
WinDir=$WinDir
}
}
}
else {"Something wrong determining the System Drive"}
}
} | select Hostname,OSArchitecture,SysDrive,WinDir,OSVersion
}#Process
End
{
}#End
}#Get-OSArchitecture
My problem begins below.
$string1 = "SALES-DENNY"
Get-OSArchitecture $string1
The above fails.
The below works.
Get-OSArchitecture "SALES-DENNY"
I expect the function to give out the correct architecture of the computer with the name "SALES-DENNY" but if I don't put it in as a string I always get a blank result.
Although it should not matter if you give the computername as hardcoded string or as a name or IP in a variable, I do believe you could improve the function by not testing the Program Files (x86) directory.
Instead, there are two other WMI functions you can rely on to get the 'bitness' of the OS:
function Get-OSArchitecture {
[CmdletBinding()]
param(
[Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
[string[]]$ComputerName = $env:COMPUTERNAME
)
process {
foreach ($computer in $ComputerName) {
Write-Verbose "Retrieving info for computer '$computer'"
$info = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computer |
Select-Object #{Name = 'HostName'; Expression = { $_.PSComputerName}},
OSArchitecture,
#{Name = 'SysDrive'; Expression = { '{0}$' -f ($_.SystemDrive).Substring(0,1) }},
#{Name = 'WinDir'; Expression = { $_.WindowsDirectory}},
#{Name = 'OSVersion'; Expression = { $_.Version }}
if ($info.OSArchitecture) {
$info.OSArchitecture = '{0}-bit' -f ($info.OSArchitecture -replace '\D+','')
}
else {
$info.OSArchitecture = '{0}-bit' -f (Get-WmiObject -Class Win32_Processor -ComputerName $computer).AddressWidth
# or do:
# $info.OSArchitecture = '{0}-bit' -f (((Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computer).SystemType -replace '\D+', '') -replace '86', '32')
}
# emit info
$info
}
}
}
Hope that helps

Local Admins report not showing domain groups

I'm trying to get the following script to export into a csv all of the local admins members and domain groups on a group of servers. It works fine to export the local admins, but I noticed that it doesn't export domain groups (ie: I have a Domain Admins group that's in the local administrators group and it doesn't show in the csv).
This is the code I have, any help would be appreciated:
param(
[parameter(Position=0,ValueFromPipeline=$true)]
$ComputerName=[Net.Dns]::GetHostName(),
[System.Management.Automation.PSCredential] $Credential,
[UInt32] $BlockSize=50
)
begin {
$WMIEnumOpts = new-object System.Management.EnumerationOptions
$WMIEnumOpts.BlockSize = $BlockSize
function Get-LocalAdminGroupMember {
param(
[String] $computerName,
[System.Management.Automation.PSCredential] $credential
)
$params = #{
"Class" = "Win32_Group"
"ComputerName" = $computerName
"Filter" = "LocalAccount=TRUE and SID='S-1-5-32-544'"
}
if ( $credential ) {
if ( $computerName -eq [Net.Dns]::GetHostName() ) {
Write-Warning "The -Credential parameter is ignored for the current computer."
}
else {
$params.Add("Credential", $credential)
}
}
Get-WmiObject #params | ForEach-Object {
$groupName = $_.Name
$_.GetRelated("Win32_Account","Win32_GroupUser","","",
"PartComponent","GroupComponent",$false,$WMIEnumOpts) | Select-Object `
#{Name="ComputerName"; Expression={$_.__SERVER}},
#{Name="Name"; Expression={$groupName}},
#{Name="Member"; Expression={$_.Caption -replace "^$($_.__SERVER)\\", ""}},
#{Name="Type"; Expression={$_.__CLASS}}
}
}
}
process {
$Filename = PATH HERE
$OutFileName = "C:\temp\admins.csv"
Get-Content $Filename | Foreach-Object {Get-LocalAdminGroupMember -computerName $_ | Select-Object * | Export-csv -NoType $OutFileName -Append}
Ah, the joys of trying to access network resources from a remote computer. You're going to lose anything that's a domain account doing what you're doing. It's jut how it works. The good news is that there's still a way to get the info you want, and you can even use Get-WmiObject to do it if you want. If you have not renamed the Administrators group (because really, who does that?), you can do this easily, but if you did and you have to look for the group by SID like you are above then you'll have to query the remote server like you are, and make adjustments with the query below with the modified name that you get back. Here's what I'd recommend doing, using the Win32_GroupUser class instead:
Get-WmiObject -ComputerName $Server -Query "SELECT * FROM win32_GroupUser WHERE GroupComponent = ""Win32_Group.Domain='$computerName',Name='Administrators'"""
To put it in place of what you have for your function, it could look something like this:
function Get-LocalAdminGroupMember {
param(
[String] $computerName,
[System.Management.Automation.PSCredential] $credential
)
$params = #{
"ComputerName" = $computerName
"Query" = "SELECT * FROM win32_GroupUser WHERE GroupComponent = ""Win32_Group.Domain='$computerName',Name='Administrators'"""
}
if ( $credential ) {
if ( $computerName -eq [Net.Dns]::GetHostName() ) {
Write-Warning "The -Credential parameter is ignored for the current computer."
}
else {
$params.Add("Credential", $credential)
}
}
Get-WmiObject #params |
Where{$_.PartComponent -match ':(.+?)\.Domain="(.+?)",Name="(.+?)"'}|
ForEach{
[PSCustomObject]#{
"ComputerName"=$computerName
"Name"='Administrators'
"Member"=$Matches[2..3] -join '\' -replace "^$computerName\\"
"Type"=$Matches[1]
}
}
}

Options to use different param

I'm looking for a way to to have a choice of a list or a single computername in a foreach loop.
If the user enters in a single computername I want the script to execute for that one computername
but if that user wants to use a path to a list of computers how could I replace $computername with the path that user wants?
function Get-OSInfo {
[CmdletBinding()]
param (
#[Parameter(ValueFromPipeline=$True,
# ValueFromPipelineByPropertyName=$True)]
[string]$computername,
[string]$errorlog = 'c:\errors.txt',
[switch]$logerrors
)
PROCESS {
foreach ($computer in $computername) {
Try {
$os = Get-WmiObject -EA Stop –Class Win32_OperatingSystem –ComputerName $computer
$cs = Get-WmiObject -EA Stop –Class Win32_ComputerSystem –ComputerName $computer
$bios = Get-WmiObject -EA Stop –Class Win32_BIOS –ComputerName $computer
$cpu = Get-WmiObject -EA Stop -class Win32_processor -ComputerName $computer
$props = #{'ComputerName'=$computer;
'OSVersion'=$os.version;
'SPVersion'=$os.servicepackmajorversion;
'OSBuild'=$os.buildnumber;
'OSArchitecture'=$os.osarchitecture;
'Manufacturer'=$cs.manufacturer;
'Model'=$cs.model;
'BIOSSerial'=$bios.serialnumber
'CPU Count'=$CPU.Count
'Memory'= [Math]::round(($cs.TotalPhysicalMemory/1gb),2)
'CPU Speed'= $CPU.MaxClockSpeed[0]}
$obj = New-Object -TypeName PSOBject -Property $props
$obj.PSObject.TypeNames.Insert(0,'Get-OS.OSInfo')
#Write-Output $obj
$obj | Export-Csv c:\test4.csv -Append
} Catch {
if ($logerrors) {
$computer | Out-File $errorlog -append
}
Write-Warning "$computer failed"
}
}
}
}
Change the type of the $ComputerName parameter to a string array instead of just a single string:
param(
[string[]]$ComputerName,
[string]$errorlog = 'c:\errors.txt',
[switch]$logerrors
)
Notice the [] after the type name, this denotes an array of strings, rather than a single string.
Now you can do:
PS C:\> $computers = Get-Content C:\computers.txt
PS C:\> Get-OSInfo -ComputerName $computers
If you'd like to be able to specify a path to a file containing the target computers as the argument to the function, you can use multiple parameter sets:
[CmdletBinding(DefaultParameterSetName='ByName')]
param(
[Parameter(ParameterSetName='ByName',ValueFromPipeline)]
[string[]]$ComputerName,
[Parameter(ParameterSetName='ByFile')]
[string]$InputFile
)
begin {
if($PSCmdlet.ParameterSetName -eq 'ByFile'){
try{
$ComputerName = Get-Content -LiteralPath $InputFile
}
catch{
throw
return
}
}
}
process {
foreach($Computer in $ComputerName){
# Work with $Computer here...
}
}

Powershell: Exporting AD MachineName/UserName/IPv4Address to a single *.csv

I have an OU that is full of default-named machines. The problem is these machines have already been sent out through 50+ sites and I don't know who has what. The below code, has done the job but I have to merge the two CSV's which isn't all that complicated but it's a step that I don't think I have to have.
Here is my current code (working with two CSV's):
###Creates temp-function to get the current user logged into machine###
function Get-LoggedOnUser {
[CmdletBinding()]
param(
[Parameter()]
[ValidateScript({ Test-Connection -ComputerName $_ -Quiet -Count 1 })]
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($comp in $ComputerName) {
$output = #{ 'ComputerName' = $comp }
$output.UserName = (Get-WmiObject -Class Win32_ComputerSystem -ComputerName $comp).UserName
[PSCustomObject]$output
}
}
###Change the -SearchBase parameters to the appropriate container.#######
$computer = Get-ADComputer -Filter * -SearchBase "OU=IMAGE,OU=WORKSTATIONS,DC=AD,DC=XXX,DC=US" |
Sort-Object Name
$allinfo = #()
foreach ($machine in $computer.Name) {
$Array = "" | Select-Object Machine
$array.Machine = $machine
Get-LoggedOnUser -ComputerName $machine |
Export-Csv "C:\Users\XXX\Desktop\loggedOnUsers.csv" -NoTypeInformation -Append
Get-LoggedOnUser -ComputerName $machine |
Test-Connection -Count 1 |
Select-Object #{ n = "Machine"; e = { $_.Address } }, Ipv4Address |
Export-Csv "C:\Users\XXXX\Desktop\ips.csv" -NoTypeInformation -Append
}
I have edited the code with XXX in some places, I'm not questioning those areas. I can't seem to get the end of the code to merge into one CSV.
Any help would be most appreciated.
I would suggest adding IP address to your function object like this:
function Get-LoggedOnUser {
[CmdletBinding()]
param(
[Parameter()]
[ValidateScript({ Test-Connection -ComputerName $_ -Quiet -Count 1})]
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($comp in $ComputerName) {
$output = #{ 'ComputerName' = $comp }
$output.UserName = (Get-WmiObject -Class Win32_ComputerSystem -ComputerName $comp).UserName
$output.Ipv4Address = (Test-Connection -ComputerName $machine -Count 1 | Select-Object -ExpandProperty Ipv4Address)
[PSCustomObject]$output
}
}
###Change the -SearchBase parameters to the appropriate container.#######
$computer = Get-ADComputer -Filter * -SearchBase "OU=IMAGE,OU=WORKSTATIONS,DC=AD,DC=XXX,DC=US" |
Sort-Object Name
$allinfo = #()
foreach ($machine in $computer.Name) {
$Array = "" | Select-Object Machine
$array.Machine = $machine
Get-LoggedOnUser -ComputerName $machine | Export-Csv "C:\Users\XXX\Desktop\\AllInOne.csv" -NoTypeInformation -Append
}

PowerShell Script to Disable/Re-enable a TCP/IP Port

Is there a way to disable and re-enable a known TCP/IP port in PowerShell?
I'll make the blind assumption that you are talking about disabling & enabling TCP/IP sockets that are hosted by IIS. (Not, say, looking for ways to block/unblock things at the Firewall level, or something else entirely.) In that case, I happen to have the necessary scripts lying around...
# Get the IIsWebServer and IIsWebServerSetting WMI objects matching a display name, and combine them into one object
function Get-IIsWeb
{
param (
[string] $displayName = "",
[string] $computer = "localhost"
)
if ($displayName -eq "")
{ $filter = "" }
else
{ $filter = "ServerComment='$displayName'"}
Get-WmiObject -namespace "root\MicrosoftIISv2" -class "IIsWebServerSetting" -filter $filter -computer $computer -authentication 6 | % {
$temp = $_
Get-WmiObject -namespace "root\MicrosoftIISv2" -class "IIsWebServer" -filter "Name='$($_.Name)'" -computer $computer -authentication 6 |
add-member -membertype NoteProperty -name Settings -value $temp -passthru
}
}
# Stop all websites on a given computer that are bound to the specified port, unless they are scoped to a
# host header or IP address
function Stop-WebsiteOnPort
{
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, valuefrompipeline=$true)]
[int] $port,
[Parameter(Position=0)]
[string] $computer = "localhost",
[Parameter()]
[string] $hostName = $null,
[Parameter()]
[string] $ip = $null
)
begin { $websites = Get-IIsWeb -computer $computer }
process
{
# I don't think you can do this filter in WQL
$websites |
? {
( $_.settings.serverbindings | ? {$_.port -eq $port -and $_.Hostname -eq $hostName -and $_.IP -eq $ip} | measure).count -gt 0
} |
% {
$_.stop()
}
}
}
The actual WMI code to re-enable a site is pretty much identical to the code for stopping one seen above. However, you'll need to do a little more work: there could be arbitrarily many sites configured to use a given port, but only 1 can run at a time. Either you'll need an additional parameter from the user, or some heuristic for picking the "right" site.