From c0f71d529c87af659d085c1c741f6d1a430bc72f Mon Sep 17 00:00:00 2001 From: Emile Date: Tue, 25 Jun 2019 15:33:26 +0200 Subject: [PATCH 01/18] Add files via upload --- ITGlue-VMHost-CreateFlexibleAsset.ps1 | 251 ++++++++++++++ ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 | 380 ++++++++++++++++++++++ ITGlue-VMHost-Setup.ps1 | 26 ++ 3 files changed, 657 insertions(+) create mode 100644 ITGlue-VMHost-CreateFlexibleAsset.ps1 create mode 100644 ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 create mode 100644 ITGlue-VMHost-Setup.ps1 diff --git a/ITGlue-VMHost-CreateFlexibleAsset.ps1 b/ITGlue-VMHost-CreateFlexibleAsset.ps1 new file mode 100644 index 0000000..dbc80e2 --- /dev/null +++ b/ITGlue-VMHost-CreateFlexibleAsset.ps1 @@ -0,0 +1,251 @@ +# Script name: ITGlue-VMHost-CreateFlexibleAsset.ps1 +# Script type: Powershell +# Script description: Creates a custom Flexible Asset called "VMHost". Use "ITGlue-VMHost-CreateFlexibleAsset.ps1" to update. +# Dependencies: Powershell 3.0 +# Script maintainer: powerpack@upstream.se +# https://en.upstream.se/powerpack/ +# -------------------------------------------------------------------------------------------------------------------------------- + +$data = @{ + type = "flexible_asset_types" + Attributes = @{ + icon = "cubes" + description = "This Flexible Asset is to be used to automate VM host documentation." + Name = "VM Host" + enabled = $true + } + relationships = @{ + flexible_asset_fields = @{ + data = @( + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 1 + Name = "VM host name" + kind = "Text" + hint = "This is the unique name and identifier of this Flexible Asset. It has to match the actual name of the VM Host to be docuemented with the associated Powershell script." + required = $true + use_for_title = $true + expiration = $false + show_in_list = $true + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 2 + Name = "VM host configuration" + kind = "Header" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $true + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 3 + Name = "VM host related IT Glue configuration" + kind = "Tag" + tag_type = "Configurations" + required = $true + use_for_title = $false + expiration = $false + show_in_list = $true + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 4 + Name = "Virtualization platform" + kind = "Select" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $true + default_value = "Hyper-V +VMware" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 5 + Name = "CPU" + kind = "Number" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 6 + Name = "RAM (GB)" + kind = "Number" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 7 + Name = "Disk information" + kind = "Textbox" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 8 + Name = "Virtual switches" + kind = "Textbox" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 9 + Name = "VM guests configuration" + kind = "Header" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $true + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 10 + Name = "Current number of VM guests on this VM host" + kind = "Number" + hint = "Number of guests detected on this VM host based on latest execution of the ducumentation atutomation script." + required = $false + use_for_title = $false + expiration = $false + show_in_list = $true + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 11 + Name = "VM guest names and information" + kind = "Textbox" + hint = "VM guest names vCPUs RAM and other infromation." + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 12 + Name = "VM guest virtual disk paths" + kind = "Textbox" + hint = "VM guests and virtual disk paths discovered on this VM host." + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 13 + Name = "VM guests snapshot information" + kind = "Textbox" + hint = "All snapshots found on the host" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 14 + Name = "VM guests BIOS settings" + kind = "Textbox" + hint = "Specifies the BIOS boot settings in each each discovered guest on this VM host." + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 15 + Name = "Assigned virtual switches and IP information" + kind = "Textbox" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 16 + Name = "Force manual sync now?" + kind = "Select" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "Yes +No" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 17 + Name = "This automated documentation is powered by Upstream Power Pack" + kind = "Header" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $true + } + } + ) + } + } +} + + +New-ITGlueFlexibleAssetTypes -data $data \ No newline at end of file diff --git a/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 b/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 new file mode 100644 index 0000000..f689531 --- /dev/null +++ b/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 @@ -0,0 +1,380 @@ +#Requires -Version 3 +#Requires -Modules @{ ModuleName="ITGlueAPI"; ModuleVersion="2.0.7" } + + +[cmdletbinding()] +param( + [Parameter(HelpMessage='The id of the asset in IT Glue')] + [long]$flexible_asset_id, + + [Parameter(HelpMessage='IT Glue api key')] + [string]$api_key, + + [Parameter(HelpMessage='Where is your data stored? EU or US?')] + [ValidateSet('US', 'EU')] + [string]$data_center, + + [Parameter(HelpMessage='The first part of your IT Glue URL when logging in in ')] + [string]$subdomain +) + +# Import the IT Glue wrapper module +Import-Module ITGlueAPI -ErrorAction Stop + +# If any parameter is missing ... +# (Cannot use mandatory because it would break setting parameters inside the script.) + +if($api_key) { + try { + Write-Verbose "Decrypting API key." + $api_key = [PSCredential]::new('null', ($api_key | ConvertTo-SecureString -ErrorAction Stop)).GetNetworkCredential().Password + Write-Verbose "Decrypted and stored." + } catch { + Write-Verbose "API key not encrypted." + } + + # Set API key for this sessions + Write-Verbose "Using specified API key." + Add-ITGlueAPIKey -api_key $api_key +} elseif(!$api_key -and $ITGlue_API_Key) { + # Use API key imported from module settings + Write-Verbose "Using API key from module settings already saved." +} else { + return "No API key was found or specified, please use -api_key to specify it and run the script again. This script will not continue." +} + +if($data_center) { + # Set URL for this sessions + Write-Verbose "Using specified data center $data_center for this session." + Add-ITGlueBaseURI -data_center $data_center +} elseif(!$data_center -and $ITGlue_Base_URI) { + # Use URL imported from module settings + Write-Verbose "Using URL from module settings already saved." +} else { + return "No data center was found or specified, please use -data_center to specify it (US or EU) and run the script again. This script will not continue." +} + +if(!$flexible_asset_id) { + return "flexible_asset_id is missing. Please specify it and run the script again. This script will not continue." +} + +# Flexible asset to update +Write-Verbose "Retreving IT Glue flexible asset id: $flexible_asset_id..." +$flexibleAsset = Get-ITGlueFlexibleAssets -id $flexible_asset_id +Write-Verbose "Done." + +# The asset's organization id +Write-Verbose "Retreving organization id..." +$organization_id = $flexibleAsset.data.attributes.'organization-id' +Write-Verbose "Done." + +Write-Verbose "Formating URL..." +$url = (Get-ITGlueBaseURI).replace('https://api', 'https://{0}' -f $subdomain) +Write-Verbose "Done." + +Write-Verbose "Retrieving configurations from IT Glue (org id: $organization_id)..." +$configurations = @{} +$page_number = 1 +do{ + Write-Verbose "Calling the IT Glue api (page $page_number)..." + $api_call = Get-ITGlueConfigurations -organization_id $organization_id -page_size 1000 -page_number ($page_number++) + foreach($_ in $api_call.data) { + $configurations[$_.attributes.name] = $_ + } +} while($api_call.links.next) +Write-Verbose "Done." + +# All VMs on the host (with some data) +Write-Verbose "Creating hashtable with HTML name and rest of data..." +$VMs = @{} +foreach($vm in Get-VM) { + $htmlname = $vm.name + + if($configurations[$vm.Name]) { + $htmlname = '{3}' -f $url, $configurations[$vm.Name].attributes.'organization-id', $configurations[$vm.Name].id, $vm.name + } else { + $configurations.GetEnumerator() | Where {$_.Name -like "*$($vm.name)*"} | ForEach-Object { + $htmlname = '{3}' -f $url, $_.value.attributes.'organization-id', $_.value.id, $vm.name + } + } + + + $VMs[$vm.name] = [PSCustomObject]@{ + name = $vm.name + vm = $vm + htmlname = $htmlname + } +} +Write-Verbose "Done." + +# Hyper-V host's disk information / "Disk information" +Write-Verbose "Getting host's disk data..." +$diskDataHTML = '
+ + + + + + + + + {0} + +
Disk nameTotal(GB)Used(GB)Free(GB)
+
' -f ((Get-PSDrive -PSProvider FileSystem).foreach{ + ' + {0} + {1} + {2} + {3} + ' -f $_.Root, [math]::round(($_.free+$_.used)/1GB), [math]::round($_.used/1GB), [math]::round($_.free/1GB)} | Out-String) +Write-Verbose "Host's disk data done. [1/7]" + +# Virtual swtiches / "Virtual switches" +Write-Verbose "Getting virtual swtiches..." +$virtualSwitchsHTML = '
+ + + + + + + + {0} + +
NameSwitch typeInterface description
+
' -f ((Get-VMSwitch).foreach{ + ' + {0} + {1} + {2} + ' -f $_.Name, $_.SwitchType, $_.NetAdapterInterfaceDescription} | Out-String) +Write-Verbose "Virtual swtiches done. [2/7]" + +# General information about virtual machines / "VM guest names and information" +Write-Verbose "Getting general guest information..." +$guestInformationHTML = '
+ + + + + + + + + + {0} + +
VM guest nameStart actionRAM (GB)vCPUSize (GB)
+
' -f ($VMs.GetEnumerator().foreach{ + $diskSize = 0 + ($_.value.vm.HardDrives | Get-VHD).FileSize.foreach{$diskSize += $_} + $diskSize = [Math]::Round($diskSize/1GB) + ' + {0} + {1} + {2} + {3} + {4} + ' -f $_.value.htmlname, $_.value.vm.AutomaticStartAction, [Math]::Round($_.value.vm.MemoryStartup/1GB), $_.value.vm.ProcessorCount, $diskSize} | Out-String) +Write-Verbose "General guest information done. [3/7]" + +# Virutal machines' disk file locations / "VM guest virtual disk paths" +Write-Verbose "Getting VM machine paths..." +$virtualMachinePathsHTML = '
+ + + + + + + {0} + +
VM guest namePath
+
' -f ($VMs.GetEnumerator().foreach{ + ' + {0} + {1} + ' -f $_.value.htmlname, ((Get-VHD -id $_.value.vm.id).path | Out-String).Replace([Environment]::NewLine, '
').TrimEnd('
')} | Out-String) +Write-Verbose "VM machine paths done. [4/7]" + +# Snapshot data / "VM guests snapshot information" +Write-Verbose "Getting snapshot data..." +$vmSnapshotHTML = '
+ + + + + + + + + + {0} + +
VMNameNameSnapshot typeCreation timeParent snapshot name
+
' -f ((Get-VMSnapshot -VMName * | Sort VMName, CreationTime).foreach{ + ' + {0} + {1} + {2} + {3} + {4} + ' -f $VMs[$_.VMName].htmlname, $_.Name, $_.SnapshotType, $_.CreationTime, $_.ParentSnapshotName} | Out-String) +Write-Verbose "Snapshot data done. [5/7]" + +# Virutal machines' bios settings / "VM guests BIOS settings" +Write-Verbose "Getting VM BIOS settings..." +# Generation 1 +$vmBiosSettingsTableData = (Get-VMBios * -ErrorAction SilentlyContinue).foreach{ + ' + {0} + {1} + {2} + {3} + Gen 1 + ' -f $VMs[$_.VMName].htmlname, ($_.StartupOrder | Out-String).Replace([Environment]::NewLine, ', ').TrimEnd(', '), 'N/A', 'N/A'} +Write-Verbose "Generation 1 done..." + +# Generation 2 +$vmBiosSettingsTableData += (Get-VMFirmware * -ErrorAction SilentlyContinue).foreach{ + ' + {0} + {1} + {2} + {3} + Gen 2 + ' -f $VMs[$_.VMName].htmlname, ($_.BootOrder.BootType | Out-String).Replace([Environment]::NewLine, ', ').TrimEnd(', '), $_.PauseAfterBootFailure, $_.SecureBoot} +Write-Verbose "Generation 2 done..." + +$vmBIOSSettingsHTML = '
+ + + + + + + + + + {0} + +
VM guest nameStartup orderPause After Boot FailureSecure BootGeneration
+
' -f ($vmBiosSettingsTableData | Out-String) +Write-Verbose "VM BIOS settings done. [6/7]" + +# Guest NICs and IPs +Write-Verbose "Getting VM NICs..." +$guestNICsIPsHTML = '
+ + + + + + + + + {0} + +
VM guest nameSwtich nameIPv4IPv6
+
' -f ((Get-VMNetworkAdapter * | Sort 'VMName').foreach{ + ' + {0} + {1} + {2} + {3} + ' -f $VMs[$_.VMName].htmlname, $_.switchname, $_.ipaddresses[0], $_.ipaddresses[1]} | Out-String) +Write-Verbose "VM NICs done. [7/7]" + + +$data = @{ + type = 'flexible-assets' + attributes = @{ + traits = @{ + # Manual sync + 'force-manual-sync-now' = 'No' + # Host platform + 'virtualization-platform' = 'Hyper-V' + # Host CPU data + 'cpu' = Get-VMHost | Select -ExpandProperty LogicalProcessorCount + # Host RAM data + 'ram-gb' = ((Get-CimInstance CIM_PhysicalMemory).capacity | Measure -Sum).Sum/1GB + # Host disk data + 'disk-information' = $diskDataHTML + # Virutal network cards (vNIC) + 'virtual-switches' = $virtualSwitchsHTML + # Number of VMs on host + 'current-number-of-vm-guests-on-this-vm-host' = ($VMs.GetEnumerator() | measure).Count + # General VM data (start type, cpu, ram...) + 'vm-guest-names-and-information' = $guestInformationHTML + # VMs' name and VHD paths + 'vm-guest-virtual-disk-paths' = $virtualMachinePathsHTML + # Snapshop data + 'vm-guests-snapshot-information' = $vmSnapshotHTML + # VMs' bios settings + 'vm-guests-bios-settings' = $vmBIOSSettingsHTML + # NIC and IP assigned to each VM + 'assigned-virtual-switches-and-ip-information' = $guestNICsIPsHTML + } + } +} +Write-Verbose "Finished build hash table." + + +Write-Verbose "Comparing data.." + +$update = $false + +if($flexibleAsset.data.attributes.traits.'force-manual-sync-now' -eq 'Yes') { + $update = $true +} elseif($data.attributes.traits.cpu -ne $flexibleAsset.data.attributes.traits.cpu) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif($data.attributes.traits.'ram-gb' -ne $flexibleAsset.data.attributes.traits.'ram-gb') { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'disk-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'disk-information'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'virtual-switches'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'virtual-switches'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif($data.attributes.traits.'current-number-of-vm-guests-on-this-vm-host' -ne $flexibleAsset.data.attributes.traits.'current-number-of-vm-guests-on-this-vm-host') { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'vm-guest-names-and-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guest-names-and-information'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'vm-guest-virtual-disk-paths'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guest-virtual-disk-paths'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'vm-guests-snapshot-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guests-snapshot-information'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'vm-guests-bios-settings'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guests-bios-settings'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'assigned-virtual-switches-and-ip-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'assigned-virtual-switches-and-ip-information'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} + +if($update) { + Write-Verbose "Begin updating asset.." + + $data["attributes"]["id"] = $flexible_asset_id + Write-Verbose "Added id to hash table." + # Visible name + $data["attributes"]["traits"]["vm-host-name"] = $flexibleAsset.data.attributes.traits.'vm-host-name' + Write-Verbose "Added VM host name to hash table." + # Tagged asset (i.e the host) + $data["attributes"]["traits"]["vm-host-related-it-glue-configuration"] = $flexibleAsset.data.attributes.traits.'vm-host-related-it-glue-configuration'.Values.id + Write-Verbose "Added VM host related IT Glue configuration to hash table." + + Write-Verbose "Uploading data for id $flexible_asset_id." + $resp = Set-ITGlueFlexibleAssets -data $data + Write-Verbose "Uploading data: done." + return $resp +} else { + Write-Verbose "No change detected. Not updating." +} \ No newline at end of file diff --git a/ITGlue-VMHost-Setup.ps1 b/ITGlue-VMHost-Setup.ps1 new file mode 100644 index 0000000..211e91e --- /dev/null +++ b/ITGlue-VMHost-Setup.ps1 @@ -0,0 +1,26 @@ +try { + Import-Module ITGlueAPI -ErrorAction Stop + Get-Variable ITGlue_API_Key -ErrorAction Stop > $null + Get-Variable ITGlue_Base_URI -ErrorAction Stop > $null +} catch { + $apikey = Read-Host "Enter IT Glue API key" + do{ + $datacenter = Read-Host "Enter IT Glue data center (EU/US)" + } until($datacenter -eq 'EU' -or $datacenter -eq 'US') + + Add-ITGlueAPIKey -Api_Key $apikey + Add-ITGlueBaseURI -data_center $datacenter + Export-ITGlueModuleSettings +} + +$flexible_asset_id = Read-Host "Enter flexible asset id (unique ID for asset to update)" + +# Create scheduled task +# Action +$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument ('-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden "{0}\ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 -flexible_asset_id {1}"' -f $PSScriptRoot, $flexible_asset_id) +# Trigger +$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) +# Settings +$settings = New-ScheduledTaskSettingsSet -WakeToRun -RestartCount 3 -RunOnlyIfNetworkAvailable -StartWhenAvailable -RestartInterval (New-TimeSpan -Minutes 3) +# Add to task scheduler +Register-ScheduledTask -Action $action -Trigger $trigger -TaskPath "\ITGlueSync\" -TaskName "Sync HyperV with IT Glue" -Settings $settings -Force \ No newline at end of file From 2a12519c45ded77e11fe96e8223909821d14f953 Mon Sep 17 00:00:00 2001 From: Emile Priller Date: Tue, 25 Jun 2019 15:36:07 +0200 Subject: [PATCH 02/18] moved to folder --- .../ITGlue-VMHost-CreateFlexibleAsset.ps1 | 500 ++++++------ .../ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 | 758 +++++++++--------- .../ITGlue-VMHost-Setup.ps1 | 50 +- hyperv-sync/README.md | 0 4 files changed, 654 insertions(+), 654 deletions(-) rename ITGlue-VMHost-CreateFlexibleAsset.ps1 => hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 (97%) rename ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 => hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 (97%) rename ITGlue-VMHost-Setup.ps1 => hyperv-sync/ITGlue-VMHost-Setup.ps1 (98%) create mode 100644 hyperv-sync/README.md diff --git a/ITGlue-VMHost-CreateFlexibleAsset.ps1 b/hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 similarity index 97% rename from ITGlue-VMHost-CreateFlexibleAsset.ps1 rename to hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 index dbc80e2..2cfaf08 100644 --- a/ITGlue-VMHost-CreateFlexibleAsset.ps1 +++ b/hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 @@ -1,251 +1,251 @@ -# Script name: ITGlue-VMHost-CreateFlexibleAsset.ps1 -# Script type: Powershell -# Script description: Creates a custom Flexible Asset called "VMHost". Use "ITGlue-VMHost-CreateFlexibleAsset.ps1" to update. -# Dependencies: Powershell 3.0 -# Script maintainer: powerpack@upstream.se -# https://en.upstream.se/powerpack/ -# -------------------------------------------------------------------------------------------------------------------------------- - -$data = @{ - type = "flexible_asset_types" - Attributes = @{ - icon = "cubes" - description = "This Flexible Asset is to be used to automate VM host documentation." - Name = "VM Host" - enabled = $true - } - relationships = @{ - flexible_asset_fields = @{ - data = @( - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 1 - Name = "VM host name" - kind = "Text" - hint = "This is the unique name and identifier of this Flexible Asset. It has to match the actual name of the VM Host to be docuemented with the associated Powershell script." - required = $true - use_for_title = $true - expiration = $false - show_in_list = $true - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 2 - Name = "VM host configuration" - kind = "Header" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $true - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 3 - Name = "VM host related IT Glue configuration" - kind = "Tag" - tag_type = "Configurations" - required = $true - use_for_title = $false - expiration = $false - show_in_list = $true - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 4 - Name = "Virtualization platform" - kind = "Select" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $true - default_value = "Hyper-V -VMware" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 5 - Name = "CPU" - kind = "Number" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $false - default_value = "" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 6 - Name = "RAM (GB)" - kind = "Number" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $false - default_value = "" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 7 - Name = "Disk information" - kind = "Textbox" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $false - default_value = "" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 8 - Name = "Virtual switches" - kind = "Textbox" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $false - default_value = "" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 9 - Name = "VM guests configuration" - kind = "Header" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $true - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 10 - Name = "Current number of VM guests on this VM host" - kind = "Number" - hint = "Number of guests detected on this VM host based on latest execution of the ducumentation atutomation script." - required = $false - use_for_title = $false - expiration = $false - show_in_list = $true - default_value = "" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 11 - Name = "VM guest names and information" - kind = "Textbox" - hint = "VM guest names vCPUs RAM and other infromation." - required = $false - use_for_title = $false - expiration = $false - show_in_list = $false - default_value = "" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 12 - Name = "VM guest virtual disk paths" - kind = "Textbox" - hint = "VM guests and virtual disk paths discovered on this VM host." - required = $false - use_for_title = $false - expiration = $false - show_in_list = $false - default_value = "" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 13 - Name = "VM guests snapshot information" - kind = "Textbox" - hint = "All snapshots found on the host" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $false - default_value = "" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 14 - Name = "VM guests BIOS settings" - kind = "Textbox" - hint = "Specifies the BIOS boot settings in each each discovered guest on this VM host." - required = $false - use_for_title = $false - expiration = $false - show_in_list = $false - default_value = "" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 15 - Name = "Assigned virtual switches and IP information" - kind = "Textbox" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $false - default_value = "" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 16 - Name = "Force manual sync now?" - kind = "Select" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $false - default_value = "Yes -No" - } - }, - @{ - type = "flexible_asset_fields" - Attributes = @{ - order = 17 - Name = "This automated documentation is powered by Upstream Power Pack" - kind = "Header" - required = $false - use_for_title = $false - expiration = $false - show_in_list = $true - } - } - ) - } - } -} - - +# Script name: ITGlue-VMHost-CreateFlexibleAsset.ps1 +# Script type: Powershell +# Script description: Creates a custom Flexible Asset called "VMHost". Use "ITGlue-VMHost-CreateFlexibleAsset.ps1" to update. +# Dependencies: Powershell 3.0 +# Script maintainer: powerpack@upstream.se +# https://en.upstream.se/powerpack/ +# -------------------------------------------------------------------------------------------------------------------------------- + +$data = @{ + type = "flexible_asset_types" + Attributes = @{ + icon = "cubes" + description = "This Flexible Asset is to be used to automate VM host documentation." + Name = "VM Host" + enabled = $true + } + relationships = @{ + flexible_asset_fields = @{ + data = @( + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 1 + Name = "VM host name" + kind = "Text" + hint = "This is the unique name and identifier of this Flexible Asset. It has to match the actual name of the VM Host to be docuemented with the associated Powershell script." + required = $true + use_for_title = $true + expiration = $false + show_in_list = $true + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 2 + Name = "VM host configuration" + kind = "Header" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $true + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 3 + Name = "VM host related IT Glue configuration" + kind = "Tag" + tag_type = "Configurations" + required = $true + use_for_title = $false + expiration = $false + show_in_list = $true + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 4 + Name = "Virtualization platform" + kind = "Select" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $true + default_value = "Hyper-V +VMware" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 5 + Name = "CPU" + kind = "Number" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 6 + Name = "RAM (GB)" + kind = "Number" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 7 + Name = "Disk information" + kind = "Textbox" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 8 + Name = "Virtual switches" + kind = "Textbox" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 9 + Name = "VM guests configuration" + kind = "Header" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $true + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 10 + Name = "Current number of VM guests on this VM host" + kind = "Number" + hint = "Number of guests detected on this VM host based on latest execution of the ducumentation atutomation script." + required = $false + use_for_title = $false + expiration = $false + show_in_list = $true + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 11 + Name = "VM guest names and information" + kind = "Textbox" + hint = "VM guest names vCPUs RAM and other infromation." + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 12 + Name = "VM guest virtual disk paths" + kind = "Textbox" + hint = "VM guests and virtual disk paths discovered on this VM host." + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 13 + Name = "VM guests snapshot information" + kind = "Textbox" + hint = "All snapshots found on the host" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 14 + Name = "VM guests BIOS settings" + kind = "Textbox" + hint = "Specifies the BIOS boot settings in each each discovered guest on this VM host." + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 15 + Name = "Assigned virtual switches and IP information" + kind = "Textbox" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 16 + Name = "Force manual sync now?" + kind = "Select" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $false + default_value = "Yes +No" + } + }, + @{ + type = "flexible_asset_fields" + Attributes = @{ + order = 17 + Name = "This automated documentation is powered by Upstream Power Pack" + kind = "Header" + required = $false + use_for_title = $false + expiration = $false + show_in_list = $true + } + } + ) + } + } +} + + New-ITGlueFlexibleAssetTypes -data $data \ No newline at end of file diff --git a/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 similarity index 97% rename from ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 rename to hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 index f689531..f9c2472 100644 --- a/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 +++ b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 @@ -1,380 +1,380 @@ -#Requires -Version 3 -#Requires -Modules @{ ModuleName="ITGlueAPI"; ModuleVersion="2.0.7" } - - -[cmdletbinding()] -param( - [Parameter(HelpMessage='The id of the asset in IT Glue')] - [long]$flexible_asset_id, - - [Parameter(HelpMessage='IT Glue api key')] - [string]$api_key, - - [Parameter(HelpMessage='Where is your data stored? EU or US?')] - [ValidateSet('US', 'EU')] - [string]$data_center, - - [Parameter(HelpMessage='The first part of your IT Glue URL when logging in in ')] - [string]$subdomain -) - -# Import the IT Glue wrapper module -Import-Module ITGlueAPI -ErrorAction Stop - -# If any parameter is missing ... -# (Cannot use mandatory because it would break setting parameters inside the script.) - -if($api_key) { - try { - Write-Verbose "Decrypting API key." - $api_key = [PSCredential]::new('null', ($api_key | ConvertTo-SecureString -ErrorAction Stop)).GetNetworkCredential().Password - Write-Verbose "Decrypted and stored." - } catch { - Write-Verbose "API key not encrypted." - } - - # Set API key for this sessions - Write-Verbose "Using specified API key." - Add-ITGlueAPIKey -api_key $api_key -} elseif(!$api_key -and $ITGlue_API_Key) { - # Use API key imported from module settings - Write-Verbose "Using API key from module settings already saved." -} else { - return "No API key was found or specified, please use -api_key to specify it and run the script again. This script will not continue." -} - -if($data_center) { - # Set URL for this sessions - Write-Verbose "Using specified data center $data_center for this session." - Add-ITGlueBaseURI -data_center $data_center -} elseif(!$data_center -and $ITGlue_Base_URI) { - # Use URL imported from module settings - Write-Verbose "Using URL from module settings already saved." -} else { - return "No data center was found or specified, please use -data_center to specify it (US or EU) and run the script again. This script will not continue." -} - -if(!$flexible_asset_id) { - return "flexible_asset_id is missing. Please specify it and run the script again. This script will not continue." -} - -# Flexible asset to update -Write-Verbose "Retreving IT Glue flexible asset id: $flexible_asset_id..." -$flexibleAsset = Get-ITGlueFlexibleAssets -id $flexible_asset_id -Write-Verbose "Done." - -# The asset's organization id -Write-Verbose "Retreving organization id..." -$organization_id = $flexibleAsset.data.attributes.'organization-id' -Write-Verbose "Done." - -Write-Verbose "Formating URL..." -$url = (Get-ITGlueBaseURI).replace('https://api', 'https://{0}' -f $subdomain) -Write-Verbose "Done." - -Write-Verbose "Retrieving configurations from IT Glue (org id: $organization_id)..." -$configurations = @{} -$page_number = 1 -do{ - Write-Verbose "Calling the IT Glue api (page $page_number)..." - $api_call = Get-ITGlueConfigurations -organization_id $organization_id -page_size 1000 -page_number ($page_number++) - foreach($_ in $api_call.data) { - $configurations[$_.attributes.name] = $_ - } -} while($api_call.links.next) -Write-Verbose "Done." - -# All VMs on the host (with some data) -Write-Verbose "Creating hashtable with HTML name and rest of data..." -$VMs = @{} -foreach($vm in Get-VM) { - $htmlname = $vm.name - - if($configurations[$vm.Name]) { - $htmlname = '{3}' -f $url, $configurations[$vm.Name].attributes.'organization-id', $configurations[$vm.Name].id, $vm.name - } else { - $configurations.GetEnumerator() | Where {$_.Name -like "*$($vm.name)*"} | ForEach-Object { - $htmlname = '{3}' -f $url, $_.value.attributes.'organization-id', $_.value.id, $vm.name - } - } - - - $VMs[$vm.name] = [PSCustomObject]@{ - name = $vm.name - vm = $vm - htmlname = $htmlname - } -} -Write-Verbose "Done." - -# Hyper-V host's disk information / "Disk information" -Write-Verbose "Getting host's disk data..." -$diskDataHTML = '
- - - - - - - - - {0} - -
Disk nameTotal(GB)Used(GB)Free(GB)
-
' -f ((Get-PSDrive -PSProvider FileSystem).foreach{ - ' - {0} - {1} - {2} - {3} - ' -f $_.Root, [math]::round(($_.free+$_.used)/1GB), [math]::round($_.used/1GB), [math]::round($_.free/1GB)} | Out-String) -Write-Verbose "Host's disk data done. [1/7]" - -# Virtual swtiches / "Virtual switches" -Write-Verbose "Getting virtual swtiches..." -$virtualSwitchsHTML = '
- - - - - - - - {0} - -
NameSwitch typeInterface description
-
' -f ((Get-VMSwitch).foreach{ - ' - {0} - {1} - {2} - ' -f $_.Name, $_.SwitchType, $_.NetAdapterInterfaceDescription} | Out-String) -Write-Verbose "Virtual swtiches done. [2/7]" - -# General information about virtual machines / "VM guest names and information" -Write-Verbose "Getting general guest information..." -$guestInformationHTML = '
- - - - - - - - - - {0} - -
VM guest nameStart actionRAM (GB)vCPUSize (GB)
-
' -f ($VMs.GetEnumerator().foreach{ - $diskSize = 0 - ($_.value.vm.HardDrives | Get-VHD).FileSize.foreach{$diskSize += $_} - $diskSize = [Math]::Round($diskSize/1GB) - ' - {0} - {1} - {2} - {3} - {4} - ' -f $_.value.htmlname, $_.value.vm.AutomaticStartAction, [Math]::Round($_.value.vm.MemoryStartup/1GB), $_.value.vm.ProcessorCount, $diskSize} | Out-String) -Write-Verbose "General guest information done. [3/7]" - -# Virutal machines' disk file locations / "VM guest virtual disk paths" -Write-Verbose "Getting VM machine paths..." -$virtualMachinePathsHTML = '
- - - - - - - {0} - -
VM guest namePath
-
' -f ($VMs.GetEnumerator().foreach{ - ' - {0} - {1} - ' -f $_.value.htmlname, ((Get-VHD -id $_.value.vm.id).path | Out-String).Replace([Environment]::NewLine, '
').TrimEnd('
')} | Out-String) -Write-Verbose "VM machine paths done. [4/7]" - -# Snapshot data / "VM guests snapshot information" -Write-Verbose "Getting snapshot data..." -$vmSnapshotHTML = '
- - - - - - - - - - {0} - -
VMNameNameSnapshot typeCreation timeParent snapshot name
-
' -f ((Get-VMSnapshot -VMName * | Sort VMName, CreationTime).foreach{ - ' - {0} - {1} - {2} - {3} - {4} - ' -f $VMs[$_.VMName].htmlname, $_.Name, $_.SnapshotType, $_.CreationTime, $_.ParentSnapshotName} | Out-String) -Write-Verbose "Snapshot data done. [5/7]" - -# Virutal machines' bios settings / "VM guests BIOS settings" -Write-Verbose "Getting VM BIOS settings..." -# Generation 1 -$vmBiosSettingsTableData = (Get-VMBios * -ErrorAction SilentlyContinue).foreach{ - ' - {0} - {1} - {2} - {3} - Gen 1 - ' -f $VMs[$_.VMName].htmlname, ($_.StartupOrder | Out-String).Replace([Environment]::NewLine, ', ').TrimEnd(', '), 'N/A', 'N/A'} -Write-Verbose "Generation 1 done..." - -# Generation 2 -$vmBiosSettingsTableData += (Get-VMFirmware * -ErrorAction SilentlyContinue).foreach{ - ' - {0} - {1} - {2} - {3} - Gen 2 - ' -f $VMs[$_.VMName].htmlname, ($_.BootOrder.BootType | Out-String).Replace([Environment]::NewLine, ', ').TrimEnd(', '), $_.PauseAfterBootFailure, $_.SecureBoot} -Write-Verbose "Generation 2 done..." - -$vmBIOSSettingsHTML = '
- - - - - - - - - - {0} - -
VM guest nameStartup orderPause After Boot FailureSecure BootGeneration
-
' -f ($vmBiosSettingsTableData | Out-String) -Write-Verbose "VM BIOS settings done. [6/7]" - -# Guest NICs and IPs -Write-Verbose "Getting VM NICs..." -$guestNICsIPsHTML = '
- - - - - - - - - {0} - -
VM guest nameSwtich nameIPv4IPv6
-
' -f ((Get-VMNetworkAdapter * | Sort 'VMName').foreach{ - ' - {0} - {1} - {2} - {3} - ' -f $VMs[$_.VMName].htmlname, $_.switchname, $_.ipaddresses[0], $_.ipaddresses[1]} | Out-String) -Write-Verbose "VM NICs done. [7/7]" - - -$data = @{ - type = 'flexible-assets' - attributes = @{ - traits = @{ - # Manual sync - 'force-manual-sync-now' = 'No' - # Host platform - 'virtualization-platform' = 'Hyper-V' - # Host CPU data - 'cpu' = Get-VMHost | Select -ExpandProperty LogicalProcessorCount - # Host RAM data - 'ram-gb' = ((Get-CimInstance CIM_PhysicalMemory).capacity | Measure -Sum).Sum/1GB - # Host disk data - 'disk-information' = $diskDataHTML - # Virutal network cards (vNIC) - 'virtual-switches' = $virtualSwitchsHTML - # Number of VMs on host - 'current-number-of-vm-guests-on-this-vm-host' = ($VMs.GetEnumerator() | measure).Count - # General VM data (start type, cpu, ram...) - 'vm-guest-names-and-information' = $guestInformationHTML - # VMs' name and VHD paths - 'vm-guest-virtual-disk-paths' = $virtualMachinePathsHTML - # Snapshop data - 'vm-guests-snapshot-information' = $vmSnapshotHTML - # VMs' bios settings - 'vm-guests-bios-settings' = $vmBIOSSettingsHTML - # NIC and IP assigned to each VM - 'assigned-virtual-switches-and-ip-information' = $guestNICsIPsHTML - } - } -} -Write-Verbose "Finished build hash table." - - -Write-Verbose "Comparing data.." - -$update = $false - -if($flexibleAsset.data.attributes.traits.'force-manual-sync-now' -eq 'Yes') { - $update = $true -} elseif($data.attributes.traits.cpu -ne $flexibleAsset.data.attributes.traits.cpu) { - Write-Verbose "Change detected. Will update asset." - $update = $true -} elseif($data.attributes.traits.'ram-gb' -ne $flexibleAsset.data.attributes.traits.'ram-gb') { - Write-Verbose "Change detected. Will update asset." - $update = $true -} elseif(($data.attributes.traits.'disk-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'disk-information'.replace("`n","").replace("`r",""))) { - Write-Verbose "Change detected. Will update asset." - $update = $true -} elseif(($data.attributes.traits.'virtual-switches'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'virtual-switches'.replace("`n","").replace("`r",""))) { - Write-Verbose "Change detected. Will update asset." - $update = $true -} elseif($data.attributes.traits.'current-number-of-vm-guests-on-this-vm-host' -ne $flexibleAsset.data.attributes.traits.'current-number-of-vm-guests-on-this-vm-host') { - Write-Verbose "Change detected. Will update asset." - $update = $true -} elseif(($data.attributes.traits.'vm-guest-names-and-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guest-names-and-information'.replace("`n","").replace("`r",""))) { - Write-Verbose "Change detected. Will update asset." - $update = $true -} elseif(($data.attributes.traits.'vm-guest-virtual-disk-paths'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guest-virtual-disk-paths'.replace("`n","").replace("`r",""))) { - Write-Verbose "Change detected. Will update asset." - $update = $true -} elseif(($data.attributes.traits.'vm-guests-snapshot-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guests-snapshot-information'.replace("`n","").replace("`r",""))) { - Write-Verbose "Change detected. Will update asset." - $update = $true -} elseif(($data.attributes.traits.'vm-guests-bios-settings'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guests-bios-settings'.replace("`n","").replace("`r",""))) { - Write-Verbose "Change detected. Will update asset." - $update = $true -} elseif(($data.attributes.traits.'assigned-virtual-switches-and-ip-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'assigned-virtual-switches-and-ip-information'.replace("`n","").replace("`r",""))) { - Write-Verbose "Change detected. Will update asset." - $update = $true -} - -if($update) { - Write-Verbose "Begin updating asset.." - - $data["attributes"]["id"] = $flexible_asset_id - Write-Verbose "Added id to hash table." - # Visible name - $data["attributes"]["traits"]["vm-host-name"] = $flexibleAsset.data.attributes.traits.'vm-host-name' - Write-Verbose "Added VM host name to hash table." - # Tagged asset (i.e the host) - $data["attributes"]["traits"]["vm-host-related-it-glue-configuration"] = $flexibleAsset.data.attributes.traits.'vm-host-related-it-glue-configuration'.Values.id - Write-Verbose "Added VM host related IT Glue configuration to hash table." - - Write-Verbose "Uploading data for id $flexible_asset_id." - $resp = Set-ITGlueFlexibleAssets -data $data - Write-Verbose "Uploading data: done." - return $resp -} else { - Write-Verbose "No change detected. Not updating." +#Requires -Version 3 +#Requires -Modules @{ ModuleName="ITGlueAPI"; ModuleVersion="2.0.7" } + + +[cmdletbinding()] +param( + [Parameter(HelpMessage='The id of the asset in IT Glue')] + [long]$flexible_asset_id, + + [Parameter(HelpMessage='IT Glue api key')] + [string]$api_key, + + [Parameter(HelpMessage='Where is your data stored? EU or US?')] + [ValidateSet('US', 'EU')] + [string]$data_center, + + [Parameter(HelpMessage='The first part of your IT Glue URL when logging in in ')] + [string]$subdomain +) + +# Import the IT Glue wrapper module +Import-Module ITGlueAPI -ErrorAction Stop + +# If any parameter is missing ... +# (Cannot use mandatory because it would break setting parameters inside the script.) + +if($api_key) { + try { + Write-Verbose "Decrypting API key." + $api_key = [PSCredential]::new('null', ($api_key | ConvertTo-SecureString -ErrorAction Stop)).GetNetworkCredential().Password + Write-Verbose "Decrypted and stored." + } catch { + Write-Verbose "API key not encrypted." + } + + # Set API key for this sessions + Write-Verbose "Using specified API key." + Add-ITGlueAPIKey -api_key $api_key +} elseif(!$api_key -and $ITGlue_API_Key) { + # Use API key imported from module settings + Write-Verbose "Using API key from module settings already saved." +} else { + return "No API key was found or specified, please use -api_key to specify it and run the script again. This script will not continue." +} + +if($data_center) { + # Set URL for this sessions + Write-Verbose "Using specified data center $data_center for this session." + Add-ITGlueBaseURI -data_center $data_center +} elseif(!$data_center -and $ITGlue_Base_URI) { + # Use URL imported from module settings + Write-Verbose "Using URL from module settings already saved." +} else { + return "No data center was found or specified, please use -data_center to specify it (US or EU) and run the script again. This script will not continue." +} + +if(!$flexible_asset_id) { + return "flexible_asset_id is missing. Please specify it and run the script again. This script will not continue." +} + +# Flexible asset to update +Write-Verbose "Retreving IT Glue flexible asset id: $flexible_asset_id..." +$flexibleAsset = Get-ITGlueFlexibleAssets -id $flexible_asset_id +Write-Verbose "Done." + +# The asset's organization id +Write-Verbose "Retreving organization id..." +$organization_id = $flexibleAsset.data.attributes.'organization-id' +Write-Verbose "Done." + +Write-Verbose "Formating URL..." +$url = (Get-ITGlueBaseURI).replace('https://api', 'https://{0}' -f $subdomain) +Write-Verbose "Done." + +Write-Verbose "Retrieving configurations from IT Glue (org id: $organization_id)..." +$configurations = @{} +$page_number = 1 +do{ + Write-Verbose "Calling the IT Glue api (page $page_number)..." + $api_call = Get-ITGlueConfigurations -organization_id $organization_id -page_size 1000 -page_number ($page_number++) + foreach($_ in $api_call.data) { + $configurations[$_.attributes.name] = $_ + } +} while($api_call.links.next) +Write-Verbose "Done." + +# All VMs on the host (with some data) +Write-Verbose "Creating hashtable with HTML name and rest of data..." +$VMs = @{} +foreach($vm in Get-VM) { + $htmlname = $vm.name + + if($configurations[$vm.Name]) { + $htmlname = '{3}' -f $url, $configurations[$vm.Name].attributes.'organization-id', $configurations[$vm.Name].id, $vm.name + } else { + $configurations.GetEnumerator() | Where {$_.Name -like "*$($vm.name)*"} | ForEach-Object { + $htmlname = '{3}' -f $url, $_.value.attributes.'organization-id', $_.value.id, $vm.name + } + } + + + $VMs[$vm.name] = [PSCustomObject]@{ + name = $vm.name + vm = $vm + htmlname = $htmlname + } +} +Write-Verbose "Done." + +# Hyper-V host's disk information / "Disk information" +Write-Verbose "Getting host's disk data..." +$diskDataHTML = '
+ + + + + + + + + {0} + +
Disk nameTotal(GB)Used(GB)Free(GB)
+
' -f ((Get-PSDrive -PSProvider FileSystem).foreach{ + ' + {0} + {1} + {2} + {3} + ' -f $_.Root, [math]::round(($_.free+$_.used)/1GB), [math]::round($_.used/1GB), [math]::round($_.free/1GB)} | Out-String) +Write-Verbose "Host's disk data done. [1/7]" + +# Virtual swtiches / "Virtual switches" +Write-Verbose "Getting virtual swtiches..." +$virtualSwitchsHTML = '
+ + + + + + + + {0} + +
NameSwitch typeInterface description
+
' -f ((Get-VMSwitch).foreach{ + ' + {0} + {1} + {2} + ' -f $_.Name, $_.SwitchType, $_.NetAdapterInterfaceDescription} | Out-String) +Write-Verbose "Virtual swtiches done. [2/7]" + +# General information about virtual machines / "VM guest names and information" +Write-Verbose "Getting general guest information..." +$guestInformationHTML = '
+ + + + + + + + + + {0} + +
VM guest nameStart actionRAM (GB)vCPUSize (GB)
+
' -f ($VMs.GetEnumerator().foreach{ + $diskSize = 0 + ($_.value.vm.HardDrives | Get-VHD).FileSize.foreach{$diskSize += $_} + $diskSize = [Math]::Round($diskSize/1GB) + ' + {0} + {1} + {2} + {3} + {4} + ' -f $_.value.htmlname, $_.value.vm.AutomaticStartAction, [Math]::Round($_.value.vm.MemoryStartup/1GB), $_.value.vm.ProcessorCount, $diskSize} | Out-String) +Write-Verbose "General guest information done. [3/7]" + +# Virutal machines' disk file locations / "VM guest virtual disk paths" +Write-Verbose "Getting VM machine paths..." +$virtualMachinePathsHTML = '
+ + + + + + + {0} + +
VM guest namePath
+
' -f ($VMs.GetEnumerator().foreach{ + ' + {0} + {1} + ' -f $_.value.htmlname, ((Get-VHD -id $_.value.vm.id).path | Out-String).Replace([Environment]::NewLine, '
').TrimEnd('
')} | Out-String) +Write-Verbose "VM machine paths done. [4/7]" + +# Snapshot data / "VM guests snapshot information" +Write-Verbose "Getting snapshot data..." +$vmSnapshotHTML = '
+ + + + + + + + + + {0} + +
VMNameNameSnapshot typeCreation timeParent snapshot name
+
' -f ((Get-VMSnapshot -VMName * | Sort VMName, CreationTime).foreach{ + ' + {0} + {1} + {2} + {3} + {4} + ' -f $VMs[$_.VMName].htmlname, $_.Name, $_.SnapshotType, $_.CreationTime, $_.ParentSnapshotName} | Out-String) +Write-Verbose "Snapshot data done. [5/7]" + +# Virutal machines' bios settings / "VM guests BIOS settings" +Write-Verbose "Getting VM BIOS settings..." +# Generation 1 +$vmBiosSettingsTableData = (Get-VMBios * -ErrorAction SilentlyContinue).foreach{ + ' + {0} + {1} + {2} + {3} + Gen 1 + ' -f $VMs[$_.VMName].htmlname, ($_.StartupOrder | Out-String).Replace([Environment]::NewLine, ', ').TrimEnd(', '), 'N/A', 'N/A'} +Write-Verbose "Generation 1 done..." + +# Generation 2 +$vmBiosSettingsTableData += (Get-VMFirmware * -ErrorAction SilentlyContinue).foreach{ + ' + {0} + {1} + {2} + {3} + Gen 2 + ' -f $VMs[$_.VMName].htmlname, ($_.BootOrder.BootType | Out-String).Replace([Environment]::NewLine, ', ').TrimEnd(', '), $_.PauseAfterBootFailure, $_.SecureBoot} +Write-Verbose "Generation 2 done..." + +$vmBIOSSettingsHTML = '
+ + + + + + + + + + {0} + +
VM guest nameStartup orderPause After Boot FailureSecure BootGeneration
+
' -f ($vmBiosSettingsTableData | Out-String) +Write-Verbose "VM BIOS settings done. [6/7]" + +# Guest NICs and IPs +Write-Verbose "Getting VM NICs..." +$guestNICsIPsHTML = '
+ + + + + + + + + {0} + +
VM guest nameSwtich nameIPv4IPv6
+
' -f ((Get-VMNetworkAdapter * | Sort 'VMName').foreach{ + ' + {0} + {1} + {2} + {3} + ' -f $VMs[$_.VMName].htmlname, $_.switchname, $_.ipaddresses[0], $_.ipaddresses[1]} | Out-String) +Write-Verbose "VM NICs done. [7/7]" + + +$data = @{ + type = 'flexible-assets' + attributes = @{ + traits = @{ + # Manual sync + 'force-manual-sync-now' = 'No' + # Host platform + 'virtualization-platform' = 'Hyper-V' + # Host CPU data + 'cpu' = Get-VMHost | Select -ExpandProperty LogicalProcessorCount + # Host RAM data + 'ram-gb' = ((Get-CimInstance CIM_PhysicalMemory).capacity | Measure -Sum).Sum/1GB + # Host disk data + 'disk-information' = $diskDataHTML + # Virutal network cards (vNIC) + 'virtual-switches' = $virtualSwitchsHTML + # Number of VMs on host + 'current-number-of-vm-guests-on-this-vm-host' = ($VMs.GetEnumerator() | measure).Count + # General VM data (start type, cpu, ram...) + 'vm-guest-names-and-information' = $guestInformationHTML + # VMs' name and VHD paths + 'vm-guest-virtual-disk-paths' = $virtualMachinePathsHTML + # Snapshop data + 'vm-guests-snapshot-information' = $vmSnapshotHTML + # VMs' bios settings + 'vm-guests-bios-settings' = $vmBIOSSettingsHTML + # NIC and IP assigned to each VM + 'assigned-virtual-switches-and-ip-information' = $guestNICsIPsHTML + } + } +} +Write-Verbose "Finished build hash table." + + +Write-Verbose "Comparing data.." + +$update = $false + +if($flexibleAsset.data.attributes.traits.'force-manual-sync-now' -eq 'Yes') { + $update = $true +} elseif($data.attributes.traits.cpu -ne $flexibleAsset.data.attributes.traits.cpu) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif($data.attributes.traits.'ram-gb' -ne $flexibleAsset.data.attributes.traits.'ram-gb') { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'disk-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'disk-information'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'virtual-switches'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'virtual-switches'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif($data.attributes.traits.'current-number-of-vm-guests-on-this-vm-host' -ne $flexibleAsset.data.attributes.traits.'current-number-of-vm-guests-on-this-vm-host') { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'vm-guest-names-and-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guest-names-and-information'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'vm-guest-virtual-disk-paths'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guest-virtual-disk-paths'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'vm-guests-snapshot-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guests-snapshot-information'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'vm-guests-bios-settings'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guests-bios-settings'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif(($data.attributes.traits.'assigned-virtual-switches-and-ip-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'assigned-virtual-switches-and-ip-information'.replace("`n","").replace("`r",""))) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} + +if($update) { + Write-Verbose "Begin updating asset.." + + $data["attributes"]["id"] = $flexible_asset_id + Write-Verbose "Added id to hash table." + # Visible name + $data["attributes"]["traits"]["vm-host-name"] = $flexibleAsset.data.attributes.traits.'vm-host-name' + Write-Verbose "Added VM host name to hash table." + # Tagged asset (i.e the host) + $data["attributes"]["traits"]["vm-host-related-it-glue-configuration"] = $flexibleAsset.data.attributes.traits.'vm-host-related-it-glue-configuration'.Values.id + Write-Verbose "Added VM host related IT Glue configuration to hash table." + + Write-Verbose "Uploading data for id $flexible_asset_id." + $resp = Set-ITGlueFlexibleAssets -data $data + Write-Verbose "Uploading data: done." + return $resp +} else { + Write-Verbose "No change detected. Not updating." } \ No newline at end of file diff --git a/ITGlue-VMHost-Setup.ps1 b/hyperv-sync/ITGlue-VMHost-Setup.ps1 similarity index 98% rename from ITGlue-VMHost-Setup.ps1 rename to hyperv-sync/ITGlue-VMHost-Setup.ps1 index 211e91e..1f7be75 100644 --- a/ITGlue-VMHost-Setup.ps1 +++ b/hyperv-sync/ITGlue-VMHost-Setup.ps1 @@ -1,26 +1,26 @@ -try { - Import-Module ITGlueAPI -ErrorAction Stop - Get-Variable ITGlue_API_Key -ErrorAction Stop > $null - Get-Variable ITGlue_Base_URI -ErrorAction Stop > $null -} catch { - $apikey = Read-Host "Enter IT Glue API key" - do{ - $datacenter = Read-Host "Enter IT Glue data center (EU/US)" - } until($datacenter -eq 'EU' -or $datacenter -eq 'US') - - Add-ITGlueAPIKey -Api_Key $apikey - Add-ITGlueBaseURI -data_center $datacenter - Export-ITGlueModuleSettings -} - -$flexible_asset_id = Read-Host "Enter flexible asset id (unique ID for asset to update)" - -# Create scheduled task -# Action -$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument ('-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden "{0}\ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 -flexible_asset_id {1}"' -f $PSScriptRoot, $flexible_asset_id) -# Trigger -$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -# Settings -$settings = New-ScheduledTaskSettingsSet -WakeToRun -RestartCount 3 -RunOnlyIfNetworkAvailable -StartWhenAvailable -RestartInterval (New-TimeSpan -Minutes 3) -# Add to task scheduler +try { + Import-Module ITGlueAPI -ErrorAction Stop + Get-Variable ITGlue_API_Key -ErrorAction Stop > $null + Get-Variable ITGlue_Base_URI -ErrorAction Stop > $null +} catch { + $apikey = Read-Host "Enter IT Glue API key" + do{ + $datacenter = Read-Host "Enter IT Glue data center (EU/US)" + } until($datacenter -eq 'EU' -or $datacenter -eq 'US') + + Add-ITGlueAPIKey -Api_Key $apikey + Add-ITGlueBaseURI -data_center $datacenter + Export-ITGlueModuleSettings +} + +$flexible_asset_id = Read-Host "Enter flexible asset id (unique ID for asset to update)" + +# Create scheduled task +# Action +$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument ('-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden "{0}\ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 -flexible_asset_id {1}"' -f $PSScriptRoot, $flexible_asset_id) +# Trigger +$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) +# Settings +$settings = New-ScheduledTaskSettingsSet -WakeToRun -RestartCount 3 -RunOnlyIfNetworkAvailable -StartWhenAvailable -RestartInterval (New-TimeSpan -Minutes 3) +# Add to task scheduler Register-ScheduledTask -Action $action -Trigger $trigger -TaskPath "\ITGlueSync\" -TaskName "Sync HyperV with IT Glue" -Settings $settings -Force \ No newline at end of file diff --git a/hyperv-sync/README.md b/hyperv-sync/README.md new file mode 100644 index 0000000..e69de29 From 45f472497a4f9688a71c2f666d41fa937ac3629d Mon Sep 17 00:00:00 2001 From: Emile Date: Tue, 25 Jun 2019 16:06:49 +0200 Subject: [PATCH 03/18] Setup instructions --- hyperv-sync/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hyperv-sync/README.md b/hyperv-sync/README.md index e69de29..9da4631 100644 --- a/hyperv-sync/README.md +++ b/hyperv-sync/README.md @@ -0,0 +1,14 @@ +1. Install the PowerShell wrapper for ITGlue's API on each Hyper-V server to sync. https://github.com/itglue/powershellwrapper +2. Run `ITGlue-VMHost-CreateFlexibleAsset.ps1` to create the flexible asset needed and add it to the side bar ("_VM Host_"). +3. Create a new _VM Host_ asset for each server you want to sync. Take note of individual IDs. +3a. subdomain.itglue.com/1234568/assets/records/**1153985665234565** +3b. You only need to give it a name and tag a related a configuration in IT Glue. + +From here on, there are multiple options on how to specify the script's paramaters *(i.e. in the file, as paramaters when calling, via module settings)* but these instructions will explain how to store API settings via the module. + +4. Assign subdomain to the variable **subdomain** in the script. +4a. If you login on happyfrog.itglue.com, enter `[string]$subdomain = 'happyfrog'`. +4b. If you login on froghappy.eu.itglue.com, enter `[string]$subdomain = 'froghappy'`. +5. Place `ITGlue-VMHost-Setup.ps1` and `ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1` in a folder to house these script for the duration of their lives *(i.e. somewhere they will not be moved from)*. +6. **IMPORTANT: Do this step as the user who will be running the script on the server** +Run `ITGlue-VMHost-Setup.ps1`. The script will ask for IT Glue API key, data center (EU/US) (if not saved already) and **flexible_asset_id**. It will save API key and data center via the module and create a new task in TaskScheduler under \ITGlueSync\ scheduled to run daily at the time when the setup was run. From 2b6bc4433716b7775c2957ba99761d51a454f2d6 Mon Sep 17 00:00:00 2001 From: Emile Date: Tue, 25 Jun 2019 16:07:32 +0200 Subject: [PATCH 04/18] Update README.md --- hyperv-sync/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hyperv-sync/README.md b/hyperv-sync/README.md index 9da4631..eae5543 100644 --- a/hyperv-sync/README.md +++ b/hyperv-sync/README.md @@ -7,8 +7,8 @@ From here on, there are multiple options on how to specify the script's paramaters *(i.e. in the file, as paramaters when calling, via module settings)* but these instructions will explain how to store API settings via the module. 4. Assign subdomain to the variable **subdomain** in the script. -4a. If you login on happyfrog.itglue.com, enter `[string]$subdomain = 'happyfrog'`. -4b. If you login on froghappy.eu.itglue.com, enter `[string]$subdomain = 'froghappy'`. +4a. If you login on happyfrog.itglue.com, enter happyfrog: `[string]$subdomain = 'happyfrog'`. +4b. If you login on froghappy.eu.itglue.com, enter froghappy: `[string]$subdomain = 'froghappy'`. 5. Place `ITGlue-VMHost-Setup.ps1` and `ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1` in a folder to house these script for the duration of their lives *(i.e. somewhere they will not be moved from)*. 6. **IMPORTANT: Do this step as the user who will be running the script on the server** Run `ITGlue-VMHost-Setup.ps1`. The script will ask for IT Glue API key, data center (EU/US) (if not saved already) and **flexible_asset_id**. It will save API key and data center via the module and create a new task in TaskScheduler under \ITGlueSync\ scheduled to run daily at the time when the setup was run. From 0f5e61c586c70129bacff1d8977896a9febdc233 Mon Sep 17 00:00:00 2001 From: Emile Priller Date: Tue, 25 Jun 2019 16:09:10 +0200 Subject: [PATCH 05/18] trigger changed from once to daily --- hyperv-sync/ITGlue-VMHost-Setup.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperv-sync/ITGlue-VMHost-Setup.ps1 b/hyperv-sync/ITGlue-VMHost-Setup.ps1 index 1f7be75..5bb2790 100644 --- a/hyperv-sync/ITGlue-VMHost-Setup.ps1 +++ b/hyperv-sync/ITGlue-VMHost-Setup.ps1 @@ -19,7 +19,7 @@ $flexible_asset_id = Read-Host "Enter flexible asset id (unique ID for asset to # Action $action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument ('-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden "{0}\ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 -flexible_asset_id {1}"' -f $PSScriptRoot, $flexible_asset_id) # Trigger -$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) +$trigger = New-ScheduledTaskTrigger -Daily -At (Get-Date) # Settings $settings = New-ScheduledTaskSettingsSet -WakeToRun -RestartCount 3 -RunOnlyIfNetworkAvailable -StartWhenAvailable -RestartInterval (New-TimeSpan -Minutes 3) # Add to task scheduler From 42b1708b78d7086f58ccb8ce2583deaff1bd3385 Mon Sep 17 00:00:00 2001 From: Emile Priller Date: Wed, 26 Jun 2019 09:54:48 +0200 Subject: [PATCH 06/18] Added MAC address --- .../ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 index f9c2472..738ae98 100644 --- a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 +++ b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 @@ -40,7 +40,7 @@ if($api_key) { # Use API key imported from module settings Write-Verbose "Using API key from module settings already saved." } else { - return "No API key was found or specified, please use -api_key to specify it and run the script again. This script will not continue." + return "No API key was found or specified, please use -api_key to specify it and run the script again." } if($data_center) { @@ -51,7 +51,7 @@ if($data_center) { # Use URL imported from module settings Write-Verbose "Using URL from module settings already saved." } else { - return "No data center was found or specified, please use -data_center to specify it (US or EU) and run the script again. This script will not continue." + return "No data center was found or specified, please use -data_center to specify it (US or EU) and run the script again." } if(!$flexible_asset_id) { @@ -74,12 +74,16 @@ Write-Verbose "Done." Write-Verbose "Retrieving configurations from IT Glue (org id: $organization_id)..." $configurations = @{} +$MACs = @{} $page_number = 1 do{ Write-Verbose "Calling the IT Glue api (page $page_number)..." $api_call = Get-ITGlueConfigurations -organization_id $organization_id -page_size 1000 -page_number ($page_number++) foreach($_ in $api_call.data) { $configurations[$_.attributes.name] = $_ + if($_.attributes.'mac-address') { + $MACs[$_.attributes.'mac-address'.replace(':','')] = $_ + } } } while($api_call.links.next) Write-Verbose "Done." @@ -89,15 +93,18 @@ Write-Verbose "Creating hashtable with HTML name and rest of data..." $VMs = @{} foreach($vm in Get-VM) { $htmlname = $vm.name - + if($configurations[$vm.Name]) { - $htmlname = '{3}' -f $url, $configurations[$vm.Name].attributes.'organization-id', $configurations[$vm.Name].id, $vm.name + $htmlname = '{3}' -f $url, $configurations[$vm.Name].attributes.'organization-id', $configurations[$vm.Name].id, $vm.name + } elseif($MACs[($vm.Name | Get-VMNetworkAdapter).MacAddress]) { + $config = $MACs[($vm.Name | Get-VMNetworkAdapter).MacAddress] + $htmlname = '{3}' -f $url, $config.attributes.'organization-id', $config.id, $config.attributes.name } else { $configurations.GetEnumerator() | Where {$_.Name -like "*$($vm.name)*"} | ForEach-Object { $htmlname = '{3}' -f $url, $_.value.attributes.'organization-id', $_.value.id, $vm.name } } - + $VMs[$vm.name] = [PSCustomObject]@{ name = $vm.name @@ -273,17 +280,24 @@ $guestNICsIPsHTML = '
Swtich name IPv4 IPv6 + MAC address {0}
' -f ((Get-VMNetworkAdapter * | Sort 'VMName').foreach{ + $macAddr = $_.MacAddress + for($i=2; $i -lt $macAddr.Length; $i=$i+3) { + $macAddr = $macAddr.Insert($i,':') + } + ' {0} {1} {2} {3} - ' -f $VMs[$_.VMName].htmlname, $_.switchname, $_.ipaddresses[0], $_.ipaddresses[1]} | Out-String) + {4} + ' -f $VMs[$_.VMName].htmlname, $_.switchname, $_.ipaddresses[0], $_.ipaddresses[1], $macAddr } | Out-String) Write-Verbose "VM NICs done. [7/7]" From 0bb0a485fd6c82d2faecb53f0f0afb3b65bebb7c Mon Sep 17 00:00:00 2001 From: Emile Priller Date: Wed, 26 Jun 2019 10:01:35 +0200 Subject: [PATCH 07/18] regex instead of forloop for macaddr --- hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 index 738ae98..0d6aa4f 100644 --- a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 +++ b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 @@ -93,7 +93,7 @@ Write-Verbose "Creating hashtable with HTML name and rest of data..." $VMs = @{} foreach($vm in Get-VM) { $htmlname = $vm.name - + if($configurations[$vm.Name]) { $htmlname = '{3}' -f $url, $configurations[$vm.Name].attributes.'organization-id', $configurations[$vm.Name].id, $vm.name } elseif($MACs[($vm.Name | Get-VMNetworkAdapter).MacAddress]) { @@ -286,18 +286,13 @@ $guestNICsIPsHTML = '
' -f ((Get-VMNetworkAdapter * | Sort 'VMName').foreach{ - $macAddr = $_.MacAddress - for($i=2; $i -lt $macAddr.Length; $i=$i+3) { - $macAddr = $macAddr.Insert($i,':') - } - ' {0} {1} {2} {3} {4} - ' -f $VMs[$_.VMName].htmlname, $_.switchname, $_.ipaddresses[0], $_.ipaddresses[1], $macAddr } | Out-String) + ' -f $VMs[$_.VMName].htmlname, $_.switchname, $_.ipaddresses[0], $_.ipaddresses[1], $($_.MacAddress -replace '(..(?!$))','$1:') } | Out-String) Write-Verbose "VM NICs done. [7/7]" From 8be4caf1b4fcd158f54b07a481b84d3f60148fb0 Mon Sep 17 00:00:00 2001 From: Emile Date: Wed, 26 Jun 2019 10:02:40 +0200 Subject: [PATCH 08/18] Update README.md --- hyperv-sync/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperv-sync/README.md b/hyperv-sync/README.md index eae5543..c38c991 100644 --- a/hyperv-sync/README.md +++ b/hyperv-sync/README.md @@ -1,5 +1,5 @@ 1. Install the PowerShell wrapper for ITGlue's API on each Hyper-V server to sync. https://github.com/itglue/powershellwrapper -2. Run `ITGlue-VMHost-CreateFlexibleAsset.ps1` to create the flexible asset needed and add it to the side bar ("_VM Host_"). +2. Run `ITGlue-VMHost-CreateFlexibleAsset.ps1` once to create the flexible asset needed and add it to the side bar ("_VM Host_"). 3. Create a new _VM Host_ asset for each server you want to sync. Take note of individual IDs. 3a. subdomain.itglue.com/1234568/assets/records/**1153985665234565** 3b. You only need to give it a name and tag a related a configuration in IT Glue. From dab9d72245c16584f8ff6f01d05d345ccb40eb1e Mon Sep 17 00:00:00 2001 From: Emile Date: Wed, 26 Jun 2019 10:03:15 +0200 Subject: [PATCH 09/18] Update README.md --- hyperv-sync/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperv-sync/README.md b/hyperv-sync/README.md index c38c991..1c0120b 100644 --- a/hyperv-sync/README.md +++ b/hyperv-sync/README.md @@ -2,7 +2,7 @@ 2. Run `ITGlue-VMHost-CreateFlexibleAsset.ps1` once to create the flexible asset needed and add it to the side bar ("_VM Host_"). 3. Create a new _VM Host_ asset for each server you want to sync. Take note of individual IDs. 3a. subdomain.itglue.com/1234568/assets/records/**1153985665234565** -3b. You only need to give it a name and tag a related a configuration in IT Glue. +3b. You only need to give it a name and tag a related a configuration in IT Glue when creating the assets. From here on, there are multiple options on how to specify the script's paramaters *(i.e. in the file, as paramaters when calling, via module settings)* but these instructions will explain how to store API settings via the module. From 4e71445ea219c6e50b570d9db2954487faea63d4 Mon Sep 17 00:00:00 2001 From: Emile Date: Wed, 26 Jun 2019 10:07:07 +0200 Subject: [PATCH 10/18] Update README.md --- hyperv-sync/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperv-sync/README.md b/hyperv-sync/README.md index 1c0120b..e98723c 100644 --- a/hyperv-sync/README.md +++ b/hyperv-sync/README.md @@ -4,7 +4,7 @@ 3a. subdomain.itglue.com/1234568/assets/records/**1153985665234565** 3b. You only need to give it a name and tag a related a configuration in IT Glue when creating the assets. -From here on, there are multiple options on how to specify the script's paramaters *(i.e. in the file, as paramaters when calling, via module settings)* but these instructions will explain how to store API settings via the module. +From here on, there are multiple options on how to specify the script's paramaters *(i.e. in the file, as paramaters when calling, via module settings)* but these instructions will store API settings via the wrapper module and asset ID in the scheduled task. 4. Assign subdomain to the variable **subdomain** in the script. 4a. If you login on happyfrog.itglue.com, enter happyfrog: `[string]$subdomain = 'happyfrog'`. From b6fe2e165d122ddb9eff8c9dfd781cb8a259e491 Mon Sep 17 00:00:00 2001 From: Emile Date: Wed, 26 Jun 2019 10:14:15 +0200 Subject: [PATCH 11/18] Update README.md --- hyperv-sync/README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hyperv-sync/README.md b/hyperv-sync/README.md index e98723c..4a17a67 100644 --- a/hyperv-sync/README.md +++ b/hyperv-sync/README.md @@ -6,9 +6,11 @@ From here on, there are multiple options on how to specify the script's paramaters *(i.e. in the file, as paramaters when calling, via module settings)* but these instructions will store API settings via the wrapper module and asset ID in the scheduled task. -4. Assign subdomain to the variable **subdomain** in the script. -4a. If you login on happyfrog.itglue.com, enter happyfrog: `[string]$subdomain = 'happyfrog'`. -4b. If you login on froghappy.eu.itglue.com, enter froghappy: `[string]$subdomain = 'froghappy'`. +4. Note down the subdomain of your IT Glue URL: +4a. happyfrog.itglue.com translate to `happyfrog`. +4b. froghappy.eu.itglue.com translates to `froghappy`. 5. Place `ITGlue-VMHost-Setup.ps1` and `ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1` in a folder to house these script for the duration of their lives *(i.e. somewhere they will not be moved from)*. -6. **IMPORTANT: Do this step as the user who will be running the script on the server** -Run `ITGlue-VMHost-Setup.ps1`. The script will ask for IT Glue API key, data center (EU/US) (if not saved already) and **flexible_asset_id**. It will save API key and data center via the module and create a new task in TaskScheduler under \ITGlueSync\ scheduled to run daily at the time when the setup was run. + +**IMPORTANT: Do the next step as the user who will be running the script on the server** + +6. Run `ITGlue-VMHost-Setup.ps1`. The script will ask for IT Glue API key, data center (EU/US) (if not saved already) and **flexible_asset_id**. It will save API key and data center via the module and create a new task in TaskScheduler under \ITGlueSync\ scheduled to run daily at the time when the setup was run. From ae0ba48787d57587084687843ab853254e25fd9e Mon Sep 17 00:00:00 2001 From: Emile Date: Wed, 26 Jun 2019 10:46:13 +0200 Subject: [PATCH 12/18] Update README.md --- hyperv-sync/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hyperv-sync/README.md b/hyperv-sync/README.md index 4a17a67..9386b82 100644 --- a/hyperv-sync/README.md +++ b/hyperv-sync/README.md @@ -13,4 +13,6 @@ From here on, there are multiple options on how to specify the script's paramate **IMPORTANT: Do the next step as the user who will be running the script on the server** -6. Run `ITGlue-VMHost-Setup.ps1`. The script will ask for IT Glue API key, data center (EU/US) (if not saved already) and **flexible_asset_id**. It will save API key and data center via the module and create a new task in TaskScheduler under \ITGlueSync\ scheduled to run daily at the time when the setup was run. +6. Run `ITGlue-VMHost-Setup.ps1`. If the IT Glue wrapper module settings are missing or not found, the script will ask for IT Glue API key and data center (EU/US). These will be saved with `Export-ITGlueModuleSettings`. Next it will ask for **flexible_asset_id** and **subdomain**. These variables will be saved in the scheduled task. + +The script will now run once every day at the time `ITGlue-VMHost-Setup.ps1` was run. It will detect changes and only updated if something changes or "Force manual sync now" is set to "Yes". From 70d2b0e940fef3d1495fe084179f82dfabc67949 Mon Sep 17 00:00:00 2001 From: Emile Priller Date: Wed, 26 Jun 2019 10:46:35 +0200 Subject: [PATCH 13/18] moved subdomain to being in scheduled task --- hyperv-sync/ITGlue-VMHost-Setup.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hyperv-sync/ITGlue-VMHost-Setup.ps1 b/hyperv-sync/ITGlue-VMHost-Setup.ps1 index 5bb2790..681c839 100644 --- a/hyperv-sync/ITGlue-VMHost-Setup.ps1 +++ b/hyperv-sync/ITGlue-VMHost-Setup.ps1 @@ -14,10 +14,11 @@ try { } $flexible_asset_id = Read-Host "Enter flexible asset id (unique ID for asset to update)" +$subdomain = Read-Host "Enter the subdomain of your IT Glue domain (without eu if present)" # Create scheduled task # Action -$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument ('-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden "{0}\ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 -flexible_asset_id {1}"' -f $PSScriptRoot, $flexible_asset_id) +$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument ('-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden "{0}\ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 -flexible_asset_id {1} -subdomain {2}"' -f $PSScriptRoot, $flexible_asset_id, $subdomain) # Trigger $trigger = New-ScheduledTaskTrigger -Daily -At (Get-Date) # Settings From 32a6dbbcc5587f5a20bb01d25e07d4f376bf6a82 Mon Sep 17 00:00:00 2001 From: Emile Priller Date: Wed, 26 Jun 2019 14:19:54 +0200 Subject: [PATCH 14/18] renamed field --- hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 b/hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 index 2cfaf08..cae92b6 100644 --- a/hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 +++ b/hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 @@ -220,7 +220,7 @@ VMware" type = "flexible_asset_fields" Attributes = @{ order = 16 - Name = "Force manual sync now?" + Name = "Force manual sync?" kind = "Select" required = $false use_for_title = $false From 851384be21e14e6c151bbd2e96575b7eac6e3b7f Mon Sep 17 00:00:00 2001 From: Emile Priller Date: Thu, 27 Jun 2019 10:54:58 +0200 Subject: [PATCH 15/18] Adds and remove related items (pre variable cleaning) --- .../ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 | 146 +++++++++++++++--- 1 file changed, 122 insertions(+), 24 deletions(-) diff --git a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 index 0d6aa4f..5ecc31d 100644 --- a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 +++ b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 @@ -93,15 +93,18 @@ Write-Verbose "Creating hashtable with HTML name and rest of data..." $VMs = @{} foreach($vm in Get-VM) { $htmlname = $vm.name + $conf_id = -1 if($configurations[$vm.Name]) { $htmlname = '{3}' -f $url, $configurations[$vm.Name].attributes.'organization-id', $configurations[$vm.Name].id, $vm.name + $conf_id = $configurations[$vm.Name].id } elseif($MACs[($vm.Name | Get-VMNetworkAdapter).MacAddress]) { $config = $MACs[($vm.Name | Get-VMNetworkAdapter).MacAddress] $htmlname = '{3}' -f $url, $config.attributes.'organization-id', $config.id, $config.attributes.name } else { $configurations.GetEnumerator() | Where {$_.Name -like "*$($vm.name)*"} | ForEach-Object { $htmlname = '{3}' -f $url, $_.value.attributes.'organization-id', $_.value.id, $vm.name + $conf_id = $_.value.id } } @@ -110,6 +113,7 @@ foreach($vm in Get-VM) { name = $vm.name vm = $vm htmlname = $htmlname + conf_id = $conf_id } } Write-Verbose "Done." @@ -135,7 +139,7 @@ $diskDataHTML = '
{2} {3} ' -f $_.Root, [math]::round(($_.free+$_.used)/1GB), [math]::round($_.used/1GB), [math]::round($_.free/1GB)} | Out-String) -Write-Verbose "Host's disk data done. [1/7]" +Write-Verbose "Host's disk data done. [1/8]" # Virtual swtiches / "Virtual switches" Write-Verbose "Getting virtual swtiches..." @@ -156,7 +160,7 @@ $virtualSwitchsHTML = '
{1} {2} ' -f $_.Name, $_.SwitchType, $_.NetAdapterInterfaceDescription} | Out-String) -Write-Verbose "Virtual swtiches done. [2/7]" +Write-Verbose "Virtual swtiches done. [2/8]" # General information about virtual machines / "VM guest names and information" Write-Verbose "Getting general guest information..." @@ -184,7 +188,7 @@ $guestInformationHTML = '
{3} {4} ' -f $_.value.htmlname, $_.value.vm.AutomaticStartAction, [Math]::Round($_.value.vm.MemoryStartup/1GB), $_.value.vm.ProcessorCount, $diskSize} | Out-String) -Write-Verbose "General guest information done. [3/7]" +Write-Verbose "General guest information done. [3/8]" # Virutal machines' disk file locations / "VM guest virtual disk paths" Write-Verbose "Getting VM machine paths..." @@ -203,7 +207,7 @@ $virtualMachinePathsHTML = '
{0} {1} ' -f $_.value.htmlname, ((Get-VHD -id $_.value.vm.id).path | Out-String).Replace([Environment]::NewLine, '
').TrimEnd('
')} | Out-String) -Write-Verbose "VM machine paths done. [4/7]" +Write-Verbose "VM machine paths done. [4/8]" # Snapshot data / "VM guests snapshot information" Write-Verbose "Getting snapshot data..." @@ -228,7 +232,7 @@ $vmSnapshotHTML = '
{3} {4} ' -f $VMs[$_.VMName].htmlname, $_.Name, $_.SnapshotType, $_.CreationTime, $_.ParentSnapshotName} | Out-String) -Write-Verbose "Snapshot data done. [5/7]" +Write-Verbose "Snapshot data done. [5/8]" # Virutal machines' bios settings / "VM guests BIOS settings" Write-Verbose "Getting VM BIOS settings..." @@ -268,7 +272,7 @@ $vmBIOSSettingsHTML = '
' -f ($vmBiosSettingsTableData | Out-String) -Write-Verbose "VM BIOS settings done. [6/7]" +Write-Verbose "VM BIOS settings done. [6/8]" # Guest NICs and IPs Write-Verbose "Getting VM NICs..." @@ -293,10 +297,11 @@ $guestNICsIPsHTML = '
{3} {4} ' -f $VMs[$_.VMName].htmlname, $_.switchname, $_.ipaddresses[0], $_.ipaddresses[1], $($_.MacAddress -replace '(..(?!$))','$1:') } | Out-String) -Write-Verbose "VM NICs done. [7/7]" +Write-Verbose "VM NICs done. [7/8]" -$data = @{ +Write-Verbose "Building final data structure..." +$asset_data = @{ type = 'flexible-assets' attributes = @{ traits = @{ @@ -327,7 +332,7 @@ $data = @{ } } } -Write-Verbose "Finished build hash table." +Write-Verbose "Finished building the final structure. [8/8]" Write-Verbose "Comparing data.." @@ -336,54 +341,147 @@ $update = $false if($flexibleAsset.data.attributes.traits.'force-manual-sync-now' -eq 'Yes') { $update = $true -} elseif($data.attributes.traits.cpu -ne $flexibleAsset.data.attributes.traits.cpu) { +} elseif($asset_data.attributes.traits.cpu -ne $flexibleAsset.data.attributes.traits.cpu) { Write-Verbose "Change detected. Will update asset." $update = $true -} elseif($data.attributes.traits.'ram-gb' -ne $flexibleAsset.data.attributes.traits.'ram-gb') { +} elseif($asset_data.attributes.traits.'ram-gb' -ne $flexibleAsset.data.attributes.traits.'ram-gb') { Write-Verbose "Change detected. Will update asset." $update = $true -} elseif(($data.attributes.traits.'disk-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'disk-information'.replace("`n","").replace("`r",""))) { +} elseif(($asset_data.attributes.traits.'disk-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'disk-information'.replace("`n","").replace("`r",""))) { Write-Verbose "Change detected. Will update asset." $update = $true -} elseif(($data.attributes.traits.'virtual-switches'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'virtual-switches'.replace("`n","").replace("`r",""))) { +} elseif(($asset_data.attributes.traits.'virtual-switches'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'virtual-switches'.replace("`n","").replace("`r",""))) { Write-Verbose "Change detected. Will update asset." $update = $true -} elseif($data.attributes.traits.'current-number-of-vm-guests-on-this-vm-host' -ne $flexibleAsset.data.attributes.traits.'current-number-of-vm-guests-on-this-vm-host') { +} elseif($asset_data.attributes.traits.'current-number-of-vm-guests-on-this-vm-host' -ne $flexibleAsset.data.attributes.traits.'current-number-of-vm-guests-on-this-vm-host') { Write-Verbose "Change detected. Will update asset." $update = $true -} elseif(($data.attributes.traits.'vm-guest-names-and-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guest-names-and-information'.replace("`n","").replace("`r",""))) { +} elseif(($asset_data.attributes.traits.'vm-guest-names-and-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guest-names-and-information'.replace("`n","").replace("`r",""))) { Write-Verbose "Change detected. Will update asset." $update = $true -} elseif(($data.attributes.traits.'vm-guest-virtual-disk-paths'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guest-virtual-disk-paths'.replace("`n","").replace("`r",""))) { +} elseif(($asset_data.attributes.traits.'vm-guest-virtual-disk-paths'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guest-virtual-disk-paths'.replace("`n","").replace("`r",""))) { Write-Verbose "Change detected. Will update asset." $update = $true -} elseif(($data.attributes.traits.'vm-guests-snapshot-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guests-snapshot-information'.replace("`n","").replace("`r",""))) { +} elseif(($asset_data.attributes.traits.'vm-guests-snapshot-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guests-snapshot-information'.replace("`n","").replace("`r",""))) { Write-Verbose "Change detected. Will update asset." $update = $true -} elseif(($data.attributes.traits.'vm-guests-bios-settings'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guests-bios-settings'.replace("`n","").replace("`r",""))) { +} elseif(($asset_data.attributes.traits.'vm-guests-bios-settings'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'vm-guests-bios-settings'.replace("`n","").replace("`r",""))) { Write-Verbose "Change detected. Will update asset." $update = $true -} elseif(($data.attributes.traits.'assigned-virtual-switches-and-ip-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'assigned-virtual-switches-and-ip-information'.replace("`n","").replace("`r",""))) { +} elseif(($asset_data.attributes.traits.'assigned-virtual-switches-and-ip-information'.replace("`n","").replace("`r","")) -ne ($flexibleAsset.data.attributes.traits.'assigned-virtual-switches-and-ip-information'.replace("`n","").replace("`r",""))) { Write-Verbose "Change detected. Will update asset." $update = $true } if($update) { Write-Verbose "Begin updating asset.." + $response = @{} - $data["attributes"]["id"] = $flexible_asset_id + $asset_data["attributes"]["id"] = $flexible_asset_id Write-Verbose "Added id to hash table." # Visible name - $data["attributes"]["traits"]["vm-host-name"] = $flexibleAsset.data.attributes.traits.'vm-host-name' + $asset_data["attributes"]["traits"]["vm-host-name"] = $flexibleAsset.data.attributes.traits.'vm-host-name' Write-Verbose "Added VM host name to hash table." # Tagged asset (i.e the host) - $data["attributes"]["traits"]["vm-host-related-it-glue-configuration"] = $flexibleAsset.data.attributes.traits.'vm-host-related-it-glue-configuration'.Values.id + $asset_data["attributes"]["traits"]["vm-host-related-it-glue-configuration"] = $flexibleAsset.data.attributes.traits.'vm-host-related-it-glue-configuration'.Values.id Write-Verbose "Added VM host related IT Glue configuration to hash table." Write-Verbose "Uploading data for id $flexible_asset_id." - $resp = Set-ITGlueFlexibleAssets -data $data + $response['asset'] = Set-ITGlueFlexibleAssets -data $asset_data Write-Verbose "Uploading data: done." - return $resp + + + Write-Verbose "Creating new related items (because there it no index/show endpoint to compare data against...)." + $new_related_items_hash = @{} + $new_related_items = New-Object System.Collections.ArrayList + $VMs.GetEnumerator() | Where {$_.value.conf_id -ne '-1'} | Foreach { + [void]$new_related_items.Add( + @{ + type= 'related_items' + attributes = @{ + destination_id = $_.value.conf_id + destination_type = 'Configuration' + } + } + ) + + $new_related_items_hash[$_.value.conf_id] = $_.value.conf_id + } + + Write-Verbose "Done." + + if(Test-Path $PSScriptRoot\hyperv-related-items.txt) { + Write-Verbose "Creating a list of old related items (because there it no index/show endpoint to compare data against...)." + $old_related_items = Get-Content $PSScriptRoot\hyperv-related-items.txt | ConvertFrom-Json + Write-Verbose "Done." + + + Write-Verbose "Comparing with related items..." + $related_items_remove = New-Object System.Collections.ArrayList + + foreach($old_id in $old_related_items) { + if(-not $new_related_items_hash["$($old_id.attributes.'destination-id')"]) { + Write-Verbose "$($old_id.id) is no longer on the host and will be removed." + + [void]$related_items_remove.Add( + @{ + type = 'related_items' + attributes = @{ + id = $old_id.id + } + } + ) + } + } + + Write-Verbose "Done." + + if($related_items_remove) { + Write-Verbose "Removing the old related items..." + + $body = @{} + + $body += @{'data'= $related_items_remove} + + $body = ConvertTo-Json -InputObject $body -Depth $ITGlue_JSON_Conversion_Depth + + $resource_uri_related_items_remove = '/{0}/{1}/relationships/related_items' -f 'flexible_assets', $flexible_asset_id + + try { + $ITGlue_Headers.Add('x-api-key', (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'N/A', $ITGlue_API_Key).GetNetworkCredential().Password) + $response['removed_related_items'] = Invoke-RestMethod -method 'DELETE' -uri ($ITGlue_Base_URI + $resource_uri_related_items_remove) -headers $ITGlue_Headers ` + -body $body -ErrorAction Stop + } catch { + Write-Error $_ + } finally { + [void] ($ITGlue_Headers.Remove('x-api-key')) # Quietly clean up scope so the API key doesn't persist + } + } + } + + + $body = @{} + + $body += @{'data'= $new_related_items} + + $body = ConvertTo-Json -InputObject $body -Depth $ITGlue_JSON_Conversion_Depth + + $resource_uri_related_items = '/{0}/{1}/relationships/related_items' -f 'flexible_assets', $flexible_asset_id + + try { + $ITGlue_Headers.Add('x-api-key', (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'N/A', $ITGlue_API_Key).GetNetworkCredential().Password) + $response['related_items'] = Invoke-RestMethod -method 'POST' -uri ($ITGlue_Base_URI + $resource_uri_related_items) -headers $ITGlue_Headers ` + -body $body -ErrorAction Stop + } catch { + Write-Error $_ + } finally { + [void] ($ITGlue_Headers.Remove('x-api-key')) # Quietly clean up scope so the API key doesn't persist + } + + $response['related_items'].data | ConvertTo-Json -Depth 100 | Out-File $PSScriptRoot\hyperv-related-items.txt -Force + + return $response + } else { Write-Verbose "No change detected. Not updating." } \ No newline at end of file From 1c56165681e824e4406f93a6e89593e4c1544b3c Mon Sep 17 00:00:00 2001 From: Emile Priller Date: Thu, 27 Jun 2019 11:27:40 +0200 Subject: [PATCH 16/18] Also add/removes configs matched on mac addr --- hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 index 5ecc31d..678c6fa 100644 --- a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 +++ b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 @@ -100,6 +100,7 @@ foreach($vm in Get-VM) { $conf_id = $configurations[$vm.Name].id } elseif($MACs[($vm.Name | Get-VMNetworkAdapter).MacAddress]) { $config = $MACs[($vm.Name | Get-VMNetworkAdapter).MacAddress] + $conf_id = $config.id $htmlname = '{3}' -f $url, $config.attributes.'organization-id', $config.id, $config.attributes.name } else { $configurations.GetEnumerator() | Where {$_.Name -like "*$($vm.name)*"} | ForEach-Object { @@ -399,7 +400,7 @@ if($update) { @{ type= 'related_items' attributes = @{ - destination_id = $_.value.conf_id + destination_id = $_.value.conf_id destination_type = 'Configuration' } } @@ -479,7 +480,7 @@ if($update) { } $response['related_items'].data | ConvertTo-Json -Depth 100 | Out-File $PSScriptRoot\hyperv-related-items.txt -Force - + return $response } else { From 8bd5f547714008901993c2159290f3eba6bef797 Mon Sep 17 00:00:00 2001 From: Emile Priller Date: Thu, 27 Jun 2019 15:20:01 +0200 Subject: [PATCH 17/18] Spelling fixes and cleaning --- .../ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 index 678c6fa..9e460fd 100644 --- a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 +++ b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 @@ -77,7 +77,7 @@ $configurations = @{} $MACs = @{} $page_number = 1 do{ - Write-Verbose "Calling the IT Glue api (page $page_number)..." + Write-Verbose "Calling the IT Glue api for configurations (page $page_number, page size 1000)..." $api_call = Get-ITGlueConfigurations -organization_id $organization_id -page_size 1000 -page_number ($page_number++) foreach($_ in $api_call.data) { $configurations[$_.attributes.name] = $_ @@ -89,7 +89,7 @@ do{ Write-Verbose "Done." # All VMs on the host (with some data) -Write-Verbose "Creating hashtable with HTML name and rest of data..." +Write-Verbose "Trying to match VMs against IT Glue configurations and building VM data object..." $VMs = @{} foreach($vm in Get-VM) { $htmlname = $vm.name @@ -98,17 +98,21 @@ foreach($vm in Get-VM) { if($configurations[$vm.Name]) { $htmlname = '{3}' -f $url, $configurations[$vm.Name].attributes.'organization-id', $configurations[$vm.Name].id, $vm.name $conf_id = $configurations[$vm.Name].id + Write-Verbose "Matched $($vm.Name) on name to $($configurations[$vm.Name].id)." } elseif($MACs[($vm.Name | Get-VMNetworkAdapter).MacAddress]) { $config = $MACs[($vm.Name | Get-VMNetworkAdapter).MacAddress] $conf_id = $config.id $htmlname = '{3}' -f $url, $config.attributes.'organization-id', $config.id, $config.attributes.name + Write-Verbose "Matched $($vm.Name) on MAC address to $($config.id)." } else { $configurations.GetEnumerator() | Where {$_.Name -like "*$($vm.name)*"} | ForEach-Object { $htmlname = '{3}' -f $url, $_.value.attributes.'organization-id', $_.value.id, $vm.name $conf_id = $_.value.id + Write-Verbose "Matched $($vm.Name) on wildcard to $($_.value.id)." } } + Write-Verbose "name = $($vm.name), vm = $($vm), conf_id = $($conf_id), htmlname = $($htmlname)" $VMs[$vm.name] = [PSCustomObject]@{ name = $vm.name @@ -117,7 +121,7 @@ foreach($vm in Get-VM) { conf_id = $conf_id } } -Write-Verbose "Done." +Write-Verbose "[1/9] VM data object done." # Hyper-V host's disk information / "Disk information" Write-Verbose "Getting host's disk data..." @@ -140,7 +144,7 @@ $diskDataHTML = '
{2} {3} ' -f $_.Root, [math]::round(($_.free+$_.used)/1GB), [math]::round($_.used/1GB), [math]::round($_.free/1GB)} | Out-String) -Write-Verbose "Host's disk data done. [1/8]" +Write-Verbose "[2/9] Host's disk data done." # Virtual swtiches / "Virtual switches" Write-Verbose "Getting virtual swtiches..." @@ -161,7 +165,7 @@ $virtualSwitchsHTML = '
{1} {2} ' -f $_.Name, $_.SwitchType, $_.NetAdapterInterfaceDescription} | Out-String) -Write-Verbose "Virtual swtiches done. [2/8]" +Write-Verbose "[3/9] Virtual swtiches done." # General information about virtual machines / "VM guest names and information" Write-Verbose "Getting general guest information..." @@ -189,7 +193,7 @@ $guestInformationHTML = '
{3} {4} ' -f $_.value.htmlname, $_.value.vm.AutomaticStartAction, [Math]::Round($_.value.vm.MemoryStartup/1GB), $_.value.vm.ProcessorCount, $diskSize} | Out-String) -Write-Verbose "General guest information done. [3/8]" +Write-Verbose "[4/9] General guest information done." # Virutal machines' disk file locations / "VM guest virtual disk paths" Write-Verbose "Getting VM machine paths..." @@ -208,7 +212,7 @@ $virtualMachinePathsHTML = '
{0} {1} ' -f $_.value.htmlname, ((Get-VHD -id $_.value.vm.id).path | Out-String).Replace([Environment]::NewLine, '
').TrimEnd('
')} | Out-String) -Write-Verbose "VM machine paths done. [4/8]" +Write-Verbose "[5/9] VM machine paths done." # Snapshot data / "VM guests snapshot information" Write-Verbose "Getting snapshot data..." @@ -233,7 +237,7 @@ $vmSnapshotHTML = '
{3} {4} ' -f $VMs[$_.VMName].htmlname, $_.Name, $_.SnapshotType, $_.CreationTime, $_.ParentSnapshotName} | Out-String) -Write-Verbose "Snapshot data done. [5/8]" +Write-Verbose "[6/9] Snapshot data done." # Virutal machines' bios settings / "VM guests BIOS settings" Write-Verbose "Getting VM BIOS settings..." @@ -273,7 +277,7 @@ $vmBIOSSettingsHTML = '
' -f ($vmBiosSettingsTableData | Out-String) -Write-Verbose "VM BIOS settings done. [6/8]" +Write-Verbose "[7/9] VM BIOS settings done." # Guest NICs and IPs Write-Verbose "Getting VM NICs..." @@ -298,7 +302,7 @@ $guestNICsIPsHTML = '
{3} {4} ' -f $VMs[$_.VMName].htmlname, $_.switchname, $_.ipaddresses[0], $_.ipaddresses[1], $($_.MacAddress -replace '(..(?!$))','$1:') } | Out-String) -Write-Verbose "VM NICs done. [7/8]" +Write-Verbose "[8/9] VM NICs done." Write-Verbose "Building final data structure..." @@ -333,7 +337,7 @@ $asset_data = @{ } } } -Write-Verbose "Finished building the final structure. [8/8]" +Write-Verbose "[9/9] Finished building the final structure." Write-Verbose "Comparing data.." @@ -400,7 +404,7 @@ if($update) { @{ type= 'related_items' attributes = @{ - destination_id = $_.value.conf_id + destination_id = $_.value.conf_id destination_type = 'Configuration' } } @@ -412,7 +416,9 @@ if($update) { Write-Verbose "Done." if(Test-Path $PSScriptRoot\hyperv-related-items.txt) { - Write-Verbose "Creating a list of old related items (because there it no index/show endpoint to compare data against...)." + Write-Verbose "$("$PSScriptRoot\hyperv-related-items.txt") found, importing last response." + + Write-Verbose "Creating a list of old related items from file..." $old_related_items = Get-Content $PSScriptRoot\hyperv-related-items.txt | ConvertFrom-Json Write-Verbose "Done." @@ -435,7 +441,7 @@ if($update) { } } - Write-Verbose "Done." + Write-Verbose "Done comparing related items." if($related_items_remove) { Write-Verbose "Removing the old related items..." @@ -452,6 +458,7 @@ if($update) { $ITGlue_Headers.Add('x-api-key', (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'N/A', $ITGlue_API_Key).GetNetworkCredential().Password) $response['removed_related_items'] = Invoke-RestMethod -method 'DELETE' -uri ($ITGlue_Base_URI + $resource_uri_related_items_remove) -headers $ITGlue_Headers ` -body $body -ErrorAction Stop + Write-Verbose "Old realted items removed." } catch { Write-Error $_ } finally { @@ -460,6 +467,7 @@ if($update) { } } + Write-Verbose "Begin uploading related items (because there is not endpoint to check current ones)..." $body = @{} @@ -473,6 +481,7 @@ if($update) { $ITGlue_Headers.Add('x-api-key', (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'N/A', $ITGlue_API_Key).GetNetworkCredential().Password) $response['related_items'] = Invoke-RestMethod -method 'POST' -uri ($ITGlue_Base_URI + $resource_uri_related_items) -headers $ITGlue_Headers ` -body $body -ErrorAction Stop + Write-Verbose "New related items updated." } catch { Write-Error $_ } finally { @@ -480,7 +489,7 @@ if($update) { } $response['related_items'].data | ConvertTo-Json -Depth 100 | Out-File $PSScriptRoot\hyperv-related-items.txt -Force - + return $response } else { From 7ed003ecf94bff463651bad18cca574a58a88999 Mon Sep 17 00:00:00 2001 From: Emile Priller Date: Fri, 28 Jun 2019 09:41:42 +0200 Subject: [PATCH 18/18] grammar fix in help message --- hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 index 9e460fd..f5f8b20 100644 --- a/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 +++ b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 @@ -14,7 +14,7 @@ param( [ValidateSet('US', 'EU')] [string]$data_center, - [Parameter(HelpMessage='The first part of your IT Glue URL when logging in in ')] + [Parameter(HelpMessage='The first part of your IT Glue URL when logging in')] [string]$subdomain )