Running powershell script in different OS - powershell

I am trying to run the below mentioned command in Server 2012 & it's pulling users from Administrators user group. But in Server 2008R2, it's pulling from entire domain
Get-WmiObject -Class Win32_GroupUser `
| where{$_.GroupComponent -like "*Administrators*"} `
|foreach {
$data = $_.PartComponent -split "\,"
$data[1].Remove(0,5).Replace('"','')
}

As you have guessed the issue is that win2008R2 has only PS 2.0.x. I think this command where{$_.GroupComponent -like "*Administrators*"} is not available at that version so it queries the whole AD as a fallback (that is a guess).
From your description I did not understand if you want to query local server or domain so I will enter both (all are functional on win2008R2 (PS version 2.0.50727)):
To query local admins and
#get servers by AD OU
If (!(Get-Module ActiveDirectory)) {
Import-Module ActiveDirectory
}
function get-localadmins{
[cmdletbinding()]
Param(
[string]$server
)
$group = get-wmiobject win32_group -ComputerName $server -Filter "LocalAccount=True AND SID='S-1-5-32-544'"
$query = "GroupComponent = `"Win32_Group.Domain='$($group.domain)'`,Name='$($group.name)'`""
$list = Get-WmiObject win32_groupuser -ComputerName $server -Filter $query
$list | %{$_.PartComponent} | % {$_.substring($_.lastindexof("Domain=") + 7).replace("`",Name=`"","\")}
}
get-localadmins 'your_server_name'
If your goal is to query the whole AD then you can use:
On windows 2008 R2 SP1 it can produce an error: System.DirectoryServices.AccountManagement.PrincipalOperationException: An error (1301) occurred while enumerating the groups. The group's SID could not be resolved.
You have to install a hotfix by Microsoft at: https://support.microsoft.com/en-us/help/2830145/sid-s-1-18-1-and-sid-s-1-18-2-cannot-be-mapped-on-windows-based-computers-in-a-domain-environment?wa=wsignin1.0%3Fwa%3Dwsignin1.0
The following code was taken from here: https://stackoverflow.com/a/8057025/6059896 (all credit to the author) - only changed the names of variables to fig my coding style.
$Recurse = $true
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$context_type = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$group_principal_identity=[System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($ct,'Administrators')
$group.GetMembers($Recurse)

Related

correct method of finding which TLS versions are supported

We'll be disabling TLSv1.0 and TLSv1.1 on our domain controllers for security reasons. But before we do that, I want to check a list of computers and see which TLS versions they have enabled, to make sure they'll keep authenticating with the domain controllers after the legacy TLS versions are disabled.
I wrote a PowerShell script that loops through the list of computers and runs "Get-TlsCipherSuite", but most of the remote computers don't recognize the command, and I don't want to install it just for this query.
I also run a query on registry "HKLM SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols" as per this Microsoft article, but the protocols registry key, does not contain any TLS entries
So basically I'm looking for the correct command or query, which Ideally I will loop through all the computers using PowerShell and get which TLS versions are supported.
Perhaps something like this can help
$cred = Get-Credential -Message "Please enter your admin credentials"
$machines = 'DC01','DC02','DC03' # the list of computernames to check
$result = Invoke-Command -ComputerName $machines -Credential $cred -ScriptBlock {
$supported = [Net.ServicePointManager]::SecurityProtocol
# values from https://learn.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype
[PsCustomObject]#{
ComputerName = $env:COMPUTERNAME
SystemDefault = [bool]($supported -eq 0)
Ssl3 = [bool]($supported -band 48)
Tls = [bool]($supported -band 192)
Tls11 = [bool]($supported -band 768)
Tls12 = [bool]($supported -band 3072)
Tls13 = [bool]($supported -band 12288)
}
}
# remove the extra properties PowerShell
$result = $result | Select-Object * -ExcludeProperty PS*, RunspaceId
# save to file if you want
$result | Export-Csv -Path 'X:\Somewhere\SecurityProtocols.csv' -NoTypeInformation
# filter out machines supporting Tls1.0 and/or Tls1.1
$result | Where-Object {$_.Tls -eq $true -or $_.Tsl11 -eq $true}
#etc.

Loop through servers and output results along with errors

I wrote a simple PowerShell script to retrieve a list of servers' last boot time and output the results to grid view. The results are immediately shown in the grid window but and comes to a short pause whenever a server is not responding to the get command, either due to WMI not running or class not registered. It then displays the error in PS and move to the next server.
Now, the results aren't helpful unless the "not responding" servers are shown in the results windows.
$servers = ('serverx','serverb')
Get-WmiObject -Class Win32_OperatingSystem -ComputerName $servers |
select csname, #{LABEL='LastBootUpTime';EXPRESSION={$_.ConvertToDateTime($_.LastBootupTime)}},
#{LABEL='LocalTime';EXPRESSION={$_.ConvertToDateTime($_.LocalDateTime)}},
#{LABEL='UpTime';EXPRESSION={(Get-Date) - $_.ConvertToDateTime($_.LastBootupTime)}},
#{LABEL='OS';EXPRESSION={$_.Caption}} |
Out-GridView
Errors type shown in PS window in Red:
Get-WmiObject : Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)) At line:1 char:12
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA) At line:1 char:12
Edit: How do I can i output the good results along with the server name if the servers that responded with an error?
For your desired result you need to query the servers individually and construct a custom object if the query fails:
$svr = 'serverx'
try {
Get-WmiObject Win32_OperatingSystem -Computer $svr -EA Stop |
select csname, #{n='LocalTime';e={...}},
#{n='UpTime';e={...}}, #{n='OS';e={...}}
} catch {
New-Object -Type PSObject -Property #{
csname = $svr
LocalTime = $null
UpTime = $null
OS = $null
}
}
Run this in a loop
$servers | ForEach-Object {
...
} | Out-GridView
Use background jobs (or something similar) instead of a plain loop to speed up the checks by running them in parallel rather than sequentially. Spawn each check as a job in the background and check for completed jobs in a loop until all jobs have completed. Collect the output from completed jobs.
Here is the full script that loops through the servers, catches non-terminating error and output to a window.
$svr = ('localhost','fail')
$Output = Foreach ($server in $svr)
{
try {
Get-WmiObject Win32_OperatingSystem -ComputerName $server -EA STOP |
select csname, #{n='LocalTime';e={$_.ConverttoDateTime($_.lastbootuptime)}},
#{n='UpTime';e={....}}, #{n='OS';e={"...."}}
} catch {
New-Object -Type PSObject -Property #{
Csname = $server
LocalTime = $null
UpTime = $null
OS = "Error" #$null
}
}
}
$output | Out-GridView

Is there a way to get VMs Operating System name from Hyper-V using powershell?

I'm writing a script on HYPER-V host for getting VMs guest informations. Is there a way to get VMs Operating System name from Hyper-V using powershell?
There are several examples using (Get-WmiObject Win32_OperatingSystem -ComputerName $vmName).name but i should get this information directly from Hyper-V because of domain restrictions.
Also i'm using hyper-v module of powershell but i couldn't see any cmdlets related to OS.
This could be retrieved from guest intrinsic exchange items.
# Filter for parsing XML data
filter Import-CimXml
{
# Create new XML object from input
$CimXml = [Xml]$_
$CimObj = New-Object -TypeName System.Object
# Iterate over the data and pull out just the value name and data for each entry
foreach ($CimProperty in $CimXml.SelectNodes("/INSTANCE/PROPERTY[#NAME='Name']"))
{
$CimObj | Add-Member -MemberType NoteProperty -Name $CimProperty.NAME -Value $CimProperty.VALUE
}
foreach ($CimProperty in $CimXml.SelectNodes("/INSTANCE/PROPERTY[#NAME='Data']"))
{
$CimObj | Add-Member -MemberType NoteProperty -Name $CimProperty.NAME -Value $CimProperty.VALUE
}
# Display output
$CimObj
}
# Prompt for the Hyper-V Server to use
$HyperVServer = Read-Host "Specify the Hyper-V Server to use (enter '.' for the local computer)"
# Prompt for the virtual machine to use
$VMName = Read-Host "Specify the name of the virtual machine"
# Get the virtual machine object
$query = "Select * From Msvm_ComputerSystem Where ElementName='" + $VMName + "'"
$Vm = gwmi -namespace root\virtualization\v2 -query $query -computername $HyperVServer
# Get the KVP Object
$query = "Associators of {$Vm} Where AssocClass=Msvm_SystemDevice ResultClass=Msvm_KvpExchangeComponent"
$Kvp = gwmi -namespace root\virtualization\v2 -query $query -computername $HyperVServer
Write-Host
Write-Host "Guest KVP information for" $VMName
# Filter the results
try {
$Kvp.GuestIntrinsicExchangeItems | Import-CimXml | where Name -eq "OSName"
}
catch {
Write-Host "Not found"
}
From Ben Armstrong’s Virtualization Blog.
Unless you're using SCVMM, Guest OS details are not available via Hyper-V PowerShell cmdlets.
You have to query the Guest itself like you've already found.
I couldn't run t1meless' script on PowerShell version 7.1, because gwmi is deprecated in 7.1, sources MS Docs and GitHub.
I rewrote the script for PS 7.1.
# Script for retrieving Hyper-V Guest Operating System.
# Tested on PowerShell 7.1
# Prompt for the Hyper-V Server to use
$hyper_v_server = Read-Host "Specify the Hyper-V Server to use (enter '.' for the local computer)"
# Prompt for the virtual machine to use
$vm_name = Read-Host "Specify the name of the virtual machine"
# Check if VM exists and is running. This script doesn't work if the VM is stopped.
# Capture error output, source: https://stackoverflow.com/a/66861283/3498768
$vm_not_found = $($vm_state = (Get-VM $vm_name).state) 2>&1
if ($vm_not_found -ne $null) {
Write-Host "$vm_name VM was not found."
exit
}
if ($vm_state -eq "Off") {
Write-Host "Cannot retrieve information of $vm_name. The VM is stopped. Only running VM information can be retrieved."
exit
}
# Get the virtual machine object
$query = "Select * From Msvm_ComputerSystem Where ElementName='" + $vm_name + "'"
$vm = Get-CimInstance -namespace root\virtualization\v2 -query $query -computername $hyper_v_server
# Get associated information
$vm_info = Get-CimAssociatedInstance -InputObject $vm
Write-Host "Guest information for" $vm_name
# Select only required information
$vm_info | Where GuestOperatingSystem |
Select -Property GuestOperatingSystem |
Format-List *

Get all suspended instances for specific application in BizTalk

In BizTalk Server Administration Console you can query for suspended service instances and then filter them by Application Name. I need such functionality without BTS Administration Console.
So far I've created Powershell script to get suspended instances:
$array = Get-WmiObject MSBTS_ServiceInstance `
-Namespace 'root\MicrosoftBizTalkServer' `
-Filter '(ServiceClass = 4 or ServiceClass = 1) `
and (ServiceStatus = 4 or ServiceStatus = 16)'
foreach ($element in $array)
{
Write-Host $element.InstanceID "-" $element.HostName "-" `
$element.ServiceStatus "-" $element.ServiceClass
}
If you run this script you'll get all suspended instances, but how to find out to what application they belong?
Any solution that uses PowerShell, WMI or C# is good for me.
I've used the Microsoft.BizTalk.Operations assembly ...
Add-Type -AssemblyName ('Microsoft.BizTalk.Operations, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL')
$dbServer = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\BizTalk Server\3.0\Administration' 'MgmtDBServer').MgmtDBServer
$dbName = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\BizTalk Server\3.0\Administration' 'MgmtDBName').MgmtDBName
$bo = New-Object Microsoft.BizTalk.Operations.BizTalkOperations $dbServer, $dbName
$serviceInstances = $bo.GetServiceInstances()
$tgt = "DeploymentFramework.Samples.HelloWorld"
foreach ($instance in $serviceInstances)
{
if ($instance.Application -ieq $tgt)
{
$completionStatus= $bo.TerminateInstance($instance.Id)
}
}
One thing I've not worked out ... Why does terminating a suspended (not resumable) service instance return Failed, yet it is terminated
Application name property is not exposed via MSBTS_ServiceInstance class. I believe the reason for that is, application concept was only introduced in BizTalk 2006 but the WMI API was present right from 2004.
I'm afraid, your only choice is to go directly to the database.
What version of BizTalk?
This works on BizTalk 2010:
$array = Get-WmiObject MSBTS_ServiceInstance `
-Namespace 'root\MicrosoftBizTalkServer' `
-Filter '(ServiceClass = 4 or ServiceClass = 1) `
and (ServiceStatus = 4 or ServiceStatus = 16)'
foreach ($element in $array)
{
Write-Host $element.ServiceName
}

Determine Users Accessing a Shared Folder Using PowerShell

I need to determine the users/sessions accessing a shared folder on a Windows XP (SP2) machine using a PowerShell script (v 1.0). This is the information displayed using Computer Management | System Tools | Shared Folders | Sessions. Can anyone give me pointers on how to go about this?
I'm guessing it will require a WMI query, but my initial search online didn't reveal what the query details will be.
Thanks, MagicAndi
I came up with the following script:
$computer = "LocalHost"
$namespace = "root\CIMV2"
$userSessions = Get-WmiObject -class Win32_ServerConnection -computername $computer -namespace $namespace
if($userSessions -ne $null)
{
Write-Host "The following users are connected to your PC: "
foreach ($userSession in $userSessions)
{
$userDetails = [string]::Format("User {0} from machine {1} on share: {2}", $userSession.UserName, $userSession.ComputerName, $userSession.ShareName)
Write-Host $userDetails
}
Read-Host
}
The following articles were useful:
http://www.activexperts.com/activmonitor/windowsmanagement/adminscripts/filesfolders/sharedfolders/
http://www.computerperformance.co.uk/powershell/powershell_wmi_shares.htm
http://www.codeproject.com/KB/cs/NetWorkSpy.aspx?msg=2384830
As always, if you can't find a way to do it in PowerShell, see if someone has done something similar in C#.
I've modified it a bit to show hostname instead of IP:
$computer = "LocalHost"
$namespace = "root\CIMV2"
$userSessions = Get-WmiObject -class Win32_ServerConnection -computername $computer -namespace $namespace
if($userSessions -ne $null)
{
Write-Host "The following users are connected to your PC: "
foreach ($userSession in $userSessions)
{
$ComputerName = [system.net.dns]::resolve($usersession.computername).hostname
$userDetails = [string]::Format("User {0} from machine {1} on share: {2}", $userSession.UserName, $ComputerName, $userSession.ShareName)
Write-Host $userDetails
}
Read-Host
}