Saturday, November 28, 2015

SCOM 2012 Network Monitoring: Rename network adapter "PORT-xxx"

Scenario:
Everyone who has implemented SCOM 2012 Network Monitoring will be satisfied with the improvements with respect to SCOM 2007. More and more devices can be deeply monitored with SCOM 2012. One of the downsides at this moment is the naming of the Network Adapters. The name inside SCOM does not always reflect the name on the switch. The alerts send for this network adapters will also contain the DisplayName. The subject of the alert with the above naming will be something like 'Interface PORT-0.15 is flapping'. This alert is not very useful. Sounds familiar?


Solution:
Following XML Management Pack from Arjan Vroege will change the DisplayName of your network adapters in SCOM. The standard names of the network adapters cannot be changed through the standard configuration settings. With this Management Pack the names will be changed by using SNMP variables.
After downloading the MP and importing it your existing Network adapters will be renamed with the following naming convention:

$Displayname = $NodeSysName + " -- " + $Description + " -- " + $InterfaceAlias


This information is collected by SCOM through the Network Discovery and are SNMP variables. If you want to change them you have to change the discovery of the Management Pack. You can find and change the discovery in the Authoring section of SCOM.

<?xml version="1.0" encoding="utf-8"?>
<ManagementPack SchemaVersion="2.0" ContentReadable="true" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Manifest>
    <Identity>
      <ID>Network.Monitoring.ChangeNetworkAdapter</ID>
      <Version>1.0.0.0</Version>
    </Identity>
    <Name>Network.Monitoring.ChangeNetworkAdapter</Name>
    <References>
      <Reference Alias="Windows">
        <ID>Microsoft.Windows.Library</ID>
        <Version>7.5.8501.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="System">
        <ID>System.Library</ID>
        <Version>7.5.8501.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="SNL">
        <ID>System.NetworkManagement.Library</ID>
        <Version>7.1.10226.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
    </References>
  </Manifest>
  <Monitoring>
    <Discoveries>
      <Discovery ID="Network.Monitoring.ChangeNetworkAdapter.Network.Monitoring.ChangeNetworkAdapter.Discovery" Target="SNL!System.NetworkManagement.NetworkAdapter" Enabled="true" ConfirmDelivery="false" Remotable="true" Priority="Normal">
        <Category>Discovery</Category>
        <DiscoveryTypes>
          <DiscoveryClass TypeID="SNL!System.NetworkManagement.NetworkAdapter" />
        </DiscoveryTypes>
        <DataSource ID="DS" TypeID="Windows!Microsoft.Windows.TimedPowerShell.DiscoveryProvider">
          <IntervalSeconds>86400</IntervalSeconds>
          <SyncTime>04:00</SyncTime>
          <ScriptName>NetworkAdapterChange.ps1</ScriptName>
          <ScriptBody>

    # NetworkAdapterChange.ps1
    # Written by Arjan Vroege. All rights reserved.

    param($SourceID, $ManagedEntityID, $Key, $Description, $InterfaceAlias, $DeviceKey, $NodeSysName, $CurDisplayName)
    $Displayname = $NodeSysName + " -- " + $Description + " -- " + $InterfaceAlias

    if ($CurDisplayName -eq $DisplayName) { continue }
    
    $scomapi = new-object -comObject "MOM.ScriptAPI"
    $DiscData = $scomapi.CreateDiscoveryData(0, $SourceID, $ManagedEntityID)

    #fill out the key properties
    $NetworkAdapter = $DiscData.CreateClassInstance("$MPElement[Name='SNL!System.NetworkManagement.NetworkAdapter']$")
    $NetworkAdapter.AddProperty("$MPElement[Name='SNL!System.NetworkManagement.NetworkAdapter']/Key$", $Key)
    $NetworkAdapter.AddProperty("$MPElement[Name='SNL!System.NetworkManagement.Node']/DeviceKey$", $DeviceKey)
    $NetworkAdapter.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", $Displayname)
    $scomapi.LogScriptEvent("NetworkAdapterChange.ps1",101,2, "Discovery was executed for $Devicekey and Current: $curDisplayName --&gt; new: $Displayname")

    # add the WebSite to the DiscoveryData object
    $discData.AddInstance($NetworkAdapter)
    $discData
  </ScriptBody>
          <Parameters>
            <Parameter>
              <Name>SourceID</Name>
              <Value>$MPElement$</Value>
            </Parameter>
            <Parameter>
              <Name>ManagedEntityID</Name>
              <Value>$Target/Id$</Value>
            </Parameter>
            <Parameter>
              <Name>Key</Name>
              <Value>$Target/Property[Type="SNL!System.NetworkManagement.NetworkAdapter"]/Key$</Value>
            </Parameter>
            <Parameter>
              <Name>Description</Name>
              <Value>$Target/Property[Type="SNL!System.NetworkManagement.NetworkAdapter"]/Description$</Value>
            </Parameter>
            <Parameter>
              <Name>InterfaceAlias</Name>
              <Value>$Target/Property[Type="SNL!System.NetworkManagement.NetworkAdapter"]/InterfaceAlias$</Value>
            </Parameter>
            <Parameter>
              <Name>DeviceKey</Name>
              <Value>$Target/Host/Property[Type="SNL!System.NetworkManagement.Node"]/DeviceKey$</Value>
            </Parameter>
            <Parameter>
              <Name>NodeSysName</Name>
              <Value>$Target/Host/Property[Type="SNL!System.NetworkManagement.Node"]/sysName$</Value>
            </Parameter>
            <Parameter>
              <Name>CurDisplayName</Name>
              <Value>$Target/Property[Type="System!System.Entity"]/DisplayName$</Value>
            </Parameter>
          </Parameters>
          <TimeoutSeconds>60</TimeoutSeconds>
          <StrictErrorHandling>true</StrictErrorHandling>
        </DataSource>
      </Discovery>
    </Discoveries>
  </Monitoring>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="Network.Monitoring.ChangeNetworkAdapter.Network.Monitoring.ChangeNetworkAdapter.Discovery">
          <Name>Network.Monitoring.ChangeNetworkAdapter.Discovery</Name>
          <Description>Description for the new discovery.</Description>
        </DisplayString>
      </DisplayStrings>
      <KnowledgeArticles></KnowledgeArticles>
    </LanguagePack>
  </LanguagePacks>
</ManagementPack>

In next article I'll show you how to "convert" GENERIC network device (Juniper EX4550 and Dell Force10 MXL 10/40GbE) to CERTIFIED using just Notepad and no XML MP development effort.

References:

DNS bulk host A and PTR records creation - PowerShell script

Sometimes we need to create many A and PTR DNS records in a short time. Using DNS Management console is not very convenient and fast method because you need to create each records separately by hand. PowerShell 3.0+ (Windows 8/2012+):

Import-CSV records.csv | %{ Add-DNSServerResourceRecordA -ZoneName corp.contoso.com -Name $_."HostName" -IPv4Address $_."IPAddress" -ComputerName <DNSServerName> -CreatePtr}

where records.csv file content would be like:

HostName,IPAddress
server01,192.168.1.101
server02,192.168.1.102
server03,192.168.1.103
server04,192.168.1.104
server05,192.168.1.105

-ComputerName <DNSServerName> is optional and required if you run script for remote DNS server only.

Friday, November 27, 2015

XML Export/Import Dell iDRAC settings via RACADM

For iDRAC 7/8 you can now use RACADM command line interface to configure BIOS, iDRAC, RAID and NIC on the server using XML file, for iDRAC 5/6 you can continue to use legacy methods via export/import ini file. It is very useful when you want to have identical cloned settings across bunch of Dell servers.
You can make use of various racadm interfaces (Local, Remote and Firmware) to export system config to XML file and import the XML config file to the server from XML file. You can use this feature to take a backup of system configuration, configuring multiple server BIOS, iDRAC, RAID and NIC and to script configuration of BIOS, iDRAC, RAID and NIC of the server.

Prerequisites for XML import/export

You need to use Open Manage 7.2+ and iDRAC7+ Firmware 1.30.30+ onward to use this feature. You need to have iDRAC/OS Administrator privilege to do XML export or import.  The read only objects will be enclosed with exclamatory tag in that XML file. iDRAC should have minimum of “express” license to export the xml file.

Export iDRAC 7/8
System Config to XML file:

Using Racadm you can export system configuration to a XML file. This can be done using any racadm interface (Local, Remote and FW Racadm – SSH, Telnet and Serial). Export of the XML file can be done to a Local File (Using Local and Remote Racadm) or to a remote CIFS / NFS share (Using Local, Remote and FW Racadm). Once XML Export command is initiated a job will be created for export operation.
Using racadm jobqueue view -i <JOB ID> command you can get the status of export job. Once job is completed the XML file which contains configuration of BIOS, iDRAC, RAID and NIC will be created on the mentioned location. 

Syntax:
racadm get -t xml -f <filename.xml> [-l <share IP/path>] [-u <username>] [-p <password>]

CIFS Share:
racadm get -t xml -f <filename.xml> -l <//share IP/path> -u <username> -p <password>

NFS Share:
racadm get -t xml -f <filename.xml> -l <share IP:/path>

Local Share:
racadm get -t xml -f <path/filename.xml>

Local folder:
racadm -r <IP address> -u root -p calvin get -t xml -f <filename>.xml


Import iDRAC 7/8 System Config from XML file:

Once System config is exported to XML file, you can import the configuration back to the system any time. You can also modify various setting on XML file before importing the XML file to iDRAC. Similar to Export, Import of the XML file can be done from a Local File (Using Local and Remote Racadm) or from a remote CIFS / NFS share (Using Local, Remote and FW Racadm). After import operation, server goes for reboot if there are any changes on BIOS, RAID and NIC attributes. Server does not require a reboot if there are changes only to iDRAC attributes or if there is no change in that exported XML file. The command itself we can specify the Shutdown type which needs to be done before import starts and end state of the server after import. The shutdown type refers the shutdown method of the server once the Import is begun, supported values “Graceful & Forced”. End state used to specify the Server state once the import operation completes, supported values are “On & Off”. Once XML Import command is executed a job will be created for an import operation.
Using racadm jobqueue view -i <JOB ID> command you can get the status of export job. 

Syntax:
racadm set -t xml -f <filename.xml> [-l <share IP/path>] [-u <username>] [-p <password>] [-b <Shut Down Type>] [-w <Wait Time>] [-s <End Power State>]

CIFS Share:
racadm set -t xml -f <filename.xml> -l <//share IP/path> -u <username> -p <password> [-b <Shutdown type>] [-w <wait time>] [-s <End state>]

NFS Share:
racadm set -t xml -f <filename.xml> -l <share IP:/path> [-b <Shutdown type>] [-w <wait time>] [-s <End state>]

Local Share:
racadm set -t xml -f <path/filename.xml> [-b <Shutdown type>] [-w <wait time>] [-s <End state>]

Local folder:
racadm -r <IP address> -u root -p calvin set -t xml -f <filename>.xml


Sample iDRAC 7/8 config XML file:


Export iDRAC 5/6 System Config to legacy INI file

Syntax:



racadm -r <IP address> -u root -p calvin getconfig -f <filename>.ini

After you have exported  iDRAC configuration to file you can edit it with any text editor and then import to multiple iDRACs. Don't forget to comment or remove "cfgNicIpAddress" and "cfgDNSRacName" from INI file before importing file to another systems to avoid IP conflicts and name duplicates. 

Import iDRAC 5/6 System Config from legacy INI file

Syntax:

racadm
-r <IP address> -u root -p calvin config -f <filename>.ini

You need to reset iDRAC after settings imported:

racadm
-r <IP address> -u root -p calvin racreset

Sample iDRAC 5/6 config INI file:


References:

Thursday, November 26, 2015

Wednesday, November 18, 2015

Clear selected disks configuration on selected multiple servers with PowerShell

Sometimes we rebuild and test environments again and again. 
It is often required to rebuild and clear disks for new attempt.
Following PowerShell script (Windows 8/2012+) will clear selected disks, remove all partition information including OEM, un-initialize, erase all data 
on selected serversIt completely clears any existing storage disk configuration and all data on selected drives PERMANENTLY! It will ask you to confirm if any system or bootable drive detected, it will clear even dynamic disks.

Download PowerShell script from
https://gallery.technet.microsoft.com/Clear-selected-disks-e0026a28 
or copy below:

<#

            Author  : Stanislav Galchonkov
            Website : http://wintech.sgal.info
            Linkedin: https://ua.linkedin.com/in/stanislav-galchonkov-aa560042/
###############################  WARNING  ################################
                                                                  
                  ***    USE AT YOUR OWN RISK    ***              
                   PERMANANT DATA LOSS WILL OCCUR                  
                                                                  
Following PowerShell script (Windows 8/2012+) will clear selected disks, remove all
partition information including OEM, un-initialize, erase all data on
selected servers only. It completely clears any existing storage disk configuration
and all data on SELECTED drives PERMANENTLY! It will ask you to confirm if any
system or bootable drive detected. It will try to put online any offline disks
and clear "read-only" flag on disk if detected.
                                                    
Notes:                                                          
                                                              
Set-ExecutionPolicy -ExecutionPolicy Unrestricted.            
Run cmdlet with Administrator rights.

Usage:
.\ClearDisksOnServers.ps1 -DisksToClear (1..14) -Servers server1,server2,server3,server4,server5,server6

Usage:
.\ClearDisksOnServers.ps1 -DisksToClear (1,2,3,7,8,10,15,19) -Servers server1,server2,server3,server4,server5,server6

If you have CSV file with server list:
Usage:
.\ClearDisksOnServers.ps1 -DisksToClear (1..14) -Servers (Import-Csv Servers.csv).servers

where CSV file Servers.csv content should be with Servers header:

Servers
server1
server2
server3
server4
server5
server6
            
Change log:
11/19/2015: Initial release
  
#>

[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[ValidateNotNull()]
[Array]$DiskNumbersToClear,
  
[Parameter(Mandatory=$True)]
[ValidateNotNull()]
[Array]$Servers
)

# $DisksToClear = (1..14)
# $servers="server1","server2","server3","server4","server5","server6"

$runStart = [DateTime]::Now

foreach ($server in $servers)
{
    # Use dispart utility if dynamic disks detected
    # Check if you run script for local or remote server

    # If server is local then run commands locally otherwise remotely
    if ($env:COMPUTERNAME -eq $server)
    {
    # Detecting dynamic disks
    # Apply legacy clean method via diskpart utility for discovered dynamic disks

    $listdisk = "list disk" | diskpart
    $Parsing = [String]::Join("`n", $listdisk) | Select-String -Pattern "Disk (\d+).{38}(.)" -AllMatches
    $DynamicDisksMapping=$Parsing.Matches | Select-Object @{Name="DiskIndex";Expression={$_.Groups[1].Value}}, @{Name="Dynamic";Expression={$_.Groups[2].Value -eq "*"}}
    $DynamicDisks=($DynamicDisksMapping | where Dynamic).DiskIndex

    if ($DynamicDisks -ne $null)
        {foreach ($DynamicDisk in $DynamicDisks)
                {if ($DisksToClear -contains $DynamicDisk) {
                    #check if Dynamic disk is bootable or system
                    $attr="select disk $DynamicDisk","attributes disk" | diskpart
                    $Bootable=$attr[11].Split(":")
                    $System=$attr[12].Split(":")
                    if ($Bootable[1] -eq " Yes" -or $System[1] -eq " Yes") {Write-Host "Warning: " $Server "Disk" $DynamicDisk is system or bootable -ForegroundColor Yellow}
                        else {
                        Write-Host Disk $DynamicDisk is dynamic disk -ForegroundColor Yellow
                        "select disk $DynamicDisk","clean","exit" | diskpart | Out-Null}}}}
    } else

    # Run diskpart commands remotely
    {
    Invoke-Command -ComputerName $server -ArgumentList $DisksToClear -ScriptBlock  {param($DisksToClear);
    # Detecting dynamic disks
    # Apply legacy clean method via diskpart utility for discovered dynamic disks

    $listdisk = "list disk" | diskpart;
    $Parsing = [String]::Join("`n", $listdisk) | Select-String -Pattern "Disk (\d+).{38}(.)" -AllMatches;
    $DynamicDisksMapping=$Parsing.Matches | Select-Object @{Name="DiskIndex";Expression={$_.Groups[1].Value}}, @{Name="Dynamic";Expression={$_.Groups[2].Value -eq "*"}};
    $DynamicDisks=($DynamicDisksMapping | where Dynamic).DiskIndex;

    if ($DynamicDisks -ne $null)
        {foreach ($DynamicDisk in $DynamicDisks)
                {if ($DisksToClear -contains $DynamicDisk)
                    {
                    #check if Dynamic disk is bootable or system
                    $attr="select disk $DynamicDisk","attributes disk" | diskpart;
                    $Bootable=$attr[11].Split(":");
                    $System=$attr[12].Split(":");
                    if ($Bootable[1] -eq " Yes" -or $System[1] -eq " Yes") {Write-Host "Warning: " $Server "Disk" $DynamicDisk is system or bootable -ForegroundColor Yellow;}
                        else {
                        Write-Host Disk $DynamicDisk is dynamic disk -ForegroundColor Yellow;
                        "select disk $DynamicDisk","clean","exit" | diskpart | Out-Null;}}}}}
    }

    $session=New-CimSession -ComputerName $server

    foreach ($DiskN in $DisksToClear)
    {
        $Disk=Get-Disk -Number $DiskN -cimsession $session
        $Confirm=$false
        # Make sure disk is Online and not ReadOnly otherwise, display reason and continue
        if ($Disk.IsOffline)
        {
            # Try to put it online
            Set-Disk -Number $DiskN -cimsession $session –IsOffline:$false -ErrorAction SilentlyContinue
            # Re-instantiate disk to update changes
            $Disk=Get-Disk -Number $DiskN -cimsession $session
        }
        if ($Disk.IsReadOnly)
        {
            # Try to clear read-only
            Set-Disk -Number $DiskN -cimsession $session –IsReadOnly:$false -ErrorAction SilentlyContinue
            # Re-instantiate disk to update changes
            $Disk=Get-Disk -Number $DiskN -cimsession $session
        }
        # Display reason if Offline or Read Only
        if ($disk.IsOffline -or $disk.IsReadOnly)
        {
            Write-Host "Warning: " -NoNewline -ForegroundColor Yellow
            Write-Host "Unable to process disk " -NoNewline
            Write-Host $disk.Number -NoNewline
            Write-Host ": Offline Reason: " -NoNewline
            Write-Host ($disk.OfflineReason) -NoNewline -ForegroundColor Yellow
            Write-Host ", HealthStatus: " -NoNewline
            Write-Host $disk.HealthStatus -ForegroundColor Yellow
        }      
        else
        {    
        # Warn and confirm to proceed if disk is bootable or system
        if ($Disk.IsBoot -or $Disk.IsSystem)
            {
            Write-Host "Warning: " -NoNewline -ForegroundColor Yellow
            Write-Host $Server "Disk" $DiskN is system or bootable -NoNewline
            $Confirm=$true
            }
        if ($Disk.PartitionStyle -ne 'raw')
            {
            Write-Host $Server Clearing $Disk.FriendlyName DiskNumber $DiskN -ForegroundColor Green
            Clear-Disk -Number $DiskN -RemoveData –RemoveOEM -Cimsession $Session -Confirm:$Confirm
            # Initialize-Disk -Number $DiskN -Cimsession $Session -PartitionStyle MBR
            }
        }
    }
    # Output physical disk counts
    Write-Host ""
    Write-Host $server Disks found:
    Get-Disk -cimsession $session | Group-Object Manufacturer,Model,Size | ft Count,Name -AutoSize
}
Write-Host Configuration and data cleared!
Write-Host ""
Write-Host "Run duration: " -NoNewline
Write-Host ([Math]::Round((([DateTime]::Now).Subtract($runStart)).TotalMinutes,2)) -ForegroundColor Yellow -NoNewline
Write-Host " minutes"