Merge remote-tracking branch 'vmware/master'

This commit is contained in:
Kyle Ruddy
2017-10-14 15:33:26 -04:00
32 changed files with 3064 additions and 189 deletions

View File

@@ -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

View File

@@ -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 = '*'

View File

@@ -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"
}
}

View File

@@ -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
}
}

View 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
}
}

View File

@@ -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

View 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.
![Get-VcdTenantReport](/media/Get-VcdTenantReport.png)
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.

View 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 = ''
}

View File

@@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -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')

View File

@@ -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
}
}

View 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'
}
}
}

View 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
}

View File

@@ -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

View File

@@ -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.

View 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
View 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"
}
}

View 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)

View 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)

View 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)}
}
}

View 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
)
}

View 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

View 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

View 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"
}
}

View 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"

View 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
View 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
View 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
View 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 }
}
}
}
}