I am currently in the process of creating a script to automate batch creation of our VMs in various vCenters, and have reached the point where the VM Creation is doing exactly what I need, but it is still creating the VM in the root folder within the vCenter.
I started looking around for solutions on how to move the VM to the correct folder, or create the VM in the correct folder from the start, and came across the Get-FolderbyPath function created by LucD.
I started playing around with the function, and am struggling to get it to give me exactly what I need.
The biggest part of the issue is that our folder structure within each vCenter is a.) very similar to each other, and b.) each vCenter contains many of the same folder names
Our Folder layout is Datacenter/Company/Project/Environment, and so as a rough example we have folders called:
Paris/Company/Project/Environment
Ottawa/Company/Project/Environment
So if I am connected to only the Paris vCenter and run Get-FolderbyPath -Path "Paris/Company/Project/Lab" I get only one result and if I pipe that to a Get-VM I can verify that I only see the VMs I expect to be seeing.
But if I am connected to vCenter using -allLinked and run the same command I get multiple results, and I believe I have narrowed down what is happening, but I am not sure why.
When running the Get-folderbypath when connected to multiple vCenters the below is what is happening
If I am looking for "Paris/Company/Project/Lab" as a path, when the script runs through the Paris vCenter I only get one result (the expected result), but then the script runs through against the Ottawa vCenter, and if it does not have an exact "Company/Project/Lab" path it returns any folder that is named "Lab" no matter where its located, so the Folder results are as below:
IE.)
Paris/Company/Project/Lab
Ottawa/Company/Project2/Lab
Ottawa/Company/Project5/Lab
Ottawa/Company4/Project/Lab
The very strange part is that we also have folders such as below that are NOT being returned by the Get-Folderbypath function:
Paris/Company/Project2/Lab
Paris/Company2/Project/Lab
Paris/Company4/Project5/Lab
But if we do have an exact "Company/Project/Lab" Path in both vCenters the results are
Paris/Company/Project/Lab
Ottawa/Company/Project/Lab
and in this case the
Ottawa/Company/Project2/Lab
Ottawa/Company/Project5/Lab
Ottawa/Company4/Project/Lab
Paths are not shown.
So I am trying to figure out how to get the Get-folderbypath function to only give me Paris/Company/Project/Lab when connected to multiple vCenters or if that exact path doesn't exist, return 0 entries so I know it doesn't exist so I can instruct my script to create the new folder as required.
I just added the Server parameter to the function.
See it that can solve your issue.
Now you can do
and also
The updated function
<#
.SYNOPSIS Retrieve folders by giving a path
.DESCRIPTION The function will retrieve a folder by it's path.
The path can contain any type of leave (folder or datacenter).
.NOTES
Author: Luc Dekens
.PARAMETER
Path The path to the folder. This is a required parameter.
.PARAMETER
Separator The character that is used to separate the leaves in the path. The default is '/'
.EXAMPLE
PS> Get-FolderByPath -Path "Folder1/Datacenter/Folder2"
.EXAMPLE
PS> Get-FolderByPath -Path "Folder1>Folder2" -Separator '>'
#>
param(
[CmdletBinding()]
[parameter(Mandatory = $true)]
[System.String[]]${Path},
[char]${Separator} = '/',
[PSObject]$Server
)
process {
if ($Server) {
if ($Server -is [String]) {
$vcs = $global:defaultVIServers | Where-Object { $_.Name -eq $Server }
}
else {
$vcs = $Server
}
}
else {
if ((Get-PowerCLIConfiguration).DefaultVIServerMode -eq "Multiple") {
$vcs = $global:defaultVIServers
}
else {
$vcs = $global:defaultVIServers[0]
}
}
foreach ($vc in $vcs) {
$si = Get-View ServiceInstance -Server $vc
$rootName = (Get-View -Id $si.Content.RootFolder -Property Name).Name
foreach ($strPath in $Path) {
$root = Get-Folder -Name $rootName -Server $vc -ErrorAction SilentlyContinue
$strPath.Split($Separator) | ForEach-Object {
$root = Get-Inventory -Name $_ -Location $root -Server $vc -ErrorAction SilentlyContinue
if ((Get-Inventory -Location $root -NoRecursion | Select-Object -ExpandProperty Name) -contains "vm") {
$root = Get-Inventory -Name "vm" -Location $root -Server $vc -NoRecursion
}
}
$root | Where-Object { $_ -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.FolderImpl] } | ForEach-Object {
Get-Folder -Name $_.Name -Location $root.Parent -NoRecursion -Server $vc
}
}
}
}
}
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
I just added the Server parameter to the function.
See it that can solve your issue.
Now you can do
and also
The updated function
<#
.SYNOPSIS Retrieve folders by giving a path
.DESCRIPTION The function will retrieve a folder by it's path.
The path can contain any type of leave (folder or datacenter).
.NOTES
Author: Luc Dekens
.PARAMETER
Path The path to the folder. This is a required parameter.
.PARAMETER
Separator The character that is used to separate the leaves in the path. The default is '/'
.EXAMPLE
PS> Get-FolderByPath -Path "Folder1/Datacenter/Folder2"
.EXAMPLE
PS> Get-FolderByPath -Path "Folder1>Folder2" -Separator '>'
#>
param(
[CmdletBinding()]
[parameter(Mandatory = $true)]
[System.String[]]${Path},
[char]${Separator} = '/',
[PSObject]$Server
)
process {
if ($Server) {
if ($Server -is [String]) {
$vcs = $global:defaultVIServers | Where-Object { $_.Name -eq $Server }
}
else {
$vcs = $Server
}
}
else {
if ((Get-PowerCLIConfiguration).DefaultVIServerMode -eq "Multiple") {
$vcs = $global:defaultVIServers
}
else {
$vcs = $global:defaultVIServers[0]
}
}
foreach ($vc in $vcs) {
$si = Get-View ServiceInstance -Server $vc
$rootName = (Get-View -Id $si.Content.RootFolder -Property Name).Name
foreach ($strPath in $Path) {
$root = Get-Folder -Name $rootName -Server $vc -ErrorAction SilentlyContinue
$strPath.Split($Separator) | ForEach-Object {
$root = Get-Inventory -Name $_ -Location $root -Server $vc -ErrorAction SilentlyContinue
if ((Get-Inventory -Location $root -NoRecursion | Select-Object -ExpandProperty Name) -contains "vm") {
$root = Get-Inventory -Name "vm" -Location $root -Server $vc -NoRecursion
}
}
$root | Where-Object { $_ -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.FolderImpl] } | ForEach-Object {
Get-Folder -Name $_.Name -Location $root.Parent -NoRecursion -Server $vc
}
}
}
}
}
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Luc,
You never fail to amaze me. This is exactly what was required as I realized part of the flaw in my logic was that I was looking for the vCenter name as the root of the path, when it is actually the hidden "Datacenters" folder and so there isn't really a way to define which vcenter root it is just by path. This will work for my purposes as I will be able to inject the logic to determine from the VM Tags what vCenter should be used and use that for the $server variable.