Update March 22, 2017: Added ” | Where-Object { $_.Type -eq “Folder”}” to primaryfolder.extensiondata.childentity (and the subfolder one) to filter the childentities to just folders.
SRM added a feature in which you map folder inventories across vCenters by assuming the folders had the same name and same structure. I already had one method for replicating folder structures (which I am replacing below), and was hoping to just count on the built-in matching feature of the UI. Unfortunately I found the UI to be a bit slow and new folders were not automatically mapped.
Fortunately, in 5.8 the API added the addfoldermapping method that allows you to map folders. Please note my previous post about solution users and needing to use wsdl/soap instead of powercli.
There are four functions below:
login-srmserverapi – as seen a previous post, this provides API access to SRM
copy-folderstructuretovc – replicates folder structure accurately. This function will handle multiple directories of the same name vs some other methods that assume distinct names
add-foldermapping – wrapper of call to SRM API
add-recursivefoldermapping – recurses through folder structure in primary site and looks for folder with same name at same place in tree on secondary site (handles folders with same name at different part of tree) then calls add-foldermapping as needed
I give an example below for using all of the functions below to create structure and do forward and reverse mappings.
Script add-srmfoldermappings.ps1 can also be found at Github.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
if ( !(Get-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) ) { if (Test-Path -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\VMware, Inc.\VMware vSphere PowerCLI' ) { $Regkey = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\VMware, Inc.\VMware vSphere PowerCLI' } else { $Regkey = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\VMware, Inc.\VMware vSphere PowerCLI' } . (join-path -path (Get-ItemProperty $Regkey).InstallPath -childpath 'Scripts\Initialize-PowerCLIEnvironment.ps1') } if ( !(Get-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) ) { Write-Host "VMware modules not loaded/unable to load" Exit 99 } function add-foldermapping () { param( #primary and secondary folder are folder objects $PrimaryFolder, $SecondaryFolder, [switch]$async ) #should already be connected to primary and secondary vcenter instances #Primary Folder MoRef Value $PrimaryFolderValue = (($PrimaryFolder).ExtensionData.MoRef).Value $PrimaryFolderType = (($PrimaryFolder).ExtensionData.MoRef).Type $SecondaryFolderValue = (($SecondaryFolder).ExtensionData.MoRef).Value $SecondaryFolderType = (($secondaryfolder).ExtensionData.MoRef).Type #Create new object for adding primary folder value $PrimaryFolderMoRef = New-Object SRM01.ManagedObjectReference #Add primary folder moref to object $PrimaryFolderMoRef.Type = $PrimaryFolderType $PrimaryFolderMoRef.Value = $PrimaryFolderValue # #Create new object for adding secondary folder value $SecondaryFolderMoRef = New-Object SRM01.ManagedObjectReference #Add primary folder moref to object $SecondaryFolderMoRef.Type = $SecondaryFolderType $SecondaryFolderMoRef.Value = $SecondaryFolderValue Try { if($async){ $global:srm01.AddFolderMappingAsync($global:mapping,$PrimaryFolderMoRef,$SecondaryFolderMoRef) # created folder mapping } else{ $global:srm01.AddFolderMapping($global:mapping,$PrimaryFolderMoRef,$SecondaryFolderMoRef) # created folder mapping } } Catch [Exception] { Write-Host -BackgroundColor Red "Issue mapping" $global:primaryVC ":" (get-VIObjectByVIView -moref $PrimaryFolderMoRef).name "to" $global:secondaryvc ":" (Get-VIObjectByVIView -MORef $SecondaryFolderMoRef).name Write-Host -BackgroundColor Red $_.Exception.Message Return } write-Host "vCenter " $global:PrimaryVC " " $PrimaryFolder.name "mapped to vCenter " $global:SecondaryVC " "$SecondaryFOlder.name } function login-srmserverapi () { param( $srmServerAddr, $user, $password, $remoteUser, $remotePassword ) [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} $global:web01 = New-WebServiceProxy("https://" + $srmServerAddr + ":9086/srm.wsdl") -Namespace SRM01 $global:srm01 = New-Object SRM01.Srmbinding $global:srm01.url = "https://" + $srmServerAddr + ":9086/vcdr/extapi/sdk" $global:srm01.CookieContainer = New-Object System.Net.CookieContainer $global:mof01 = New-Object SRM01.ManagedObjectReference $global:mof01.type = "SrmServiceInstance" $global:mof01.value = $global:mof01.type $global:srmApi01 = ($global:srm01.RetrieveContent($global:mof01)).srmApi $global:protection01 = ($global:srm01.RetrieveContent($global:mof01)).protection $global:recovery01 = ($global:srm01.RetrieveContent($global:mof01)).recovery $global:mapping = ($global:srm01.RetrieveContent($global:mof01)).InventoryMapping Try { # $global:srm01.SrmLoginSites($global:mof01, $user, $password, $remoteuser, $remotepassword,'') } Catch [Exception] { Write-Host -BackgroundColor Red "Unable to connect to SRM $srmServerAddr" Write-Host -BackgroundColor Red $_.Exception.Message Return } Write-Host -ForegroundColor Yellow "Connected to SRM $srmServerAddr" } function add-recursivefoldermapping () { #recursive function to decent through two sets of folders, match names, and add foldermappings param ( $primaryfolder, $secondaryFolder ) $primaryFolderArr = $primaryFolder.extensiondata.childentity | Where-Object { $_.Type -eq "Folder"} $secondaryFolderArr = $secondaryFolder.extensiondata.childentity | Where-Object { $_.Type -eq "Folder"} #if there are children, map children if($primaryFolderArr){ foreach ($primaryFolderMoRef in $primaryFolderArr){ try { $primarySubFOlder = get-viobjectbyviview -moref $primaryFolderMoRef -Server $global:primaryVC -ErrorAction stop } catch [Exception] { Write-Host -BackgroundColor Red "Error with" $primaryFolderMoRef "in" $global:primaryvc "under folder" $PrimaryFolder.name Write-Host -BackgroundColor Red $_.Exception.Message break } foreach ($secondaryFolderMoRef in $secondaryFolderArr){ try { $secondarySubFOlder = get-viobjectbyviview -moref $secondaryFolderMoRef -Server $global:secondaryVC -ErrorAction stop } catch [Exception] { Write-Host -BackgroundColor Red "Error with" $secondaryFolderMoRef "in" $global:secondaryvc "under folder" $secondaryFolder.name break } if($primarySubFOlder.name -eq $secondarySubFOlder.name){ #instead of just mapping the folders, add-recursivefoldermapping -primaryfolder $primarySubFOlder -secondaryfolder $secondarySubFOlder } } } } else{ #map current folders add-foldermapping -primaryfolder $primaryfolder -secondaryfolder $secondaryFolder -async:$true } } ## #need to add the export/import functionality followed by running addthat mapping in order to create missing folders function copy-folderstructuretovc () { param( $primaryFolder, $SecondaryFolder ) $primaryFolderArr = $primaryFolder.extensiondata.childentity | Where-Object { $_.Type -eq "Folder"} $secondaryFolderArr = $secondaryFolder.extensiondata.childentity | Where-Object { $_.Type -eq "Folder"} #if there are children, map children if($primaryFolderArr){ foreach ($primaryFolderMoRef in $primaryFolderArr){ try { $primarySubFOlder = $null $primarySubFOlder = get-viobjectbyviview -moref $primaryFolderMoRef -Server $global:primaryVC -ErrorAction stop } catch [Exception] { Write-Host -BackgroundColor Red "Error with" $primaryFolderMoRef "in" $global:primaryvc "under folder" $PrimaryFolder.name Write-Host -BackgroundColor Red $_.Exception.Message break } #check if folder already exists try { $secondarySubFOlder = $null $secondarysubfolder=Get-Folder -Name $primarySubFOlder.name -Location ($SecondaryFolder) -NoRecursion -Server $global:secondaryvc -ErrorAction Stop } catch [Exception]{ #do noth ing } if($Secondarysubfolder){ Write-Host $primarySubFOlder.name "already exists in" $SecondaryFolder.name "in vCenter" $global:secondaryvc } else{ #create folder try { $secondarysubfolder=New-Folder -Name $primarySubFOlder.name -Location ($SecondaryFolder) -Server $global:secondaryvc -ErrorAction Stop Write-Host "Creating folder" $primarySubFOlder.name "in" $secondaryfolder } catch [Exception]{ Write-Host -BackgroundColor Red "Error creating" $primarysubFolder.name "in" $SecondaryFolder.name "in vCenter" $global:secondaryvc Write-Host -BackgroundColor Red $_.Exception.Message break } } #recurse copy-folderstructuretovc -primaryfolder $primarySubFOlder -secondaryfolder $secondarySubFOlder } } } ## ###main # $global:primaryVC = "primaryvc" $global:secondaryVC = "secondaryvc" $user = "username" #note single quotes seem to work best below $password = 'mypass' $primarySRMServerAddr="primarysrmserver" $secondarySRMServerAddr="secondarysrmserver" login-srmserverapi -srmserveraddr $primarySRMServerAddr -user $user -password $password -remoteuser $user -remotepassword $password Connect-VIServer $global:primaryvc Connect-VIServer $global:secondaryvc -notdefault $PrimaryFolder = (get-datacenter "myprimarydc" -Server $global:primaryvc).getvmfolder() $secondaryFolder = (Get-Datacenter "mydrdc" -Server $global:secondaryvc).getvmfolder() #replicate folder structure copy-folderstructuretovc -primaryfolder $PrimaryFolder -secondaryFolder $SecondaryFolder #add folder mappings primary -> secondary add-recursivefoldermapping -primaryfolder $PrimaryFolder -secondaryFolder $SecondaryFolder #login to secondary srm server login-srmserverapi -srmserveraddr $secondarySRMServerAddr -user $user -password $password -remoteuser $user -remotepassword $password #swap VC variables so that we can go reverse $global:primaryVC = "secondaryvc" $global:secondaryVC = "primaryvc" #add folder mapping secondary -> primary add-recursivefoldermapping -primaryfolder $SecondaryFolder -secondaryFolder $PrimaryFolder |
First of all, thanks for sharing! Exactly what I was needing. In the function copy-folderstructuretovc under create folder, I modified it to look like this:
#create folder
try
{
# Added if statement to avoid creating folders for each individual VM
if ($primarySubFOlder.id -like “Folder*”)
{
$secondarysubfolder = New-Folder -Name $primarySubFOlder.name -Location ($SecondaryFolder) -Server $global:secondaryvc -ErrorAction Stop
Write-Host “Creating folder” $primarySubFOlder.name “in” $secondaryfolder
}
}
Without that if statement, it was creating folders named after each VM. There may be a better way to handle that, but that is what I came up with. Let me know what you think. Thanks again for sharing!
Thanks for the feedback I’ll take a lot ok at your change
Ken,
I validated the issue, I was testing with an empty folder tree and didn’t realize that I would have non-folders in that array. I corrected these lines in the copy-folderstructuretovc and add-recursivefoldermapping functions:
$primaryFolderArr = $primaryFolder.extensiondata.childentity
$secondaryFolderArr = $secondaryFolder.extensiondata.childentity
to
$primaryFolderArr = $primaryFolder.extensiondata.childentity | Where-Object { $_.Type -eq “Folder”}
$secondaryFolderArr = $secondaryFolder.extensiondata.childentity | Where-Object { $_.Type -eq “Folder”}
This will restrict those arrays to just folders and will be more efficient then checking the type of the object later-on (saves some unnecessary recursion).