PS – nslookup to serverlist

Hello together,

here i have a simple but nice snippet. This executes a “nslookup” in powershell on a list of computers.

Small but very effective:

$servers = get-content "path_to_the_file"
foreach ($server in $servers) {
$addresses = [System.Net.Dns]::GetHostAddresses($server)
foreach($a in $addresses) {
"{0},{1}" -f $server, $a.IPAddressToString
}}

~David

PS – Get-LastLinesFromFile

Hello together,

sometimes we want to open big files and read the data from it. And by big i mean really big. But many times we also only need some of the last lines – may be the newest ones. Herefore you can use the following script. By this way you do not load the whole file into cache first. This is fast and prevents also upcoming crashes.

<#
.Synopsis
   Gets last Lines of a file.
.EXAMPLE
    Get-LastLinesFromFile -Path "c:\Temp\BigData.csv" -Last 20
#>
function Get-LastLinesFromFile{
param ( $Path, 
      [int]$Last = 10, 
      [int]$ApproxCharsPerLine = 50
) 
    $item = (Get-item $path) 
    if (-not $item) {return} 
    $Stream = $item.Open([System.IO.FileMode]::Open, 
                       [System.IO.FileAccess]::Read, 
                        [System.IO.FileShare]::ReadWrite) 
    $reader = New-Object System.IO.StreamReader($Stream) 

    #Retrieving first set of Lines
    if ($charsPerLine * $last -lt $item.length) {
         $buf=$reader.BaseStream.seek((-1 * $last * $ApproxCharsPerLine) ,[System.IO.SeekOrigin]::End) 
    } 

    $content=$reader.ReadToEnd()
    $LineCount=($content -Split "`n").Count
    $CharsMax = $last * $ApproxCharsPerLine
    $CharsMin = 0

    while ($LineCount -ne $Last)
    {
        if ($LineCount -gt $Last)
        {
            #$content=$reader.ReadToEnd()
            $content=$content -split "`n" -replace "\s+$","" | Select-Object -last $Last
            $LineCount=($content -Split "`n").Count
        
            #obsolete method to near to aiming number
            #new method has better performance
            #$CharsMin =$CharsMin+ ([Math]::Round(($CharsMax-$CharsMin)/2))
            #$reader.BaseStream.seek(-1 * ($CharsMax-$CharsMin),[System.IO.SeekOrigin]::End) 
            #$content=$reader.ReadToEnd()
            #$LineCount=($content -Split "`n").Count
        }
        else
        {
            $CharsMax = $CharsMax+ ([Math]::Round(($CharsMax-$CharsMin)*2))
            $buf=$reader.BaseStream.seek(-1 * ($CharsMax-$CharsMin) ,[System.IO.SeekOrigin]::End) 
            $content=$reader.ReadToEnd()
            $LineCount=($content  -Split "`n").Count
        }
    }

    $Stream.Close() 
    $reader.Close() 

    return $content
}

Have fun with it.

~David

PS – DFS-Export

Hello all,

to export DFS-namespaces the tool dfsutil can be used.

Here is an simple example herefore.

$DFSRoots = (Get-DFSNRoot).Path
$Date = Get-Date -Format yyy-MM-dd
ForEach ($Root in $DFSRoots)
    { 
       dfsutil root export $($Root) $("D:\DFSBCK-$Root-$Date.xml")
    }

~David

PS – Retrieve desktop content of many computers

Hello all,

here i have an example which shows how to get the desktop content from many computers.
A nice feature here is that it gets the desktops of the signed on users. So you won´t grab unnecessary contents.

$Computers = 'localhost'

foreach ($Computer in $Computers){

    #Gets the logged on user
    $User = Get-WmiObject win32_logonsession -ComputerName $Computer -Filter "Logontype = '2' or Logontype='11' or logontype='10'" |
                foreach {Get-WmiObject win32_loggedonuser -ComputerName $Computer -filter "Dependent = '\\\\.\\root\\cimv2:Win32_LogonSession.LogonId=`"$($_.logonid)`"'" | select Antecedent } |
                foreach { ($_.antecedent.split('"'))[1] + "\" + ($_.antecedent.split('"'))[3] } | Where-Object{$_ -notlike "*DWM*"} | select -unique

    #Catches Domain name
    if ($user -like "*\*")
    {
        $user = $user.Split('\')[-1]
    }

    $searchPath="\\$Computer\C$\Users\$User\Desktop\"
    Get-ChildItem -Path $searchPath -Recurse
}

This logic can also be modified easily for other purposes.

Have fun with it!

~David

PS – LoggedOnUser

Hello all,

the following code shows how to gather the actual logged on user. This snippet can be integradted easily in other scripts.


Get-WmiObject win32_logonsession -ComputerName $comp -Filter "Logontype = '2' or Logontype='11' or logontype='10'" |
foreach {Get-WmiObject win32_loggedonuser -ComputerName $comp -filter "Dependent = '\\\\.\\root\\cimv2:Win32_LogonSession.LogonId=`"$($_.logonid)`"'" | select Antecedent } |
foreach { ($_.antecedent.split('"'))[1] + "\" + ($_.antecedent.split('"'))[3] } |  Where-Object{$_ -notlike "*DWM*"} | select -unique

~David

Windows Client – failing Updates

Hello together,

i have worked in a 3rd level support desk where we had to fight against many errors with Windows updates. By this time i worked out a script which fixed nearly all of the errors at the first try.

The DISM commands are only availlable on Windows 8 / 10 devices.
Just copy the whole script into a batch file and execute it as administrator.

DISM /Online /Cleanup-Image /CheckHealth
DISM /Online /Cleanup-Image /ScanHealth
DISM /Online /Cleanup-Image /RestoreHealth
DISM /Online /Cleanup-Image /ScanHealth

sfc /scannow
findstr /c:"[SR]" %windir%\logs\cbs\cbs.log > c:\windows\logs\cbs\sfcdetails.log

net stop wuauserv
net stop cryptSvc
net stop bits
net stop msiserver
ren %WINDIR%\SoftwareDistribution SoftwareDistribution.bak
ren %WINDIR%\System32\catroot2 catroot2.bak
net start wuauserv
net start cryptSvc
net start bits
net start msiserver

fsutil resource setautoreset true c:\
echo #### Info:
fsutil resource info C:

echo MSI
sc config msiserver start= demand
Net stop msiserver
MSIExec /unregister
MSIExec /regserver
regsvr32.exe /s %windir%\system32\msi.dll
Net start msiserver
sc config msiserver start= auto

shutdown /g /t 60

In c:\windows\logs\cbs\sfcdetails.log the log of the sfc is stored. It is always good to throw an eye into this log. Sometimes trivial errors are visible in here.

After the usage of the script the computer has to be rebooted twice. This is an experience value because of the sfc and the fsutil command.

If the error persists you have also more steps you can do. One of the first things should be to do a clean boot. Some programs intervent the update process – for example firewall, anti virus etc. If it gets little harder you should analyze the cbs.log which is stored under c:\windows\logs\CBS\cbs.log and also the DISM log (if Win 8 or 10) stored in c:\windows\logs\DISM.

You can gather also some information in the eventlogs. Herefore open the eventviewer and the tab “Installation”. All elements with the ID “3” are errors.

If you use still Windows 7 you have the option to try also the CheckSUR. This “Hotfix” will prove all installed updates and try to repair them. You can find it here.
Be sure that you download the correct version for your OS and let it run. It may take several time up to some hours. When its finished it will also write a log-file in c:\windows\logs\cbs\checksur.log.
You should verify it to get more hints what the main problem is.

If you find any errors you can resolve them like specified here

Good look and please prevent me with feedback if you were lucky using this methode.

~David

PS – parameter by reference

Many developers know them – parameter by reference. By this way the variables will not be duplicated. A pointer is created which points to the actual variable. If you modify this referenced parameter you will modify the referenced variable. So you can make changes to many parameters and will not create a lot of functions with returnvalues.

[string]$Global = ''


function TestRefFunction([ref]$Param)
{
    [string]$Param.Value = '5'
    $Param | Out-Default
}

TestRefFunction([ref]$Global)

$Global | Out-Default

By this example the transmitted parameter “$global” is a reference. So all changes to it in the function will also be written into the global variable. In this example so 2 times a “5” will be given out.

~David

PS – bulk-import of AD-Users with skipping blank values

In the following example an bulk-import of AD-Users is done. The clever thing – blank parameters will be skipped. A condition for this to work properly is that the the headers of the csv-file must match the names in the AD.

Import-Module ActiveDirectory
$users = Import-Csv -Path C:\temp\export\workspace.csv
foreach ($user in $users) {
    $properties = @{}
    $user | Get-Member -Type NoteProperty | Select -Expand Name | Foreach {
        if ($user."$_") { $properties.Add("$_", $user."$_") }
    }
    Set-ADUser -Identity $user.sAMAccountName @properties -Verbose
}

~David

PS – configuration for bigger scripts and simple logging

Hello all

In this post i want to show you how to create a configuration with a XML-file easily.
Lots of configuration parameters can be placed in here so that the end user makes his changes in this XML-file where the impact of modifications can be mitigated. Also it cleans up the source code. By loading all relevant parameters within one file you have to look and store all properties at only one place.

Also you can place complex configuration lists and conditional matrixes in it to load them later on with powershell and filter the outcome.

Some examples:

Loading the XML-file

[xml] $configXml = Get-Content -Path "$PSScriptRoot\Config.xml" -ErrorAction 'Stop'

Yeah – that easy.

Exemplaric structure of a file:

<?xml version="1.0" standalone="yes"?>
<Config>
   <DBConnectionString>Provider = OraOLEDB.Oracle; Data Source = ds ; User Id = USER ; Password = pwuser; OLEDB.NET = True;</DBConnectionString>
   <LogDir>c:\Logs</LogDir>
   <ConfigValue Value1="val1"></ConfigValue>
   <ConditionalMatrix>
     <Parameter Value1="val1" Value2="start" BoolValue="1"></Parameter>
     <Parameter Value1="val1" Value2="end" BoolValue="0"></Parameter>
     <Parameter Value1="val2" Value2="test" BoolValue="1"></Parameter>
     <Parameter Value1="val2" Value2="test" BoolValue="0"></Parameter>
     <Parameter Value1="val3" Value2="prod" BoolValue="1"></Parameter>
     <Parameter Value1="val3" Value2="test" BoolValue="0"></Parameter>
   </ConditionalMatrix>
</Config>

Retrieving the data:

#fetching the data directly
$ConnectionString= $configXML.Config.DBConnectionString
$Value1= $configXML.Config.ConfigValue.Value1

$Value1ByConditionalMatrix= ($configXML.Config.ConditionalMatrix.Parameter  | Where-Object {$_.Value2-eq "test" -and $_.BoolValue-eq "1"}).Value1

Easy isn´t it?! Now you have to load the configuration within the script from a relativ path. This could also be the path where afterwards the loggings could be stored into.

A very simple way for logging is to set gather the logging path from config/relative and write log files with a date in it. So you get an overview for each day.

$LogDir=$configXML.Config.LogDir
$LogFile = "$LogDir\Log" + ((Get-Date).Day) + "_" + ((Get-Date).Month) + "_" + ((Get-Date).Year) + "_" + ((Get-Date).Hour) + "_" + ((Get-Date).Minute) +".log"
(Get-Date).ToString()  + " Something happened $Object" >> $LogFile

This can also be empowered even more if you build a separate function for this. So you can grab executing function, date, user etc. which will be written to a log file.

I hope i could give you some hints of the power with working xml-configuration files and enabling you the view for very simple logging.

~David

PS – loading powershell cmdlets for SCCM

If you have SCCM in use you should also throw an eye into the specific powershell cmdlets.
To use them you must previously load them with “Import-Module”. Use also a dynamic environment variable to do this so you can copy it easily from one computer to another without resetting the specific path.

Import-Module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1)

Exemplaric function:

Get-CMDevice -CollectionName "Win 10 Desktops"

There are up to now a nearly uncountable bunch of cmdlets. You can do nearly everything with these powershell cmdlets in powershell. By automizing some processes you will reduce the errors by human inputs and also increase the speed of this processes.

~David