Merge remote-tracking branch 'vmware/master'
This commit is contained in:
@@ -9,15 +9,32 @@ VMware-vCD-Module PowerShell Module
|
||||
|
||||
Markus Kraus [@vMarkus_K](https://twitter.com/vMarkus_K)
|
||||
|
||||
MY CLOUD-(R)EVOLUTION [mycloudrevolution.com](http://mycloudrevolution.com/)
|
||||
|
||||
|
||||
## Project WebSite:
|
||||
[mycloudrevolution.com](http://mycloudrevolution.com/)
|
||||
[PowerCLI vCloud Director Customer Provisioning](https://mycloudrevolution.com/2017/06/13/powercli-vcloud-director-customer-provisioning/)
|
||||
|
||||
[PowerCLI – Create vCloud Director Edge Gateway](https://mycloudrevolution.com/2017/06/27/powercli-create-vcloud-director-edge-gateway/)
|
||||
|
||||
|
||||
## Project Documentation:
|
||||
|
||||
[Read the Docs](http://vmware-vcd-module.readthedocs.io/)
|
||||
[Read the Docs - VMware-vCD-Module](http://vmware-vcd-module.readthedocs.io/)
|
||||
|
||||
## Project Description:
|
||||
|
||||
The 'VMware-vCD-Module' PowerShell Module is focused on the initial craation of VMware vCloud Director Objects like Org, Org User, Org VDC.
|
||||
The 'VMware-vCD-Module' PowerShell Module is focused on the initial creation of VMware vCloud Director Objects like Org, Org User, Org VDC with External Networks or Edge Gateway.
|
||||
|
||||
All Functions in this Module can be used as standalone Cmdlet but also the ``Invoke-My OnBoarding`` Functions to process a JSON File and create all Objects at once.
|
||||
|
||||
### Fully tested Versions:
|
||||
|
||||
Powershell: v4, v5
|
||||
|
||||
PowerCLI: 6.5.1
|
||||
|
||||
VMware vCloud Director: 8.10.1
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# RootModule = ''
|
||||
|
||||
# Die Versionsnummer dieses Moduls
|
||||
ModuleVersion = '0.2.0'
|
||||
ModuleVersion = '1.0.0'
|
||||
|
||||
# ID zur eindeutigen Kennzeichnung dieses Moduls
|
||||
GUID = '1ef8a2de-ca22-4c88-8cdb-e00f35007d2a'
|
||||
@@ -21,7 +21,7 @@ GUID = '1ef8a2de-ca22-4c88-8cdb-e00f35007d2a'
|
||||
Author = 'Markus'
|
||||
|
||||
# Unternehmen oder Hersteller dieses Moduls
|
||||
CompanyName = 'Unbekannt'
|
||||
CompanyName = 'mycloudrevolution.com'
|
||||
|
||||
# Urheberrechtserklärung für dieses Modul
|
||||
Copyright = '(c) 2017 Markus. Alle Rechte vorbehalten.'
|
||||
@@ -64,12 +64,13 @@ Copyright = '(c) 2017 Markus. Alle Rechte vorbehalten.'
|
||||
|
||||
# Die Module, die als geschachtelte Module des in "RootModule/ModuleToProcess" angegebenen Moduls importiert werden sollen.
|
||||
NestedModules = @('functions\Invoke-MyOnBoarding.psm1',
|
||||
'functions\New-MyEdgeGateway.psm1',
|
||||
'functions\New-MyOrg.psm1',
|
||||
'functions\New-MyOrgAdmin.psm1',
|
||||
'functions\New-MyOrgVdc.psm1')
|
||||
|
||||
# Aus diesem Modul zu exportierende Funktionen
|
||||
FunctionsToExport = 'Invoke-MyOnBoarding', 'New-MyOrg', 'New-MyOrgAdmin', 'New-MyOrgVdc'
|
||||
FunctionsToExport = 'Invoke-MyOnBoarding', 'New-MyEdgeGateway', 'New-MyOrg', 'New-MyOrgAdmin', 'New-MyOrgVdc'
|
||||
|
||||
# Aus diesem Modul zu exportierende Cmdlets
|
||||
CmdletsToExport = '*'
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
"StorageProfile":"Standard-DC01",
|
||||
"ProviderVDC":"Provider-VDC-DC01",
|
||||
"NetworkPool":"Provider-VDC-DC01-NetPool",
|
||||
"ExternalNetwork": "External-OrgVdcNet"
|
||||
"ExternalNetwork": "External-OrgVdcNet",
|
||||
"EdgeGateway": "Yes",
|
||||
"IPAddress":"192.168.100.1",
|
||||
"SubnetMask":"255.255.255.0",
|
||||
"Gateway":"192.168.100.254",
|
||||
"IPRangeStart":"192.168.100.2",
|
||||
"IPRangeEnd":"192.168.100.3"
|
||||
}
|
||||
}
|
||||
@@ -1,172 +1,191 @@
|
||||
#Requires -Version 4
|
||||
#Requires -Modules VMware.VimAutomation.Cloud, @{ModuleName="VMware.VimAutomation.Cloud";ModuleVersion="6.3.0.0"}
|
||||
Function Invoke-MyOnBoarding {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates all vCD Objecst for a new IAAS Customer
|
||||
|
||||
.DESCRIPTION
|
||||
Creates all vCD Objects for a new IAAS Customer
|
||||
|
||||
All Objects are:
|
||||
* Org
|
||||
* Default Org Admin
|
||||
* Org VDC
|
||||
** Private Catalog
|
||||
** Optional Bridged Network
|
||||
|
||||
JSON Config Example:
|
||||
|
||||
{
|
||||
"Org": {
|
||||
"Name":"TestOrg",
|
||||
"FullName": "Test Org",
|
||||
"Description":"Automation Test Org"
|
||||
},
|
||||
"OrgAdmin": {
|
||||
"Name":"TestOrgAdmin",
|
||||
"Pasword": "myPassword1!",
|
||||
"FullName":"Test OrgAdmin",
|
||||
"EmailAddress":"test@admin.org"
|
||||
},
|
||||
"OrgVdc": {
|
||||
"Name":"TestOrgVdc",
|
||||
"FixedSize": "M",
|
||||
"CPULimit": "1000",
|
||||
"MEMLimit":"1000",
|
||||
"StorageLimit":"1000",
|
||||
"StorageProfile":"Standard-DC01",
|
||||
"ProviderVDC":"Provider-VDC-DC01",
|
||||
"NetworkPool":"Provider-VDC-DC01-NetPool",
|
||||
"ExternalNetwork": "External_OrgVdcNet"
|
||||
}
|
||||
}
|
||||
|
||||
.NOTES
|
||||
File Name : Invoke-MyOnBoarding.ps1
|
||||
Author : Markus Kraus
|
||||
Version : 1.2
|
||||
State : Ready
|
||||
|
||||
.LINK
|
||||
https://mycloudrevolution.com/
|
||||
|
||||
.EXAMPLE
|
||||
Invoke-MyOnBoarding -ConfigFile ".\OnBoarding.json" -Enabled:$true
|
||||
|
||||
.EXAMPLE
|
||||
Invoke-MyOnBoarding -ConfigFile ".\OnBoarding.json" -Enabled:$false
|
||||
|
||||
.PARAMETER ConfigFile
|
||||
Full Path to the JSON Config File
|
||||
|
||||
.PARAMETER Enabled
|
||||
Should the Customer be enabled after creation
|
||||
|
||||
Default: $False
|
||||
|
||||
#>
|
||||
Param (
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Full Path to the JSON Config File")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[String] $ConfigFile,
|
||||
[Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Should the Customer be enabled after creation")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[Switch]$Enabled
|
||||
)
|
||||
Process {
|
||||
|
||||
$Valid = $true
|
||||
|
||||
Write-Verbose "## Import JSON Config"
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Importing JSON Config...`n"
|
||||
$Configs = Get-Content -Raw -Path $ConfigFile -ErrorAction Continue | ConvertFrom-Json -ErrorAction Continue
|
||||
|
||||
if (!($Configs)) {
|
||||
$Valid = $false
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Importing JSON Config Failed" -ForegroundColor Red
|
||||
}
|
||||
else {
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Importing JSON Config OK" -ForegroundColor Green
|
||||
}
|
||||
|
||||
if ($Valid) {
|
||||
try{
|
||||
Write-Verbose "## Create Org"
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new Org...`n" -ForegroundColor Yellow
|
||||
$Trash = New-MyOrg -Name $Configs.Org.Name -FullName $Configs.Org.Fullname -Description $Configs.Org.Description -Enabled:$Enabled
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new Org OK" -ForegroundColor Green
|
||||
Get-Org -Name $Configs.Org.Name | Select-Object Name, FullName, Enabled | Format-Table -AutoSize
|
||||
}
|
||||
catch {
|
||||
$Valid = $false
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new Org Failed" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
if ($Valid) {
|
||||
try{
|
||||
Write-Verbose "## Create OrgAdmin"
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgAdmin...`n" -ForegroundColor Yellow
|
||||
$Trash = New-MyOrgAdmin -Name $Configs.OrgAdmin.Name -Pasword $Configs.OrgAdmin.Pasword -FullName $Configs.OrgAdmin.FullName -EmailAddress $Configs.OrgAdmin.EmailAddress -Org $Configs.Org.Name -Enabled:$Enabled
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgAdmin OK" -ForegroundColor Green
|
||||
Get-CIUser -Org $Configs.Org.Name -Name $Configs.OrgAdmin.Name | Select-Object Name, FullName, Email | Format-Table -AutoSize
|
||||
}
|
||||
catch {
|
||||
$Valid = $false
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgAdmin Failed" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
if ($Valid) {
|
||||
try{
|
||||
Write-Verbose "## Create OrgVdc"
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgVdc...`n" -ForegroundColor Yellow
|
||||
|
||||
if ($Configs.OrgVdc.FixedSize){
|
||||
|
||||
Write-Host "Fixed Size (T-Shirt Size) '$($Configs.OrgVdc.FixedSize)' Org VDC Requested!"
|
||||
|
||||
switch ($Configs.OrgVdc.FixedSize) {
|
||||
M {
|
||||
[String]$CPULimit = 36000
|
||||
[String]$MEMLimit = 122880
|
||||
[String]$StorageLimit = 1048576
|
||||
}
|
||||
L {
|
||||
[String]$CPULimit = 36000
|
||||
[String]$MEMLimit = 245760
|
||||
[String]$StorageLimit = 1048576
|
||||
}
|
||||
default {throw "Invalid T-Shirt Size!"}
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
Write-Host "Custom Org VDC Size Requested!"
|
||||
|
||||
$CPULimit = $Configs.OrgVdc.CPULimit
|
||||
$MEMLimit = $Configs.OrgVdc.MEMLimit
|
||||
$StorageLimit = $Configs.OrgVdc.StorageLimit
|
||||
|
||||
}
|
||||
|
||||
if ($Configs.OrgVdc.ExternalNetwork){
|
||||
$Trash = New-MyOrgVdc -Name $Configs.OrgVdc.Name -CPULimit $CPULimit -MEMLimit $MEMLimit -StorageLimit $StorageLimit -Networkpool $Configs.OrgVdc.NetworkPool -StorageProfile $Configs.OrgVdc.StorageProfile -ProviderVDC $Configs.OrgVdc.ProviderVDC -ExternalNetwork $Configs.OrgVdc.ExternalNetwork -Org $Configs.Org.Name -Enabled:$Enabled
|
||||
}
|
||||
else {
|
||||
$Trash = New-MyOrgVdc -Name $Configs.OrgVdc.Name -CPULimit $CPULimit -MEMLimit $MEMLimit -StorageLimit $StorageLimit -Networkpool $Configs.OrgVdc.NetworkPool -StorageProfile $Configs.OrgVdc.StorageProfile -ProviderVDC $Configs.OrgVdc.ProviderVDC -Org $Configs.Org.Name -Enabled:$Enabled
|
||||
}
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgVdc OK" -ForegroundColor Green
|
||||
Get-OrgVdc -Org $Configs.Org.Name -Name $Configs.OrgVdc.Name | Select-Object Name, Enabled, CpuAllocationGhz, MemoryLimitGB, StorageLimitGB, AllocationModel, ThinProvisioned, UseFastProvisioning, `
|
||||
@{N="StorageProfile";E={$_.ExtensionData.VdcStorageProfiles.VdcStorageProfile.Name}}, `
|
||||
@{N='VCpuInMhz';E={$_.ExtensionData.VCpuInMhz}} | Format-Table -AutoSize
|
||||
}
|
||||
catch {
|
||||
$Valid = $false
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgVdc Failed" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output "Overall Execution was Valid: $Valid"
|
||||
}
|
||||
}
|
||||
#Requires -Version 4
|
||||
#Requires -Modules VMware.VimAutomation.Cloud, @{ModuleName="VMware.VimAutomation.Cloud";ModuleVersion="6.3.0.0"}
|
||||
Function Invoke-MyOnBoarding {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates all vCD Objecst for a new IAAS Customer
|
||||
|
||||
.DESCRIPTION
|
||||
Creates all vCD Objects for a new IAAS Customer
|
||||
|
||||
All Objects are:
|
||||
* Org
|
||||
* Default Org Admin
|
||||
* Org VDC
|
||||
** Private Catalog
|
||||
** Optional Bridged Network
|
||||
|
||||
JSON Config Example:
|
||||
|
||||
{
|
||||
"Org": {
|
||||
"Name":"TestOrg",
|
||||
"FullName": "Test Org",
|
||||
"Description":"Automation Test Org"
|
||||
},
|
||||
"OrgAdmin": {
|
||||
"Name":"TestOrgAdmin",
|
||||
"Pasword": "myPassword1!",
|
||||
"FullName":"Test OrgAdmin",
|
||||
"EmailAddress":"test@admin.org"
|
||||
},
|
||||
"OrgVdc": {
|
||||
"Name":"TestOrgVdc",
|
||||
"FixedSize": "M",
|
||||
"CPULimit": "1000",
|
||||
"MEMLimit":"1000",
|
||||
"StorageLimit":"1000",
|
||||
"StorageProfile":"Standard-DC01",
|
||||
"ProviderVDC":"Provider-VDC-DC01",
|
||||
"NetworkPool":"Provider-VDC-DC01-NetPool",
|
||||
"ExternalNetwork": "External_OrgVdcNet",
|
||||
"EdgeGateway": "Yes",
|
||||
"IPAddress":"192.168.100.1",
|
||||
"SubnetMask":"255.255.255.0",
|
||||
"Gateway":"192.168.100.254",
|
||||
"IPRangeStart":"192.168.100.2",
|
||||
"IPRangeEnd":"192.168.100.3"
|
||||
}
|
||||
}
|
||||
|
||||
.NOTES
|
||||
File Name : Invoke-MyOnBoarding.ps1
|
||||
Author : Markus Kraus
|
||||
Version : 1.3
|
||||
State : Ready
|
||||
|
||||
.LINK
|
||||
https://mycloudrevolution.com/
|
||||
|
||||
.EXAMPLE
|
||||
Invoke-MyOnBoarding -ConfigFile ".\OnBoarding.json" -Enabled:$true
|
||||
|
||||
.EXAMPLE
|
||||
Invoke-MyOnBoarding -ConfigFile ".\OnBoarding.json" -Enabled:$false
|
||||
|
||||
.PARAMETER ConfigFile
|
||||
Full Path to the JSON Config File
|
||||
|
||||
.PARAMETER Enabled
|
||||
Should the Customer be enabled after creation
|
||||
|
||||
Default: $False
|
||||
|
||||
#>
|
||||
Param (
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Full Path to the JSON Config File")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[String] $ConfigFile,
|
||||
[Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Should the Customer be enabled after creation")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[Switch]$Enabled
|
||||
)
|
||||
Process {
|
||||
|
||||
$Valid = $true
|
||||
|
||||
Write-Verbose "## Import JSON Config"
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Importing JSON Config...`n"
|
||||
$Configs = Get-Content -Raw -Path $ConfigFile -ErrorAction Continue | ConvertFrom-Json -ErrorAction Continue
|
||||
|
||||
if (!($Configs)) {
|
||||
$Valid = $false
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Importing JSON Config Failed" -ForegroundColor Red
|
||||
}
|
||||
else {
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Importing JSON Config OK" -ForegroundColor Green
|
||||
}
|
||||
|
||||
if ($Valid) {
|
||||
try{
|
||||
Write-Verbose "## Create Org"
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new Org...`n" -ForegroundColor Yellow
|
||||
$Trash = New-MyOrg -Name $Configs.Org.Name -FullName $Configs.Org.Fullname -Description $Configs.Org.Description -Enabled:$Enabled
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new Org OK" -ForegroundColor Green
|
||||
Get-Org -Name $Configs.Org.Name | Select-Object Name, FullName, Enabled | Format-Table -AutoSize
|
||||
}
|
||||
catch {
|
||||
$Valid = $false
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new Org Failed" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
if ($Valid) {
|
||||
try{
|
||||
Write-Verbose "## Create OrgAdmin"
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgAdmin...`n" -ForegroundColor Yellow
|
||||
$Trash = New-MyOrgAdmin -Name $Configs.OrgAdmin.Name -Pasword $Configs.OrgAdmin.Pasword -FullName $Configs.OrgAdmin.FullName -EmailAddress $Configs.OrgAdmin.EmailAddress -Org $Configs.Org.Name -Enabled:$Enabled
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgAdmin OK" -ForegroundColor Green
|
||||
Get-CIUser -Org $Configs.Org.Name -Name $Configs.OrgAdmin.Name | Select-Object Name, FullName, Email | Format-Table -AutoSize
|
||||
}
|
||||
catch {
|
||||
$Valid = $false
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgAdmin Failed" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
if ($Valid) {
|
||||
try{
|
||||
Write-Verbose "## Create OrgVdc"
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgVdc...`n" -ForegroundColor Yellow
|
||||
|
||||
if ($Configs.OrgVdc.FixedSize){
|
||||
|
||||
Write-Host "Fixed Size (T-Shirt Size) '$($Configs.OrgVdc.FixedSize)' Org VDC Requested!"
|
||||
|
||||
switch ($Configs.OrgVdc.FixedSize) {
|
||||
M {
|
||||
[String]$CPULimit = 36000
|
||||
[String]$MEMLimit = 122880
|
||||
[String]$StorageLimit = 1048576
|
||||
}
|
||||
L {
|
||||
[String]$CPULimit = 36000
|
||||
[String]$MEMLimit = 245760
|
||||
[String]$StorageLimit = 1048576
|
||||
}
|
||||
default {throw "Invalid T-Shirt Size!"}
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
Write-Host "Custom Org VDC Size Requested!"
|
||||
|
||||
$CPULimit = $Configs.OrgVdc.CPULimit
|
||||
$MEMLimit = $Configs.OrgVdc.MEMLimit
|
||||
$StorageLimit = $Configs.OrgVdc.StorageLimit
|
||||
|
||||
}
|
||||
|
||||
if ($Configs.OrgVdc.ExternalNetwork -and $Configs.OrgVdc.EdgeGateway -like "Yes"){
|
||||
Write-Host "Edge Gateway for Org VDC '$($Configs.OrgVdc.Name)' Requested!"
|
||||
$Trash = New-MyOrgVdc -Name $Configs.OrgVdc.Name -CPULimit $CPULimit -MEMLimit $MEMLimit -StorageLimit $StorageLimit -Networkpool $Configs.OrgVdc.NetworkPool `
|
||||
-StorageProfile $Configs.OrgVdc.StorageProfile -ProviderVDC $Configs.OrgVdc.ProviderVDC -Org $Configs.Org.Name -Enabled:$Enabled
|
||||
|
||||
$EdgeName = $Configs.Org.Name + "-ESG01"
|
||||
$Trash = New-MyEdgeGateway -Name $EdgeName -OrgVDCName $Configs.OrgVdc.Name -Orgname $Configs.Org.Name -ExternalNetwork $Configs.OrgVdc.ExternalNetwork `
|
||||
-IPAddress $Configs.OrgVdc.IPAddress -SubnetMask $Configs.OrgVdc.SubnetMask -Gateway $Configs.OrgVdc.Gateway -IPRangeStart $Configs.OrgVdc.IPRangeStart -IPRangeEnd $Configs.OrgVdc.IPRangeEnd
|
||||
}
|
||||
elseif ($Configs.OrgVdc.ExternalNetwork -and $Configs.OrgVdc.EdgeGateway -like "No"){
|
||||
Write-Host "External Network for Org VDC '$($Configs.OrgVdc.Name)' Requested!"
|
||||
$Trash = New-MyOrgVdc -Name $Configs.OrgVdc.Name -CPULimit $CPULimit -MEMLimit $MEMLimit -StorageLimit $StorageLimit -Networkpool $Configs.OrgVdc.NetworkPool `
|
||||
-StorageProfile $Configs.OrgVdc.StorageProfile -ProviderVDC $Configs.OrgVdc.ProviderVDC -ExternalNetwork $Configs.OrgVdc.ExternalNetwork -Org $Configs.Org.Name -Enabled:$Enabled
|
||||
}
|
||||
else {
|
||||
Write-Host "No external Connection for Org VDC '$($Configs.OrgVdc.Name)' Requested!"
|
||||
$Trash = New-PecOrgVdc -Name $Configs.OrgVdc.Name -CPULimit $CPULimit -MEMLimit $MEMLimit -StorageLimit $StorageLimit -Networkpool $ProVdcNetworkPool.Name `
|
||||
-StorageProfile $Configs.OrgVdc.StorageProfile -ProviderVDC $Configs.OrgVdc.ProviderVDC -Org $Configs.Org.Name -Enabled:$Enabled
|
||||
}
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgVdc OK" -ForegroundColor Green
|
||||
Get-OrgVdc -Org $Configs.Org.Name -Name $Configs.OrgVdc.Name | Select-Object Name, Enabled, CpuAllocationGhz, MemoryLimitGB, StorageLimitGB, AllocationModel, ThinProvisioned, UseFastProvisioning, `
|
||||
@{N="StorageProfile";E={$_.ExtensionData.VdcStorageProfiles.VdcStorageProfile.Name}}, `
|
||||
@{N='VCpuInMhz';E={$_.ExtensionData.VCpuInMhz}} | Format-Table -AutoSize
|
||||
|
||||
if ($Configs.OrgVdc.EdgeGateway -like "Yes"){
|
||||
Search-Cloud -QueryType EdgeGateway -Name $EdgeName | Select Name, IsBusy, GatewayStatus, HaStatus | ft -AutoSize
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$Valid = $false
|
||||
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgVdc Failed" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
161
Modules/VMware-vCD-Module/functions/New-MyEdgeGateway.psm1
Normal file
161
Modules/VMware-vCD-Module/functions/New-MyEdgeGateway.psm1
Normal file
@@ -0,0 +1,161 @@
|
||||
#Requires -Version 4
|
||||
#Requires -Modules VMware.VimAutomation.Cloud, @{ModuleName="VMware.VimAutomation.Cloud";ModuleVersion="6.3.0.0"}
|
||||
Function New-MyEdgeGateway {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates a new Edge Gateway with Default Parameters
|
||||
|
||||
.DESCRIPTION
|
||||
Creates a new Edge Gateway with Default Parameters
|
||||
|
||||
Default Parameters are:
|
||||
* Size
|
||||
* HA State
|
||||
* DNS Relay
|
||||
|
||||
|
||||
.NOTES
|
||||
File Name : New-MyEdgeGateway.ps1
|
||||
Author : Markus Kraus
|
||||
Version : 1.0
|
||||
State : Ready
|
||||
|
||||
.LINK
|
||||
https://mycloudrevolution.com/
|
||||
|
||||
.EXAMPLE
|
||||
New-MyEdgeGateway -Name "TestEdge" -OrgVDCName "TestVDC" -OrgName "TestOrg" -ExternalNetwork "ExternalNetwork" -IPAddress "192.168.100.1" -SubnetMask "255.255.255.0" -Gateway "192.168.100.254" -IPRangeStart ""192.168.100.2" -IPRangeEnd ""192.168.100.3" -Verbose
|
||||
|
||||
.PARAMETER Name
|
||||
Name of the New Edge Gateway as String
|
||||
|
||||
.PARAMETER OrgVDCName
|
||||
OrgVDC where the new Edge Gateway should be created as string
|
||||
|
||||
.PARAMETER OrgName
|
||||
Org where the new Edge Gateway should be created as string
|
||||
|
||||
.PARAMETER ExternalNetwork
|
||||
External Network of the new Edge Gateway as String
|
||||
|
||||
.PARAMETER IPAddress
|
||||
IP Address of the New Edge Gateway as IP Address
|
||||
|
||||
.PARAMETER SubnetMask
|
||||
Subnet Mask of the New Edge Gateway as IP Address
|
||||
|
||||
.PARAMETER Gateway
|
||||
Gateway of the New Edge Gateway as IP Address
|
||||
|
||||
.PARAMETER IPRangeStart
|
||||
Sub Allocation IP Range Start of the New Edge Gateway as IP Address
|
||||
|
||||
.PARAMETER IPRangeEnd
|
||||
Sub Allocation IP Range End of the New Edge Gateway as IP Address
|
||||
|
||||
.PARAMETER Timeout
|
||||
Timeout for the Edge Gateway to get Ready
|
||||
|
||||
Default: 120s
|
||||
|
||||
#>
|
||||
Param (
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Name of the New Edge Gateway as String")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[String] $Name,
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="OrgVDC where the new Edge Gateway should be created as string")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[String] $OrgVdcName,
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Org where the new Edge Gateway should be created as string")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[String] $OrgName,
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="External Network of the New Edge Gateway as String")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[String] $ExternalNetwork,
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="IP Address of the New Edge Gateway as IP Address")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[IPAddress] $IPAddress,
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Subnet Mask of the New Edge Gateway as IP Address")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[IPAddress] $SubnetMask,
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Gateway of the New Edge Gateway as IP Address")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[IPAddress] $Gateway,
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Sub Allocation IP Range Start the New Edge Gateway as IP Address")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[IPAddress] $IPRangeStart,
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Sub Allocation IP Range End the New Edge Gateway as IP Address")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[IPAddress] $IPRangeEnd,
|
||||
[Parameter(Mandatory=$False, ValueFromPipeline=$False,HelpMessage="Timeout for the Edge Gateway to get Ready")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[int] $Timeout = 120
|
||||
)
|
||||
Process {
|
||||
|
||||
## Get Org vDC
|
||||
Write-Verbose "Get Org vDC"
|
||||
[Array] $orgVdc = Get-Org -Name $OrgName | Get-OrgVdc -Name $OrgVdcName
|
||||
|
||||
if ( $orgVdc.Count -gt 1) {
|
||||
throw "Multiple OrgVdcs found!"
|
||||
}
|
||||
elseif ( $orgVdc.Count -lt 1) {
|
||||
throw "No OrgVdc found!"
|
||||
}
|
||||
## Get External Network
|
||||
Write-Verbose "Get External Network"
|
||||
$extNetwork = Get-ExternalNetwork | Get-CIView -Verbose:$False | Where-Object {$_.name -eq $ExternalNetwork}
|
||||
|
||||
## Build EdgeGatway Configuration
|
||||
Write-Verbose "Build EdgeGatway Configuration"
|
||||
$EdgeGateway = New-Object VMware.VimAutomation.Cloud.Views.Gateway
|
||||
$EdgeGateway.Name = $Name
|
||||
$EdgeGateway.Configuration = New-Object VMware.VimAutomation.Cloud.Views.GatewayConfiguration
|
||||
#$EdgeGateway.Configuration.BackwardCompatibilityMode = $false
|
||||
$EdgeGateway.Configuration.GatewayBackingConfig = "compact"
|
||||
$EdgeGateway.Configuration.UseDefaultRouteForDnsRelay = $false
|
||||
$EdgeGateway.Configuration.HaEnabled = $false
|
||||
|
||||
$EdgeGateway.Configuration.EdgeGatewayServiceConfiguration = New-Object VMware.VimAutomation.Cloud.Views.GatewayFeatures
|
||||
$EdgeGateway.Configuration.GatewayInterfaces = New-Object VMware.VimAutomation.Cloud.Views.GatewayInterfaces
|
||||
|
||||
$EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface = New-Object VMware.VimAutomation.Cloud.Views.GatewayInterface
|
||||
$EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].name = $extNetwork.Name
|
||||
$EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].DisplayName = $extNetwork.Name
|
||||
$EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].Network = $extNetwork.Href
|
||||
$EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].InterfaceType = "uplink"
|
||||
$EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].UseForDefaultRoute = $true
|
||||
$EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].ApplyRateLimit = $false
|
||||
|
||||
$ExNetexternalSubnet = New-Object VMware.VimAutomation.Cloud.Views.SubnetParticipation
|
||||
$ExNetexternalSubnet.Gateway = $Gateway.IPAddressToString
|
||||
$ExNetexternalSubnet.Netmask = $SubnetMask.IPAddressToString
|
||||
$ExNetexternalSubnet.IpAddress = $IPAddress.IPAddressToString
|
||||
$ExNetexternalSubnet.IpRanges = New-Object VMware.VimAutomation.Cloud.Views.IpRanges
|
||||
$ExNetexternalSubnet.IpRanges.IpRange = New-Object VMware.VimAutomation.Cloud.Views.IpRange
|
||||
$ExNetexternalSubnet.IpRanges.IpRange[0].StartAddress = $IPRangeStart.IPAddressToString
|
||||
$ExNetexternalSubnet.IpRanges.IpRange[0].EndAddress = $IPRangeEnd.IPAddressToString
|
||||
|
||||
$EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].SubnetParticipation = $ExNetexternalSubnet
|
||||
|
||||
## Create EdgeGatway
|
||||
Write-Verbose "Create EdgeGatway"
|
||||
$CreateEdgeGateway = $orgVdc.ExtensionData.CreateEdgeGateway($EdgeGateway)
|
||||
|
||||
## Wait for EdgeGatway to become Ready
|
||||
Write-Verbose "Wait for EdgeGatway to become Ready"
|
||||
while((Search-Cloud -QueryType EdgeGateway -Name $Name -Verbose:$False).IsBusy -eq $True){
|
||||
$i++
|
||||
Start-Sleep 5
|
||||
if($i -gt $Timeout) { Write-Error "Creating Edge Gateway."; break}
|
||||
Write-Progress -Activity "Creating Edge Gateway" -Status "Wait for Edge to become Ready..."
|
||||
}
|
||||
Write-Progress -Activity "Creating Edge Gateway" -Completed
|
||||
Start-Sleep 1
|
||||
|
||||
Search-Cloud -QueryType EdgeGateway -Name $Name | Select-Object Name, IsBusy, GatewayStatus, HaStatus | Format-Table -AutoSize
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ Function New-MyOrgVdc {
|
||||
Org where the new Org VDC should be created as string
|
||||
|
||||
.PARAMETER Timeout
|
||||
Timeout for teh Org VDC to get Ready
|
||||
Timeout for the Org VDC to get Ready
|
||||
|
||||
Default: 120s
|
||||
|
||||
@@ -103,7 +103,7 @@ Function New-MyOrgVdc {
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Org where the new Org VDC should be created as string")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[String] $Org,
|
||||
[Parameter(Mandatory=$False, ValueFromPipeline=$False,HelpMessage="Timeout for teh Org VDC to get Ready")]
|
||||
[Parameter(Mandatory=$False, ValueFromPipeline=$False,HelpMessage="Timeout for the Org VDC to get Ready")]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[int] $Timeout = 120
|
||||
)
|
||||
@@ -145,7 +145,7 @@ Function New-MyOrgVdc {
|
||||
## Wait for getting Ready
|
||||
Write-Verbose "Wait for getting Ready"
|
||||
$i = 0
|
||||
while(($orgVdc = Get-OrgVdc -Name $Name).Status -eq "NotReady"){
|
||||
while(($orgVdc = Get-OrgVdc -Name $Name -Verbose:$false).Status -eq "NotReady"){
|
||||
$i++
|
||||
Start-Sleep 2
|
||||
if($i -gt $Timeout) { Write-Error "Creating Org Failed."; break}
|
||||
@@ -175,7 +175,7 @@ Function New-MyOrgVdc {
|
||||
|
||||
## Wait for getting Ready
|
||||
Write-Verbose "Wait for getting Ready"
|
||||
while(($orgVdc = Get-OrgVdc -Name $name).Status -eq "NotReady"){
|
||||
while(($orgVdc = Get-OrgVdc -Name $name -Verbose:$false).Status -eq "NotReady"){
|
||||
$i++
|
||||
Start-Sleep 1
|
||||
if($i -gt $Timeout) { Write-Error "Update Org Failed."; break}
|
||||
@@ -201,7 +201,7 @@ Function New-MyOrgVdc {
|
||||
|
||||
## Wait for getting Ready
|
||||
Write-Verbose "Wait for getting Ready"
|
||||
while(($orgVdc = Get-OrgVdc -Name $name).Status -eq "NotReady"){
|
||||
while(($orgVdc = Get-OrgVdc -Name $name -Verbose:$false).Status -eq "NotReady"){
|
||||
$i++
|
||||
Start-Sleep 1
|
||||
if($i -gt $Timeout) { Write-Error "Update Org Failed."; break}
|
||||
@@ -236,7 +236,7 @@ Function New-MyOrgVdc {
|
||||
$EnableOrgVdc = Set-OrgVdc -OrgVdc $Name -Enabled:$True
|
||||
$orgVdcView = Get-OrgVdc $Name | Get-CIView
|
||||
$extNetwork = $_.externalnetwork
|
||||
$extNetwork = Get-ExternalNetwork | Get-CIView | Where-Object {$_.name -eq $ExternalNetwork}
|
||||
$extNetwork = Get-ExternalNetwork | Get-CIView -Verbose:$false | Where-Object {$_.name -eq $ExternalNetwork}
|
||||
$orgNetwork = new-object vmware.vimautomation.cloud.views.orgvdcnetwork
|
||||
$orgNetwork.name = $ExternalNetwork
|
||||
$orgNetwork.Configuration = New-Object VMware.VimAutomation.Cloud.Views.NetworkConfiguration
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 89 KiB |
30
Modules/VMware-vCD-TenantReport/README.md
Normal file
30
Modules/VMware-vCD-TenantReport/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
VMware-vCD-TenantReport PowerShell Module
|
||||
=============
|
||||
|
||||
# About
|
||||
|
||||
## Project Owner:
|
||||
|
||||
Markus Kraus [@vMarkus_K](https://twitter.com/vMarkus_K)
|
||||
|
||||
MY CLOUD-(R)EVOLUTION [mycloudrevolution.com](http://mycloudrevolution.com/)
|
||||
|
||||
## Project WebSite:
|
||||
|
||||
[mycloudrevolution.com](http://mycloudrevolution.com/)
|
||||
|
||||
## Project Documentation:
|
||||
|
||||
[Read the Docs](http://readthedocs.io/)
|
||||
|
||||
## Project Description:
|
||||
|
||||
The 'VMware-vCD-TenantReport' PowerShell Module creates with the Fuction 'Get-VcdTenantReport' a HTML Report of your vCloud Director Objects.
|
||||
|
||||

|
||||
|
||||
Big thanks to [Timothy Dewin](https://twitter.com/tdewin) for his great [PowerStartHTML](https://github.com/tdewin/randomsamples/tree/master/powerstarthtml) PowerShell Module which is used to generate the Report for this Module.
|
||||
|
||||
|
||||
|
||||
|
||||
122
Modules/VMware-vCD-TenantReport/VMware-vCD-TenantReport.psd1
Normal file
122
Modules/VMware-vCD-TenantReport/VMware-vCD-TenantReport.psd1
Normal file
@@ -0,0 +1,122 @@
|
||||
#
|
||||
# Modulmanifest für das Modul "VMware-vCD-TenantReport"
|
||||
#
|
||||
# Generiert von: Markus Kraus
|
||||
#
|
||||
# Generiert am: 8/22/2017
|
||||
#
|
||||
|
||||
@{
|
||||
|
||||
# Die diesem Manifest zugeordnete Skript- oder Binärmoduldatei.
|
||||
# RootModule = 'VMware-vCD-TenantReport.psm1'
|
||||
|
||||
# Die Versionsnummer dieses Moduls
|
||||
ModuleVersion = '1.0.2'
|
||||
|
||||
# ID zur eindeutigen Kennzeichnung dieses Moduls
|
||||
GUID = '21a71eaa-d259-48c5-8482-643ba152af76'
|
||||
|
||||
# Autor dieses Moduls
|
||||
Author = 'Markus'
|
||||
|
||||
# Unternehmen oder Hersteller dieses Moduls
|
||||
CompanyName = 'mycloudrevolution.com'
|
||||
|
||||
# Urheberrechtserklärung für dieses Modul
|
||||
Copyright = '(c) 2017 Markus Kraus. Alle Rechte vorbehalten.'
|
||||
|
||||
# Beschreibung der von diesem Modul bereitgestellten Funktionen
|
||||
# Description = ''
|
||||
|
||||
# Die für dieses Modul mindestens erforderliche Version des Windows PowerShell-Moduls
|
||||
# PowerShellVersion = ''
|
||||
|
||||
# Der Name des für dieses Modul erforderlichen Windows PowerShell-Hosts
|
||||
# PowerShellHostName = ''
|
||||
|
||||
# Die für dieses Modul mindestens erforderliche Version des Windows PowerShell-Hosts
|
||||
# PowerShellHostVersion = ''
|
||||
|
||||
# Die für dieses Modul mindestens erforderliche Microsoft .NET Framework-Version
|
||||
# DotNetFrameworkVersion = ''
|
||||
|
||||
# Die für dieses Modul mindestens erforderliche Version der CLR (Common Language Runtime)
|
||||
# CLRVersion = ''
|
||||
|
||||
# Die für dieses Modul erforderliche Prozessorarchitektur ("Keine", "X86", "Amd64").
|
||||
# ProcessorArchitecture = ''
|
||||
|
||||
# Die Module, die vor dem Importieren dieses Moduls in die globale Umgebung geladen werden müssen
|
||||
# RequiredModules = @()
|
||||
|
||||
# Die Assemblys, die vor dem Importieren dieses Moduls geladen werden müssen
|
||||
# RequiredAssemblies = @()
|
||||
|
||||
# Die Skriptdateien (PS1-Dateien), die vor dem Importieren dieses Moduls in der Umgebung des Aufrufers ausgeführt werden.
|
||||
# ScriptsToProcess = @()
|
||||
|
||||
# Die Typdateien (.ps1xml), die beim Importieren dieses Moduls geladen werden sollen
|
||||
# TypesToProcess = @()
|
||||
|
||||
# Die Formatdateien (.ps1xml), die beim Importieren dieses Moduls geladen werden sollen
|
||||
# FormatsToProcess = @()
|
||||
|
||||
# Die Module, die als geschachtelte Module des in "RootModule/ModuleToProcess" angegebenen Moduls importiert werden sollen.
|
||||
NestedModules = @( "modules/PowerStartHTML/PowerStartHTML.psd1",
|
||||
"functions/Get-VcdTenantReport.psm1" )
|
||||
|
||||
# Aus diesem Modul zu exportierende Funktionen
|
||||
FunctionsToExport = '*'
|
||||
|
||||
# Aus diesem Modul zu exportierende Cmdlets
|
||||
CmdletsToExport = '*'
|
||||
|
||||
# Die aus diesem Modul zu exportierenden Variablen
|
||||
VariablesToExport = '*'
|
||||
|
||||
# Aus diesem Modul zu exportierende Aliase
|
||||
AliasesToExport = '*'
|
||||
|
||||
# Aus diesem Modul zu exportierende DSC-Ressourcen
|
||||
# DscResourcesToExport = @()
|
||||
|
||||
# Liste aller Module in diesem Modulpaket
|
||||
# ModuleList = @()
|
||||
|
||||
# Liste aller Dateien in diesem Modulpaket
|
||||
# FileList = @()
|
||||
|
||||
# Die privaten Daten, die an das in "RootModule/ModuleToProcess" angegebene Modul übergeben werden sollen. Diese können auch eine PSData-Hashtabelle mit zusätzlichen von PowerShell verwendeten Modulmetadaten enthalten.
|
||||
PrivateData = @{
|
||||
|
||||
PSData = @{
|
||||
|
||||
# 'Tags' wurde auf das Modul angewendet und unterstützt die Modulermittlung in Onlinekatalogen.
|
||||
# Tags = @()
|
||||
|
||||
# Eine URL zur Lizenz für dieses Modul.
|
||||
# LicenseUri = ''
|
||||
|
||||
# Eine URL zur Hauptwebsite für dieses Projekt.
|
||||
# ProjectUri = ''
|
||||
|
||||
# Eine URL zu einem Symbol, das das Modul darstellt.
|
||||
# IconUri = ''
|
||||
|
||||
# 'ReleaseNotes' des Moduls
|
||||
# ReleaseNotes = ''
|
||||
|
||||
} # Ende der PSData-Hashtabelle
|
||||
|
||||
} # Ende der PrivateData-Hashtabelle
|
||||
|
||||
# HelpInfo-URI dieses Moduls
|
||||
# HelpInfoURI = ''
|
||||
|
||||
# Standardpräfix für Befehle, die aus diesem Modul exportiert werden. Das Standardpräfix kann mit "Import-Module -Prefix" überschrieben werden.
|
||||
# DefaultCommandPrefix = ''
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
function Get-VcdTenantReport {
|
||||
<#
|
||||
.NOTES
|
||||
===========================================================================
|
||||
Created by: Markus Kraus
|
||||
Twitter: @VMarkus_K
|
||||
Private Blog: mycloudrevolution.com
|
||||
===========================================================================
|
||||
Changelog:
|
||||
1.0.0 - Inital Release
|
||||
1.0.1 - Removed "Test-IP" Module
|
||||
1.0.2 - More Detailed Console Log
|
||||
===========================================================================
|
||||
External Code Sources:
|
||||
Examle Usage of BOOTSTRAP with PowerShell
|
||||
https://github.com/tdewin/randomsamples/tree/master/powershell-veeamallstat
|
||||
BOOTSTRAP with PowerShell
|
||||
https://github.com/tdewin/randomsamples/tree/master/powerstarthtml
|
||||
===========================================================================
|
||||
Tested Against Environment:
|
||||
vCD Version: 8.20
|
||||
PowerCLI Version: PowerCLI 6.5.1
|
||||
PowerShell Version: 5.0
|
||||
OS Version: Windows 8.1
|
||||
Keyword: VMware, vCD, Report, HTML
|
||||
===========================================================================
|
||||
|
||||
.DESCRIPTION
|
||||
This Function creates a HTML Report for your vCloud Director Organization.
|
||||
|
||||
This Function is fully tested as Organization Administrator.
|
||||
With lower permissions a unexpected behavior is possible.
|
||||
|
||||
.Example
|
||||
Get-VcdTenantReport -Server $ServerFQDN -Org $OrgName -Credential $MyCedential
|
||||
|
||||
.Example
|
||||
Get-VcdTenantReport -Server $ServerFQDN -Org $OrgName -Path "C:\Temp\Report.html"
|
||||
|
||||
.PARAMETER Server
|
||||
The FQDN of your vCloud Director Endpoint.
|
||||
|
||||
.PARAMETER Org
|
||||
The Organization Name.
|
||||
|
||||
.PARAMETER Credential
|
||||
PowerShell Credentials to access the Eénvironment.
|
||||
|
||||
.PARAMETER Path
|
||||
The Path of the exported HTML Report.
|
||||
|
||||
#>
|
||||
#Requires -Version 5
|
||||
#Requires -Modules VMware.VimAutomation.Cloud, @{ModuleName="VMware.VimAutomation.Cloud";ModuleVersion="6.5.1.0"}
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False)]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[String] $Server,
|
||||
[Parameter(Mandatory=$True, ValueFromPipeline=$False)]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[String] $Org,
|
||||
[Parameter(Mandatory=$False, ValueFromPipeline=$False)]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[PSCredential] $Credential,
|
||||
[Parameter(Mandatory=$false, ValueFromPipeline=$False)]
|
||||
[ValidateNotNullorEmpty()]
|
||||
[String] $Path = ".\Report.html"
|
||||
|
||||
)
|
||||
|
||||
Process {
|
||||
|
||||
# Start Connection to vCD
|
||||
|
||||
if ($global:DefaultCIServers) {
|
||||
"$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Disconnect existing vCD Server ..."
|
||||
$Trash = Disconnect-CIServer -Server * -Force:$true -Confirm:$false -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
"$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Connect vCD Server ..."
|
||||
if ($Credential) {
|
||||
$Trash = Connect-CIServer -Server $Server -Org $Org -Credential $Credential -ErrorAction Stop
|
||||
}
|
||||
else {
|
||||
$Trash = Connect-CIServer -Server $Server -Org $Org -ErrorAction Stop
|
||||
}
|
||||
"$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Create HTML Report..."
|
||||
|
||||
# Init HTML Report
|
||||
$ps = New-PowerStartHTML -title "vCD Tenant Report"
|
||||
|
||||
#Set CSS Style
|
||||
$ps.cssStyles['.bgtitle'] = "background-color:grey"
|
||||
$ps.cssStyles['.bgsubsection'] = "background-color:#eee;"
|
||||
|
||||
# Processing Data
|
||||
## Get Main Objects
|
||||
[Array] $OrgVdcs = Get-OrgVdc
|
||||
[Array] $Catalogs = Get-Catalog
|
||||
[Array] $Users = Get-CIUser
|
||||
|
||||
## Add Header to Report
|
||||
$ps.Main().Add("div","jumbotron").N()
|
||||
$ps.Append("h1","display-3",("vCD Tenant Report" -f $OrgVdcs.Count)).Append("p","lead","Organization User Count: {0}" -f $Users.Count).Append("p","lead","Organization Catalog Count: {0}" -f $Catalogs.Count).Append("p","lead","Organization VDC Count: {0}" -f $OrgVdcs.Count).Append("hr","my-4").Append("p","font-italic","This Report lists the most important objects in your vCD Environmet. For more details contact your Service Provider").N()
|
||||
|
||||
## add Org Users to Report
|
||||
$ps.Main().Append("h2",$null,"Org Users").N()
|
||||
|
||||
$ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"User Name").Append("th",$null,"Locked").Append("th",$null,"DeployedVMCount").Append("th",$null,"StoredVMCount").N()
|
||||
$ps.Add("tr").N()
|
||||
|
||||
foreach ($User in $Users) {
|
||||
$ps.Append("td",$null,$User.Name).N()
|
||||
$ps.Append("td",$null,$User.Locked).N()
|
||||
$ps.Append("td",$null,$User.DeployedVMCount).N()
|
||||
$ps.Append("td",$null,$User.StoredVMCount).N()
|
||||
$ps.Up().N()
|
||||
|
||||
}
|
||||
$ps.Up().N()
|
||||
|
||||
## add Org Catalogs to Report
|
||||
$ps.Main().Append("h2",$null,"Org Catalogs").N()
|
||||
|
||||
foreach ($Catalog in $Catalogs) {
|
||||
$ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"Catalog Name").N()
|
||||
$ps.Add("tr").N()
|
||||
$ps.Append("td",$null,$Catalog.Name).Up().N()
|
||||
|
||||
$ps.Add("td","bgsubsection").N()
|
||||
$ps.Add("table","table bgcolorsub").N()
|
||||
$ps.Add("tr").N()
|
||||
|
||||
$headers = @("Item")
|
||||
foreach ($h in $headers) {
|
||||
$ps.Append("th",$null,$h).N()
|
||||
}
|
||||
$ps.Up().N()
|
||||
|
||||
### add Itens of the Catalog to the Report
|
||||
[Array] $Items = $Catalog.ExtensionData.CatalogItems.CatalogItem
|
||||
|
||||
foreach ($Item in $Items) {
|
||||
$ps.Add("tr").N()
|
||||
$ps.Append("td",$null,$Item.Name).N()
|
||||
|
||||
$ps.Up().N()
|
||||
|
||||
}
|
||||
|
||||
$ps.Up().Up().N()
|
||||
}
|
||||
$ps.Up().N()
|
||||
|
||||
## add Org VDC`s to the Report
|
||||
$ps.Main().Append("h2",$null,"Org VDCs").N()
|
||||
|
||||
foreach ($OrgVdc in $OrgVdcs) {
|
||||
$ps.Main().Add('table','table table-striped table-inverse').Add("tr").Append("th",$null,"VDC Name").Append("th",$null,"Enabled").Append("th",$null,"CpuUsedGHz").Append("th",$null,"MemoryUsedGB").Append("th",$null,"StorageUsedGB").Up().N()
|
||||
$ps.Add("tr").N()
|
||||
$ps.Append("td",$null,$OrgVdc.Name).Append("td",$null,$OrgVdc.Enabled).Append("td",$null,$OrgVdc.CpuUsedGHz).Append("td",$null,$OrgVdc.MemoryUsedGB).Append("td",$null,[Math]::Round($OrgVdc.StorageUsedGB,2)).Up().N()
|
||||
|
||||
### add Edge Gateways of this Org VDC to Report
|
||||
$ps.Main().Append("h3",$null,"Org VDC Edge Gateways").N()
|
||||
[Array] $Edges = Search-Cloud -QueryType EdgeGateway -Filter "Vdc==$($OrgVdc.Id)"
|
||||
|
||||
foreach ($Edge in $Edges) {
|
||||
$ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"Edge Name").N()
|
||||
$ps.Add("tr").N()
|
||||
$ps.Append("td",$null,$Edge.Name).Up().N()
|
||||
|
||||
$ps.Add("td","bgsubsection").N()
|
||||
$ps.Add("table","table bgcolorsub").N()
|
||||
$ps.Append("tr").Append("td","font-weight-bold","HaStatus").Append("td",$null,($Edge.HaStatus)).N()
|
||||
$ps.Append("td","font-weight-bold","AdvancedNetworkingEnabled").Append("td",$null,$Edge.AdvancedNetworkingEnabled).N()
|
||||
$ps.Append("tr").Append("td","font-weight-bold","NumberOfExtNetworks").Append("td",$null,($Edge.NumberOfExtNetworks)).N()
|
||||
$ps.Append("td","font-weight-bold","NumberOfOrgNetworks").Append("td",$null,$Edge.NumberOfOrgNetworks).N()
|
||||
|
||||
$ps.Up().Up().N()
|
||||
}
|
||||
$ps.Up().N()
|
||||
|
||||
### add Org Networks of this Org VDC to Report
|
||||
$ps.Main().Append("h3",$null,"Org VDC Networks").N()
|
||||
[Array] $Networks = $OrgVdc | Get-OrgVdcNetwork
|
||||
|
||||
foreach ($Network in $Networks) {
|
||||
$ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"Network Name").N()
|
||||
$ps.Add("tr").N()
|
||||
$ps.Append("td",$null,$Network.Name).Up().N()
|
||||
|
||||
$ps.Add("td","bgsubsection").N()
|
||||
$ps.Add("table","table bgcolorsub").N()
|
||||
$ps.Append("tr").Append("td","font-weight-bold","DefaultGateway").Append("td",$null,($Network.DefaultGateway)).N()
|
||||
$ps.Append("td","font-weight-bold","Netmask").Append("td",$null,$Network.Netmask).N()
|
||||
$ps.Append("tr").Append("td","font-weight-bold","NetworkType").Append("td",$null,($Network.NetworkType)).N()
|
||||
$ps.Append("td","font-weight-bold","StaticIPPool").Append("td",$null,$Network.StaticIPPool).N()
|
||||
|
||||
$ps.Up().Up().N()
|
||||
}
|
||||
$ps.Up().N()
|
||||
|
||||
### add vApps of this Org VDC to Report
|
||||
$ps.Main().Append("h3",$null,"Org VDC vApps").N()
|
||||
|
||||
[Array] $Vapps = $OrgVdc | Get-CIVApp
|
||||
|
||||
foreach ($Vapp in $Vapps) {
|
||||
$ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"vApp Name").Append("th",$null,"Owner").Up().N()
|
||||
$ps.Add("tr").N()
|
||||
$ps.Append("td",$null,$Vapp.Name).Append("td",$null,$Vapp.Owner).Up().N()
|
||||
|
||||
#### add VMs of this vApp to Report
|
||||
$ps.Add("td","bgsubsection").N()
|
||||
$ps.Add("table","table bgcolorsub").N()
|
||||
$ps.Add("tr").N()
|
||||
|
||||
$headers = @("Name","Status","GuestOSFullName","CpuCount","MemoryGB")
|
||||
foreach ($h in $headers) {
|
||||
$ps.Append("th",$null,$h).N()
|
||||
}
|
||||
$ps.Up().N()
|
||||
|
||||
[Array] $VMs = $Vapp | Get-CIVM
|
||||
|
||||
foreach ($VM in $VMs) {
|
||||
$ps.Add("tr").N()
|
||||
$ps.Append("td",$null,$VM.Name).N()
|
||||
$ps.Append("td",$null,$VM.Status).N()
|
||||
$ps.Append("td",$null,$VM.GuestOSFullName).N()
|
||||
$ps.Append("td",$null,$VM.CpuCount).N()
|
||||
$ps.Append("td",$null,$VM.MemoryGB).N()
|
||||
|
||||
$ps.Up().N()
|
||||
|
||||
}
|
||||
$ps.Up().Up().N()
|
||||
|
||||
}
|
||||
$ps.Up().N()
|
||||
|
||||
}
|
||||
$ps.save($Path)
|
||||
|
||||
"$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Open HTML Report..."
|
||||
Start-Process $Path
|
||||
|
||||
}
|
||||
}
|
||||
BIN
Modules/VMware-vCD-TenantReport/media/Get-VcdTenantReport.png
Normal file
BIN
Modules/VMware-vCD-TenantReport/media/Get-VcdTenantReport.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
Binary file not shown.
@@ -0,0 +1,237 @@
|
||||
class PowerStartHTML {
|
||||
[string]$PowerStartHtmlTemplate = "<html><head><meta charset=`"UTF-8`"/><title></title><style></style></head><body><div/></body></html>"
|
||||
[xml]$xmlDocument = $null
|
||||
$onLoadJS = $null
|
||||
$cssStyles= @{}
|
||||
$lastEl = $null
|
||||
$newEl = $null
|
||||
$indentedOutput = $false
|
||||
$bootstrapAtCompile = $false
|
||||
PowerStartHTML([string]$title) {
|
||||
$this.xmlDocument = $this.PowerStartHtmlTemplate
|
||||
$this.xmlDocument.html.head.title = $title
|
||||
$this.lastEl = $this.xmlDocument.html.body.ChildNodes[0]
|
||||
$this.onLoadJS = New-Object System.Collections.Generic.List[System.String]
|
||||
}
|
||||
[string] GetHtml() {
|
||||
$xmlclone = $this.xmlDocument.Clone()
|
||||
$csb = [System.Text.StringBuilder]::new()
|
||||
foreach ($cssStyle in $this.cssStyles.GetEnumerator()) {
|
||||
$null = $csb.AppendFormat("{0} {{ {1} }}",$cssStyle.Name,$cssStyle.Value)
|
||||
}
|
||||
$this.xmlDocument.html.head.style = $csb.toString()
|
||||
$this.AddBootStrapAtCompile()
|
||||
if($this.onLoadJS.Count -gt 0) {
|
||||
$this.onLoadJs.Insert(0,"`r`n`$(document).ready(function() {")
|
||||
$this.onLoadJs.Add("})`r`n")
|
||||
$el = $this.xmlDocument.CreateElement("script")
|
||||
$el.AppendChild($this.xmlDocument.CreateTextNode([System.String]::Join("`r`n",$this.onLoadJs)))
|
||||
$this.xmlDocument.html.body.AppendChild($el)
|
||||
}
|
||||
$ms = [System.IO.MemoryStream]::new()
|
||||
$xmlWriter = [System.Xml.XmlTextWriter]::new($ms,[System.Text.Encoding]::UTF8)
|
||||
if($this.indentedOutput) {
|
||||
$xmlWriter.Formatting = [System.Xml.Formatting]::Indented
|
||||
}
|
||||
$this.xmlDocument.WriteContentTo($xmlWriter)
|
||||
$xmlWriter.Flush()
|
||||
$ms.Flush()
|
||||
#make sure that everytime we do gethtml we keep it clean
|
||||
$this.xmlDocument = $xmlclone
|
||||
$ms.Position = 0;
|
||||
$sr = [System.IO.StreamReader]::new($ms);
|
||||
return ("<!DOCTYPE html>{0}`r`n" -f $sr.ReadToEnd())
|
||||
}
|
||||
Save($path) {
|
||||
$this.GetHtml() | Set-Content -path $path -Encoding UTF8
|
||||
}
|
||||
|
||||
AddAttr($el,$name,$value) {
|
||||
$attr = $this.xmlDocument.CreateAttribute($name)
|
||||
$attr.Value = $value
|
||||
$el.Attributes.Append($attr)
|
||||
}
|
||||
|
||||
AddAttrs($el,$dict) {
|
||||
foreach($a in $dict.GetEnumerator()) {
|
||||
$this.AddAttr($el,$a.Name,$a.Value)
|
||||
}
|
||||
}
|
||||
[PowerStartHTML] AddBootStrap() {
|
||||
$this.bootstrapAtCompile = $true
|
||||
return $this
|
||||
}
|
||||
AddJSScript($href,$integrity) {
|
||||
$el = $this.xmlDocument.CreateElement("script")
|
||||
$attrs = @{
|
||||
"src"="$href";
|
||||
"integrity"="$integrity";
|
||||
"crossorigin"="anonymous"
|
||||
}
|
||||
$this.AddAttrs($el,$attrs)
|
||||
$el.AppendChild($this.xmlDocument.CreateTextNode(""))
|
||||
$this.xmlDocument.html.body.AppendChild($el)
|
||||
}
|
||||
AddBootStrapAtCompile() { #Bootstrap script needs to be added at the end
|
||||
if($this.bootstrapAtCompile) {
|
||||
$el = $this.xmlDocument.CreateElement("link")
|
||||
$attrs = @{
|
||||
"rel"="stylesheet";
|
||||
"href"='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css';
|
||||
"integrity"="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M";
|
||||
"crossorigin"="anonymous"
|
||||
}
|
||||
$this.AddAttrs($el,$attrs)
|
||||
$el.AppendChild($this.xmlDocument.CreateTextNode(""))
|
||||
$this.xmlDocument.html.head.AppendChild($el)
|
||||
$this.AddJSScript('https://code.jquery.com/jquery-3.2.1.slim.min.js',"sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN")
|
||||
$this.AddJSScript('https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js',"sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4")
|
||||
$this.AddJSScript('https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js',"sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1")
|
||||
}
|
||||
}
|
||||
[PowerStartHTML] AddContainerAttrToMain() {
|
||||
$this.AddAttr($this.xmlDocument.html.body.ChildNodes[0],"class","container")
|
||||
return $this
|
||||
}
|
||||
[PowerStartHTML] Append($elType = "table",$className=$null,[string]$text=$null) {
|
||||
$el = $this.xmlDocument.CreateElement($elType)
|
||||
if($text -ne $null) {
|
||||
$el.AppendChild($this.xmlDocument.CreateTextNode($text))
|
||||
}
|
||||
if($className -ne $null) {
|
||||
$this.AddAttr($el,"class",$className)
|
||||
}
|
||||
$this.lastEl.AppendChild($el)
|
||||
$this.newEl = $el
|
||||
|
||||
return $this
|
||||
}
|
||||
[PowerStartHTML] Append($elType = "table",$className=$null) { return $this.Append($elType,$className,$null) }
|
||||
[PowerStartHTML] Append($elType = "table") { return $this.Append($elType,$null,$null) }
|
||||
[PowerStartHTML] Add($elType = "table",$className=$null,[string]$text=$null) {
|
||||
$this.Append($elType,$className,$text)
|
||||
$this.lastEl = $this.newEl
|
||||
return $this
|
||||
}
|
||||
[PowerStartHTML] Add($elType = "table",$className=$null) { return $this.Add($elType,$className,$null) }
|
||||
[PowerStartHTML] Add($elType = "table") { return $this.Add($elType,$null,$null) }
|
||||
[PowerStartHTML] Main() {
|
||||
$this.lastEl = $this.xmlDocument.html.body.ChildNodes[0];
|
||||
return $this
|
||||
}
|
||||
[PowerStartHTML] Up() {
|
||||
$this.lastEl = $this.lastEl.ParentNode;
|
||||
return $this
|
||||
}
|
||||
N() {}
|
||||
}
|
||||
class PowerStartHTMLPassThroughLine {
|
||||
$object;$cells
|
||||
PowerStartHTMLPassThroughLine($object) {
|
||||
$this.object = $object;
|
||||
$this.cells = new-object System.Collections.HashTable;
|
||||
}
|
||||
}
|
||||
class PowerStartHTMLPassThroughElement {
|
||||
$name;$text;$element;$id
|
||||
PowerStartHTMLPassThroughElement($name,$text,$element,$id) {
|
||||
$this.name = $name; $this.text = $text; $this.element = $element;$this.id = $id
|
||||
}
|
||||
}
|
||||
function New-PowerStartHTML {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$title,
|
||||
[switch]$nobootstrap=$false
|
||||
)
|
||||
$pshtml = (new-object PowerStartHTML($title))
|
||||
if(-not $nobootstrap) {
|
||||
$pshtml.AddBootStrap().AddContainerAttrToMain().N()
|
||||
}
|
||||
return $pshtml
|
||||
}
|
||||
function Add-PowerStartHTMLTable {
|
||||
param(
|
||||
[Parameter(Mandatory=$True,ValueFromPipeline=$True)]$object,
|
||||
[PowerStartHTML]$psHtml,
|
||||
[string]$tableTitle = $null,
|
||||
[string]$tableClass = $null,
|
||||
[string]$idOverride = $(if($tableTitle -ne $null) {($tableTitle.toLower() -replace "[^a-z0-9]","-") }),
|
||||
[switch]$passthroughTable = $false,
|
||||
[switch]$noheaders = $false
|
||||
)
|
||||
begin {
|
||||
if($tableTitle -ne $null) {
|
||||
$psHtml.Main().Append("h1",$null,$tableTitle).N()
|
||||
if($idOverride -ne $null) {
|
||||
$psHtml.AddAttr($psHtml.newEl,"id","header-$idOverride")
|
||||
}
|
||||
}
|
||||
$psHtml.Main().Add("table").N()
|
||||
[int]$r = 0
|
||||
[int]$c = 0
|
||||
if($idOverride -ne $null) {
|
||||
$psHtml.AddAttr($psHtml.newEl,"id","table-$idOverride")
|
||||
}
|
||||
if($tableClass -ne $null) {
|
||||
$psHtml.AddAttr($psHtml.newEl,"class",$tableClass)
|
||||
}
|
||||
[bool]$isFirst = $true
|
||||
}
|
||||
process {
|
||||
$c = 0
|
||||
|
||||
$props = $object | Get-Member -Type Properties
|
||||
if(-not $noheaders -and $isFirst) {
|
||||
$psHtml.Add("tr").N()
|
||||
if($idOverride -ne $null) {
|
||||
$psHtml.AddAttr($psHtml.newEl,"id","table-$idOverride-trh")
|
||||
}
|
||||
$props | % {
|
||||
$n = $_.Name;
|
||||
$psHtml.Append("th",$null,$n).N()
|
||||
if($idOverride -ne $null) {
|
||||
$cellid = "table-$idOverride-td-$r-$c"
|
||||
$psHtml.AddAttr($psHtml.newEl,"id",$cellid)
|
||||
}
|
||||
$c++
|
||||
}
|
||||
$c = 0
|
||||
$psHtml.Up().N()
|
||||
}
|
||||
|
||||
$psHtml.Add("tr").N()
|
||||
if($idOverride -ne $null) {
|
||||
$psHtml.AddAttr($psHtml.newEl,"id","table-$idOverride-tr-$r")
|
||||
}
|
||||
$pstableln = [PowerStartHTMLPassThroughLine]::new($object)
|
||||
|
||||
$props | % {
|
||||
$n = $_.Name;
|
||||
$psHtml.Append("td",$null,$object."$n").N()
|
||||
$cellid = $null
|
||||
if($idOverride -ne $null) {
|
||||
$cellid = "table-$idOverride-td-$r-$c"
|
||||
$psHtml.AddAttr($psHtml.newEl,"id",$cellid)
|
||||
}
|
||||
if($passthroughTable) {
|
||||
$pstableln.cells.Add($n,[PowerStartHTMLPassThroughElement]::new($n,($object."$n"),$psHtml.newEl,$cellid))
|
||||
}
|
||||
|
||||
$c++
|
||||
}
|
||||
if($passthroughTable) {
|
||||
$pstableln
|
||||
}
|
||||
$psHtml.Up().N()
|
||||
$isFirst = $false
|
||||
$r++
|
||||
}
|
||||
end {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Export-ModuleMember -Function @('New-PowerStartHTML','Add-PowerStartHTMLTable')
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
$moduleRoot = Resolve-Path "$PSScriptRoot\.."
|
||||
$moduleName = "VMware-vCD-TenantReport"
|
||||
|
||||
Describe "General project validation: $moduleName" {
|
||||
|
||||
$scripts = Get-ChildItem $moduleRoot -Include *.ps1, *.psm1, *.psd1 -Recurse
|
||||
|
||||
# TestCases are splatted to the script so we need hashtables
|
||||
$testCase = $scripts | Foreach-Object {@{file = $_}}
|
||||
It "Script <file> should be valid powershell" -TestCases $testCase {
|
||||
param($file)
|
||||
|
||||
$file.fullname | Should Exist
|
||||
|
||||
$contents = Get-Content -Path $file.fullname -ErrorAction Stop
|
||||
$errors = $null
|
||||
$null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors)
|
||||
$errors.Count | Should Be 0
|
||||
}
|
||||
|
||||
It "Module '$moduleName' can import cleanly" {
|
||||
{Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -force } | Should Not Throw
|
||||
}
|
||||
}
|
||||
18
Modules/VMware.Hosted/VMware.Hosted.psd1
Normal file
18
Modules/VMware.Hosted/VMware.Hosted.psd1
Normal file
@@ -0,0 +1,18 @@
|
||||
@{
|
||||
ModuleToProcess = 'VMware.Hosted.psm1'
|
||||
ModuleVersion = '1.0.0.0'
|
||||
GUID = '11393D09-D6B8-4E79-B9BC-247F1BE66683'
|
||||
Author = 'William Lam'
|
||||
CompanyName = 'primp-industries.com'
|
||||
Copyright = '(c) 2017. All rights reserved.'
|
||||
Description = 'Powershell Module for VMware Fusion 10 REST API'
|
||||
PowerShellVersion = '5.0'
|
||||
FunctionsToExport = 'Get-HostedCommand','Connect-HostedServer','Disconnect-HostedServer','Get-HostedVM','Start-HostedVM','Stop-HostedVM','Suspend-HostedVM','Resume-HostedVM','New-HostedVM','Remove-HostedVM','Get-HostedVMSharedFolder','New-HostedVMSharedFolder','Remove-HostedVMSharedFolder','Get-HostedVMNic','Get-HostedNetworks'
|
||||
PrivateData = @{
|
||||
PSData = @{
|
||||
Tags = @('Fusion','REST','vmrest')
|
||||
LicenseUri = 'https://www.tldrlegal.com/l/mit'
|
||||
ProjectUri = 'https://github.com/lamw/PowerCLI-Example-Scripts/tree/master/Modules/VMware.Hosted'
|
||||
}
|
||||
}
|
||||
}
|
||||
501
Modules/VMware.Hosted/VMware.Hosted.psm1
Normal file
501
Modules/VMware.Hosted/VMware.Hosted.psm1
Normal file
@@ -0,0 +1,501 @@
|
||||
Function Get-Confirmation {
|
||||
$choice = ""
|
||||
while ($choice -notmatch "[y|n]"){
|
||||
$choice = read-host "Do you want to continue? (Y/N)"
|
||||
}
|
||||
if($choice -ne 'y') {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Function Connect-HostedServer {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Server,
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Username,
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Password,
|
||||
[parameter(Mandatory=$false,ValueFromPipeline=$true)][string]$Protocol = "http",
|
||||
[parameter(Mandatory=$false,ValueFromPipeline=$true)][int]$Port = 8697
|
||||
)
|
||||
$pair = $Username+":"+$Password
|
||||
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
|
||||
$base64 = [System.Convert]::ToBase64String($bytes)
|
||||
$basicAuthValue = "Basic $base64"
|
||||
$headers = @{Authorization = $basicAuthValue}
|
||||
|
||||
$Global:DefaultHostedServer = [pscustomobject] @{
|
||||
Server=$Protocol + "://" + $server + ":$Port/api";
|
||||
Protcol=$Protocol
|
||||
Headers=$headers
|
||||
}
|
||||
|
||||
if($DefaultHostedServer.Protcol -eq "https") {
|
||||
# PowerShell Core has a nice -SkipCertificateCheck but looks like Windows does NOT :(
|
||||
if($PSVersionTable.PSEdition -eq "Core") {
|
||||
$Global:fusionCommand = "Invoke-Webrequest -SkipCertificateCheck "
|
||||
} else {
|
||||
# Needed for Windows PowerShell to handle HTTPS scenario
|
||||
# https://stackoverflow.com/a/15627483
|
||||
$Provider = New-Object Microsoft.CSharp.CSharpCodeProvider
|
||||
$Compiler = $Provider.CreateCompiler()
|
||||
$Params = New-Object System.CodeDom.Compiler.CompilerParameters
|
||||
$Params.GenerateExecutable = $false
|
||||
$Params.GenerateInMemory = $true
|
||||
$Params.IncludeDebugInformation = $false
|
||||
$Params.ReferencedAssemblies.Add("System.DLL") > $null
|
||||
$TASource=@'
|
||||
namespace Local.ToolkitExtensions.Net.CertificatePolicy
|
||||
{
|
||||
public class TrustAll : System.Net.ICertificatePolicy
|
||||
{
|
||||
public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
|
||||
$TAAssembly=$TAResults.CompiledAssembly
|
||||
## We create an instance of TrustAll and attach it to the ServicePointManager
|
||||
$TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
|
||||
[System.Net.ServicePointManager]::CertificatePolicy = $TrustAll
|
||||
$Global:fusionCommand = "Invoke-Webrequest "
|
||||
}
|
||||
} else {
|
||||
$Global:fusionCommand = "Invoke-Webrequest "
|
||||
}
|
||||
$Global:DefaultHostedServer
|
||||
}
|
||||
|
||||
Function Disconnect-HostedServer {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Server
|
||||
)
|
||||
|
||||
$Global:DefaultHostedServer = $null
|
||||
}
|
||||
|
||||
Function Get-HostedVM {
|
||||
Param (
|
||||
[parameter(Mandatory=$false,ValueFromPipeline=$true)][string]$Id
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
if($Id) {
|
||||
$vmUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id
|
||||
try {
|
||||
$params = "-Headers `$Global:DefaultHostedServer.Headers -Uri $vmUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vm = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Invalid VM Id $Id"
|
||||
break
|
||||
}
|
||||
|
||||
$vmIPUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/ip"
|
||||
try {
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmIPUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vmIPResults = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
$vmIP = $vmIPResults.ip
|
||||
} catch {
|
||||
$vmIP = "N/A"
|
||||
}
|
||||
|
||||
$vmPowerUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/power"
|
||||
try {
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vmPower = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
$vmPower = "N/A"
|
||||
}
|
||||
|
||||
$results = [pscustomobject] @{
|
||||
Id = $vm.Id;
|
||||
CPU = $vm.Cpu.processors;
|
||||
Memory = $vm.Memory;
|
||||
PowerState = $vmPower.power_state;
|
||||
IPAddress = $vmIP;
|
||||
}
|
||||
$results
|
||||
} else {
|
||||
$uri = $Global:DefaultHostedServer.Server + "/vms"
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $uri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
try {
|
||||
Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Failed to list VMs"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Function Start-HostedVM {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
$vmPowerUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/power"
|
||||
try {
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vmPower = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Invalid VM Id $Id"
|
||||
break
|
||||
}
|
||||
|
||||
if($vmPower.power_state -eq "poweredOff" -or $vmPower.power_state -eq "suspended") {
|
||||
try {
|
||||
Write-Host "Powering on VM $Id ..."
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method PUT -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `"on`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vm = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Unable to Power On VM $Id"
|
||||
break
|
||||
}
|
||||
} else {
|
||||
Write-Host "VM $Id is already Powered On"
|
||||
}
|
||||
}
|
||||
|
||||
Function Stop-HostedVM {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id,
|
||||
[parameter(Mandatory=$false,ValueFromPipeline=$true)][boolean]$Soft,
|
||||
[parameter(Mandatory=$false,ValueFromPipeline=$true)][boolean]$Confirm = $true
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
if($Confirm) {
|
||||
Get-Confirmation
|
||||
}
|
||||
|
||||
$vmPowerUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/power"
|
||||
try {
|
||||
$params += "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vmPower = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Invalid VM Id $Id"
|
||||
break
|
||||
}
|
||||
|
||||
if($vmPower.power_state -eq "poweredOn") {
|
||||
if($Soft) {
|
||||
try {
|
||||
Write-Host "Shutting down VM $Id ..."
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method PUT -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `"shutdown`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vm = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Unable to Shutdown VM $Id"
|
||||
break
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Write-Host "Powering off VM $Id ..."
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method PUT -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `"off`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vm = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Unable to Power Off VM $Id"
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Host "VM $Id is already Powered Off"
|
||||
}
|
||||
}
|
||||
|
||||
Function Suspend-HostedVM {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id,
|
||||
[parameter(Mandatory=$false,ValueFromPipeline=$true)][boolean]$Confirm
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
if($Confirm) {
|
||||
Get-Confirmation
|
||||
}
|
||||
|
||||
$vmPowerUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/power"
|
||||
try {
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vmPower = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Invalid VM Id $Id"
|
||||
break
|
||||
}
|
||||
|
||||
if($vmPower.power_state -eq "poweredOn") {
|
||||
try {
|
||||
Write-Host "Suspending VM $Id ..."
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method PUT -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `"suspend`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vm = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Unable to suspend VM $Id"
|
||||
break
|
||||
}
|
||||
} else {
|
||||
Write-Host "VM $Id can not be suspended because it is either Powered Off or Suspended"
|
||||
}
|
||||
}
|
||||
|
||||
Function Resume-HostedVM {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
$vmPowerUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/power"
|
||||
try {
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vmPower = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Invalid VM Id $Id"
|
||||
break
|
||||
}
|
||||
|
||||
if($vmPower.power_state -eq "suspended") {
|
||||
try {
|
||||
Start-HostedVM -Id $Id
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Unable to Resume VM $Id"
|
||||
break
|
||||
}
|
||||
} else {
|
||||
Write-Host "VM $Id is not Suspended"
|
||||
}
|
||||
}
|
||||
|
||||
Function New-HostedVM {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$ParentId,
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Name
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
$vm = Get-HostedVM -Id $ParentId
|
||||
|
||||
if($vm -match "Invalid VM Id") {
|
||||
Write-host -ForegroundColor Red "Unable to find existing VM Id $ParentId"
|
||||
break
|
||||
}
|
||||
|
||||
$vmUri = $Global:DefaultHostedServer.Server + "/vms"
|
||||
$body = @{"ParentId"="$ParentId";"Name"=$Name}
|
||||
$body = $body | ConvertTo-Json
|
||||
|
||||
try {
|
||||
Write-Host "Cloning VM $ParentId to $Name ..."
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmUri -Method POST -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `$body"
|
||||
$command = $Global:fusionCommand + $params
|
||||
Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Failed to Clone VM Id $ParentId"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Function Remove-HostedVM {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id,
|
||||
[parameter(Mandatory=$false,ValueFromPipeline=$true)][boolean]$Confirm = $true
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
$vm = Get-HostedVM -Id $Id
|
||||
|
||||
if($vm -match "Invalid VM Id") {
|
||||
Write-host -ForegroundColor Red "Unable to find existing VM Id $Id"
|
||||
break
|
||||
}
|
||||
|
||||
if($Confirm) {
|
||||
Get-Confirmation
|
||||
}
|
||||
|
||||
$vmUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id
|
||||
try {
|
||||
Write-Host "Deleting VM Id $Id ..."
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmUri -Method DELETE -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Failed to Delete VM Id $Id"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Function Get-HostedVMSharedFolder {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
$folderUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/sharedfolders"
|
||||
try {
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $folderUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Invalid VM Id $Id"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Function New-HostedVMSharedFolder {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id,
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$FolderName,
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$HostPath
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
$body = @{"folder_id"="$FolderName";"host_path"=$HostPath;"flags"=4}
|
||||
$body = $body | ConvertTo-Json
|
||||
|
||||
$folderUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/sharedfolders"
|
||||
try {
|
||||
Write-Host "Creating new Shared Folder $FolderName to $HostPath for VM Id $Id ..."
|
||||
$params += "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $folderUri -Method POST -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `$body"
|
||||
$command = $Global:fusionCommand + $params
|
||||
Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Failed to create Shared Folder for VM Id $Id"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Function Remove-HostedVMSharedFolder {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id,
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$FolderName,
|
||||
[parameter(Mandatory=$false,ValueFromPipeline=$true)][boolean]$Confirm = $true
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
if($Confirm) {
|
||||
Get-Confirmation
|
||||
}
|
||||
|
||||
$folderUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/sharedfolders/" + $FolderName
|
||||
try {
|
||||
Write-Host "Removing Shared Folder $FolderName for VM Id $Id ..."
|
||||
$params += "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $folderUri -Method DELETE -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Failed to remove Shared Folder for VM Id $Id"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Function Get-HostedVMNic {
|
||||
Param (
|
||||
[parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id
|
||||
)
|
||||
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
$vmNicUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/nic"
|
||||
try {
|
||||
$params += "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmNicUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$vmNics = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Invalid VM Id $Id"
|
||||
break
|
||||
}
|
||||
|
||||
$results = @()
|
||||
foreach ($vmNic in $vmNics.nics) {
|
||||
$tmp = [pscustomobject] @{
|
||||
Index = $vmNic.index;
|
||||
Type = $vmNic.Type;
|
||||
VMnet = $vmNic.Vmnet;
|
||||
}
|
||||
$results+=$tmp
|
||||
}
|
||||
$results
|
||||
}
|
||||
|
||||
Function Get-HostedNetworks {
|
||||
if(!$Global:DefaultHostedServer) {
|
||||
Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer"
|
||||
exit
|
||||
}
|
||||
|
||||
$networksUri = $Global:DefaultHostedServer.Server + "/vmnet"
|
||||
try {
|
||||
$params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $networksUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`""
|
||||
$command = $Global:fusionCommand + $params
|
||||
$networks = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
Write-host -ForegroundColor Red "Unable to retrieve Networks"
|
||||
break
|
||||
}
|
||||
|
||||
$results = @()
|
||||
foreach ($network in $networks.vmnets) {
|
||||
$tmp = [pscustomobject] @{
|
||||
Name = $network.Name;
|
||||
Type = $network.Type;
|
||||
DHCP = $network.Dhcp;
|
||||
Network = $network.subnet;
|
||||
Netmask = $network.mask;
|
||||
}
|
||||
$results+=$tmp
|
||||
}
|
||||
$results
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#Script Module : VMware.Hv.Helper
|
||||
#Version : 1.1
|
||||
#Version : 1.2
|
||||
|
||||
#Copyright © 2016 VMware, Inc. All Rights Reserved.
|
||||
|
||||
@@ -5776,7 +5776,8 @@ function Set-HVPool {
|
||||
.NOTES
|
||||
Author : Praveen Mathamsetty.
|
||||
Author email : pmathamsetty@vmware.com
|
||||
Version : 1.1
|
||||
Version : 1.2
|
||||
Updated : Mark Elvers <mark.elvers@tunbury.org>
|
||||
|
||||
===Tested Against Environment====
|
||||
Horizon View Server Version : 7.0.2, 7.1.0
|
||||
@@ -5818,6 +5819,16 @@ function Set-HVPool {
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Spec,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]
|
||||
$globalEntitlement,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[boolean]$allowUsersToChooseProtocol,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[boolean]$enableHTMLAccess,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
$HvServer = $null
|
||||
)
|
||||
@@ -5906,6 +5917,36 @@ function Set-HVPool {
|
||||
$updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.enableProvisioning' `
|
||||
-value $false
|
||||
}
|
||||
|
||||
if ($PSBoundParameters.ContainsKey("allowUsersToChooseProtocol")) {
|
||||
$updates += Get-MapEntry -key 'desktopSettings.displayProtocolSettings.allowUsersToChooseProtocol' -value $allowUsersToChooseProtocol
|
||||
}
|
||||
|
||||
if ($PSBoundParameters.ContainsKey("enableHTMLAccess")) {
|
||||
$updates += Get-MapEntry -key 'desktopSettings.displayProtocolSettings.enableHTMLAccess' -value $enableHTMLAccess
|
||||
}
|
||||
|
||||
$info = $services.PodFederation.PodFederation_get()
|
||||
if ($globalEntitlement -and ("ENABLED" -eq $info.localPodStatus.status)) {
|
||||
$QueryFilterEquals = New-Object VMware.Hv.QueryFilterEquals
|
||||
$QueryFilterEquals.memberName = 'base.displayName'
|
||||
$QueryFilterEquals.value = $globalEntitlement
|
||||
$defn = New-Object VMware.Hv.QueryDefinition
|
||||
$defn.queryEntityType = 'GlobalEntitlementSummaryView'
|
||||
$defn.Filter = $QueryFilterEquals
|
||||
$query_service_helper = New-Object VMware.Hv.QueryServiceService
|
||||
try {
|
||||
$queryResults = $query_service_helper.QueryService_Query($services,$defn)
|
||||
$globalEntitlementid = $queryResults.Results.id
|
||||
if ($globalEntitlementid.length -eq 1) {
|
||||
$updates += Get-MapEntry -key 'globalEntitlementData.globalEntitlement' -value $globalEntitlementid
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host "GlobalEntitlement " $_
|
||||
}
|
||||
}
|
||||
|
||||
$desktop_helper = New-Object VMware.Hv.DesktopService
|
||||
foreach ($item in $poolList.Keys) {
|
||||
Write-Host "Updating the Pool: " $poolList.$item
|
||||
@@ -8597,6 +8638,185 @@ function Get-HVGlobalEntitlement {
|
||||
}
|
||||
|
||||
|
||||
function Set-HVGlobalEntitlement {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Sets the existing pool properties.
|
||||
|
||||
.DESCRIPTION
|
||||
This cmdlet allows user to edit global entitlements.
|
||||
|
||||
.PARAMETER DisplayName
|
||||
Display Name of Global Entitlement.
|
||||
|
||||
.PARAMETER Description
|
||||
Description of Global Entitlement.
|
||||
|
||||
.PARAMETER EnableHTMLAccess
|
||||
If set to true, the desktops that are associated with this GlobalEntitlement must also have HTML Access enabled.
|
||||
|
||||
.PARAMETER Key
|
||||
Property names path separated by . (dot) from the root of desktop spec.
|
||||
|
||||
.PARAMETER Value
|
||||
Property value corresponds to above key name.
|
||||
|
||||
.PARAMETER HvServer
|
||||
View API service object of Connect-HVServer cmdlet.
|
||||
|
||||
.PARAMETER Spec
|
||||
Path of the JSON specification file containing key/value pair.
|
||||
|
||||
.EXAMPLE
|
||||
Set-HVGlobalEntitlement -DisplayName 'MyGlobalEntitlement' -Spec 'C:\Edit-HVPool\EditPool.json' -Confirm:$false
|
||||
Updates pool configuration by using json file
|
||||
|
||||
.EXAMPLE
|
||||
Set-HVGlobalEntitlement -DisplayName 'MyGlobalEntitlement' -Key 'base.description' -Value 'update description'
|
||||
Updates pool configuration with given parameters key and value
|
||||
|
||||
.EXAMPLE
|
||||
Set-HVGlobalEntitlement -DisplayName 'MyGlobalEntitlement' -enableHTMLAccess $true
|
||||
Set Allow HTML Access on a global entitlement. Note that it must also be enabled on the Pool and as of 7.3.0 Allow User to Choose Protocol must be enabled (which is unfortunately read-only)
|
||||
|
||||
.EXAMPLE
|
||||
Get-HVGlobalEntitlement | Set-HVGlobalEntitlement -Disable
|
||||
Disable all global entitlements
|
||||
|
||||
.OUTPUTS
|
||||
None
|
||||
|
||||
.NOTES
|
||||
Author : Mark Elvers
|
||||
Author email : mark.elvers@tunbury.org
|
||||
Version : 1.0
|
||||
|
||||
===Tested Against Environment====
|
||||
Horizon View Server Version : 7.3.0, 7.3.1
|
||||
PowerCLI Version : PowerCLI 6.5.1
|
||||
PowerShell Version : 5.0
|
||||
#>
|
||||
|
||||
[CmdletBinding(
|
||||
SupportsShouldProcess = $true,
|
||||
ConfirmImpact = 'High'
|
||||
)]
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory = $true,ParameterSetName = 'option')]
|
||||
[string] $displayName,
|
||||
|
||||
[Parameter(ValueFromPipeline = $true,ParameterSetName = 'pipeline')]
|
||||
$GlobalEntitlements,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Key,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
$Value,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Spec,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[switch]$Enable,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[switch]$Disable,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[boolean]$enableHTMLAccess,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
$HvServer = $null
|
||||
)
|
||||
|
||||
begin {
|
||||
$services = Get-ViewAPIService -hvServer $hvServer
|
||||
if ($null -eq $services) {
|
||||
Write-Error "Could not retrieve ViewApi services from connection object"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
process {
|
||||
$info = $services.PodFederation.PodFederation_get()
|
||||
if ("ENABLED" -ne $info.localPodStatus.status) {
|
||||
Write-Host "Multi-DataCenter-View/CPA is not enabled"
|
||||
return
|
||||
}
|
||||
|
||||
$confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys
|
||||
$geList = @{}
|
||||
if ($displayName) {
|
||||
try {
|
||||
$ge = Get-HVGlobalEntitlement -displayName $displayName -suppressInfo $true -hvServer $hvServer
|
||||
} catch {
|
||||
Write-Error "Make sure Get-HVGlobalEntitlement advanced function is loaded, $_"
|
||||
break
|
||||
}
|
||||
if ($ge) {
|
||||
$geList.add($ge.id, $ge.base.DisplayName)
|
||||
} else {
|
||||
Write-Error "No globalentitlement found with name: [$displayName]"
|
||||
break
|
||||
}
|
||||
} elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $GlobalEntitlements) {
|
||||
foreach ($item in $GlobalEntitlements) {
|
||||
if ($item.GetType().name -eq 'GlobalEntitlementSummaryView') {
|
||||
$geList.add($item.id, $item.Base.DisplayName)
|
||||
} else {
|
||||
Write-Error "In pipeline did not get object of expected type GlobalEntitlementSummaryView"
|
||||
[System.gc]::collect()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$updates = @()
|
||||
if ($key -and $value) {
|
||||
$updates += Get-MapEntry -key $key -value $value
|
||||
} elseif ($key -or $value) {
|
||||
Write-Error "Both key:[$key] and value:[$value] needs to be specified"
|
||||
}
|
||||
if ($spec) {
|
||||
try {
|
||||
$specObject = Get-JsonObject -specFile $spec
|
||||
} catch {
|
||||
Write-Error "Json file exception, $_"
|
||||
return
|
||||
}
|
||||
foreach ($member in ($specObject.PSObject.Members | Where-Object { $_.MemberType -eq 'NoteProperty' })) {
|
||||
$updates += Get-MapEntry -key $member.name -value $member.value
|
||||
}
|
||||
}
|
||||
|
||||
if ($Enable) {
|
||||
$updates += Get-MapEntry -key 'base.enabled' -value $true
|
||||
}
|
||||
elseif ($Disable) {
|
||||
$updates += Get-MapEntry -key 'base.enabled' -value $false
|
||||
}
|
||||
|
||||
if ($PSBoundParameters.ContainsKey("enableHTMLAccess")) {
|
||||
$updates += Get-MapEntry -key 'base.enableHTMLAccess' -value $enableHTMLAccess
|
||||
}
|
||||
|
||||
$ge_helper = New-Object VMware.HV.GlobalEntitlementService
|
||||
foreach ($item in $geList.Keys) {
|
||||
Write-Host "Updating the Entitlement: " $geList.$item
|
||||
if (!$confirmFlag -OR $pscmdlet.ShouldProcess($geList.$item)) {
|
||||
$ge_helper.GlobalEntitlement_Update($services, $item, $updates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end {
|
||||
[System.gc]::collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Remove-HVGlobalEntitlement {
|
||||
|
||||
<#
|
||||
@@ -9371,4 +9591,5 @@ function Set-HVGlobalSettings {
|
||||
}
|
||||
}
|
||||
|
||||
Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement, Set-HVMachine, New-HVGlobalEntitlement, Remove-HVGlobalEntitlement, Get-HVGlobalEntitlement, Get-HVPodSession, Set-HVApplicationIcon, Remove-HVApplicationIcon, Get-HVGlobalSettings, Set-HVGlobalSettings
|
||||
Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement, Set-HVMachine, New-HVGlobalEntitlement, Remove-HVGlobalEntitlement, Get-HVGlobalEntitlement, Get-HVPodSession, Set-HVApplicationIcon, Remove-HVApplicationIcon, Get-HVGlobalSettings, Set-HVGlobalSettings, Set-HVGlobalEntitlement
|
||||
|
||||
|
||||
@@ -83,6 +83,13 @@ New-VIProperty -Name EncryptionKeyId -ObjectType HardDisk -Value {
|
||||
}
|
||||
} -BasedOnExtensionProperty 'Backing.KeyId' -Force | Out-Null
|
||||
|
||||
New-VIProperty -Name KMSserver -ObjectType VMHost -Value {
|
||||
Param ($VMHost)
|
||||
if ($VMHost.CryptoSafe) {
|
||||
$VMHost.ExtensionData.Runtime.CryptoKeyId.ProviderId.Id
|
||||
}
|
||||
} -BasedOnExtensionProperty 'Runtime.CryptoKeyId.ProviderId.Id' -Force | Out-Null
|
||||
|
||||
Function Enable-VMHostCryptoSafe {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
@@ -1027,7 +1034,7 @@ Function Set-VMDiskEncryptionKey {
|
||||
C:\PS>$KMSCluster = Get-KMSCluster | select -last 1
|
||||
C:\PS>$VM = Get-VM -Name win2012
|
||||
C:\PS>$HardDisk = get-vm $vm|Get-HardDisk
|
||||
C:\PS>$HardDisk|$Set-VMEncryptionKey -VM $VM -KMSClusterId $KMSCluster.Id -Deep
|
||||
C:\PS>$HardDisk| Set-VMDiskEncryptionKey -VM $VM -KMSClusterId $KMSCluster.Id -Deep
|
||||
|
||||
Deep rekeys all the disks of the $VM using a new key.
|
||||
The key is generted from the KMS whose clusterId is $KMSCluster.Id.
|
||||
|
||||
30
Scripts/Get-BasicVMCapacityReport
Normal file
30
Scripts/Get-BasicVMCapacityReport
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
$myCol = @()
|
||||
$start = (Get-Date).AddDays(-30)
|
||||
$finish = Get-Date
|
||||
$cluster= "Demo"
|
||||
|
||||
$objServers = Get-Cluster $cluster | Get-VM
|
||||
foreach ($server in $objServers) {
|
||||
if ($server.guest.osfullname -ne $NULL){
|
||||
if ($server.guest.osfullname.contains("Windows")){
|
||||
$stats = get-stat -Entity $server -Stat "cpu.usage.average","mem.usage.average" -Start $start -Finish $finish
|
||||
|
||||
$ServerInfo = "" | Select-Object vName, OS, Mem, AvgMem, MaxMem, CPU, AvgCPU, MaxCPU, pDisk, Host
|
||||
$ServerInfo.vName = $server.name
|
||||
$ServerInfo.OS = $server.guest.osfullname
|
||||
$ServerInfo.Host = $server.vmhost.name
|
||||
$ServerInfo.Mem = $server.memoryGB
|
||||
$ServerInfo.AvgMem = $("{0:N2}" -f ($stats | Where-Object {$_.MetricId -eq "mem.usage.average"} | Measure-Object -Property Value -Average).Average)
|
||||
$ServerInfo.MaxMem = $("{0:N2}" -f ($stats | Where-Object {$_.MetricId -eq "mem.usage.average"} | Measure-Object -Property Value -Maximum).Maximum)
|
||||
$ServerInfo.CPU = $server.numcpu
|
||||
$ServerInfo.AvgCPU = $("{0:N2}" -f ($stats | Where-Object {$_.MetricId -eq "cpu.usage.average"} | Measure-Object -Property Value -Average).Average)
|
||||
$ServerInfo.MaxCPU = $("{0:N2}" -f ($stats | Where-Object {$_.MetricId -eq "cpu.usage.average"} | Measure-Object -Property Value -Maximum).Maximum)
|
||||
$ServerInfo.pDisk = [Math]::Round($server.ProvisionedSpaceGB,2)
|
||||
|
||||
$mycol += $ServerInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$myCol | Sort-Object vName | Export-Csv "VM_report.csv" -NoTypeInformation
|
||||
235
Scripts/Get-CIVMData.ps1
Normal file
235
Scripts/Get-CIVMData.ps1
Normal file
@@ -0,0 +1,235 @@
|
||||
Function Get-CIVMData
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Gathers information about a target CIVM
|
||||
|
||||
.DESCRIPTION
|
||||
This function gathers CIVM Name, Parent vApp (obj), Parent vApp Name, All network adapters
|
||||
(including IP, NIC index, and network), and vCenter VMX path details returning the resulting
|
||||
ordered list.
|
||||
|
||||
.PARAMETER CIVM
|
||||
The target vCloud VM from which information will be gathered
|
||||
|
||||
.NOTES
|
||||
Author: Brian Marsh
|
||||
Version: 1.0
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
Param (
|
||||
[Parameter(
|
||||
Position=0,
|
||||
Mandatory=$true,
|
||||
ValueFromPipeline=$true,
|
||||
ValueFromPipelineByPropertyName=$true)
|
||||
]
|
||||
[VMware.VimAutomation.Cloud.Types.V1.CIVM] $CIVM
|
||||
)
|
||||
BEGIN
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PROCESS
|
||||
{
|
||||
$NewObj = [Ordered]@{}
|
||||
$NewObj.GetCIVMData = @{}
|
||||
$NewObj.GetCIVMData.Successful = $true
|
||||
|
||||
# Get the vCenter VM from the vCloud VM object
|
||||
$vm = $civm | Get-VM -Debug:$False -Verbose:$False
|
||||
|
||||
Write-Verbose "Storing CIVM Name: $($CIVM.Name)/ Status: $($CIVM.Status)"
|
||||
$NewObj.Name = $CIVM.Name
|
||||
$NewObj.Status = $CIVM.Status
|
||||
|
||||
Write-Verbose "Recording Reservations"
|
||||
$NewObj.Reservations = @{}
|
||||
$NewObj.Reservations.CPU = @{}
|
||||
$NewObj.Reservations.Memory = @{}
|
||||
|
||||
$NewObj.Reservations.CPU.Reservation = $vm.ExtensionData.ResourceConfig.CpuAllocation.Reservation
|
||||
$NewObj.Reservations.CPU.Limit = $vm.ExtensionData.ResourceConfig.CpuAllocation.Limit
|
||||
$NewObj.Reservations.Memory.Reservation = $vm.ExtensionData.ResourceConfig.MemoryAllocation.Reservation
|
||||
$NewObj.Reservations.Memory.Limit = $vm.ExtensionData.ResourceConfig.MemoryAllocation.Limit
|
||||
|
||||
# Get the UUid from the Id, split out the UUID and pass it along
|
||||
# Sample Id: urn:vcloud:vm:d9ca710d-cdf2-44eb-a274-26e1dcfd01bb
|
||||
Write-Verbose "Storing CIVM UUID: $(($CIVM.Id).Split(':')[3])"
|
||||
$NewObj.Uuid = ($CIVM.Id).Split(':')[3]
|
||||
|
||||
Write-Verbose "Gathering Network details"
|
||||
$vAppNetworkAdapters = @()
|
||||
$NetworkAdapters = Get-CINetworkAdapter -VM $civm -Debug:$False -Verbose:$False
|
||||
|
||||
foreach ($networkAdapter in $networkAdapters)
|
||||
{
|
||||
# Remove any existing VMNIC variables
|
||||
Remove-Variable -Name VMNic -ErrorAction SilentlyContinue
|
||||
|
||||
$vAppNicInfo = [Ordered]@{}
|
||||
$vAppNicInfo.NIC = ("NIC" + $networkAdapter.Index)
|
||||
$vAppNicInfo.Index = $networkAdapter.Index
|
||||
$vAppNicInfo.Connected = $networkAdapter.Connected
|
||||
$vAppNicInfo.ExternalIP = $networkAdapter.IpAddress
|
||||
$vAppNicInfo.InternalIP = $networkAdapter.ExternalIpAddress
|
||||
$vAppNicInfo.MacAddress = $networkAdapter.MACAddress
|
||||
|
||||
$vAppNicInfo.vAppNetwork = [Ordered]@{}
|
||||
$vAppNicInfo.vAppNetwork.Name = $networkAdapter.VAppNetwork.Name
|
||||
|
||||
<#
|
||||
There is a chance that the vApp Network Name may not match a PortGroup which causes issues upon importing the VM after migration.
|
||||
To fix this issue, we'll try to find get the PortGroup in this data gathering stage. If it is not found, we'll move on to attempted
|
||||
remediation:
|
||||
1) Get the vCenter VM network adapter that corresponds to this vCloud Director VM network adapter (where MAC Addresses match)
|
||||
2) If the vCenter VM network adapter's network name doesn't match 'none' (indicating the VM is powered off) and the vCenter Network
|
||||
name does not match the vCloud Director network name, set this target object's vAppNetwork Name to the vCenter PortGroup
|
||||
3) If the vCenter VM network adapter's network name is 'none' then this VM is probably powered off and the network information is
|
||||
not defined in vCenter. In this case, we mark the get-data as unsuccessful, set an error message and return.
|
||||
#>
|
||||
try
|
||||
{
|
||||
$vm | Get-VMHost -Debug:$false -Verbose:$false | Get-VDSwitch -Debug:$false -Verbose:$false -ErrorAction Stop | `
|
||||
Get-VDPortgroup -name $networkAdapter.vAppNetwork.Name -Debug:$false -Verbose:$false -ErrorAction Stop | Out-Null
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Debug "Portgroup not found by name $($networkAdapter.vAppNetwork.Name), Debug?"
|
||||
Write-Verbose "Portgroup not found by name $($networkAdapter.vAppNetwork.Name), attempting fall back."
|
||||
# Get VIVM network adapter where adapter mac matches vappnicinfo MacAddress
|
||||
$VMNic = $vm | Get-NetworkAdapter -Debug:$false -Verbose:$false | Where-Object { $_.MacAddress -eq $vAppNicInfo.MacAddress }
|
||||
|
||||
# If VMNic Network Name doesn't match 'none' and doesn't match the vAppNetworkName, set vAppNetwork name to VMNic Network name
|
||||
If ( ($VMNic.NetworkName -notlike 'none') -and ($VMNic.NetworkName -ne $vAppNicInfo.vAppNetwork.Name))
|
||||
{
|
||||
$vAppNicInfo.vAppNetwork.Name = $VMNic.NetworkName
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Debug "Tried to recover from missing network port group. Failed. Debug?"
|
||||
$ErrorMessage = "VM [ $($CIVM.Name) ] has vAppNetwork connection that doesn't exist in vCenter [ $($vAppNicInfo.vAppNetwork.Name) ]"
|
||||
$NewObj.GetCIVMData.Successful = $False
|
||||
$NewObj.GetCIVMData.Error = $ErrorMessage
|
||||
Write-Error $ErrorMessage
|
||||
|
||||
#Return whatever object we have at this point
|
||||
$NewObj
|
||||
|
||||
Return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$vAppNetworkAdapters += $vAppNicInfo
|
||||
}
|
||||
|
||||
Write-Verbose "Checking for Duplicate name upon Import"
|
||||
Try
|
||||
{
|
||||
$DupeVM = Get-VM -Name $NewObj.NewName -Debug:$false -Verbose:$false -ErrorAction Stop -ErrorVariable DupeVM
|
||||
If ($DupeVM)
|
||||
{
|
||||
$NewObj.GetCIVMData.Successful = $False
|
||||
$NewObj.GetCIVMData.Error = "VM with name $($NewObj.NewName) already exists in vCenter"
|
||||
Write-Error "VM with name $($NewObj.NewName) already exists in vCenter"
|
||||
|
||||
#Return whatever object we have at this point
|
||||
$NewObj
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
Catch
|
||||
{
|
||||
Write-Verbose "No Duplicate Name Found!"
|
||||
}
|
||||
|
||||
$NewObj.vAppNetworkAdapters = $vAppNetworkAdapters
|
||||
|
||||
Write-Verbose "Setting VIVIM object, parent vApp details, and CIVM object"
|
||||
try
|
||||
{
|
||||
$NewObj.VIVM = $vm
|
||||
$NewObj.ToolsStatus = $vm.ExtensionData.Guest.ToolsStatus
|
||||
$NewObj.ToolsRunningStatus = $vm.ExtensionData.Guest.ToolsRunningStatus
|
||||
$NewObj.HasSnapshots = ($vm | Get-Snapshot -Debug:$false -Verbose:$false -ErrorAction Stop | Select-Object Name, Description,VMId)
|
||||
$NewObj.NeedsConsolidation = $vm.ExtensionData.Runtime.ConsolidationNeeded
|
||||
$NewObj.OldMoref = $vm.Id
|
||||
$NewObj.VmPathName = $vm.ExtensionData.Config.Files.VmPathName
|
||||
$NewObj.ParentVApp = $CIVM.VApp.Name
|
||||
$NewObj.StorageReservation = ($vm |Get-DatastoreCluster -Debug:$false -Verbose:$false -ErrorAction Stop | Select-Object -ExpandProperty Name)
|
||||
$NewObj.CIVMId = $CIVM.Id
|
||||
}
|
||||
catch
|
||||
{
|
||||
$NewObj.GetCIVMData.Successful = $False
|
||||
$NewObj.GetCIVMData.Error = "VM [ $($CIVM.Name) ] something went wrong while gathering details: $_"
|
||||
Write-Debug "VM [ $($CIVM.Name) ] something went wrong while gathering details: $_, Debug"
|
||||
Write-Error "VM [ $($CIVM.Name) ] something went wrong while gathering details: $_. "
|
||||
|
||||
#Return whatever object we have at this point
|
||||
$NewObj
|
||||
|
||||
Return
|
||||
}
|
||||
|
||||
# If ToolsStatus is not 'toolsOk' and status is not "PoweredOn", bomb out. We won't be able to power this VM off later.
|
||||
If ($NewObj.ToolsRunningStatus -ne 'guestToolsRunning' -and $NewObj.status -eq "PoweredOn")
|
||||
{
|
||||
$NewObj.GetCIVMData.Successful = $False
|
||||
$NewObj.GetCIVMData.Error = "VM [ $($CIVM.Name) ] tools are not running but the VM is powered On. Fix and try again."
|
||||
Write-Debug "VM [ $($CIVM.Name) ] tools are not running but the VM is powered On, Debug"
|
||||
Write-Error "VM [ $($CIVM.Name) ] tools are not running but the VM is powered On. "
|
||||
|
||||
#Return whatever object we have at this point
|
||||
$NewObj
|
||||
|
||||
Return
|
||||
}
|
||||
|
||||
If ($NewObj.HasSnapshots)
|
||||
{
|
||||
$NewObj.GetCIVMData.Successful = $False
|
||||
$NewObj.GetCIVMData.Error = "VM [ $($CIVM.Name) ] has snapshots. Remove before trying again."
|
||||
Write-Debug "VM [ $($CIVM.Name) ] has snapshots. Remove before trying again, Debug"
|
||||
Write-Error "VM [ $($CIVM.Name) ] has snapshots. Remove before trying again."
|
||||
|
||||
#Return whatever object we have at this point
|
||||
$NewObj
|
||||
|
||||
Return
|
||||
}
|
||||
|
||||
Write-Verbose "Determining the VMX Path for this VM"
|
||||
|
||||
# Get this VM's path on disk
|
||||
$vmPathName = $vm.ExtensionData.Config.Files.VmPathName
|
||||
|
||||
# Determine in which Datacenter this VM resides
|
||||
$datacenter = $vm | get-Datacenter -Debug:$False -Verbose:$False | Select-Object -expand name
|
||||
|
||||
# Split out the datastore from the path name
|
||||
$datastore = $vmPathName.Split("]")[0].split("[")[1]
|
||||
|
||||
# Split out the folder from the path name
|
||||
$vmFolderPath = $vmPathName.Split("/")[0].split("]")[1].trim()
|
||||
|
||||
# Re-combine into a valid folder path
|
||||
$vmxPath = "vmstore:\$($datacenter)\$($datastore)\$vmFolderPath"
|
||||
|
||||
Write-Verbose "VMXPath $vmxPath"
|
||||
$NewObj.vmxPath = $vmxPath
|
||||
|
||||
$NewObj
|
||||
|
||||
}
|
||||
|
||||
END
|
||||
{
|
||||
Write-Debug "About to exit Get-CIVMData, anything else?"
|
||||
Write-Verbose "Exited Get-CIVMData"
|
||||
}
|
||||
}
|
||||
4
Scripts/Get-TotalDiskUsage.ps1
Normal file
4
Scripts/Get-TotalDiskUsage.ps1
Normal file
@@ -0,0 +1,4 @@
|
||||
#Script returns total disk usage by all Powered On VMs in the environment in Gigabytes
|
||||
#Author: Chris Bradshaw via https://isjw.uk/using-powercli-to-measure-vm-disk-space-usage/
|
||||
|
||||
[math]::Round(((get-vm | Where-object{$_.PowerState -eq "PoweredOn" }).UsedSpaceGB | measure-Object -Sum).Sum)
|
||||
6
Scripts/Get-TotalMemoryAllocation.ps1
Normal file
6
Scripts/Get-TotalMemoryAllocation.ps1
Normal file
@@ -0,0 +1,6 @@
|
||||
#Script gets total memory allocation in GB of all powered on VMs in the environment
|
||||
#Author: Chris Bradshaw via https://isjw.uk/powercli-snippet-total-memory-allocation/
|
||||
|
||||
[System.Math]::Round(((get-vm |
|
||||
where-object{$_.PowerState -eq "PoweredOn" }).MemoryGB |
|
||||
Measure-Object -Sum).Sum ,0)
|
||||
17
Scripts/Get-VMHostWWPN.ps1
Normal file
17
Scripts/Get-VMHostWWPN.ps1
Normal file
@@ -0,0 +1,17 @@
|
||||
function Get-VMHostWWPN {
|
||||
<#
|
||||
Script name: Get-VMHostWWPN.ps1
|
||||
Created on: 08/31/2017
|
||||
Author: Robin Haberstroh, @strohland
|
||||
Description: This script returns the WWPN of the hosts FiberChannel HBA in a readable format that corresponds to what storage team expects
|
||||
Dependencies: None known
|
||||
#>
|
||||
param(
|
||||
[string]$cluster
|
||||
)
|
||||
|
||||
Get-Cluster $cluster | get-vmhost | get-vmhosthba -type FibreChannel |
|
||||
format-table VMHost, Device, @{
|
||||
n='WorldWidePortName';e={[convert]::ToString($_.PortWorldWideName, 16)}
|
||||
}
|
||||
}
|
||||
11
Scripts/Get-VMToolsParts.ps1
Normal file
11
Scripts/Get-VMToolsParts.ps1
Normal file
@@ -0,0 +1,11 @@
|
||||
$vms = Get-VM | where {$_.PowerState -eq "PoweredOn" -and $_.GuestId -match "Windows"}
|
||||
|
||||
ForEach ($vm in $vms){
|
||||
Write-Host $vm
|
||||
$namespace = "root\CIMV2"
|
||||
$componentPattern = "hcmon|vmci|vmdebug|vmhgfs|VMMEMCTL|vmmouse|vmrawdsk|vmxnet|vmx_svga"
|
||||
(Get-WmiObject -class Win32_SystemDriver -computername $vm -namespace $namespace |
|
||||
where-object { $_.Name -match $componentPattern } |
|
||||
Format-Table -Auto Name,State,StartMode,DisplayName
|
||||
)
|
||||
}
|
||||
29
Scripts/SetClusterMultiPathToRoundRobin.ps1
Normal file
29
Scripts/SetClusterMultiPathToRoundRobin.ps1
Normal file
@@ -0,0 +1,29 @@
|
||||
<#
|
||||
Script name: SetClusterMultiPathToRoundRobin.ps1
|
||||
Created on: 09/14/2017
|
||||
Author: Alan Comstock, @Mr_Uptime
|
||||
Description: Set the MultiPath policy for FC devices to RoundRobin for all hosts in a cluster.
|
||||
Dependencies: None known
|
||||
PowerCLI Version: VMware PowerCLI 6.5 Release 1 build 4624819
|
||||
PowerShell Version: 5.1.14393.1532
|
||||
OS Version: Windows 10
|
||||
#>
|
||||
|
||||
#Check for any Fibre Channel devices that are not set to Round Robin in a cluster.
|
||||
#Get-Cluster -Name CLUSTERNAME | Get-VMhost | Get-VMHostHba -Type "FibreChannel" | Get-ScsiLun -LunType disk | Where { $_.MultipathPolicy -notlike "RoundRobin" } | Select CanonicalName,MultipathPolicy
|
||||
|
||||
#Set the Multipathing Policy to Round Robin for any Fibre Channel devices that are not Round Robin in a cluster
|
||||
$cluster = Get-Cluster CLUSTERNAME
|
||||
$hostlist = Get-VMHost -Location $cluster | Sort Name
|
||||
$TotalHostCount = $hostlist.count
|
||||
$hostincrement = 0
|
||||
while ($hostincrement -lt $TotalHostCount){ #Host Loop
|
||||
$currenthost = $hostlist[$hostincrement].Name
|
||||
Write-Host "Working on" $currenthost
|
||||
$scsilun = Get-VMhost $currenthost | Get-VMHostHba -Type "FibreChannel" | Get-ScsiLun -LunType disk | Where { $_.MultipathPolicy -notlike "RoundRobin" }
|
||||
if ($scsilun -ne $null){
|
||||
Set-ScsiLun -ScsiLun $scsilun -MultipathPolicy RoundRobin
|
||||
}
|
||||
$hostincrement++ #bump the host increment
|
||||
}
|
||||
#The End
|
||||
17
Scripts/SetMultiPathToRoundRobin
Normal file
17
Scripts/SetMultiPathToRoundRobin
Normal file
@@ -0,0 +1,17 @@
|
||||
<#
|
||||
Script name: SetMultiPathToRoundRobin.ps1
|
||||
Created on: 09/13/2017
|
||||
Author: Alan Comstock, @Mr_Uptime
|
||||
Description: Set the MultiPath policy for FC devices to RoundRobin
|
||||
Dependencies: None known
|
||||
PowerCLI Version: VMware PowerCLI 6.5 Release 1 build 4624819
|
||||
PowerShell Version: 5.1.14393.1532
|
||||
OS Version: Windows 10
|
||||
#>
|
||||
|
||||
#Check a host for any Fibre Channel devices that are not set to Round Robin. Modify to check clusters if needed.
|
||||
Get-VMhost HOSTNAME | Get-VMHostHba -Type "FibreChannel" | Get-ScsiLun -LunType disk | Where { $_.MultipathPolicy -notlike "RoundRobin" } | Select CanonicalName,MultipathPolicy
|
||||
|
||||
#Set the Multipathing Policy on a host to Round Robin for any Fibre Channel devices that are not Round Robin
|
||||
$scsilun = Get-VMhost HOSTNAME | Get-VMHostHba -Type "FibreChannel" | Get-ScsiLun -LunType disk | Where { $_.MultipathPolicy -notlike "RoundRobin" }
|
||||
Set-ScsiLun -ScsiLun $scsilun -MultipathPolicy RoundRobin
|
||||
58
Scripts/ha-vcenter-deploy-template.json
Normal file
58
Scripts/ha-vcenter-deploy-template.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"__version": "0.1",
|
||||
"__comments": "Configuration for ha-vcenter-deploy.ps1 - www.definit.co.uk",
|
||||
"target": {
|
||||
"server": "vcsa.definit.local",
|
||||
"user": "administrator@vsphere.local",
|
||||
"password": "VMware1!",
|
||||
"datacenter": "Lab",
|
||||
"cluster": "Workload",
|
||||
"datastore": "vsanDatastore",
|
||||
"folder": "Nested Labs/HA-vCenter",
|
||||
"portgroup": "HA-vCenter-Management",
|
||||
"ha-portgroup": "HA-vCenter-Heartbeat",
|
||||
"network": {
|
||||
"netmask": "255.255.255.0",
|
||||
"gateway": "10.0.11.1",
|
||||
"prefix": "24",
|
||||
"dns": "192.168.1.20",
|
||||
"domain": "definit.local",
|
||||
"ntp": "192.168.1.1"
|
||||
}
|
||||
},
|
||||
"sources": {
|
||||
"VCSAInstaller": "e:\\Pod-Deploy\\vSphere\\VMware-VCSA-all-6.5.0-4944578"
|
||||
},
|
||||
"active": {
|
||||
"deploymentSize": "small",
|
||||
"name": "ha-vc-active",
|
||||
"ip": "10.0.11.10",
|
||||
"ha-ip": "172.16.1.1",
|
||||
"hostname": "ha-vc.definit.local",
|
||||
"rootPassword": "VMware1!",
|
||||
"sso": {
|
||||
"domain": "vsphere.local",
|
||||
"site": "Default-First-Site",
|
||||
"password": "VMware1!"
|
||||
},
|
||||
"datacenter": "HA-vCenter-Datacenter",
|
||||
"cluster": "HA-vCenter-Cluster-1",
|
||||
"distributedSwitch": "HA-vCenter-VDS",
|
||||
"portgroup": "HA-vCenter-PortGroup"
|
||||
},
|
||||
"cluster": {
|
||||
"passive-ip": "172.16.1.2",
|
||||
"passive-name": "ha-vc-passive",
|
||||
"witness-ip": "172.16.1.3",
|
||||
"witness-name": "ha-vc-witness",
|
||||
"ha-mask": "255.255.255.248"
|
||||
},
|
||||
"general": {
|
||||
"syslog": "192.168.1.26",
|
||||
"ssh": true,
|
||||
"log": "ha-vcenter-deploy.log"
|
||||
},
|
||||
"license": {
|
||||
"vcenter": "7H23H-11111-22222-33333-90ZQN"
|
||||
}
|
||||
}
|
||||
388
Scripts/ha-vcenter-deploy.ps1
Normal file
388
Scripts/ha-vcenter-deploy.ps1
Normal file
@@ -0,0 +1,388 @@
|
||||
<#
|
||||
Script name: ha-vcenter-deploy.ps1
|
||||
Created on: 30/06/2017
|
||||
Author: Sam McGeown, @sammcgeown
|
||||
Description: The purpose of the script is to deploy vCenter in High Availability mode, using the advanced method. See https://www.definit.co.uk/2017/06/powershell-deploying-vcenter-high-availability-in-advanced-mode/
|
||||
Dependencies: None known
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory=$true)] [String]$configFile,
|
||||
[switch]$deployActive,
|
||||
[switch]$licenseVCSA,
|
||||
[switch]$addSecondaryNic,
|
||||
[switch]$prepareVCHA,
|
||||
[switch]$clonePassiveVM,
|
||||
[switch]$cloneWitnessVM,
|
||||
[switch]$configureVCHA,
|
||||
[switch]$resizeWitness,
|
||||
[switch]$createDRSRule
|
||||
)
|
||||
|
||||
if($psboundparameters.count -eq 1) {
|
||||
# Only the configFile is passed, set all steps to true
|
||||
$deployActive = $true
|
||||
$licenseVCSA = $true
|
||||
$addSecondaryNic = $true
|
||||
$prepareVCHA = $true
|
||||
$clonePassiveVM = $true
|
||||
$cloneWitnessVM = $true
|
||||
$configureVCHA = $true
|
||||
$resizeWitness = $true
|
||||
$createDRSRule = $true
|
||||
}
|
||||
|
||||
# Import the PowerCLI and DNS modules
|
||||
Get-Module -ListAvailable VMware*,DnsServer | Import-Module
|
||||
if ( !(Get-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) ) {
|
||||
throw "PowerCLI must be installed"
|
||||
}
|
||||
# Written by Sam McGeown @sammcgeown - www.definit.co.uk
|
||||
# Hat tips and thanks go to...
|
||||
# William Lam http://www.virtuallyghetto.com/2016/11/vghetto-automated-vsphere-lab-deployment-for-vsphere-6-0u2-vsphere-6-5.html
|
||||
# http://www.virtuallyghetto.com/2017/01/exploring-new-vcsa-vami-api-wpowercli-part-1.html
|
||||
|
||||
# Get the folder location
|
||||
$ScriptLocation = Split-Path -Parent $PSCommandPath
|
||||
|
||||
# Import the JSON Config File
|
||||
$podConfig = (get-content $($configFile) -Raw) | ConvertFrom-Json
|
||||
|
||||
# Path to VCSA Install Sources
|
||||
$VCSAInstaller = "$($podConfig.sources.VCSAInstaller)"
|
||||
|
||||
# Log File
|
||||
$verboseLogFile = $podConfig.general.log
|
||||
|
||||
$StartTime = Get-Date
|
||||
|
||||
Function Write-Log {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String]$message,
|
||||
[switch]$Warning,
|
||||
[switch]$Info
|
||||
)
|
||||
$timeStamp = Get-Date -Format "dd-MM-yyyy hh:mm:ss"
|
||||
Write-Host -NoNewline -ForegroundColor White "[$timestamp]"
|
||||
if($Warning){
|
||||
Write-Host -ForegroundColor Yellow " WARNING: $message"
|
||||
} elseif($Info) {
|
||||
Write-Host -ForegroundColor White " $message"
|
||||
}else {
|
||||
Write-Host -ForegroundColor Green " $message"
|
||||
}
|
||||
$logMessage = "[$timeStamp] $message" | Out-File -Append -LiteralPath $verboseLogFile
|
||||
}
|
||||
|
||||
function Get-VCSAConnection {
|
||||
param(
|
||||
[string]$vcsaName,
|
||||
[string]$vcsaUser,
|
||||
[string]$vcsaPassword
|
||||
)
|
||||
$existingConnection = $global:DefaultVIServers | where-object -Property Name -eq -Value $vcsaName
|
||||
if($existingConnection -ne $null) {
|
||||
return $existingConnection;
|
||||
} else {
|
||||
$connection = Connect-VIServer -Server $vcsaName -User $vcsaUser -Password $vcsaPassword -WarningAction SilentlyContinue;
|
||||
if($connection -ne $null) {
|
||||
return $connection;
|
||||
} else {
|
||||
throw "Unable to connect to $($vcsaName)..."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Close-VCSAConnection {
|
||||
param(
|
||||
[string]$vcsaName
|
||||
)
|
||||
if($vcsaName.Length -le 0) {
|
||||
if($Global:DefaultVIServers -ne $null) {
|
||||
Disconnect-VIServer -Server $Global:DefaultVIServers -Confirm:$false -ErrorAction SilentlyContinue
|
||||
}
|
||||
} else {
|
||||
$existingConnection = $global:DefaultVIServers | where-object -Property Name -eq -Value $vcsaName
|
||||
if($existingConnection -ne $null) {
|
||||
Disconnect-VIServer -Server $existingConnection -Confirm:$false;
|
||||
} else {
|
||||
Write-Warning -Message "Could not find an existing connection named $($vcsaName)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-PodFolder {
|
||||
param(
|
||||
$vcsaConnection,
|
||||
[string]$folderPath
|
||||
)
|
||||
$folderArray = $folderPath.split("/")
|
||||
$parentFolder = Get-Folder -Server $vcsaConnection -Name vm
|
||||
foreach($folder in $folderArray) {
|
||||
$folderExists = Get-Folder -Server $vcsaConnection | Where-Object -Property Name -eq -Value $folder
|
||||
if($folderExists -ne $null) {
|
||||
$parentFolder = $folderExists
|
||||
} else {
|
||||
$parentFolder = New-Folder -Name $folder -Location $parentFolder
|
||||
}
|
||||
}
|
||||
return $parentFolder
|
||||
}
|
||||
|
||||
|
||||
Close-VCSAConnection
|
||||
|
||||
if($deployActive) {
|
||||
Write-Log "#### Deploying Active VCSA ####"
|
||||
$pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password
|
||||
$pCluster = Get-Cluster -Name $podConfig.target.cluster -Server $pVCSA
|
||||
$pDatastore = Get-Datastore -Name $podConfig.target.datastore -Server $pVCSA
|
||||
$pPortGroup = Get-VDPortgroup -Name $podConfig.target.portgroup -Server $pVCSA
|
||||
$pFolder = Get-PodFolder -vcsaConnection $pVCSA -folderPath $podConfig.target.folder
|
||||
|
||||
Write-Log "Disabling DRS on $($podConfig.target.cluster)"
|
||||
$pCluster | Set-Cluster -DrsEnabled:$true -DrsAutomationLevel:PartiallyAutomated -Confirm:$false | Out-File -Append -LiteralPath $verboseLogFile
|
||||
|
||||
|
||||
Write-Log "Creating DNS Record"
|
||||
Add-DnsServerResourceRecordA -Name $podConfig.active.name -ZoneName $podConfig.target.network.domain -AllowUpdateAny -IPv4Address $podConfig.active.ip -ComputerName "192.168.1.20" -CreatePtr -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Log "Deploying VCSA"
|
||||
$config = (Get-Content -Raw "$($VCSAInstaller)\vcsa-cli-installer\templates\install\embedded_vCSA_on_VC.json") | convertfrom-json
|
||||
$config.'new.vcsa'.vc.hostname = $podConfig.target.server
|
||||
$config.'new.vcsa'.vc.username = $podConfig.target.user
|
||||
$config.'new.vcsa'.vc.password = $podConfig.target.password
|
||||
$config.'new.vcsa'.vc.datacenter = @($podConfig.target.datacenter)
|
||||
$config.'new.vcsa'.vc.datastore = $podConfig.target.datastore
|
||||
$config.'new.vcsa'.vc.target = @($podConfig.target.cluster)
|
||||
$config.'new.vcsa'.vc.'deployment.network' = $podConfig.target.portgroup
|
||||
$config.'new.vcsa'.os.'ssh.enable' = $podConfig.general.ssh
|
||||
$config.'new.vcsa'.os.password = $podConfig.active.rootPassword
|
||||
$config.'new.vcsa'.appliance.'thin.disk.mode' = $true
|
||||
$config.'new.vcsa'.appliance.'deployment.option' = $podConfig.active.deploymentSize
|
||||
$config.'new.vcsa'.appliance.name = $podConfig.active.name
|
||||
$config.'new.vcsa'.network.'system.name' = $podConfig.active.hostname
|
||||
$config.'new.vcsa'.network.'ip.family' = "ipv4"
|
||||
$config.'new.vcsa'.network.mode = "static"
|
||||
$config.'new.vcsa'.network.ip = $podConfig.active.ip
|
||||
$config.'new.vcsa'.network.'dns.servers'[0] = $podConfig.target.network.dns
|
||||
$config.'new.vcsa'.network.prefix = $podConfig.target.network.prefix
|
||||
$config.'new.vcsa'.network.gateway = $podConfig.target.network.gateway
|
||||
$config.'new.vcsa'.sso.password = $podConfig.active.sso.password
|
||||
$config.'new.vcsa'.sso.'domain-name' = $podConfig.active.sso.domain
|
||||
$config.'new.vcsa'.sso.'site-name' = $podConfig.active.sso.site
|
||||
|
||||
Write-Log "Creating VCSA JSON Configuration file for deployment"
|
||||
|
||||
$config | ConvertTo-Json | Set-Content -Path "$($ENV:Temp)\active.json"
|
||||
if((Get-VM | Where-Object -Property Name -eq -Value $podConfig.active.name) -eq $null) {
|
||||
Write-Log "Deploying OVF, this may take a while..."
|
||||
Invoke-Expression "$($VCSAInstaller)\vcsa-cli-installer\win32\vcsa-deploy.exe install --no-esx-ssl-verify --accept-eula --acknowledge-ceip $($ENV:Temp)\active.json"| Out-File -Append -LiteralPath $verboseLogFile
|
||||
$vcsaDeployOutput | Out-File -Append -LiteralPath $verboseLogFile
|
||||
Write-Log "Moving $($podConfig.active.name) to $($podConfig.target.folder)"
|
||||
if((Get-VM | where {$_.name -eq $podConfig.active.name}) -eq $null) {
|
||||
throw "Could not find VCSA VM. The script was unable to find the deployed VCSA"
|
||||
}
|
||||
Get-VM -Name $podConfig.active.name | Move-VM -Destination $pFolder | Out-File -Append -LiteralPath $verboseLogFile
|
||||
} else {
|
||||
Write-Log "VCSA exists, skipping" -Warning
|
||||
}
|
||||
Close-VCSAConnection
|
||||
}
|
||||
|
||||
|
||||
if($licenseVCSA) {
|
||||
Write-Log "#### Configuring VCSA ####"
|
||||
Write-Log "Getting connection to the new VCSA"
|
||||
$nVCSA = Get-VCSAConnection -vcsaName $podConfig.active.ip -vcsaUser "administrator@$($podConfig.active.sso.domain)" -vcsaPassword $podConfig.active.sso.password
|
||||
|
||||
Write-Log "Installing vCenter License"
|
||||
$serviceInstance = Get-View ServiceInstance -Server $nVCSA
|
||||
$licenseManagerRef=$serviceInstance.Content.LicenseManager
|
||||
$licenseManager=Get-View $licenseManagerRef
|
||||
$licenseManager.AddLicense($podConfig.license.vcenter,$null) | Out-File -Append -LiteralPath $verboseLogFile
|
||||
$licenseAssignmentManager = Get-View $licenseManager.LicenseAssignmentManager
|
||||
Write-Log "Assigning vCenter Server License"
|
||||
try {
|
||||
$licenseAssignmentManager.UpdateAssignedLicense($nVCSA.InstanceUuid, $podConfig.license.vcenter, $null) | Out-File -Append -LiteralPath $verboseLogFile
|
||||
}
|
||||
catch {
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
Write-Log $ErrorMessage -Warning
|
||||
}
|
||||
Close-VCSAConnection -vcsaName $podConfig.active.ip
|
||||
}
|
||||
|
||||
if($addSecondaryNic) {
|
||||
Write-Log "#### Adding HA Network Adapter ####"
|
||||
$pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password
|
||||
|
||||
if((Get-VM -Server $pVCSA -Name $podConfig.active.name | Get-NetworkAdapter).count -le 1) {
|
||||
Write-Log "Adding HA interface"
|
||||
Get-VM -Server $pVCSA -Name $podConfig.active.name | New-NetworkAdapter -Portgroup (Get-VDPortgroup -Name $podConfig.target."ha-portgroup") -Type Vmxnet3 -StartConnected | Out-File -Append -LiteralPath $verboseLogFile
|
||||
}
|
||||
Close-VCSAConnection
|
||||
|
||||
$nVCSA = Get-VCSAConnection -vcsaName $podConfig.active.ip -vcsaUser "administrator@$($podConfig.active.sso.domain)" -vcsaPassword $podConfig.active.sso.password
|
||||
|
||||
Write-Log "Configuring HA interface"
|
||||
$CisServer = Connect-CisServer -Server $podConfig.active.ip -User "administrator@$($podConfig.active.sso.domain)" -Password $podConfig.active.sso.password
|
||||
|
||||
$ipv4API = (Get-CisService -Name 'com.vmware.appliance.techpreview.networking.ipv4')
|
||||
$specList = $ipv4API.Help.set.config.CreateExample()
|
||||
$createSpec = [pscustomobject] @{
|
||||
address = $podConfig.active."ha-ip";
|
||||
default_gateway = "";
|
||||
interface_name = "nic1";
|
||||
mode = "is_static";
|
||||
prefix = "29";
|
||||
}
|
||||
$specList += $createSpec
|
||||
$ipv4API.set($specList)
|
||||
Close-VCSAConnection
|
||||
}
|
||||
|
||||
if($prepareVCHA) {
|
||||
Write-Log "#### Preparing vCenter HA mode ####"
|
||||
|
||||
$nVCSA = Get-VCSAConnection -vcsaName $podConfig.active.ip -vcsaUser "administrator@$($podConfig.active.sso.domain)" -vcsaPassword $podConfig.active.sso.password
|
||||
|
||||
Write-Log "Preparing vCenter HA"
|
||||
$ClusterConfig = Get-View failoverClusterConfigurator
|
||||
|
||||
$PassiveIpSpec = New-Object VMware.Vim.CustomizationFixedIp
|
||||
$PassiveIpSpec.IpAddress = $podConfig.cluster."passive-ip"
|
||||
|
||||
$PassiveNetwork = New-object VMware.Vim.CustomizationIPSettings
|
||||
$PassiveNetwork.Ip = $PassiveIpSpec
|
||||
$PassiveNetwork.SubnetMask = $podConfig.cluster."ha-mask"
|
||||
|
||||
$PassiveNetworkSpec = New-Object Vmware.Vim.PassiveNodeNetworkSpec
|
||||
$PassiveNetworkSpec.IpSettings = $PassiveNetwork
|
||||
|
||||
$WitnessIpSpec = New-Object VMware.Vim.CustomizationFixedIp
|
||||
$WitnessIpSpec.IpAddress = $podConfig.cluster."witness-ip"
|
||||
|
||||
$WitnessNetwork = New-object VMware.Vim.CustomizationIPSettings
|
||||
$WitnessNetwork.Ip = $WitnessIpSpec
|
||||
$WitnessNetwork.SubnetMask = $podConfig.cluster."ha-mask"
|
||||
|
||||
$WitnessNetworkSpec = New-Object VMware.Vim.NodeNetworkSpec
|
||||
$WitnessNetworkSpec.IpSettings = $WitnessNetwork
|
||||
|
||||
$ClusterNetworkSpec = New-Object VMware.Vim.VchaClusterNetworkSpec
|
||||
$ClusterNetworkSpec.WitnessNetworkSpec = $WitnessNetworkSpec
|
||||
$ClusterNetworkSpec.PassiveNetworkSpec = $PassiveNetworkSpec
|
||||
|
||||
$PrepareTask = $ClusterConfig.prepareVcha_task($ClusterNetworkSpec)
|
||||
|
||||
Close-VCSAConnection
|
||||
}
|
||||
|
||||
if($clonePassiveVM) {
|
||||
Write-Log "#### Cloning VCSA for Passive Node ####"
|
||||
|
||||
$pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password
|
||||
$pVMHost = Get-Random (Get-VMhost -Location $podConfig.target.cluster)
|
||||
$pFolder = Get-PodFolder -vcsaConnection $pVCSA -folderPath $podConfig.target.folder
|
||||
|
||||
$activeVM = Get-VM -Name $podConfig.active.name
|
||||
$CloneSpecName = "vCHA_ClonePassive"
|
||||
|
||||
Write-Log "Creating customization spec"
|
||||
# Clean up any old spec
|
||||
Get-OSCustomizationSpec -Name $CloneSpecName -ErrorAction SilentlyContinue | Remove-OSCustomizationSpec -Confirm:$false -ErrorAction SilentlyContinue | Out-File -Append -LiteralPath $verboseLogFile
|
||||
New-OSCustomizationSpec -Name $CloneSpecName -OSType Linux -Domain $podConfig.target.network.domain -NamingScheme fixed -DnsSuffix $podConfig.target.network.domain -NamingPrefix $podConfig.active.hostname -DnsServer $podConfig.target.network.dns -Type NonPersistent | Out-File -Append -LiteralPath $verboseLogFile
|
||||
Get-OSCustomizationNicMapping -OSCustomizationSpec $CloneSpecName | Set-OSCustomizationNicMapping -IpMode UseStaticIP -IpAddress $podConfig.active.ip -SubnetMask $podConfig.target.network.netmask -DefaultGateway $podConfig.target.network.gateway | Out-File -Append -LiteralPath $verboseLogFile
|
||||
New-OSCustomizationNicMapping -OSCustomizationSpec $CloneSpecName -IpMode UseStaticIP -IpAddress $podConfig.cluster."passive-ip" -SubnetMask $podConfig.cluster."ha-mask" -DefaultGateway $podConfig.target.network.gateway | Out-File -Append -LiteralPath $verboseLogFile
|
||||
|
||||
Write-Log "Cloning Active VCSA to Passive VCSA"
|
||||
$passiveVM = New-VM -Name $podConfig.cluster."passive-name" -VM $activeVM -OSCustomizationSpec $CloneSpecName -VMhost $pVMHost -Server $pVCSA -Location $pFolder | Start-VM | Out-File -Append -LiteralPath $verboseLogFile
|
||||
|
||||
# Ensure the network adapters are connected
|
||||
$passiveVM | Get-NetworkAdapter | Set-NetworkAdapter -Connected:$true -Confirm:$false
|
||||
|
||||
Write-Log "Waiting for VMware Tools"
|
||||
$passiveVM | Wait-Tools
|
||||
|
||||
Close-VCSAConnection
|
||||
}
|
||||
|
||||
if($cloneWitnessVM) {
|
||||
Write-Log "#### Cloning VCSA for Witness Node ####"
|
||||
|
||||
$pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password
|
||||
$pVMHost = Get-Random (Get-VMhost -Location $podConfig.target.cluster)
|
||||
$pFolder = Get-PodFolder -vcsaConnection $pVCSA -folderPath $podConfig.target.folder
|
||||
|
||||
$activeVM = Get-VM -Name $podConfig.active.name
|
||||
$CloneSpecName = "vCHA_CloneWitness"
|
||||
|
||||
Write-Log "Creating customization spec"
|
||||
# Clean up any old spec
|
||||
Get-OSCustomizationSpec -Name $CloneSpecName -ErrorAction SilentlyContinue | Remove-OSCustomizationSpec -Confirm:$false -ErrorAction SilentlyContinue | Out-File -Append -LiteralPath $verboseLogFile
|
||||
New-OSCustomizationSpec -Name $CloneSpecName -OSType Linux -Domain $podConfig.target.network.domain -NamingScheme fixed -DnsSuffix $podConfig.target.network.domain -NamingPrefix $podConfig.active.hostname -DnsServer $podConfig.target.network.dns -Type NonPersistent | Out-File -Append -LiteralPath $verboseLogFile
|
||||
New-OSCustomizationNicMapping -OSCustomizationSpec $CloneSpecName -IpMode UseStaticIP -IpAddress $podConfig.cluster."witness-ip" -SubnetMask $podConfig.cluster."ha-mask" -DefaultGateway $podConfig.target.network.gateway | Out-File -Append -LiteralPath $verboseLogFile
|
||||
|
||||
Write-Log "Cloning Active VCSA to Witness VCSA"
|
||||
$witnessVM = New-VM -Name $podConfig.cluster."witness-name" -VM $activeVM -OSCustomizationSpec $CloneSpecName -VMhost $pVMHost -Server $pVCSA -Location $pFolder | Start-VM | Out-File -Append -LiteralPath $verboseLogFile
|
||||
|
||||
# Ensure the network adapters are connected
|
||||
$witnessVM | Get-NetworkAdapter | Set-NetworkAdapter -Connected:$true -Confirm:$false
|
||||
|
||||
Write-Log "Waiting for VMware Tools"
|
||||
$witnessVM | Wait-Tools
|
||||
|
||||
Close-VCSAConnection
|
||||
}
|
||||
|
||||
if($configureVCHA) {
|
||||
Write-Log "#### Configuring vCenter HA mode ####"
|
||||
|
||||
$nVCSA = Get-VCSAConnection -vcsaName $podConfig.active.ip -vcsaUser "administrator@$($podConfig.active.sso.domain)" -vcsaPassword $podConfig.active.sso.password
|
||||
|
||||
$ClusterConfig = Get-View failoverClusterConfigurator
|
||||
$ClusterConfigSpec = New-Object VMware.Vim.VchaClusterConfigSpec
|
||||
$ClusterConfigSpec.PassiveIp = $podConfig.cluster."passive-ip"
|
||||
$ClusterConfigSpec.WitnessIp = $podConfig.cluster."witness-ip"
|
||||
$ConfigureTask = $ClusterConfig.configureVcha_task($ClusterConfigSpec)
|
||||
Write-Log "Waiting for cluster configuration task"
|
||||
Start-Sleep -Seconds 30
|
||||
|
||||
Close-VCSAConnection -vcsaName $podConfig.active.ip
|
||||
}
|
||||
|
||||
if($resizeWitness) {
|
||||
Write-Log "#### Resizing Witness Node ####"
|
||||
$pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password
|
||||
|
||||
$witnessVM = Get-VM -Name $podConfig.cluster."witness-name"
|
||||
Write-Log "Waiting for Witness node to shut down"
|
||||
$witnessVM | Stop-VMGuest -Confirm:$false | Out-File -Append -LiteralPath $verboseLogFile
|
||||
do {
|
||||
Start-Sleep -Seconds 3
|
||||
$witnessVM = Get-VM -Name $podConfig.cluster."witness-name"
|
||||
} until($witnessVM.PowerState -eq "Poweredoff")
|
||||
Write-Log "Setting CPU and Memory"
|
||||
$witnessVM | Set-VM -MemoryGB 1 -NumCpu 1 -Confirm:$false | Out-File -Append -LiteralPath $verboseLogFile
|
||||
Write-Log "Starting Witness VM"
|
||||
$witnessVM | Start-VM | Out-File -Append -LiteralPath $verboseLogFile
|
||||
Close-VCSAConnection
|
||||
}
|
||||
|
||||
if($createDRSRule) {
|
||||
Write-Log "#### Creating DRS Rule ####"
|
||||
$pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password
|
||||
$pCluster = Get-Cluster $podConfig.target.cluster
|
||||
$vCHA = Get-VM -Name $podConfig.active.name,$podConfig.cluster."passive-name",$podConfig.cluster."witness-name"
|
||||
New-DRSRule -Name "vCenter HA" -Cluster $pCluster -VM $vCHA -KeepTogether $false | Out-File -Append -LiteralPath $verboseLogFile
|
||||
Write-Log "Enabling DRS on $($podConfig.target.cluster)"
|
||||
$pCluster | Set-Cluster -DrsEnabled:$true -DrsAutomationLevel:FullyAutomated -Confirm:$false | Out-File -Append -LiteralPath $verboseLogFile
|
||||
Close-VCSAConnection
|
||||
}
|
||||
|
||||
|
||||
$EndTime = Get-Date
|
||||
$duration = [math]::Round((New-TimeSpan -Start $StartTime -End $EndTime).TotalMinutes,2)
|
||||
|
||||
Write-Log "Pod Deployment Completed in $($duration) minutes"
|
||||
40
Scripts/vCenterSnapshot.ps1
Normal file
40
Scripts/vCenterSnapshot.ps1
Normal file
@@ -0,0 +1,40 @@
|
||||
<#
|
||||
.NOTES
|
||||
Script name: vCenterSnapshot.ps1
|
||||
Created on: 20/09/2017
|
||||
Author: Lukas Winn, @lukaswinn
|
||||
Dependencies: Password is set to VMware123 in my test environment but this can be changed.
|
||||
|
||||
.DESCRIPTION
|
||||
Script to retrieve snapshot information for all VM's in a given vCenter
|
||||
|
||||
#>
|
||||
Write-Host "`nGet VM Snapshot Information!"
|
||||
Write-Host "Copyright 2017 Lukas Winn / @lukaswinn"
|
||||
Write-Host "Version 1.0" "`n"
|
||||
|
||||
$vCenter = Read-Host -prompt 'Enter FQDN / IP address of vCenter'
|
||||
|
||||
if ($vCenter) {
|
||||
$vcUser = Read-Host -prompt 'Username'
|
||||
|
||||
Write-Host 'vCenter:' $vCenter ''
|
||||
|
||||
# Connect to vCenter with $vCenter variable value
|
||||
Connect-VIServer -Server $vCenter -User $vcUser -Password VMware123
|
||||
|
||||
Write-Host "`nConnected to vCenter: " $vCenter
|
||||
Write-Host 'Retrieving snapshot information...'
|
||||
Write-Progress -Activity 'Working...'
|
||||
|
||||
# Get VM snapshot information and output in table format
|
||||
$getSnap = Get-VM | Get-Snapshot | sort SizeGB -descending | Select VM, Name, Created, @{Label="Size";Expression={"{0:N2} GB" -f ($_.SizeGB)}}, Id
|
||||
$getSnap | Format-Table | Out-Default
|
||||
|
||||
# Close connection to active vCenter
|
||||
Disconnect-VIServer $vCenter -Confirm:$false
|
||||
Write-Host 'Connection closed to' $vCenter
|
||||
}
|
||||
else {
|
||||
Write-Warning "Error: No data entered for vCenter!"
|
||||
}
|
||||
191
Scripts/vmCreationNotes.ps1
Normal file
191
Scripts/vmCreationNotes.ps1
Normal file
@@ -0,0 +1,191 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
VM_CreationNotes to replace the Notes on newly deployed virtual machines with
|
||||
information regarding there deployment, including date, time, user and method
|
||||
of deployment.
|
||||
|
||||
.DESCRIPTION
|
||||
VM_CreationNotes is run daily as a scheduled task requiring no interaction.
|
||||
The script will take in vCenter events for the latest 24 hour period filtering
|
||||
for vm creation, clone or vapp deployment and parse the data.
|
||||
Utilizes GET-VIEventsPlus by Luc Dekens for faster event gathering
|
||||
.NOTES
|
||||
File Name : VM_CreationNotes.ps1
|
||||
Author : KWH
|
||||
Version : 1.03
|
||||
License : GNU GPL 3.0 www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
.INPUTS
|
||||
No inputs required
|
||||
.OUTPUTS
|
||||
No Output is produced
|
||||
|
||||
.PARAMETER config
|
||||
No Parameters
|
||||
|
||||
.PARAMETER Outputpath
|
||||
No Parameters
|
||||
|
||||
.PARAMETER job
|
||||
No Parameters
|
||||
.CHANGE LOG
|
||||
#20170301 KWH - Removed canned VM Initialize script in favor of get-module
|
||||
#20170303 KWH - Change VIEvent Call to date range rather than maxSamples
|
||||
# KWH - Optimized event call where to array match
|
||||
# KWH - Updated Synopsis and Description
|
||||
# KWH - Changed vcenter list to text file input
|
||||
# KWH - Added Register Events to list
|
||||
# KWH - Included Get-VIEventPlus by LucD
|
||||
#20170321 KWH - Added event $VIEvent array declaration/reset and $VM reset on loops
|
||||
# KWH - Converted returned events to Local Time
|
||||
#>
|
||||
|
||||
<#
|
||||
.SYNOPSIS Function GET-VIEventPlus Returns vSphere events
|
||||
.DESCRIPTION The function will return vSphere events. With
|
||||
the available parameters, the execution time can be
|
||||
improved, compered to the original Get-VIEvent cmdlet.
|
||||
.NOTES Author: Luc Dekens
|
||||
.PARAMETER Entity
|
||||
When specified the function returns events for the
|
||||
specific vSphere entity. By default events for all
|
||||
vSphere entities are returned.
|
||||
.PARAMETER EventType
|
||||
This parameter limits the returned events to those
|
||||
specified on this parameter.
|
||||
.PARAMETER Start
|
||||
The start date of the events to retrieve
|
||||
.PARAMETER Finish
|
||||
The end date of the events to retrieve.
|
||||
.PARAMETER Recurse
|
||||
A switch indicating if the events for the children of
|
||||
the Entity will also be returned
|
||||
.PARAMETER User
|
||||
The list of usernames for which events will be returned
|
||||
.PARAMETER System
|
||||
A switch that allows the selection of all system events.
|
||||
.PARAMETER ScheduledTask
|
||||
The name of a scheduled task for which the events
|
||||
will be returned
|
||||
.PARAMETER FullMessage
|
||||
A switch indicating if the full message shall be compiled.
|
||||
This switch can improve the execution speed if the full
|
||||
message is not needed.
|
||||
.EXAMPLE
|
||||
PS> Get-VIEventPlus -Entity $vm
|
||||
.EXAMPLE
|
||||
PS> Get-VIEventPlus -Entity $cluster -Recurse:$true
|
||||
#>
|
||||
function Get-VIEventPlus {
|
||||
|
||||
param(
|
||||
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]]$Entity,
|
||||
[string[]]$EventType,
|
||||
[DateTime]$Start,
|
||||
[DateTime]$Finish = (Get-Date),
|
||||
[switch]$Recurse,
|
||||
[string[]]$User,
|
||||
[Switch]$System,
|
||||
[string]$ScheduledTask,
|
||||
[switch]$FullMessage = $false
|
||||
)
|
||||
|
||||
process {
|
||||
$eventnumber = 100
|
||||
$events = @()
|
||||
$eventMgr = Get-View EventManager
|
||||
$eventFilter = New-Object VMware.Vim.EventFilterSpec
|
||||
$eventFilter.disableFullMessage = ! $FullMessage
|
||||
$eventFilter.entity = New-Object VMware.Vim.EventFilterSpecByEntity
|
||||
$eventFilter.entity.recursion = &{if($Recurse){"all"}else{"self"}}
|
||||
$eventFilter.eventTypeId = $EventType
|
||||
if($Start -or $Finish){
|
||||
$eventFilter.time = New-Object VMware.Vim.EventFilterSpecByTime
|
||||
if($Start){
|
||||
$eventFilter.time.beginTime = $Start
|
||||
}
|
||||
if($Finish){
|
||||
$eventFilter.time.endTime = $Finish
|
||||
}
|
||||
}
|
||||
if($User -or $System){
|
||||
$eventFilter.UserName = New-Object VMware.Vim.EventFilterSpecByUsername
|
||||
if($User){
|
||||
$eventFilter.UserName.userList = $User
|
||||
}
|
||||
if($System){
|
||||
$eventFilter.UserName.systemUser = $System
|
||||
}
|
||||
}
|
||||
if($ScheduledTask){
|
||||
$si = Get-View ServiceInstance
|
||||
$schTskMgr = Get-View $si.Content.ScheduledTaskManager
|
||||
$eventFilter.ScheduledTask = Get-View $schTskMgr.ScheduledTask |
|
||||
where {$_.Info.Name -match $ScheduledTask} |
|
||||
Select -First 1 |
|
||||
Select -ExpandProperty MoRef
|
||||
}
|
||||
if(!$Entity){
|
||||
$Entity = @(Get-Folder -Name Datacenters)
|
||||
}
|
||||
$entity | %{
|
||||
$eventFilter.entity.entity = $_.ExtensionData.MoRef
|
||||
$eventCollector = Get-View ($eventMgr.CreateCollectorForEvents($eventFilter))
|
||||
$eventsBuffer = $eventCollector.ReadNextEvents($eventnumber)
|
||||
while($eventsBuffer){
|
||||
$events += $eventsBuffer
|
||||
$eventsBuffer = $eventCollector.ReadNextEvents($eventnumber)
|
||||
}
|
||||
$eventCollector.DestroyCollector()
|
||||
}
|
||||
$events
|
||||
}
|
||||
}
|
||||
|
||||
#Run parameters - Change below if username or vcenter list source changes
|
||||
$dayBtwnRuns = 1
|
||||
$AdminName = "username"
|
||||
$credfile = "c:\Scripts\common\credentials\runtime-cred.txt"
|
||||
$vcfile = "c:\Scripts\Common\inputlists\vcenterlist.txt"
|
||||
|
||||
$vmCreationTypes = @() #Remark out any event types not desired below
|
||||
$vmCreationTypes += "VmCreatedEvent"
|
||||
$vmCreationTypes += "VmBeingClonedEvent"
|
||||
$vmCreationTypes += "VmBeingDeployedEvent"
|
||||
$vmCreationTypes += "VmRegisteredEvent"
|
||||
$newline = "`r`n"
|
||||
|
||||
#Convert Password and username to credential object
|
||||
$password = Get-Content $CredFile | ConvertTo-SecureString
|
||||
$Cred = New-Object -Typename System.Management.Automation.PSCredential -argumentlist $AdminName,$password
|
||||
|
||||
#Load vCenter List
|
||||
$vCenterServers = Get-Content $vcfile
|
||||
|
||||
If ($daysBtwnRuns -gt 0) {$daysBtwnRuns = -$daysBtwnRuns}
|
||||
$Today = Get-Date
|
||||
$StartDate = ($Today).AddDays($dayBtwnRuns)
|
||||
|
||||
ForEach ($vcenter in $vCenterServers){
|
||||
Connect-VIServer $vcenter -Credential $Cred -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
|
||||
$TargetVM = $null
|
||||
$VIEvent = @()
|
||||
$Today = Get-Date
|
||||
$StartDate = ($Today).AddDays($dayBtwnRuns)
|
||||
$VIEvent = Get-VIEventPlus -Start $StartDate -Finish $Today -EventType $vmCreationTypes
|
||||
|
||||
$VIEvent|%{
|
||||
$NewNote = ""
|
||||
$VM = $null
|
||||
$VM = Get-View -Id $_.VM.Vm -Server $vcenter -Property Name,Config
|
||||
|
||||
If ($VM){
|
||||
$NewNote = $VM.Config.GuestFullName+$newline
|
||||
$NewNote += "Deployed: "+$_.CreatedTime.ToLocaltime().DateTime+$newline
|
||||
$NewNote += "Deployed by "+$_.UserName+$newline
|
||||
$NewNote += $_.FullFormattedMessage
|
||||
Set-VM -VM $VM.Name -Notes $NewNote -Confirm:$false
|
||||
}
|
||||
}
|
||||
|
||||
Disconnect-VIServer -Confirm:$false}
|
||||
181
get-peakvms.ps1
Normal file
181
get-peakvms.ps1
Normal file
@@ -0,0 +1,181 @@
|
||||
<#
|
||||
Script name: get-peakvms.ps1
|
||||
Created on: 09/06/2016
|
||||
Author: Scott White, @soggychipsnz, https://github.com/LatexGolem
|
||||
Description: This script will interrogate vcenter to find the peak users of network or storage usage.
|
||||
This was quite handy for me to quickly identify the source of issues such as an inbound DDOS, outbound DOS or a server pummiling storage due to swapping/etc.
|
||||
I would suggest that when you first run the script you run it in host mode, which will identify where the hotspot is. Then once the hot host has been identified, run it in VM mode.
|
||||
For each statistic the number of operations/packets is collectioned along ith the throughput split into reads or writes/sent or recieved - giving you peak and 95th percentiles
|
||||
Really need to get around to tidying this up :)
|
||||
Results will be outputted to OGV and into a timestamped CSV in the current working directory
|
||||
Dependencies: Nothing special. Assumes vsphere logging has 20 second samples for the last 60 minutes and 5 minute samples in the past 24 hours.
|
||||
#>
|
||||
|
||||
$clusterfilter = "*"
|
||||
|
||||
#Gather basic stuff
|
||||
do{
|
||||
$modes = @("host","vm")
|
||||
$mode = $modes | OGV -PassThru -Title "Select mode of operation"
|
||||
$stats = @("Disk Throughput","Network Throughput")
|
||||
$stat = $stats | OGV -PassThru -Title "Select type of Stat to check"
|
||||
if (($mode.count -eq 2) -or ($stat.count -eq 2) ){write-host "Please select only a single mode of operation and statistic type."}
|
||||
}while ($mode.count -eq 2 -and $stat.count -eq 2)
|
||||
|
||||
$hour = 1..24
|
||||
$hour = $hour | OGV -PassThru -Title "Select number of hours to go back in time"
|
||||
|
||||
|
||||
#Helper Stuff
|
||||
$start = (Get-Date).addHours(-$hour)
|
||||
$finish = (Get-Date)
|
||||
$duration = $finish - $start | %{[int]$_.TotalSeconds}
|
||||
#If Start is within the last hour, use 20 second sampling otherwise 5 minute avg
|
||||
if (((Get-Date) - $start ).TotalSeconds -gt 3700) {$interval=300} else {$interval=20}
|
||||
|
||||
function getstats($starttime,$endtime,$sample,$stat,$entity){
|
||||
(Get-Stat -Entity $entity -Stat $stat -IntervalSecs $sample -Start $starttime -Finish $endtime).value | measure -Sum | %{$_.sum}
|
||||
}
|
||||
|
||||
|
||||
if ($mode -eq "host"){
|
||||
$clusters = get-cluster $clusterfilter
|
||||
$vmhosts = $clusters | OGV -PassThru -Title "Select Cluster(s) to target, to get all member Hosts"| get-vmhost
|
||||
|
||||
if ($stat -eq "Network Throughput"){
|
||||
$master = @()
|
||||
$vmhosts | %{
|
||||
$metric ="net.packetsRx.summation"
|
||||
$pktrx = getstats $start $finish $interval $metric $_
|
||||
|
||||
$metric ="net.packetsTx.summation"
|
||||
$pkttx = getstats $start $finish $interval $metric $_
|
||||
|
||||
$metric ="net.bytesRx.average"
|
||||
$bytesrx = getstats $start $finish $interval $metric $_
|
||||
|
||||
$metric ="net.bytesTx.average"
|
||||
$bytestx = getstats $start $finish $interval $metric $_
|
||||
|
||||
$row = "" | select name,pktrx,pkttx,bytesrx,bytestx
|
||||
$row.name = $_.name
|
||||
$row.pktrx = $pktrx
|
||||
$row.pkttx = $pkttx
|
||||
$row.bytesrx = $bytesrx
|
||||
$row.bytestx = $bytestx
|
||||
$master += $row
|
||||
}
|
||||
$master | ogv #sort -Property name | Format-Table
|
||||
}
|
||||
if ($stat -eq "Disk Throughput"){
|
||||
#Target the datastore, just one.
|
||||
$datastore = Get-Datastore -vmhost $vmhost[0]| OGV -PassThru -Title "Select target datastore"
|
||||
#Yes this is fugly.
|
||||
$instance = ($datastore | Get-View).Info.Url.Split("/") | select -last 2 | select -First 1
|
||||
|
||||
$master = @()
|
||||
$vmhosts | %{
|
||||
$metric ="datastore.datastoreReadIops.latest"
|
||||
$rop = (Get-Stat -Entity $_ -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish | ?{$_.Instance -match $instance}).value | measure -Sum | %{$_.sum}
|
||||
|
||||
$metric ="datastore.datastoreWriteIops.latest"
|
||||
$wrop = (Get-Stat -Entity $_ -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish | ?{$_.Instance -match $instance}).value | measure -Sum | %{$_.sum}
|
||||
|
||||
$metric ="datastore.datastoreReadBytes.latest"
|
||||
$rbytes = (Get-Stat -Entity $_ -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish | ?{$_.Instance -match $instance}).value | measure -Sum | %{$_.sum}
|
||||
|
||||
$metric ="datastore.datastoreWriteBytes.latest"
|
||||
$wrbytes = (Get-Stat -Entity $_ -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish | ?{$_.Instance -match $instance}).value | measure -Sum | %{$_.sum}
|
||||
|
||||
$row = "" | select name,rop,wrop,rbytes,wrbytes
|
||||
$row.name = $_.name
|
||||
$row.rop = $rop
|
||||
$row.wrop = $wrop
|
||||
$row.rbytes = $rbytes
|
||||
$row.wrbytes = $wrbytes
|
||||
$master += $row
|
||||
}
|
||||
$master | ogv
|
||||
}
|
||||
|
||||
}if ($mode -eq "vm"){
|
||||
Write-Host "Do note doing things on a vmbasis take quite some time (please isolate on a host basis first)"
|
||||
#Currently only works on a cluster basis
|
||||
$clusters = get-cluster $clusterfilter
|
||||
$vms = $clusters |get-vmhost | OGV -PassThru -Title "Select Hosts(s) to target, to get all member VMs"| get-vm | ?{$_.powerstate -match "poweredOn"}
|
||||
|
||||
if ($stat -eq "Network Throughput"){
|
||||
$master = new-object system.collections.arraylist
|
||||
$vms | %{
|
||||
$metric ="net.packetsRx.summation"
|
||||
$pktrx = getstats $start $finish $interval $metric $_
|
||||
|
||||
$metric ="net.packetsTx.summation"
|
||||
$pkttx = getstats $start $finish $interval $metric $_
|
||||
|
||||
$metric ="net.bytesRx.average"
|
||||
$bytesrx = getstats $start $finish $interval $metric $_
|
||||
|
||||
$metric ="net.bytesTx.average"
|
||||
$bytestx = getstats $start $finish $interval $metric $_
|
||||
|
||||
$row = "" | select name,pktrx,pkttx,bytesrx,bytestx
|
||||
$row.name = $_.name
|
||||
$row.pktrx = $pktrx
|
||||
$row.pkttx = $pkttx
|
||||
$row.bytesrx = $bytesrx
|
||||
$row.bytestx = $bytestx
|
||||
$master += $row
|
||||
}
|
||||
$master | ogv
|
||||
}
|
||||
if ($stat -eq "Disk Throughput"){
|
||||
$master = new-object system.collections.arraylist
|
||||
|
||||
$vms = $vms |?{$_.PowerState -eq "PoweredOn"}
|
||||
foreach ($vm in $vms){
|
||||
$row = ""| select name,iops,riops,riops95,rpeak,wiops,wiops95,wpeak,throughputGBpduration,wMBps,rMBps,datastore,used,prov,iops95
|
||||
|
||||
$metric = "datastore.numberReadAveraged.average"
|
||||
$rawdata = Get-Stat -Entity $vm -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish
|
||||
$tmp = $rawdata.value | measure -average -Maximum
|
||||
$row.riops = [int] $tmp.average
|
||||
$row.rpeak = [int] $tmp.Maximum
|
||||
$row.riops95 = ($rawdata.value | sort)[[math]::Round(($rawdata.count-1) * .95)]
|
||||
|
||||
$metric = "datastore.numberwriteaveraged.average"
|
||||
$rawdata = Get-Stat -Entity $vm -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish
|
||||
$tmp = $rawdata.value | measure -average -Maximum
|
||||
$row.wiops = [int] $tmp.average
|
||||
$row.wpeak = [int] $tmp.Maximum
|
||||
$row.wiops95 = ($rawdata.value | sort)[[math]::Round(($rawdata.count-1) * .95)]
|
||||
|
||||
$row.iops = ($row.wiops + $row.riops)
|
||||
|
||||
$metric = "datastore.write.average"
|
||||
$rawdatawr = Get-Stat -Entity $vm -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish
|
||||
|
||||
$metric = "datastore.read.average"
|
||||
$rawdatar = Get-Stat -Entity $vm -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish
|
||||
$reads = $rawdatar.value | measure -Sum | %{$_.sum}| %{$_ /($duration/$interval)/1024/1024}
|
||||
$writes = $rawdatawr.value | measure -Sum | %{$_.sum}| %{$_ /($duration/$interval)/1024/1024}
|
||||
$total = $reads * $duration + $writes * $duration
|
||||
|
||||
|
||||
$row.name = $vm.name
|
||||
$row.throughputGBpduration = [decimal]::round($total,2)
|
||||
$row.wMBps = [decimal]::round($writes*1024,2)
|
||||
$row.rMBps = [decimal]::round($reads*1024,2)
|
||||
$row.datastore = ($vm.DatastoreIdList[0] | Get-VIObjectByVIView).name #(($vm.DatastoreIdList| select -First 1 | Get-VIObjectByVIView).Name)
|
||||
|
||||
$row.used = [System.Math]::Round(($vm.UsedSpaceGB))
|
||||
$row.prov = [System.Math]::Round(($vm.ProvisionedSpaceGB))
|
||||
$row.iops95 = $row.riops95 + $row.wiops95
|
||||
|
||||
$master += $row
|
||||
}
|
||||
$master | ogv
|
||||
}
|
||||
|
||||
}
|
||||
$master | Export-Csv "$mode$stat-$(get-date -Format HHmm).csv"
|
||||
53
get-ping.ps1
Normal file
53
get-ping.ps1
Normal file
@@ -0,0 +1,53 @@
|
||||
Function Get-PingStatus
|
||||
{
|
||||
param(
|
||||
[Parameter(ValueFromPipeline=$true)]
|
||||
[string]$device,
|
||||
|
||||
[validateSet("Online","Offline","ObjectTable")]
|
||||
[String]$getObject
|
||||
)
|
||||
|
||||
begin{
|
||||
$hash = @()
|
||||
|
||||
}
|
||||
process{
|
||||
|
||||
$device| foreach {
|
||||
if (Test-Connection $_ -Count 1 -Quiet) {
|
||||
|
||||
if(-not($GetObject)){write-host -ForegroundColor green "Online: $_ "}
|
||||
|
||||
$Hash = $Hash += @{Online="$_"}
|
||||
}else{
|
||||
|
||||
if(-not($GetObject)){write-host -ForegroundColor Red "Offline: $_ "}
|
||||
|
||||
$Hash = $Hash += @{Offline="$_"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end {
|
||||
if($GetObject) {
|
||||
|
||||
$Global:Objects = $Hash | foreach { [PSCustomObject]@{
|
||||
|
||||
DeviceName = $_.Values| foreach { "$_" }
|
||||
Online = $_.Keys| where {$_ -eq "Online"}
|
||||
offline = $_.Keys| where {$_ -eq "Offline"}
|
||||
}
|
||||
}
|
||||
|
||||
Switch -Exact ($GetObject)
|
||||
{
|
||||
|
||||
'Online' { $Global:Objects| where 'online'| select -ExpandProperty DeviceName }
|
||||
'Offline' { $Global:Objects| where 'offline'| select -ExpandProperty DeviceName }
|
||||
'ObjectTable' { return $Global:Objects }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user