diff --git a/hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 b/hyperv-sync/ITGlue-VMHost-CreateFlexibleAsset.ps1 new file mode 100644 index 0000000..cae92b6 --- /dev/null +++ b/hyperv-sync/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?" + 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/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 new file mode 100644 index 0000000..f5f8b20 --- /dev/null +++ b/hyperv-sync/ITGlue-VMHost-FeedFlexibleAssetHyperV.ps1 @@ -0,0 +1,497 @@ +#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')] + [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." +} + +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." +} + +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 = @{} +$MACs = @{} +$page_number = 1 +do{ + 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] = $_ + if($_.attributes.'mac-address') { + $MACs[$_.attributes.'mac-address'.replace(':','')] = $_ + } + } +} while($api_call.links.next) +Write-Verbose "Done." + +# All VMs on the host (with some 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 + $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 + 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 + vm = $vm + htmlname = $htmlname + conf_id = $conf_id + } +} +Write-Verbose "[1/9] VM data object 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 "[2/9] Host's disk data done." + +# 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 "[3/9] Virtual swtiches done." + +# 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 "[4/9] General guest information done." + +# 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 "[5/9] VM machine paths done." + +# 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 "[6/9] Snapshot data done." + +# 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 "[7/9] VM BIOS settings done." + +# Guest NICs and IPs +Write-Verbose "Getting VM NICs..." +$guestNICsIPsHTML = '
+ + + + + + + + + + {0} + +
VM guest nameSwtich nameIPv4IPv6MAC address
+
' -f ((Get-VMNetworkAdapter * | Sort 'VMName').foreach{ + ' + {0} + {1} + {2} + {3} + {4} + ' -f $VMs[$_.VMName].htmlname, $_.switchname, $_.ipaddresses[0], $_.ipaddresses[1], $($_.MacAddress -replace '(..(?!$))','$1:') } | Out-String) +Write-Verbose "[8/9] VM NICs done." + + +Write-Verbose "Building final data structure..." +$asset_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 "[9/9] Finished building the final structure." + + +Write-Verbose "Comparing data.." + +$update = $false + +if($flexibleAsset.data.attributes.traits.'force-manual-sync-now' -eq 'Yes') { + $update = $true +} elseif($asset_data.attributes.traits.cpu -ne $flexibleAsset.data.attributes.traits.cpu) { + Write-Verbose "Change detected. Will update asset." + $update = $true +} elseif($asset_data.attributes.traits.'ram-gb' -ne $flexibleAsset.data.attributes.traits.'ram-gb') { + Write-Verbose "Change detected. Will update asset." + $update = $true +} 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(($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($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(($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(($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(($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(($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(($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 = @{} + + $asset_data["attributes"]["id"] = $flexible_asset_id + Write-Verbose "Added id to hash table." + # Visible 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) + $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." + $response['asset'] = Set-ITGlueFlexibleAssets -data $asset_data + Write-Verbose "Uploading data: done." + + + 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 "$("$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." + + + 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 comparing related items." + + 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 + Write-Verbose "Old realted items removed." + } catch { + Write-Error $_ + } finally { + [void] ($ITGlue_Headers.Remove('x-api-key')) # Quietly clean up scope so the API key doesn't persist + } + } + } + + Write-Verbose "Begin uploading related items (because there is not endpoint to check current ones)..." + + $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 + Write-Verbose "New related items updated." + } 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 diff --git a/hyperv-sync/ITGlue-VMHost-Setup.ps1 b/hyperv-sync/ITGlue-VMHost-Setup.ps1 new file mode 100644 index 0000000..681c839 --- /dev/null +++ b/hyperv-sync/ITGlue-VMHost-Setup.ps1 @@ -0,0 +1,27 @@ +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)" +$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} -subdomain {2}"' -f $PSScriptRoot, $flexible_asset_id, $subdomain) +# Trigger +$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 +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..9386b82 --- /dev/null +++ b/hyperv-sync/README.md @@ -0,0 +1,18 @@ +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` 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 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 store API settings via the wrapper module and asset ID in the scheduled task. + +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)*. + +**IMPORTANT: Do the next step as the user who will be running the script on the server** + +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".