Add and remove lines in text based config files with PowerShell

7. April 2017

blog.feldmann.io

PowerShell Version: >4 
Modules: none

I want to take some text file and look for a specific line, then delete that line and add some other line at the end of the file. Here’s how I made it work:

# define file for input/output
$outfile = "C:\test.properties"
# set line to be removed
$old_content = 'install=1'
# set line to be added
$new_content = 'install=0'

#check if new line is already in place and then skip
$check = get-content $outfile | select-string -pattern $new_content
if (!$check)
    {
    #use temporary variable for enabling overwrite
    $tmp = get-content $outfile | select-string -pattern $old_content -notmatch
    $tmp | Set-Content $outfile
    # add new content at the end of the file
    $new_content | Out-File $outfile -Append ascii
    }

Backup HP ProCurve Switches via SSH, TFTP and PowerShell

15. February 2017

blog.feldmann.io

PowerShell Version: >4 
Modules: Posh-SSH

So, this is a pretty specific one! I have been tasked to backup old and new ProCurve Switches and of course I am using PowerShell for this 😉 I found a script about doing this via SFTP where you enable ip ssh filetransfer on the switches and get the files from there but unfortunately two of the 2610’s in the environment do a reboot on every sftp connection…so…that is not an option here 😉 The only good alternative I came up with was to SSH on the client and use TFTP to copy the configs where you want them. The tftp client is enabled on the ProCurve’s by default so what you need to do this is PowerShell with Posh-SSH, and a TFTP Server:

#import posh-ssh
Import-Module -name posh-ssh

# globals
$today = Get-Date -Format "ddMMyyy"
$month = Get-Date -Format MMMM
$year = Get-Date -Format "yyyy"
$tftp_server = "IP OF YOUR TFTP SERVER"

# create a folder for every year
Get-Item "C:\switch_backup\$year\" -ErrorAction SilentlyContinue
if (!$?)
    {
    New-Item "C:\switch_backup\$year\" -ItemType Directory
    }

# create a folder for every month
Get-Item "C:\switch_backup\$year\$month\" -ErrorAction SilentlyContinue
if (!$?)
    {
    New-Item "C:\switch_backup\$year\$month\" -ItemType Directory
    }

# create a folder for every day
Get-Item "C:\switch_backup\$year\$month\$today\" -ErrorAction SilentlyContinue
if (!$?)
    {
    New-Item "C:\switch_backup\$year\$month\$today\" -ItemType Directory
    }

# simple credential handling
$username = "manager"
$pwfile = "C:\tmp\cred.txt"
$Credentials=New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, (Get-Content $pwfile | ConvertTo-SecureString)

# put all the devices in this array
$switches_array = @()
$switches_array = "SWITCHIP","SWITCHIP","SWITCHIP"

foreach ($switch in $switches_array)
    {
    # create a folder for every device
    Get-Item "C:\switch_backup\$year\$month\$today\$switch" -ErrorAction SilentlyContinue
    if (!$?)
        {
        New-Item "C:\switch_backup\$year\$month\$today\$switch" -ItemType Directory
        }
    # start the SSH Session
    New-SSHSession -ComputerName $switch -Credential $Credentials -AcceptKey:$true
    $session = Get-SSHSession -Index 0
    # usual SSH won't work, we need a shell stream for the procurve
    $stream = $session.Session.CreateShellStream("dumb", 0, 0, 0, 0, 1000)
    # send a "space" for the "Press any key to continue" and wait before you issue the next command
    $stream.Write("`n")
    Sleep 5
    # copy startup-config and wait before you issue the next command
    $stream.Write("copy startup-config tftp $tftp_server \$year\$month\$today\$switch\startup-config`n")
    Sleep 10
    # copy running-config and wait before you issue the next command
    $stream.Write("copy running-config tftp $tftp_server \$year\$month\$today\$switch\running-config`n")
    Sleep 10
    # disconnect from host
    Remove-SSHSession -SessionId 0
    # compare running and startup config and remove running-config if equal
    $running_config = Get-Content C:\switch_backup\$year\$month\$today\$switch\running-config -ErrorAction SilentlyContinue
    $startup_config = Get-Content C:\switch_backup\$year\$month\$today\$switch\startup-config -ErrorAction SilentlyContinue
    $comparison = Compare-Object -ReferenceObject $startup_config -DifferenceObject $running_config
    if (!$comparison)
        {
        Remove-Item C:\switch_backup\$year\$month\$today\$switch\running-config -Force
        }
    }

As for the creds I chose a password file as secure-string – not the safest but fairly easy to work with:

Read-Host "Enter Password:" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File "C:\tmp\cred.txt"

 

Monitor scheduled Tasks using PowerShell and check_mk

2. February 2017

blog.feldmann.io

PowerShell Version: >1 
Modules: none

As I am focused on a new check_mk implementation there might be a lot of PowerShell/check_mk around here for a bit 😉 This time I wrote something to monitor the result of a scheduled task the oldschool way. With PowerShell >4 you can use the Get-ScheduledTask function as shown here but as I want this to run even on PowerShell v1 I used the old schtasks /query command line function:

#enter task name with path in scheduler
$name = "TEST"
#gather information
$scheduled_task = schtasks /query /TN $name /v /fo LIST
# EN - "Last Result" ; DE - "Letztes Ergebnis"
$scheduled_task_result = $scheduled_task | Select-String -Pattern "Last Result"
#fix name for check_mk
if ($name -like "*\*")
    {
    $name = $name -replace '.*\\',''
    }
#task errorstate 1
if ($scheduled_task_result -like "*1*")
    {
    echo `<`<`<local`>`>`>
    echo "2 $name $name=1 Scheduled Task not successful!"
    }
#task successful
elseif ($scheduled_task_result -like "*0*")
    {
    echo `<`<`<local`>`>`>
    echo "0 $name $name=0 Scheduled Task successful!"
    }
#task errorstate $?
else
    {
    echo `<`<`<local`>`>`>
    echo "1 $name $name=2 Status unclear!"
    }

Using PowerShell and check_mk to monitor Citrix Licensing Usage

24. January 2017

blog.feldmann.io

PowerShell Version: >4 
Modules: none

I just wrote a custom local check for check_mk to get the Citrix Licensing Usage checked. All you need to do is fill in the $license_server and maybe customize $percent_warning and $percent_critical. The check reads the licensing WMI and gives you OK / WARNING / CRIT in check_mk for the corresponding variables. It will also show the actual used licenses as performance data:

# reset arrays
$inusecount = @()
$count = @()

# globals

$license_server = "YOUR_LICENSE_SERVER"
$percent_warning = "90"
$percent_critical = "95"

# script
 
$citrix_licenses = Get-WmiObject -class “Citrix_GT_License_Pool” -namespace “ROOT\CitrixLicensing” -ComputerName $license_server| select Count, InUseCount
foreach ($citrix_license in $citrix_licenses)
    {
    if ($($citrix_license.inusecount) -ne "0")
        {
        $inusecount += $($citrix_license.inusecount)
        $count += $($citrix_license.Count)
        }
    }

# summing up variables for multiple licenses

$inusecount_sum = $(($inusecount | Measure-Object -Sum).Sum)
$count_sum = $(($count | Measure-Object -Sum).Sum)

# calculation and echo

$PercentageNum = [math]::round(($inusecount_sum/$count_sum)*100,0)
if ($PercentageNum -lt $percent_warning)
    {
    echo `<`<`<local`>`>`>
    echo "0 CitrixLicensing used_licenses=$inusecount_sum $PercentageNum percent of licenses in use ($inusecount_sum of $count_sum)"
    }
elseif ($PercentageNum -gt $percent_critical)
    {
    echo `<`<`<local`>`>`>
    echo "2 CitrixLicensing used_licenses=$inusecount_sum $PercentageNum percent of licenses in use ($inusecount_sum of $count_sum)"
    }
else
    {
    echo `<`<`<local`>`>`>
    echo "1 CitrixLicensing used_licenses=$inusecount_sum $PercentageNum percent of licenses in use ($inusecount_sum of $count_sum)"
    }

Removing vCenter Snapshots with PowerShell and VMware vSphere PowerCLI

24. November 2016

blog.feldmann.io

PowerShell Version: >4 
Modules: VMware vSphere PowerCLI

While having issues with HP Data Protector and VMware backups I created a script that logs in to different vCenter Servers and checks for Data Protector Snapshots that are older than x and removes them. This script can easily be customized to suite your environment and needs.

# variables
$name = "_DP_VEPA_SNAP_"
$hours = "0"
$vcenters = "VCENTER1-FQDN","VCENTER2-FQDN"

$vc_username = "USERNAME"
$vc_password = "PASSWORD"

# load powercli commands
add-pssnapin vmw* | out-null

# for every vcenter
foreach ($vcenter in $vcenters)
    {
    # connect
    Connect-VIServer -Server $vcenter -User $vc_username -Password $vc_password -WarningAction SilentlyContinue
    $snapshots = Get-Snapshot -vm * -name "$name" | Where {$_.Created -lt (Get-Date).AddHours($hours)}
        if ($? -eq $false)
            {
            "### $vcenter ###"
            "No matching Snapshots found."
            }
        else
            {
            "### $vcenter ###"
            $snapshots | select VM, @{Name="Age";Expression={((Get-Date)-$_.Created).Hours}}, SizeMB | Format-Table -Autosize
            # remove snapshots
            "Deleting..."
            $snapshots | Remove-Snapshot -confirm:$false
            "Done!"
            }
    # disconnect
    Disconnect-VIServer -Confirm:$False | out-null
    }

Look for unused GPOs with PowerShell

blog.feldmann.io

PowerShell Version: 4 
Modules: GroupPolicy

If you find yourself lost in the jungle of existing GPOs in your environment use some PowerShell to sort out the ones that might not be needed anymore.

# first of all get all GPOs that are available in the domain - make sure to use a domain administrator account for this
$gpos = Get-GPO -All
$gpo_compare_unlinked = @()
$gpo_compare_notapplied = @()

"Unlinked GPOs:"
foreach ($gpo in $gpos)
    {
     # to gather all needed information we will create a xml report of every GPO and sort out what we need...if GPO.LinksTo.SOMPath is empty the GPO is not linked
    [xml]$xmldata = Get-GPOReport -ReportType xml -Name $($gpo.Displayname)  
    if ($($xmldata.GPO.LinksTo.SOMPath) -eq $null)
        {
        #output the name of the unlinked GPO
        $gpo.Displayname
        # add the name to an array for later comparison
        $gpo_compare_unlinked += $gpo.Displayname   
        }
    }
# add a line break for a better overview
"`r"
"GPOs that are linked but deactivated:"
foreach ($gpo in $gpos)
    {
    # these GPOs are linked but their status is disabled as seen in GPO.LinksTo.enabled
    [xml]$xmldata = Get-GPOReport -ReportType xml -Name $($gpo.Displayname)
    if ($($xmldata.GPO.LinksTo.enabled)-eq $false)
        {
        $gpo.Displayname
        }
    }
 "`r"
"GPOs that are not applied:"
foreach ($gpo in $gpos)
    {
    # this was a bit tricky as the xml data does not show the information we need, it only shows the status -Apply Group Policy- if it is assigned
    [xml]$xmldata = Get-GPOReport -ReportType xml -Name $($gpo.Displayname)
    if ($($($xmldata.GPO.SecurityDescriptor.Permissions.TrusteePermissions.Standard.GPOGroupedAccessEnum) | where {($_ -like "Apply Group Policy")}) -ne "Apply Group Policy")
        {
        $gpo.Displayname
        $gpo_compare_notapplied += $gpo.Displayname
        }
    }
 "`r"
"GPOS that are neither linked nor applied:"
# use compare of the two created arrays to find the GPOs that are basically trash (unused)
(Compare-Object $gpo_compare_unlinked $gpo_compare_notapplied -IncludeEqual | where SideIndicator -eq "==").InputObject

Create check_mk host checks with PowerShell

4. December 2015

blog.feldmann.io

PowerShell Version: >2 
Modules: none

If you use check_mk to monitor your environment you are able to create simple local host checks using PowerShell.

Your check_mk agent should be installed on the target host in a specific location, such as C:\Program Files (x86)\check_mk

After the agent installation you should have a local folder (C:\Program Files (x86)\check_mk\local) in which you can put a simple PowerShell Get-Content script to return whatever you have scripted in a check_mk readable format:

Get-Content -path "C:\Program Files (x86)\check_mk\tmp\xxxxx.log"

To achieve the check_mk readable format make sure to print your scripted output into something like this:

<<<local>>>
0 CHECK_NAME – OK – CHECK_OUTPUT|PERF_DATA_DESCRIPTION=CHECK_OUTPUT(INT)

I created a short example with a folder count:

$outfile = "C:\Program Files (x86)\check_mk\tmp\xxxxx.log"
#check folder count on c
$c_folder_count = (Get-Childitem c:\ | measure).count
#check_mk output
echo `<`<`<local`>`>`> | Out-File $outfile
echo "0 FOLDER-COUNT-C - OK - We have $c_folder_count folders on drive C|folders_on_c=$c_folder_count" | Out-File $outfile -Append

The output to file C:\Program Files (x86)\check_mk\tmp\xxxx.log is:

<<<local>>>
0 FOLDER-COUNT-C – OK – We have 40 folders on drive C|folders_on_c=40

You are now able to do a short cmk -II hostname && cmk -R on your check_mk server and the new check should appear on your host.

The next step would be different states for different results:

<<<local>>>
0 CHECK_NAME – OKCHECK_OUTPUT|PERF_DATA_DESCRIPTION=CHECK_OUTPUT(INT)

<<<local>>>
1 CHECK_NAME – WARNING CHECK_OUTPUT|PERF_DATA_DESCRIPTION=CHECK_OUTPUT(INT)

<<<local>>>
2 CHECK_NAME – CRITICAL CHECK_OUTPUT|PERF_DATA_DESCRIPTION=CHECK_OUTPUT(INT)

With a simple if we could generate different outputs for different cases:

$outfile = "C:\Program Files (x86)\check_mk\tmp\xxxxx.log"

#check folder count on c
$c_folder_count = (Get-Childitem c:\ | measure).count

#check_mk output
echo `<`<`<local`>`>`> | Out-File $outfile
if ($c_folder_count -lt 30)
    {
    #if count is below 100 we are fine
    echo "0 FOLDER-COUNT-C - OK - We have $c_folder_count folders on drive C|folders_on_c=$c_folder_count" | Out-File $outfile -Append
    }
else
    {
    #if the count is over 100 we get create a warning
    echo "1 FOLDER-COUNT-C - WARNING - We have $c_folder_count folders on drive C|folders_on_c=$c_folder_count" | Out-File $outfile -Append
    }

It is important to give only the second Out-File an Append, so that the output will be overwritten every time the script is running.

You could now create a local task, running the script once a week/day/hour to generate the output for check_mk.

Restart host after certain days of uptime

3. December 2015

blog.feldmann.io

PowerShell Version: >1
Modules: none

Nobody wants Windows hosts with too much uptime so this script combined with a planned task during the weekly maintenance downtime might help:

# prepare WMI class
$gwmi= Get-WmiObject -Class Win32_OperatingSystem -Computer localhost
# convert LastBootUpTime and select only the "Days" value
$uptime = [DateTime]::Now - $gwmi.ConvertToDateTime($gwmi.LastBootUpTime) | select Days
# check if uptime is gt 60 days and if so -> restart
if ($uptime.days -gt 60)
    {
    Restart-Computer
    }

You might want to use the -force parameter on Restart-Computer for an immediate restart.

Receive gateway from local network interface with PowerShell

25. November 2015

blog.feldmann.io

PowerShell Version: >1
Modules: none

I needed a way to log the first local interface’s gateway for debugging reasons, here is how I did it:

$netcards = Get-WmiObject Win32_NetworkAdapterConfiguration
# reset the array with every run of the script
$netcard_gateway_array = @()
    # ...we might have more than one network interface present
    foreach ($netcard in $netcards)
        {
        # don't show interfaces without default gw
        if ($netcard.DefaultIPGateway -ne $null)
            {
            # for more than one interface present we need an array to store the information
            $netcard_gateway_array += $netcard.DefaultIPGateway
            }
        }
# receive only the first interface's gateway
$netcard_gateway_array[0]

Enable Users to log off their own RDP TS session with PowerShell

24. November 2015

ps

PowerShell Version: >1
Modules: none

This script will check your TS servers for a session with $env:Username and logoff that session. This of course only works if $env:username on client and TS are the same 😉 Also I made an effort to make it work on PS v1 for all clients to be able to execute this.

# mention all terminal servers here
$server1 = "SERVER1-FQDN"
$server2 = "SERVER2-FQDN"

# build an array for the foreach
$ts_server_array = @()
$ts_server_array += $server1
$ts_server_array += $server2

# do this for each terminal server in the environment
foreach ($ts_server in $ts_server_array)
    {
        # use qwinsta for oldschool session log off (maximum compatibility)
        $qwinsta = qwinsta /server:$ts_server | ForEach-Object {$_.Trim() -replace "\s+",","}
        $activeusersTS = @()
        foreach ($item in $qwinsta)
            {
            if ($item -like "rdp*")
                {
                # remove the "rdp-tcp#... if present"
                $activeusersTS += $item -replace "^(.*?),",""
                }
            else
                {
                # if there is no "rdp-tcp#..." in the line we're good
                $activeusersTS += $item
                }
            }
        foreach ($user in $activeusersTS)
            {
            if ($user -like "$env:Username*")
                {
                # get User line and remove everything left but the Session ID
                $userid = $user -replace "^(.*?),","" -replace ",.*",""
                # logoff User
                rwinsta $userid /server:$ts_server
                }
            }
    }
← Previous Page