Detect SMB1 version via powershell for all OSes - powershell

My workflow:
check if server is pingable
find if they are domain connected or not and perform a task accordingly. if Operating system 2012 and/or R2 ,2016 or 2019 newer OSes then I will run Get-SmbServerConfiguration cmdlet. if machine is not a part of default domain then else block will run.
if Operating system 2003 or 2008 oldest OSes then I will run Get-Wmi cmdlet. if machine is not a part of default domain then else block will run.
Finally , I will concentanate $results variable.
My question is :
1- How can we get remotely regedit value for 2003 or 2008 oldest OSes IS NOT a part of default domain insie else block?
Also , Condition will be like below.
if SMB1 value is "0" then result will be `false`
if SMB1 value is "1" then result will be `true`
if SMB1 value is not exist then result will be `not exist value`
2- How can I create object properties $SMBAudit variable ? because , I will concentanate all outputs inside $results variable.
$reg = [wmiclass]"\\$computer\root\default:StdRegProv"
$SMBAudit = $reg.GetStringValue($basekey, $subkey, $value).sValue
My desired output :
Computername,SMB1Enabled
Host01,True
Host02,False
I will write so far a script like below. but I am stucking somethings.
Script :
# Computer List
$allComputers = Get-Content .\path\to\computers.txt
read-host -assecurestring | convertfrom-securestring | out-file C:\mysecurestring_domain.txt
read-host -assecurestring | convertfrom-securestring | out-file C:\mysecurestring_local.txt
# Create empty array of results
$Results = #()
# Loop through computers
foreach($computer in $allComputers) {
# check if server is pingable before running the query on the server
if (Test-Connection $computer -Count 1 -Quiet) {
Write-Host "`n`n$computer is online!" -BackgroundColor Green -ForegroundColor Black
}
if(Get-ADComputer -Filter {Name -eq $computer -and OperatingSystem -notlike '*Windows*Server*2003*' -and OperatingSystem -notlike '*Windows*Server*2008*'})
{
#"machine $_ is a part of default domain"
# The command we want to run
$username = "domain01\admin01"
$password = Get-Content 'C:\mysecurestring_domain.txt' | ConvertTo-SecureString
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password
$SMB = Invoke-Command -ComputerName $computer -Credential $cred -ScriptBlock {Get-SmbServerConfiguration | Select EnableSMB1Protocol }
# Create properties
$Properties = #{
# Populate the properties "Computername" and "SMB1Enabled" with variables
Computername = $Computer
SMB1Enabled = $SMB.EnableSMB1Protocol
}
# Add the properties to the result for each object
$Results += New-Object psobject -Property $Properties
}
else
{
#"machine $_ IS NOT a part of default domain"
$username = "localadmin01"
$password = Get-Content 'C:\mysecurestring_local.txt' | ConvertTo-SecureString
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password
$SMB = Invoke-Command -ComputerName $computer -Credential $cred -ScriptBlock {Get-SmbServerConfiguration | Select EnableSMB1Protocol }
# Create properties
$Properties = #{
# Populate the properties "Computername" and "SMB1Enabled" with variables
Computername = $Computer
SMB1Enabled = $SMB.EnableSMB1Protocol
}
# Add the properties to the result for each object
$Results += New-Object psobject -Property $Properties
}
# Oldest OSes
if(Get-ADComputer -Filter {Name -eq $computer -and OperatingSystem -notlike '*Windows*Server*2012*' -and OperatingSystem -notlike '*Windows*Server*2016*' -and OperatingSystem -notlike '*Windows*Server*2019*'})
{
#"machine $_ is a part of default domain"
# The command we want to run
<# HKEY_CLASSES_ROOT (2147483648 (0x80000000))
HKEY_CURRENT_USER (2147483649 (0x80000001))
HKEY_LOCAL_MACHINE (2147483650 (0x80000002))
HKEY_USERS (2147483651 (0x80000003))
HKEY_CURRENT_CONFIG (2147483653 (0x80000005))
#>
$basekey = [uint32]'0x80000002'
$subkey = 'SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters'
$value = 'SMB1'
$reg = [wmiclass]"\\$computer\root\default:StdRegProv"
$SMBAudit = $reg.GetStringValue($basekey, $subkey, $value).sValue
}
else
{
#"machine $_ IS NOT a part of default domain"
}
# Output
$Results | Select-Object Computername, SMB1Enabled | Out-File -Filepath c:\temp\smb1-computers.txt

I think you are over complicating this and although not tested by me, you could try this:
# Computer List
$allComputers = Get-Content '.\path\to\computers.txt'
# get credentials for domain-joined machines and for local machines
$domainCred = Get-Credential -UserName "domain01\admin01" -Message "Please enter the DOMAIN password"
$localCred = Get-Credential -UserName "localadmin01" -Message "Please enter the LOCAL password"
# loop through the list of computers and collect output in variable $Results
$Results = foreach($computer in $allComputers) {
# check if server is pingable before running the query on the server
if (Test-Connection -ComputerName $computer -Count 1 -Quiet) {
Write-Host "$computer is online!" -BackgroundColor Green -ForegroundColor Black
$server = Get-ADComputer -Filter "Name -eq '$computer'" -Properties OperatingSystem -ErrorAction SilentlyContinue
# if domain joined, use $domainCred, otherwise $localCred
if ($server) {
$cred = $domainCred
$version = ([regex]'Windows Server (\d+)').Match($server.OperatingSystem).Groups[1].Value
}
else {
$cred = $localCred
$info = Get-WmiObject -ComputerName $computer -Credential $cred -Class Win32_OperatingSystem
$version = ([regex]'Windows Server (\d+)').Match($info.Caption).Groups[1].Value
}
if ($version -eq '2003') {
# try reading the registry
try {
$RegBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $Computer)
$RegKey = $RegBase.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
$SMB = $RegKey.GetValue("SMB1")
[PsCustomObject]#{ ComputerName = $computer; SMB1Enabled = ($null -eq $SMB -or [int]$SMB -eq 1) }
}
catch {
[PsCustomObject]#{ ComputerName = $computer; SMB1Enabled = 'Could not read Remote Registry' }
}
finally {
if ($RegBase) { $RegBase.Close() }
if ($RegKey) { $RegKey.Close() }
}
}
elseif ($version -eq '2008') {
# Older OS
try {
# try via WinRM
$SMB = Invoke-Command -ComputerName $computer -Credential $cred -ScriptBlock {
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters' -Name SMB1
} -ErrorAction Stop
# output an object
[PsCustomObject]#{ ComputerName = $computer; SMB1Enabled = ($null -eq $SMB -or [int]$SMB -eq 1) }
}
catch {
# try reading the registry
try {
$RegBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $Computer)
$RegKey = $RegBase.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
$SMB = $RegKey.GetValue("SMB1")
[PsCustomObject]#{ ComputerName = $computer; SMB1Enabled = ($null -eq $SMB -or [int]$SMB -eq 1) }
}
catch {
[PsCustomObject]#{ ComputerName = $computer; SMB1Enabled = 'Could not read Remote Registry' }
}
finally {
if ($RegBase) { $RegBase.Close() }
if ($RegKey) { $RegKey.Close() }
}
}
}
else {
# Newer OS
$SMB = Invoke-Command -ComputerName $computer -Credential $cred -ScriptBlock { Get-SmbServerConfiguration | Select-Object EnableSMB1Protocol }
# output an object
[PsCustomObject]#{ ComputerName = $computer; SMB1Enabled = $SMB.EnableSMB1Protocol }
}
}
else {
Write-Warning "Computer $computer is off-line"
# output an object anyway, so that in the CSV it is known that the computer didn't ping
[PsCustomObject]#{ ComputerName = $computer; SMB1Enabled = 'Off-Line' }
}
}
# Output on screen
$Results | Format-Table -AutoSize
# Output to CSV file
$Results | Export-Csv -Path 'c:\temp\smb1-computers.csv' -NoTypeInformation -UseCulture

Related

Remotely registry path controlling via powershell

My conditions are :
if NeedReboot key is exist then result will be Pending reboot
if NeedReboot key is not exist then result will be NO Pending reboot
My desired output :
Computername,Rebootrequired
Host01,Pending reboot
Host02,NO Pending reboot
Script :
$allComputers = Get-Content '.\path\to\computers.txt'
$domainCred = Get-Credential -UserName "domain01\admin01" -Message "Please enter the DOMAIN password"
$Results = foreach($computer in $allComputers) {
$cred = $domainCred
try {
$VmTools = Invoke-Command -ComputerName $computer -Credential $cred -ScriptBlock {
Test-Path -Path "HKLM:\SOFTWARE\VMware, Inc.\NeedReboot\"
} -ErrorAction Stop
[PsCustomObject]#{ ComputerName = $computer; RebootRequired = ([int]$VmTools -eq "True") }
}
}
$Results | Format-Table -AutoSize
$Results | Export-Csv -Path 'c:\temp\vmtools.csv' -NoTypeInformation -UseCulture
Try removing the final backslash after the registry path.
Then, Test-Path just returns $true or $false, so converting that to [int] and comparing to the string "True" makes no sense..
Try
[PsCustomObject]#{
ComputerName = $computer
RebootRequired = if($VmTools) {"Pending Reboot") } else {"NO Pending Reboot"}
}
The code is also missing a catch{..} block that should accompany the try {..} block. Something like:
catch {
[PsCustomObject]#{
ComputerName = $computer
RebootRequired = "Error reading registry"
}
}

Powershell, I do input a list gather data and output that whole list into one CSV

I am creating a script that reads a list of computer names and collects data from security event logs about who is on the computer, how long they have been on for, and how long it has been since the computer has restarted. I have it working except that it does not output all the data into one CSV. I just receive one CSV file with one computer name.
function Get-KioskInfo {
param (
[parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True,Position=0)]
[Alias('PSComputerName','DNSHostName','CN','Hostname')]
[string]
$ComputerName = $env:COMPUTERNAME
)
#PARAM
$User = try {(Get-WmiObject -ComputerName $ComputerName Win32_ComputerSystem | Select-Object -ExpandProperty username).trimstart("NG\")} catch {Write-Output "User not detected";break}
$BootStart = ((get-date) - (Get-CimInstance win32_operatingsystem -ComputerName $ComputerName).LastBootUpTime).Days
#These variables are for the DATE & Time calculation
If ($user -NE $null)
{ Write-Verbose 1
# Do something
$Date1 = Get-date
Write-Verbose 2
$SP = Get-WinEvent -ComputerName $ComputerName -FilterHashTable #{LogName = "Security";ID="5379";Data=$User; StartTime=((Get-Date).AddDays(-1))}
Write-Verbose 3
$Date2 =($SP | select -first 1).timecreated
Write-Verbose 4
$USERLOGTIME = ($Date1-$Date2).hours.tostring("N2")
Write-Verbose 5
}
else{Write-Output "No user";break}
Write-Verbose 6
#Rename-Computer -ComputerName "Srv01" -NewName "Server001" -DomainCredential Domain01\Admin01 -Force ------ Rename script for computers if it is needed.
#$computers = Get-Content C:\Users\jaycbee\Desktop\kiosknames.txt ------ To load kiosk list
#foreach ($c in $computers) {start-job -Name $c -ScriptBlock ${Function:get-kioskinfo} -ArgumentList $c} for learning how to do a foreach script
Write "Computer Name: $Computername"
Write "---USER---"
Write "Name: $User"
Write "Log in Time $USERLOGTIME"
Write "Boot start $BootStart days ago"
$ComputerName | ForEach-Object {
if (Test-Connection -ComputerName $ComputerName -Count 1 -Quiet)
{
Invoke-Command -ComputerName $ComputerName {
}
} # Offline Check
else
{
Write-Host "Computer is Unreachable or Offline" -ForegroundColor Gray
}
} # Foreach
$Continue = Read-Host "WARNING! This will READ LIST of computers in \\ou\ouor-groups\Desktop_Support\SD\Kiosks\kiosknames.txt Type CONTINUE to proceed."
if ($Continue -eq "CONTINUE")
{
$Computers = Get-Content '\\ou\ouor-groups\Desktop Support\SD\Kiosks\kiosknames.txt'
foreach ($C in $Computers) {start-job -Name $c -ScriptBlock ${Function:get-kioskinfo} -ArgumentList $c
}
}
[pscustomobject]#{ Name = $ComputerName ; User = $User ; "User Log in time in hours" = $USERLOGTIME;"BootStart days ago" = $BootStart} | export-csv -path "\\ou\ouor-groups\Desktop Support\SD\Kiosks\test45$ComputerName.csv" -Append
} #Function
#For each-computer | do this at this location,
Continuing from my comment. I too wonder why the use of jobs for this use case. Unless you are doing this on hundreds of computers, thus needing parallel processing.
This refactor/formatting is just my way of making sense of what you posted. I'm old, and crowded code just really hurts my eyes. ;-} Yet, code the way you like of course. ;-}
I do not have an environment to test this, but give it a shot.
function Get-KioskInfo
{
param
(
[parameter(ValueFromPipeline = $True,ValueFromPipelineByPropertyName = $True,Position = 0)]
[Alias(
'PSComputerName',
'DNSHostName',
'CN',
'Hostname'
)]
[string]
$ComputerName = $env:COMPUTERNAME
)
($User = try
{
(Get-WmiObject -ComputerName $ComputerName Win32_ComputerSystem |
Select-Object -ExpandProperty username).trimstart("NG\")
}
catch
{
'User not detected'
break
}
)
($BootStart = ((get-date) - (Get-CimInstance win32_operatingsystem -ComputerName $ComputerName).LastBootUpTime).Days)
If ($user -NE $null)
{
($Date1 = Get-date)
($SP = Get-WinEvent -ComputerName $ComputerName -FilterHashTable #{
LogName = 'Security'
ID = '5379'
Data = $User
StartTime = ((Get-Date).AddDays(-1))
})
($Date2 = (
$SP |
select -first 1
).timecreated)
($USERLOGTIME = ($Date1-$Date2).hours.tostring('N2'))
}
else
{
'No user'
break
}
"Computer Name: $Computername
---USER---
Name: $User
Log in Time $USERLOGTIME
Boot start $BootStart days ago"
$ComputerName |
ForEach-Object {
if (Test-Connection -ComputerName $ComputerName -Count 1 -Quiet)
{Invoke-Command -ComputerName $ComputerName}
else
{Write-Warning -Message 'Computer is Unreachable or Offline'}
}
$UserMessage = '
WARNING!
This will READ LIST of computers in:
\\ou\ouor-groups\Desktop_Support\SD\Kiosks\kiosknames.txt
Type CONTINUE to proceed'
$Continue = Read-Host $UserMessage
if ($Continue -eq 'CONTINUE')
{
Get-Content '\\ou\ouor-groups\Desktop Support\SD\Kiosks\kiosknames.txt' |
foreach {
{start-job -Name $PSItem -ScriptBlock ${Function:get-kioskinfo} -ArgumentList $PSItem}
[pscustomobject]#{
Name = $ComputerName
User = $User
'User Log in time in hours' = $USERLOGTIME
'BootStart days ago' = $BootStart
}
} |
Export-Csv -path "$PWD\$ComputerName.csv" -Append
}
}
These didn't help me with my solution, but you were right about the start-jobs. I have to rework the entire script in order to get the correct info.

How to restart services on remote server in PowerShell

I am trying to do something very simple but have lost my way. I have this script that works almost perfect. It goes into a list of servers checks for 2 services and checks if they are running. Then, it generates a list of servers that are running or stopped. Now, what I need it to do is to restart the service when it finds that it is stopped...
$Computers = Get-Content -path C:\computers.txt
$Computerobject = $null
FOREACH ( $Computer IN $Computers )
{
TRY
{
$Services = $null
$Services = (Get-Service -Name 'CrystalToPDF','Bradware - BoxUploadService' -computername $Computer -ErrorAction Stop | Select-Object Status, Name, DisplayName )
Add-Member -InputObject $Services -NotePropertyName ComputerName -NotePropertyValue $Computer
$ComputerProperties = [ordered] #{ComputerName=$Services.ComputerName; ServiceName=$Services.Name; Status=$Services.Status}
$Computerobject = New-Object PSObject -Property $ComputerProperties
$Computerobject
}
CATCH
{
Write-Host "Both services are not installed on $Computer." -ForegroundColor Red
}
}
Following from my comment.
Like this:
$Computers = Get-Content -path C:\computers.txt
$Computerobject = $null
FOREACH ( $Computer IN $Computers )
{
TRY
{
$Services = $null
$Services = (Get-Service -Name 'CrystalToPDF','Bradware - BoxUploadService' -computername $Computer -ErrorAction Stop | Select-Object Status, Name, DisplayName )
if($Services.Status -eq "Stopped"){ Get-Service -Name 'CrystalToPDF','Bradware - BoxUploadService' -computername $Computer | Start-Service}
Add-Member -InputObject $Services -NotePropertyName ComputerName -NotePropertyValue $Computer
$ComputerProperties = [ordered] #{ComputerName=$Services.ComputerName; ServiceName=$Services.Name; Status=$Services.Status}
$Computerobject = New-Object PSObject -Property $ComputerProperties
$Computerobject
}
CATCH
{
Write-Host "Both services are not installed on $Computer." -ForegroundColor Red
}
}

PowerShell create a new object and add the values to an array

What I am trying to achieve here is add the servers and the updates that are not installed on the server to an array and create a new object that is going to display the names of the servers in one column and the missing updates on another column, but at the end I am getting an empty Grid-View table.
The values for the servers and updates are read from a file.
Write-Host
#Read the password from stdin and store it in a variable
$password = Read-Host -AsSecureString -Prompt "Enter your password"
Write-Host
#Get credentials and password for later user
$cred = New-Object System.Management.Automation.PSCredential ("Administrator#testing.local", $password )
#Get the list of available servers to test
$servers = Get-Content -Path $HOME\Desktop\servers.txt
#Get the list of available updates that need to be installed on the server
$available_updates = Get-Content $HOME\Desktop\update.txt
$add_updates = #()
$add_updates_and_servers = #()
#Get each server name from the list and execute the following commands
foreach ($server in $servers) {
#Test if the server is reponding
$ping = Test-Connection $server -Count 1 -Quiet
#If the above command returns True continue
if ($ping -eq "True") {
#Write a message saying Testing server_name
Write-Host "Testing $server"
foreach ($update in $available_updates) {
#Check if update is installed
$updates_from_os = Invoke-Command -ComputerName $server -Credential $cred -ScriptBlock { Get-HotFix | Select-Object -Property HotFixID | Where-Object -Property HotFixID -EQ $Using:update } -HideComputerName | Select-Object -ExpandProperty HotFixID
if (!$updates_from_os) {
$add_updates += $update
}
}
New-Object -TypeName PSObject -Property $updates -OutVariable final
$updates = #{
"Server" = $server
"Updates" = $add_updates
}
}
$add_updates_and_servers += $final
}
$add_updates_and_servers | Out-GridView
For what is probably happening with your script:
I suspect that each time you calling the statement New-Object -TypeName PSObject -Property $updates -OutVariable final You overwriting any previous created $final object which references to the same objects as your $add_updates_and_servers collection.
Anyways, try to avoid using the increase assignment operator (+=) to create a collection, instead stream the results to a variable (or even better, directly to next/final cmdlet: ... }| Out-GridView).
Something like:
$add_updates_and_servers = foreach ($server in $servers) {
$ping = Test-Connection $server -Count 1 -Quiet
if ($ping -eq "True") {
Write-Host "Testing $server"
$add_updates = #(
foreach ($update in $available_updates) {
$updates_from_os = Invoke-Command -ComputerName $server -Credential $cred -ScriptBlock { Get-HotFix | Select-Object -Property HotFixID | Where-Object -Property HotFixID -EQ $Using:update } -HideComputerName | Select-Object -ExpandProperty HotFixID
if (!$updates_from_os) { $update }
}
)
[PSCustomObject]#{
"Server" = $server
"Updates" = $add_updates
}
}
}
Note: in case you want each $update in a separate column, also have a look at: Not all properties displayed

Retrieve software that has been authorized to pass through firewall in powershell

The code below turns off firewall on each remote computers and return any computers that was turned off. I am also trying to retrieve software that has been authorized to pass through firewall for each computer.
I understand that I am using try, catch so is there any way to print the output of $Appfilter to offComp&programsALLO.txt ? The text file just prints the value of $Appfilter.
The output should ideally look like:
Computers:
"name of computer" followed by "programs allowed"
Here is the code:
Get-ADComputer -Filter * | Select-Object -ExpandProperty Name | Out-File .\ADcomputers.txt
$LaunchLine = 'powershell.exe -Version 4.0 -Command "& {netsh advfirewall set allprofiles state off}"'
$Appfilter = 'powershell.exe -Version 4.0 -Command "& {Get-NetFirewallApplicationFilter -program * | fl program}"'
$ComputerList = Get-Content .\adcomputers.txt
foreach($Computer in $ComputerList) {
[String]$wmiPath = "\\{0}\root\cimv2:win32_process" -f $computer
try {
[wmiclass]$Executor = $wmiPath
$executor.Create($LaunchLine, $Appfilter)
} catch {
Add-Content offComp&programsALLO.txt "computers:$Computer, $Appfilter "
}
}
I would use Invoke-Command with the -ComputerName parameter if possible:
#store AD Computer names in an array
$computerList = (Get-ADComputer -Filter *).Name
#declare results arrays
$results = #()
$offline = #()
#for each computer
foreach($computer in $computerList) {
#if computer responds to ping
if(Test-Connection $computer -Count 2 -Quiet -ErrorAction SilentlyContinue) {
#disable firewall
Invoke-Command -ComputerName $computer -ScriptBlock {
netsh advfirewall set allprofiles state off
} | Out-Null
#store retrieved authorized programs list in an array
$programs = Invoke-Command -ComputerName $computer -ScriptBlock {
(Get-NetFirewallApplicationFilter).Program
}
#build results object and add it to results array
$results += [PSCustomObject]#{
ComputerName = $computer
Programs = $programs -join ";"
}
} else {
#build results object and add it to offline array
$offline += [PSCustomObject]#{
ComputerName = $computer
Status = "OFFLINE"
}
}
}
#export results to files
$results | Out-File "report.txt"
$offline | Out-File "offline.txt"