vCenter Alarms export/import revisited and fixed

Exporting and importing vCenter Alarms has some extensive history within the VMware community. There was a continued demand for easy to use and trustworthy tools/scripts. The two main sources I got in contact with were the VMware KB and this thread on the community, and not to forget this great tool from the vCenter 4/5 era.

Recently I was asked by a valued colleague (thanks Willem!) to help him with his challenge to find and use a tool/script that could consistently export Alarms from one source vCenter and import it to several other target vCenters. Although I once graduated as a programmer I must admit I’m not that agile anymore on the subject. Nevertheless I took on the challenge, did some investigation and dug into the programming details of PowerCLI. We tried the referenced existing scripts but they all failed to do the job properly on more recent versions (6.7/7.0) of vCenter. Main issues encountered were:

  • Double results > vCenter connections
  • Differences between new and existing Alarms > SystemName
  • Long Alarm names fail to import > max 80 chars
  • Overwriting (modified) Alarms > delete before import
  • vCenter specific Alarms > exclude them

From the script review I decided the export/import scripts from the community were best suited to continue my efforts to solve the issues.

Let’s start of with the export part of the solution. This is the easy one and already ran without major issues. The only pieces added are the exclusion of vCenter specific Alarms (short investigation by Willem showed these are all containing the word “Exhaustion”) and disconnecting from vCenters which otherwise caused double results.

function ExportAlarm() {
   if( -not (Test-Path -Path $exportFolder -PathType Container) ) {
      mkdir -Path $exportFolder}
 Connect-VIServer $serverToExportFrom -User 'administrator' -Password 'VMware1!'
 # Exclude vCenter specific Alarms containing "Exhaustion"in the name AND matching the filter
   $alarmToExport = Get-AlarmDefinition $alarmToExportName | ? {$_.name -notmatch "Exhaustion"}
   $alarmToExport | % {
      $a = Get-View -Id $_.Id
      $a.Info | Export-Clixml -Path ($exportFolder + $_.Id + ".alarm.xml") -Depth ( [System.Int32]::MaxValue )}
  }
 $serverToExportFrom = "192.168.1.232"
 $alarmToExportName = "Alarmtest"
 $exportFolder = "C:\Users\Administrator\alarms\"
 $alarmToExportName = "*"
 ExportAlarm
 Disconnect-VIServer * -Confirm:$False -Force:$True

The result of this script is a bunch of XML-files in the specified folder containing all alarmToExportName-filtered alarms except the vCenter-specific alarms. These files will be the source for the import-script.

The import-script contains the most critical changes for where things went horribly wrong. Most of the listed issues are solved here in shown in Orange.

function ImportAlarm {
 Connect-VIServer $serverToImportTo -User 'administrator@vsphere.local'-Password 'VMware1!'

# Remove all but vCenter-specific Alarms from the target vCenter and wait for 15 seconds before we bulk-import the new Alarms
 Get-AlarmDefinition | ? {$_.name -notmatch "Exhaustion"} |Remove-AlarmDefinition -Confirm:$false
 sleep 15

 Get-ChildItem -Path ($exportFolder + "*.alarm.xml") | % {
 $deserializedAlarmInfo = Import-Clixml -Path $_.FullName
  # We need to limit the Alarm-name to 80 characters for it to import correctly
  $deserializedAlarmInfo.Name = ($deserializedAlarmInfo.Name[0..76] -join "")
  $importedAlarmInfo = ConvertFromDeserialized( $deserializedAlarmInfo )
  $entity = Get-Folder -NoRecursion 
  $alarmManager = Get-View -Id 'AlarmManager-AlarmManager'
  $alarmManager.CreateAlarm($entity.Id, $importedAlarmInfo)}
 }
 This function converts a Powershell object deserialized from xml (with Import-Clixml) to its original type (that has been previously serialized with Export-Clixml)
 function ConvertFromDeserialized {
 param($deserializedObject)
 if($deserializedObject -eq $null){return $null}
 $deserializedTypeName = ($deserializedObject | Get-Member -Force | where { $_.Name -eq "psbase" } ).TypeName;
 if($deserializedTypeName.StartsWith("Deserialized.")) {
   $originalTypeName = $deserializedTypeName.Replace("Deserialized.", "")
   $result = New-Object -TypeName $originalTypeName
   $resultType = $result.GetType()
 if($resultType.IsEnum){
      $result = [Enum]::Parse($resultType, $deserializedObject, $true)
      return $result}
 $deserializedObject | Get-Member | % {
# exclude "SystemName" entries from the XML-import as these will fail during the CreateAlarm call
   if($_.MemberType -eq "Property" -and $_.Name -ne "SystemName") {
     $resultProperty = $resultType.GetProperty($_.Name)
     if($resultProperty.CanWrite ){
        $propertyValue = ( Invoke-Expression ('$deserializedObject.' + $_.Name) | % { ConvertFromDeserialized( $_ ) } )
        if($propertyValue -and $resultProperty.PropertyType.IsArray ) {
           if($propertyValue.GetType().IsArray){
              $elementTypeName = $resultProperty.PropertyType.AssemblyQualifiedName.Replace("[]", "")
              $elementType = [System.Type]::GetType($elementTypeName)
              $array = [System.Array]::CreateInstance($elementType, $propertyValue.Count)
              for($i = 0; $i -lt $array.Length; $i++){
                  $array[$i] = $propertyValue[$i]}
                  $propertyValue = $array
              } else {
                  $elementTypeName = $resultProperty.PropertyType.AssemblyQualifiedName.Replace("[]", "")
                  $elementType = [System.Type]::GetType($elementTypeName)
                  $array = [System.Array]::CreateInstance($elementType, 1)
                  $array[0] = $propertyValue
                  $propertyValue = $array}}
           $resultProperty.SetValue($result, $propertyValue, $null)}}}
     } else {
       $result = $deserializedObject}  
 return $result     
 }
 $serverToImportTo = "vcsa1.vmw.local"
 $exportFolder = "C:\Users\Administrator\alarms\"
 ImportAlarm
 Disconnect-VIServer * -Confirm:$false -Force:$true

With the above you should now be able to consistently export and import vCenter Alarms in your environment. And, of course, you can add a lot of additional features to enhance the script with more security, input-boxes, filename-conversions and prefix additions. I leave it up to you to figure that out.

Marco Baaijen

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment