VMware Cloud Community
jvignacioproj
Contributor
Contributor

PowerCLI scipt question

Hi,

I've got this script (from vc1 or Site A) to help copy a template, convert to VM, migrate VM to vCenter2 in Site B and convert VM to template  but I now need to do the following:

1. Instead of doing each VM one by one, I want to do all of them at the same time in parralel so it doesnt take so long.

2. Instead of have two separate scripts for linux and windows and 2 different template csv's, have 1 script and manage all but have some smarts to recognise its a linux template or windows template (perhaps in the CSV have template name and OS name). Also the linux and windows templates need to land on different ESXi and datastores in vc2.

thanks for your help.

 

#Variables

$vc1 = 'vc1'

$vc2 = 'vc2'

$credentials = Get-Credential

  

#Connect to vCenters

Connect-VIServer -Server $vc1 -Credential $credentials

Connect-VIServer -Server $vc2 -Credential $credentials

#Import CSV file with template names

$currentTemplates = Import-csv templates.csv

  

foreach($template in $currentTemplates){

 

#Check if all templates exist in vCenter 2. If found, delete template permanently (inventory and files). If not found (error action), go to Catch.

Try{

$checkTemplates = Get-Template -Name $($template.template) -Server $vc2 -ErrorAction Stop

Remove-Template -Template $checkTemplates -Server $vc2 -DeletePermanently -Confirm:$false

Write-Output "Removed template $($template.template) on vCenter $vc2"

}

Catch{

Write-Output "Template $($template.template) not found on vCenter $vc2"

}

#Clone existing templates in vCenter1 to vCenter1 in the same folder with a "-clone" amended to the name.

$newTemplate = "$($template.template)-clone"

 

New-Template -Template $($template.template) -Name $newTemplate -Confirm:$false

Write-Output "Cloned template $($template.template) to $newTemplate on vCenter $vc1"

# Get destination host and datastore

 

$esx2 = Get-VMHost -Name host2 -Server $vc2

$ds2 = Get-Datastore -Name ds2 -Server $vc2

 

Write-Output "Converting template $newTemplate to a VM on vCenter $vc1....."

 

Set-Template -Template $newTemplate -ToVM -Confirm:$false -Server $vc1

#Remove the NIC from VM and migrate to vCenter 2

Write-Output "Migrating VM $newTemplate to vCenter $vc2 on host $esx2 and datastore $ds2..."

$nic = Get-NetworkAdapter -VM $newTemplate

Remove-NetworkAdapter -NetworkAdapter $nic -Confirm:$false

Move-VM -VM $newTemplate -Destination $esx2 -Datastore $ds2 

New-NetworkAdapter -VM $newTemplate -NetworkName "PortGroup Name" -StartConnected -Type Vmxnet3

  

Write-Output "Renaming VM $newTemplate to $($template.template) on vCenter $vc2...."

$newVM = Set-VM -VM $newTemplate -Name $($template.template) -Confirm:$false -Server $vc2

Move-VM -VM $newVM -destination "myfolder" -Server$vc2

Write-Output "Converting VM $newVM to a template on vCenter $vc2....."

Set-VM -VM $newVM -ToTemplate -Confirm:$false -Server $vc2

}

  

Disconnect-VIServer -Server $vc1 -Confirm:$false

Disconnect-VIServer -Server $vc2 -Confirm:$false

Reply
0 Kudos
30 Replies
LucD
Leadership
Leadership

Does this mean my reply in Re: Mware -clone to template (from template) powercli​ was incorrect (since you didn't mark it with a Correct Answer)?
And since you are not using the InventoryLocation parameter, I assume you are still on the older PowerCLI version?

Is it not possible to add the destination ESXi and datastores in the CSV?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
jvignacioproj
Contributor
Contributor

Hi LucD,

I was trying to reply on that post but it wouldnt let me.

Its not incorrect but I needed a work around in the interm. Yes still using old version. 

Yes its possible to add in the CSV but how would that look like in the script? 

Reply
0 Kudos
LucD
Leadership
Leadership

When you add two columns in the CSV (VMHost and Datastore), you could do something like this

#Variables

$vc1 = 'vc1'

$vc2 = 'vc2'

$credentials = Get-Credential

 

#Connect to vCenters

Connect-VIServer -Server $vc1 -Credential $credentials

Connect-VIServer -Server $vc2 -Credential $credentials

#Import CSV file with template names

$currentTemplates = Import-csv templates.csv

 

foreach($template in $currentTemplates){

    

    #Check if all templates exist in vCenter 2. If found, delete template permanently (inventory and files). If not found (error action), go to Catch.

    Try{

        $checkTemplates = Get-Template -Name $($template.template) -Server $vc2 -ErrorAction Stop

        Remove-Template -Template $checkTemplates -Server $vc2 -DeletePermanently -Confirm:$false

        Write-Output "Removed template $($template.template) on vCenter $vc2"

    }

    Catch{

        Write-Output "Template $($template.template) not found on vCenter $vc2"

    }

    #Clone existing templates in vCenter1 to vCenter1 in the same folder with a "-clone" amended to the name.

    $newTemplate = "$($template.template)-clone"

    

    New-Template -Template $($template.template) -Name $newTemplate -Confirm:$false

    Write-Output "Cloned template $($template.template) to $newTemplate on vCenter $vc1"

    # Get destination host and datastore

    

    $esx2 = Get-VMHost -Name $template.VMHost -Server $vc2

    $ds2 = Get-Datastore -Name $template.Datastore -Server $vc2

    

    Write-Output "Converting template $newTemplate to a VM on vCenter $vc1....."

    

    Set-Template -Template $newTemplate -ToVM -Confirm:$false -Server $vc1

    #Remove the NIC from VM and migrate to vCenter 2

    Write-Output "Migrating VM $newTemplate to vCenter $vc2 on host $esx2 and datastore $ds2..."

    $nic = Get-NetworkAdapter -VM $newTemplate

    Remove-NetworkAdapter -NetworkAdapter $nic -Confirm:$false

    Move-VM -VM $newTemplate -Destination $esx2 -Datastore $ds2

    New-NetworkAdapter -VM $newTemplate -NetworkName "PortGroup Name" -StartConnected -Type Vmxnet3

     

    Write-Output "Renaming VM $newTemplate to $($template.template) on vCenter $vc2...."

    $newVM = Set-VM -VM $newTemplate -Name $($template.template) -Confirm:$false -Server $vc2

    Move-VM -VM $newVM -destination "myfolder" -Server$vc2

    Write-Output "Converting VM $newVM to a template on vCenter $vc2....."

    Set-VM -VM $newVM -ToTemplate -Confirm:$false -Server $vc2

}

 

Disconnect-VIServer -Server $vc1 -Confirm:$false

Disconnect-VIServer -Server $vc2 -Confirm:$false


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

jvignacioproj
Contributor
Contributor

oh nice! so it means if I have this in a CSV

                                                                                         
TemplateVMHostDatastore
template1host1datastore1
template2host2datastore2
template3host1datastore1

 

each loop will run through each row?.. example.. template1, host1, datastore1 THEN template2, host2, datastore2 THEN template3, host1, datastore1

Reply
0 Kudos
LucD
Leadership
Leadership

Correct, each row will create a new VM on the VMHost and Datastore in that row.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
jvignacioproj
Contributor
Contributor

thanks LucD.

One more question per my original post. If I want to run all the templates in parallel rather than one by one through each loop, how can I achieve this?

Reply
0 Kudos
LucD
Leadership
Leadership

In this case I would opt for PowerShell background jobs (Start-Job).

The alternative is to use background tasks on vSphere (RunAsync), but seen the many actions needed to copy 1 template, I'm afraid that would make the script needlessly complex.

With Start-Job the script mostly stays the same, you just need to pass the variables for the specific run.

There is also the question of how many of these background jobs you can reasonable run on your workstation.

Each background PowerShell job requires resources.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
jvignacioproj
Contributor
Contributor

thanks LucD. I'll look into this! consider this closed . thank you!
Reply
0 Kudos
jvignacioproj
Contributor
Contributor

sorry can I ask a silly question, how dou know when to use:

$($template.template) or $template.template / $template.datastore... basically whats the difference $($x.x) over $x.x

Reply
0 Kudos
jvignacioproj
Contributor
Contributor

example why cant this be $esx2 = Get-VMHost -Name $($template.VMHost) -Server $vc2
Reply
0 Kudos
LucD
Leadership
Leadership

It's all about variable substitution in a string.

On a parameter you can use the value like this (variable.property)

$esx2 = Get-VMHost -Name $template.VMHost -Server $vc2

But in a string it is important to tell the variable substitution process what to do.

For example in

"$($template.VMHost)"

we tell the mechanism that it first has to replace everything between the $() with the value of $template.VMHost.

If we would do

"$template.VMHost"

the mechanism would fetch the value of $template, and then append the literal text '.VMHost'

The result would be two different strings.

When you need to use a property of a variable in a string, always place it between $().
That way the substitution mechanism (in strings) knows what to do.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
jvignacioproj
Contributor
Contributor

thanks LucD..perfect explanation.

i've setup this script in a task windows scheduler but it seems to fail on vMotion to vc2 as it needs a username / password. I run the script using a service account.

error: Move-VM : 3/10/2018 11:12:57 AM Move-VM In order to migrate a VM to a different vCenter Server you need to connect to the destination server by username and password.

Script

#Variables

$vcho = 'vc1'

$vcbrs = 'vc2'

 

#Connect to vCenters

Connect-VIServer -Server $vcho 

Connect-VIServer -Server $vcbrs 

any ideas?

 
#Variables
$vcho = 'hait-vcen-tev01'
$vcbrs = 'bait-vcen-tev01'
 
 
 
#Connect to vCenters
Connect-VIServer -Server $vcho 
Connect-VIServer -Server $vcbrs 
Reply
0 Kudos
LucD
Leadership
Leadership

Can you logon with that service account?

If yes, you could create with New-VICredentialStoreItem en entry for VC2.

$vcsa = 'vc2'

$user = 'Administrator@vsphere.local'

$pswd = 'VMware1!'

New-VICredentialStoreItem -Host $vcsa -User $user -Password $pswd

That way you can pick up the credentials in your scheduled script, something like this

$cred = Get-VICredentialStoreItem -File $CredFile -Host $vc2

$srv = Connect-VIServer -Server $vc2 -User $cred.User -Password $cred.Password

Note that you have to create that VICredentialStoreItem with the same account and on the same computer with which you run the scheduled task.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
jvignacioproj
Contributor
Contributor

Thanks LucD.

I can logon with the service account.

Question, this seems to store credentials in an xml file under %APPDATA%\VMware\credstore\vicredentials.xml.

Is it encrypted? Just doesn't seem safe...

thanks

Reply
0 Kudos
LucD
Leadership
Leadership

Yes, it is encrypted.

It uses DPAPI, the MSFT cryptographic API available in Windows (which is also the reason why the VICredentialStore cmdlets are not supported on PowerShell Core).

The encrypted password is indeed stored on your station, and can only be decrypted on that same station and by the same user.

The reason is that the encryption key is also stored on the station and linked to the user account.

Is it secure?

Yes and no.

For day-to-day use it is considered safe, but an experienced hacker can reverse the DPAPI encryption.

There have been reported methods on how to do that.

Ultimately, if security is of the utmost importance in your environment, you will need to go further then just encrypted passwords.

Procedures with 2FA and/or a security token should be considered.

On the side, we had an interesting thread on a similar subject some time ago, see Is there a way to log onto vsphere clients without seeing clear text password?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
jvignacioproj
Contributor
Contributor

Hi LucD,

I've tried the below like this:

#Variables

$vc1 = 'vc01'

$vc2 = 'vc02'

$credfile = 'vicredentials.xml'

$cred = Get-VICredentialStoreItem -File $credfile -Host $vc2

#Connect to vCenters

Connect-VIServer -Server $vc1

Connect-VIServer -Server $vc2 -User $cred.User -Password $cred.Password

Its actually working but I also get this error message:

Get-VICredentialStoreItem : Cannot bind parameter 'File' to the target. Exception setting "File": "Credentials file doesn't exist."

At line:12 char:41

+ $cred = Get-VICredentialStoreItem -File $credfile -Host $vc2

+                                         ~~~~~~~~~

    + CategoryInfo          : WriteError: (:) [Get-VICredentialStoreItem], ParameterBindingException

    + FullyQualifiedErrorId : ParameterBindingFailed,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVICredentialStoreItem

Again its working by just running the script manually (havent tried the scedule task yet) but I still get the error above. Should I be concerned about it?

Also confirming I can see %APPDATA%\VMware\credstore\vicredentials.xml

thanks

Reply
0 Kudos
LucD
Leadership
Leadership

Did you try specifying the full path, instead of just the filename, to the $credfile variable?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
jvignacioproj
Contributor
Contributor

I havent tried that yet but why is it still working?

Reply
0 Kudos
LucD
Leadership
Leadership

Are you sure there are no open connections when you run the script?
Check the content of $global:defaultviservers.

The Connect-VIServer to vc2 definitely fails, and according to the message because it can't find the credentials XML file.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos