Tuesday, March 22, 2016

Determine if a computer is connected via Wired or Wireless network (or both)

This is not the first time this has come up for me and it is not a trivial thing to determine.  So here's the PS script to do it. Please give me a thumbs up, a comment, or a link back to this blog if you use it.

There are quite a few lines commented out, they can be uncommented to give you more information about the variables within the script (or you can just delete them if you don't care).  I left them there mostly for myself in case I find a use case that I need to know more about the adapters.
#Get-NetConnectionType.ps1
#by Mark Randol
#randoltech.blogspot.net
cls
#PhysicalAdapter cannot be only criteria because, for no apparent reason, some virtual adapters set this to true.  Especially VPN adapters.
#AdapterType eliminates anything that is not TCP/IP
#NetConnectionStatus of 2 = connected
$AdapterTypes = (Get-WmiObject -Namespace "root/WMI" -Query "SELECT * FROM MSNdis_PhysicalMediumType")
$Adapters = (Get-WmiObject -class win32_networkadapter | Where-Object {$_.PhysicalAdapter -eq "True" -and $_.AdapterType -eq "Ethernet 802.3" -and $_.NetConnectionStatus -eq "2"})
$NetworkConnected = $false
$WiredConnected = $false
$WirelessConnected = $false

#Spit out a header line with how many adapters are connected
$AdaptersCount = ($Adapters | measure).count
if ($AdaptersCount -lt 1)
    {
        $HeaderString = "There are no adapters connected"
    }
if ($AdaptersCount -eq 1)
    {
        $HeaderString = "There is $AdaptersCount adapter connected"
    }
if ($AdaptersCount -gt 1)
    {
        #Two or more connected adapters means one of two things:
        #  a. Both your wired and wireless NICs are currently connected.
        #  b. You have multiple NICs (generally a server like a Virtual Server Host may have multiple NICs on multiple networks simultaneously).
        $HeaderString = "There are $AdaptersCount adapters connected"
    }

#Loop through all of the adapters and for each one check its type to see if it is wired or wireless
#Only return those that are connected and are TCP/IP wired or wireless adapters
foreach ($Adapter in $Adapters)
{
    foreach ($AdapterType in $AdapterTypes)
    {
        if ($AdapterType.InstanceName -eq $Adapter.Name)
        {
            if ($AdapterType.NdisPhysicalMediumType -eq 0)
            {
                #$AdapterType | format-list -Property [a-z]*
                <#write-host ("-------------------------------------------")
                $AdapterSettings = (Get-WmiObject -Class win32_NetworkAdapterSetting | where-object {$_.Element -eq $Adapter.Path})
                $AdapterOutput = $Adapter.ProductName
                $AdapterOutput = $AdapterOutput + ", "
                $AdapterOutput = $AdapterOutput + $Adapter.NetConnectionID
                $AdapterOutput = $AdapterOutput + ", Wired"
                write-host $AdapterOutput
                write-host ("-------------------------------------------")#>
                $NetworkConnected = $true
                $WiredConnected = $true
            }
            if ($AdapterType.NdisPhysicalMediumType -eq 9)
            {
                #$AdapterType | format-list -Property [a-z]*
                <#write-host ("-------------------------------------------")
                $AdapterSettings = (Get-WmiObject -Class win32_NetworkAdapterSetting | where-object {$_.Element -eq $Adapter.Path})
                $AdapterOutput = $Adapter.ProductName
                $AdapterOutput = $AdapterOutput + ", "
                $AdapterOutput = $AdapterOutput + $Adapter.NetConnectionID
                $AdapterOutput = $AdapterOutput + ", Wireless"
                write-host $AdapterOutput
                write-host ("-------------------------------------------")#>
                $NetworkConnected = $true
                $WirelessConnected = $true
            }
        }
    }
}
write-host ("Network connected = $NetworkConnected - $HeaderString")
write-host ("Connected via a wire = $WiredConnected")
write-host ("Connected wirelessly = $WirelessConnected")

Thursday, March 17, 2016

Determining which users are using offline files

One of my colleagues came to me today seeking advice on how to check which users are using offline files.  I honestly don't know why he needs to know this but, according to him, it is something that quite a lot of people on the Internet need to know.

Here's what we came up with...

He provided a registry area:
HKEY_CURRENT_USER\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\NetCache\SyncItemLog

Apparently this registry area is something that he found and it is not well known.  What he had discovered is that if a user has chosen to use offline files there will be sub-keys generated here.  If the user has not turned on offline files then there will not be any sub-keys.  The sub-key names are UNC paths to offline file locations.

So, this is where he came to me and said "How do we gather up this information on all of our users?"

What I came up with is two-fold.  First I created the PowerShell script below to gather the information into a log file.  The problem, however, is that the script must run under the user context of whichever user you want to check.  You can't run it as yourself or as system because it would not be able to read items from the other user's HKEY_CURRENT_USER hive.

The solution to the problem of running it as the local user is simply change the $OutputLog variable to point to a central share to which all of the users can write and then create a package with the script.  Have the package execute once per user per machine, under the user credentials, and only if a user is logged in.

Hopefully others find this useful.  If so, please +1, leave a commment, and/or link back to this blog.

Here's the script:

#Test-OfflineFileUse.ps1
#by Mark Randol
#randoltech.blogspot.net
new-psdrive -Name O -PSProvider FileSystem -Root "\\server\share"
$OutputLog = "O:\OfflineFilesCheck.csv"
Clear-Host
Set-Location -Path "Registry::HKEY_CURRENT_USER\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\NetCache"
$SyncItemLogRegKey = (Get-ChildItem -Path . -Name)
IF ($SyncItemLogRegKey -like 'SyncItemLog')
{
    $SyncKey = (Get-ChildItem -path .\$SyncItemLogRegKey -Name)
    foreach ($SubKey in $SyncKey)
    {
        if ($SubKey -ne $null)
        {
            $LogOutputString = $env:USERDOMAIN + '\' + $env:USERNAME + ',' + $env:COMPUTERNAME + ',' + $SubKey
            Out-File -FilePath $OutputLog -Append -InputObject $LogOutputString
        }
    }
}
ELSE
{
    $SyncKey = "SyncItemLog registry key does not exist"
    $LogOutputString = $env:USERDOMAIN + '\' + $env:USERNAME + ',' + $env:COMPUTERNAME + ',' + $SyncKey
    Out-File -FilePath $OutputLog -Append -InputObject $LogOutputString
}

Wednesday, March 9, 2016

Powershell - Ping test a list of servers

It amazes me that if you search "Powershell Ping List Servers" it doesn't return something like this.  So, here you go...

# Script by Mark Randol
# randoltech.blogspot.com

# edit the input and output file paths/names at the end of script

# the first line of input file MUST BE ComputerName
# then the list of whatever computers you want to test after that

function Test-ListOfServers
{
    [CmdletBinding(DefaultParameterSetName='Parameter Set 1',
                  SupportsShouldProcess=$true,
                  PositionalBinding=$false,
                  HelpUri = 'http://www.microsoft.com/',
                  ConfirmImpact='Medium')]
    [OutputType([String])]
    Param
    (
        # Computer Name
        [Parameter(ValueFromPipeline=$true,
                  ValueFromPipelineByPropertyName=$true,
                  ValueFromRemainingArguments=$false,
                  Position=0)]
        [Alias("computer","comp")]
        [String]
        $ComputerName
    )

    Begin
    {
    }
    Process
    {
        if (test-connection $ComputerName -Count 1 -Quiet)
            {
            $PingResult="$ComputerName,responds to ping"
            }
        else
            {
            $PingResult="$ComputerName,does not respond to ping"
            }
        out-file -Append -Encoding "Default" -FilePath $OutputFile -InputObject $PingResult
    }
    End
    {
    }
}

$InputFile="H:\WindowsPowerShell\PingThese.csv"
$OutputFile="H:\WindowsPowerShell\PingResults.csv"
Import-Csv $InputFile | Test-ListOfServers

Tuesday, March 8, 2016

Use SCCM to Centrally Manage Java Configuration

Managing java settings, from site exceptions lists to trusted certs to which TLS versions to use, has been a major pain in the butt for techs and administrators for a long time.  Using SCCM's Compliance Settings (formerly Desired Configuration Management) we can make fairly quick work of this.

I am going to give you is two scripts.  The first one checks the four files against your central store to be sure that what is on the client machine matches the central store (discovery script).  The second script copies the files from your central store to the client machine (remediation script).  The contents of the four Java settings/configuration files are up to you.

Steps to set up your configuration item:
  • Build a configuration item with a compliance rule of type "Script" and data type "String".
  • Copy the scripts into the Discovery Script and Remediation Script areas of your CI.  Both scripts are powershell.
  • Put your Java files in a central place that all of your machine accounts have access to read (users don't need access but the machines do).
  • Modify the Discovery Script and Remediation Script in your CI and change the CentralFileLocation variable to point to the location of your files.
  • Add a compliance rule that evaluates as compliant if the value returned by the script = "True All Java files pass all checks."
  • Check the check-box "Run the specified remediation script when this setting is noncompliant"
  • Add the CI to a baseline and deploy the baseline to a collection.  Be sure that in the deployment you have selected "Remediate noncompliant rules when supported".

Discovery Script:

<#
.Synopsis
   Check the files in %windir%\Sun\Java\Deployment against centrally managed files.
.DESCRIPTION
   This script is meant to be used in conjunction with the Copy_JavaSettings.ps1 script to
   manage Java JRE settings from a central location.  This script checks the files on the
   local workstation.  If the files do not match the central repository then Copy_JavaSettings.ps1
   can be used to write the correct files.
   It is suggested that the two files be used together in an SCCM configuration item for checking
   and automated remediation.
   You will need to set $CentralFileLocation to the location of your centrally managed files.
   That should be the only change that you need to make to the script.  It is suggested that
   you use a location in your sysvol as shown in the example.
   Success condition will return "True All Java files pass all checks."
   Failure conditions will return "False " followed by the reason for failure.
#>

function Check-LocalJavaFolder
{
    [OutputType([Boolean],[String])]
    $Compliant=$false
    $JavaFolderExist=Test-Path $JavaFolder
    #write-host ("Checking Java Folder Existence")
    if ($JavaFolderExist -eq $true)
    {
        $Compliant=$true
        $Problem="Java Folder Exists in $JavaFolder"
    }
    else
    {
        $Compliant=$false
        $Problem='Folder %windir%\Sun\Java\Deployment does not exist.'
    }
    Return $Compliant, $Problem
}

function Check-JavaSettingsFileExist
{
    [OutputType([Boolean],[String])]
    $Compliant=$false
    $DeploymentConfigFileExist=Test-Path $DeploymentConfigFile
    $DeploymentPropertiesFileExist=Test-Path $DeploymentPropertiesFile
    $ExceptionSitesFileExist=Test-Path $ExceptionSitesFile
    $TrustedCertsFileExist=Test-Path $TrustedCertsFile
    if ($DeploymentConfigFileExist -ne $true)
    {
        $Compliant=$false
        $Problem="$DeploymentConfigFile does not exist."
        Return $Compliant, $Problem
    }
    if ($DeploymentPropertiesFileExist -ne $true)
    {
        $Compliant=$false
        $Problem="$DeploymentPropertiesFile does not exist."
        Return $Compliant, $Problem
    }
    if ($ExceptionSitesFileExist -ne $true)
    {
        $Compliant=$false
        $Problem="$ExceptionSitesFile does not exist."
        Return $Compliant, $Problem
    }
    if ($TrustedCertsFileExist -ne $true)
    {
        $Compliant=$false
        $Problem="$TrustedCertsFile does not exist."
        Return $Compliant, $Problem
    }
    $Compliant=$true
    $Problem='All managed files exist.'
    Return $Compliant, $Problem
}

function Check-JavaSettingsFileSizes
{
    [OutputType([Boolean],[String])]
    $Compliant=$false
    $DeploymentConfigFileSize=(Get-Item $CentralDeploymentConfigFile).length
    $DeploymentConfigLocalFileSize=(Get-Item $DeploymentConfigFile).Length
    if ($DeploymentConfigLocalFileSize -ne $DeploymentConfigFileSize)
    {
        $Compliant=$false
        $Problem="$DeploymentConfigFile file size is not correct."
        Return $Compliant, $Problem
    }
    $DeploymentPropertiesFileSize=(Get-Item $CentralDeploymentPropertiesFile).length
    $DeploymentPropertiesLocalFileSize=(Get-Item $DeploymentPropertiesFile).Length
    if ($DeploymentPropertiesLocalFileSize -ne $DeploymentPropertiesFileSize)
    {
        $Compliant=$false
        $Problem="$DeploymentPropertiesFile file size is not correct."
        Return $Compliant, $Problem
    }
    $ExceptionSitesFileSize=(Get-Item $CentralExceptionSitesFile).length
    $ExceptionSitesLocalFileSize=(Get-Item $ExceptionSitesFile).Length
    if ($ExceptionSitesLocalFileSize -ne $ExceptionSitesFileSize)
    {
        $Compliant=$false
        $Problem="$ExceptionSitesFile file size is not correct."
        Return $Compliant, $Problem
    }
    $TrustedCertsFileSize=(Get-Item $CentralTrustedCertsFile).length
    $TrustedCertsLocalFileSize=(Get-Item $TrustedCertsFile).Length
    if ($TrustedCertsLocalFileSize -ne $TrustedCertsFileSize)
    {
        $Compliant=$false
        $Problem="$TrustedCertsFile file size is not correct."
        Return $Compliant, $Problem
    }
    $Compliant=$true
    $Problem='All managed files are correct size.'
    Return $Compliant, $Problem
}

function Check-JavaSettingsFileDates
{
    [OutputType([Boolean],[String])]
    $Compliant=$false
    $DeploymentConfigFileDate=(Get-Item $CentralDeploymentConfigFile).length
    $DeploymentConfigLocalFileDate=(Get-Item $DeploymentConfigFile).Length
    if ($DeploymentConfigLocalFileDate -ne $DeploymentConfigFileDate)
    {
        $Compliant=$false
        $Problem="$DeploymentConfigFile file date is not correct."
        Return $Compliant, $Problem
    }
    $DeploymentPropertiesFileDate=(Get-Item $CentralDeploymentPropertiesFile).length
    $DeploymentPropertiesLocalFileDate=(Get-Item $DeploymentPropertiesFile).Length
    if ($DeploymentPropertiesLocalFileDate -ne $DeploymentPropertiesFileDate)
    {
        $Compliant=$false
        $Problem="$DeploymentPropertiesFile file date is not correct."
        Return $Compliant, $Problem
    }
    $ExceptionSitesFileDate=(Get-Item $CentralExceptionSitesFile).length
    $ExceptionSitesLocalFileDate=(Get-Item $ExceptionSitesFile).Length
    if ($ExceptionSitesLocalFileDate -ne $ExceptionSitesFileDate)
    {
        $Compliant=$false
        $Problem="$ExceptionSitesFile file date is not correct."
        Return $Compliant, $Problem
    }
    $TrustedCertsFileDate=(Get-Item $CentralTrustedCertsFile).length
    $TrustedCertsLocalFileDate=(Get-Item $TrustedCertsFile).Length
    if ($TrustedCertsLocalFileDate -ne $TrustedCertsFileDate)
    {
        $Compliant=$false
        $Problem="$TrustedCertsFile file date is not correct."
        Return $Compliant, $Problem
    }
    $Compliant=$true
    $Problem='All managed files are correct dates.'
    Return $Compliant, $Problem
}


$CentralFileLocation="\\domain.com\sysvol\domain.com\Java"
$WindowsFolder=$env:windir
$JavaFolder="$WindowsFolder\Sun\Java\Deployment"
$DeploymentConfigFile=$JavaFolder+"\deployment.config"
$DeploymentPropertiesFile=$JavaFolder+"\deployment.properties"
$ExceptionSitesFile=$JavaFolder+"\exception.sites"
$TrustedCertsFile=$JavaFolder+"\trusted.certs"
$CentralDeploymentConfigFile=$CentralFileLocation+"\deployment.config"
$CentralDeploymentPropertiesFile=$CentralFileLocation+"\deployment.properties"
$CentralExceptionSitesFile=$CentralFileLocation+"\exception.sites"
$CentralTrustedCertsFile=$CentralFileLocation+"\trusted.certs"
$Compliant=$false
$Problem='Compliant'

clear-host
$Compliance=(Check-LocalJavaFolder)
If ($Compliance -match $true)
    {
        $Compliance=(Check-JavaSettingsFileExist)
    }
If ($Compliance -match $true)
    {
        $Compliance=(Check-JavaSettingsFileSizes)
    }
If ($Compliance -match $true)
    {
        $Compliance=(Check-JavaSettingsFileDates)
    }
If ($Compliance -match $true)
    {
        $Compliance=($true, "All Java files pass all checks.")
        write-host $Compliance
    }
    else
    {
        write-host $Compliance
    }



Remediation Script:


<#
.Synopsis
    Copies files used for centralized management of Java JRE from central location to local workstation.
.DESCRIPTION
   This script will copy the centrally managed Java config files from a
   central repository (definied by $CentralFileLocation) to %windir%\Sun\Java\Deployment
   You will need to set $CentralFileLocation to the location of your centrally managed files.
   That should be the only change that you need to make to the script.  It is suggested that
   you use a location in your sysvol as shown in the example.
#>

$CentralFileLocation="\\domain.com\sysvol\domain.com\Java"
$WindowsFolder=$env:windir
$JavaFolder="$WindowsFolder\Sun\Java\Deployment"
$DeploymentConfigFile=$JavaFolder+"\deployment.config"
$DeploymentPropertiesFile=$JavaFolder+"\deployment.properties"
$ExceptionSitesFile=$JavaFolder+"\exception.sites"
$TrustedCertsFile=$JavaFolder+"\trusted.certs"
$CentralDeploymentConfigFile=$CentralFileLocation+"\deployment.config"
$CentralDeploymentPropertiesFile=$CentralFileLocation+"\deployment.properties"
$CentralExceptionSitesFile=$CentralFileLocation+"\exception.sites"
$CentralTrustedCertsFile=$CentralFileLocation+"\trusted.certs"

#Check first part of path - if it doesn't exist then create it
  $JavaFolder="$WindowsFolder\Sun"
  $JavaFolderExist=Test-Path $JavaFolder
  if ($JavaFolderExist -eq $false) {New-Item $JavaFolder -type directory}
#Check second part of path - if it doesn't exist then create it
  $JavaFolder="$WindowsFolder\Sun\Java"
  $JavaFolderExist=Test-Path $JavaFolder
  if ($JavaFolderExist -eq $false) {New-Item $JavaFolder -type directory}
#Check last part of path - if it doesn't exist then create it
  $JavaFolder="$WindowsFolder\Sun\Java\Deployment"
  $JavaFolderExist=Test-Path $JavaFolder
  if ($JavaFolderExist -eq $false)     {New-Item $JavaFolder -type directory}

Clear-Host

#clear any contents that might already be there
Get-ChildItem -Path $JavaFolder -Include *.* -File -Recurse | foreach { $_.Delete()}

#Drop in our configuration files
Copy-Item -path $CentralDeploymentConfigFile -destination $DeploymentConfigFile
Copy-Item -path $CentralDeploymentPropertiesFile -destination $DeploymentPropertiesFile
Copy-Item -path $CentralExceptionSitesFile -destination $ExceptionSitesFile
Copy-Item -path $CentralTrustedCertsFile -destination $TrustedCertsFile

Tuesday, March 1, 2016

Loading the SCOM PowerShell Module

Loading the SCOM PowerShell Module

This is a little script that I got from cchamp over on Technet that will allow you to load up the SCOM module into your local PowerShell without having to load the entire SCOM console.

-----------------------------------------------------------
-----------------------------------------------------------

$OMCmdletsTest = (Get-Module|% {$_.Name}) -Join ' '
If (!$OMCmdletsTest.Contains('OperationsManager')) {
    $ModuleFound = $false
    $SetupKeys = @('HKLM:\Software\Microsoft\Microsoft Operations Manager\3.0\Setup',       
        'HKLM:\SOFTWARE\Microsoft\System Center Operations Manager\12\Setup')
    foreach($setupKey in $SetupKeys) {
        If ((Test-Path $setupKey) -and ($ModuleFound -eq $false)) {
            $setupKey = Get-Item -Path $setupKey           
            $installDirectory = $setupKey.GetValue('InstallDirectory')
            $psmPath = $installdirectory + '\Powershell\OperationsManager\OperationsManager.psm1'
            If (Test-Path $psmPath) {
                $ModuleFound = $true
            }
        }
    }
    If ($ModuleFound) {
        Import-Module $psmPath
    } else {
        Import-Module OperationsManager
    }
}

-----------------------------------------------------------
-----------------------------------------------------------

Thanks cchamp.