I need to create to Redundant VMs (both on 2 different datastores) via function - vmware

I have defined all the needed variables in a separate Variable_Defination.ps1 file and have called it up here.
Is there a better way to do this?
I just want a script to create 2 redundant VMs both running on separate datastores and having Affinity rule set up for disaster recovery cases
. ./Variable_Defination.ps1
function CreateRedundantVMs {
Param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]$ClusterName,
[string]$ComputerName1,
[string]$ComputerName2,
[string]$sourcetemplate,
[string]$InfraResourcePoolName,
[string]$OSCustomizationspec,
[string]$description
)
$Viabledatastores = (Get-Cluster $ClusterName | Get-Datastore) |
Where {$_.Name -like '*vSSD*'} |
Sort-Object FreeSpaceGB -Descending |
Select -First 2 -ErrorAction 'Stop'
if ($Viabledatastores) {
$Viabledatastores
} else {
Write-Host "No Viable Datastores found"
break;
}
Write-Verbose "`n---------------------- Creating redundant VMs now ------------------------`n " -Verbose
New-VM -Name $ComputerName1 -ResourcePool $InfraResourcePoolName -Datastore $Viabledatastores[0] -Description $description -Template $sourcetemplate -OSCustomizationspec $OSCustomizationspec -DiskStorageFormat Thin
New-VM -Name $ComputerName2 -ResourcePool $InfraResourcePoolName -Datastore $Viabledatastores[1] -Description $description -Template $sourcetemplate -OSCustomizationspec $OSCustomizationspec -DiskStorageFormat Thin
Start-Sleep -s 3
Write-Host "`n -------------------------------------------------------------------------`n "
Write-Verbose -Message "Virtual Machine $ComputerName1 and $ComputerName2 Deployed. Powering On" -Verbose
Write-Host "`n -------------------------------------------------------------------------`n "
Start-VM -VM $ComputerName1
Start-VM -VM $ComputerName2
}
# calling function
CreateRedundantVMs $ClusterName $ComputerName1 $ComputerName2 $sourcetemplate $InfraResourcePoolName $OSCustomizationspec $description

Related

Remediating an ESXi host with PowerCLI

Code:
$baseline = "HPE 7.0U3f"
$vmhost = "HOSTNAME"
$baseline = Get-Baseline | Where-Object {$_.name -eq $baseline}
Attach-Baseline -Entity (get-vmhost $vmhost) -Baseline $baseline
$baseline | remediate-inventory -entity $vmhost -confirm:$false
Error:
Update-Entity The operation for the entity
"HOSTNAME" failed with the following message: "The
operation is not supported on the selected inventory objects. Check
the events for the objects select ed for the operation."
Manually attaching the image and remediating the host works just fine. What did I miss?

Creating AWS SSM Document to run Powershell commands

Im trying to make an SSM Document that runs the following Powershell script:
New-Item -ItemType Directory -Force -Path C:\temp
$checkupdatesscript = "`$UpdateSession = New-Object -ComObject Microsoft.Update.Session"
$checkupdatesscript | Out-File C:\temp\checkwindowsupdates.ps1
Add-Content -Path C:\temp\checkwindowsupdates.ps1 -Value "`$UpdateSearcher = `$UpdateSession.CreateupdateSearcher()"
Add-Content -Path C:\temp\checkwindowsupdates.ps1 -Value "`$Updates = #(`$UpdateSearcher.Search(`"IsHidden=0 and IsInstalled=0`").Updates)"
Add-Content -Path C:\temp\checkwindowsupdates.ps1 -Value "`$Updates | Select-Object Title > C:\temp\windowsupdates.txt"
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument "C:\temp\checkwindowsupdates.ps1"
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5)
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "Check Windows Updates" -Description "checks for any outstanding windows updates every 5 minutes"
I have created the following SSM Document in YAML:
---
schemaVersion: "2.2"
description: "Creates script and scheduled task to check for any outstanding windows updates every 5 minutes"
mainSteps:
- action: "aws:runPowerShellScript"
name: "RunCommands"
inputs:
runCommand:
- "New-Item -ItemType Directory -Force -Path C:\temp"
- "$checkupdatesscript = \"`$UpdateSession = New-Object -ComObject Microsoft.Update.Session\""
- "$checkupdatesscript | Out-File C:\temp\checkwindowsupdates.ps1"
- "Add-Content -Path C:\temp\checkwindowsupdates.ps1 -Value \"`$UpdateSearcher = `$UpdateSession.CreateupdateSearcher()\""
- "Add-Content -Path C:\temp\checkwindowsupdates.ps1 -Value \"`$Updates = #(`$UpdateSearcher.Search(`\"IsHidden=0 and IsInstalled=0`\").Updates)\""
- "Add-Content -Path C:\temp\checkwindowsupdates.ps1 -Value \"`$Updates | Select-Object Title > C:\temp\windowsupdates.txt\""
- "$action = New-ScheduledTaskAction -Execute \"Powershell.exe\" -Argument \"C:\temp\checkwindowsupdates.ps1\""
- "$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5)"
- "Register-ScheduledTask -Action $action -Trigger $trigger -TaskName \"Check Windows Updates\" -Description \"checks for any outstanding windows updates every 5 minutes\""
But am receiving an error when attempting to create the document. The error is "InvalidDocumentContent: null"
I am assuming I have messed up the YAML but can't seem to figure out what.
The issue did lie within the YAML. I had not escaped the backslashes in the file paths. The correct yaml should look like this:
---
schemaVersion: "2.2"
description: "Creates script and scheduled task to check for any outstanding windows updates every 5 minutes"
mainSteps:
- action: "aws:runPowerShellScript"
name: "RunCommands"
inputs:
runCommand:
- "New-Item -ItemType Directory -Force -Path C:\\temp"
- "$checkupdatesscript = \"`$UpdateSession = New-Object -ComObject Microsoft.Update.Session\""
- "$checkupdatesscript | Out-File C:\\temp\\checkwindowsupdates.ps1"
- "Add-Content -Path C:\\temp\\checkwindowsupdates.ps1 -Value \"`$UpdateSearcher = `$UpdateSession.CreateupdateSearcher()\""
- "Add-Content -Path C:\\temp\\checkwindowsupdates.ps1 -Value \"`$Updates = #(`$UpdateSearcher.Search(`\"IsHidden=0 and IsInstalled=0`\").Updates)\""
- "Add-Content -Path C:\\temp\\checkwindowsupdates.ps1 -Value \"`$Updates | Select-Object Title > C:\\temp\\windowsupdates.txt\""
- "$action = New-ScheduledTaskAction -Execute \"Powershell.exe\" -Argument \"C:\\temp\\checkwindowsupdates.ps1\""
- "$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5)"
- "Register-ScheduledTask -Action $action -Trigger $trigger -TaskName \"Check Windows Updates\" -Description \"checks for any outstanding windows updates every 5 minutes\""

EC2 User data - Disable it after all conditions are met in user data

I'm trying to rename hostname and add to AD of a spot instance. It is a simple powershell script. I've read the docs that by default user data will be disable after it gets executed once and if <persist>true</persist> is used it will not be disabled.
I think I saw somewhere this(enabling to be run at each startup) is done via taskscheduler but can't find the link.
Can someone point me to the task scheduler job or the way to manually disable the userdata once my if conditions are met.
<powershell>
Set-ExecutionPolicy unrestricted -Force
$instanceName = "test-name5"
$username = "domain\username"
$password = "password" | ConvertTo-SecureString -AsPlainText -Force
$cred = New-Object -typename System.Management.Automation.PSCredential($username, $password)
Start-Sleep -s 5
$hostname = hostname
$domain = (Get-WmiObject win32_computersystem).Domain
if (!($hostname -eq $instanceName)){
Rename-Computer -NewName $instanceName -restart -force
}Elseif (!($domain -eq 'my.domain.local')){
Start-Sleep -s 5
Add-Computer -DomainName my.domain.local -OUPath "OU=Windows,OU=QAServers,OU=Servers,DC=my,DC=domain,DC=local" -Credential $cred -Force -Restart -erroraction 'stop'
}Else {
####code to disable the running of userdata once above conditions
are met####
}
</powershell>
<persist>true</persist>
It's worth reading the ec2config-service documentation, as the setting you want is referenced in there.
You want the Ec2HandleUserData setting, which is configured in the Config.xml.
Powershell can easily update this setting:
$path = 'C:\Program Files\Amazon\Ec2ConfigService\Settings\config.xml'
$xml = [xml](Get-Content $path)
$state = $xml.Ec2ConfigurationSettings.Plugins.Plugin | where {$_.Name -eq 'Ec2HandleUserData'}
$state.State = 'Disabled'
$xml.Save($path)
I use this code when creating custom AMI's to re-enable userdata handling ($state.State = 'Enabled').
EDIT: The above is for ec2config not ec2launch which is what the OP is using. I'd missed this originally.
I this case I think you need to change the way your script runs, rather than use <persist> and then try to disable its functionality, I would remove the persist tag and call InitializeInstance.ps1 –Schedule (documentation link) in your if for the conditions you want the userdata to re-run:
if ($hostname -ne $instanceName) {
& C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 -Schedule
Rename-Computer -NewName $instanceName -Restart -Force
}
elseif ($domain -ne 'my.domain.local') {
& C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 -Schedule
Add-Computer -DomainName aws.macmillan.local -OUPath "OU=Windows,OU=QAServers,OU=Servers,DC=my,DC=domain,DC=local" -Credential $cred -Force -Restart -ErrorAction 'stop'
}
As I said in the comments of the previous answer, I had 3 options and since I found the aws scheduled task I went with the last option. Answering my own question since it'll be easy to spot the code.
<powershell>
Set-ExecutionPolicy unrestricted -Force
#Enter instance hostname here
$instanceName = "test-name8"
$username = "domain\username"
#Using ssm parameter store to avoid having the password in plaintext
$password = (Get-SSMParameterValue -Name AD-Password -WithDecryption $True -Region us-east-1).Parameters[0].Value | ConvertTo-SecureString -asPlainText -Force
Start-Sleep -s 3
$cred = New-Object -typename System.Management.Automation.PSCredential($username, $password)
Start-Sleep -s 5
$hostname = hostname
$domain = (Get-WmiObject win32_computersystem).Domain
if ($hostname -ne $instanceName){
Rename-Computer -NewName $instanceName -restart -force
}Elseif ($domain -ne 'my.domain.local'){
Start-Sleep -s 5
Add-Computer -DomainName my.domain.local -OUPath "OU=Windows,OU=QAServers,OU=Servers,DC=my,DC=domain,DC=local" -Credential $cred -Force -Restart -erroraction 'stop'
}Else {
Disable-ScheduledTask -TaskName "Amazon Ec2 Launch - Userdata Execution"
Unregister-ScheduledTask -TaskName "Amazon Ec2 Launch - Userdata Execution"
}
</powershell>
<persist>true</persist>
note: a role that has ssm policies must be attached while launching the server for this ssm parameter command to work.
I was solving similar issue and I had to change Windows Server 2016 hostname and enroll it to Elastic Server Fleet. Also I could not allow instance to be rebooted. I used this code to solve this.
NB. I understand that it is not direct way of doing this and has numerous drawbacks, but in my circumstances goal was achieved without negative impact.
<powershell>
$ComputerName = "MyPCRandomName"
Set-ItemProperty -path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" -name "Hostname" -value $ComputerName
elastic-agent enroll --enrollment-token 123 --url=321
</powershell>

Creating a ticket in VMWare using PowerCLI when free storage in datastore is too less

I am able to get the free space as ouptut by using the following code.
$body +=echo "------------Free space on Datastore.--------------"`r`n""`r`n""
$body +=get-datastore -name *datastore1* | sort Name | ForEach-Object {
$output=New-Object -TypeName PSObject -Property #{
Freespace = "$([math]::Round($_.FreeSpaceGB, 2)) GB"
Datastore_Name = $_.Name
}
}
Write-Output $output
Is it possible to raise a ticket if the free space is less than 2 GB? If so, how should I change my code?
EDIT :
if (get-datastore | where {$_.FreeSpaceGB -lt 2}){"dosomething"}
or
foreach ($ds in (get-datastore | where {$_.FreeSpaceGB -lt 2})){"dosomething"}

vCenter VM permission query using Powershell / PowerCLI

I've been trying to use Powershell with imported PowerCLI commands for VMware administration and I've hit a snag. What I'm trying to do is query all VM's in a location (doesn't matter where), and for every VM I want the group with "Virtual Machine User with Snapshot" permission, and then use that group name to run a Get-ADGroupMembers query for everyone in that group. I also have to remove the domain prefix from the AD query, which would otherwise cause an error.
After some more playing around with outputting hash table information into the csv, as opposed to 'SystemObject[]', I finally got the script so it doesn't return errors EXCEPT on VM's where there is more than one group. It throws an error but the script continues, and just outputs the members of the first group.
How do I get it to do a recursive AD query for every group that is pulled into the owner groups hashtable? The output would be the same as for all the other VM's, just with a line for each group and members.
$AllVMs = #()
$vms = get-vm * -Location datacenter
foreach ($vm in $vms)
{
$owners = Get-VIPermission $vm.name | where-object {$_.role -eq "virtual machine user with snapshot"}
foreach ($owner in $owners)
{
$members = Get-ADGroupMember ($owners.principal -replace '^prefix\\')
$temp = New-Object psobject |
Add-Member Noteproperty "Name" -value $vm.name -PassThru |
Add-Member Noteproperty "Owner" -value (#($owners.principal) -join ',') -PassThru |
Add-Member Noteproperty "Members" -value (#($members.SamAccountName) -join ',') -passthru
$AllVMs+=$temp
}
$AllVMs | Export-Csv -Path c:\users\me\desktop\AllVMs.csv
I was playing around with it some more today and figured it out! I'm running the script right now against a datacenter with 350+ machines so technically I don't know 100% that it works, but it worked against 3 machines :-) I also added a line to list every machine that's owned by more than one group - handy for troubleshooting. Here's the script:
$AllVMs = #()
$vms = get-vm -Location DATACENTER
foreach ($vm in $vms)
{
$owners = #(Get-VIPermission $vm.name | where-object {$_.role -eq "virtual machine user with snapshot"})
if ($owners.count -gt 1) {write-host "** Note ** '$vm' has"$owners.count "owner groups"}
foreach ($owner in $owners)
{
$members = Get-ADGroupMember ($owner.principal -replace '^prefix\\')
$temp = New-Object psobject |
Add-Member Noteproperty "Name" -value $vm.name -PassThru |
Add-Member Noteproperty "Owner" -value (#($owner.principal) -join ',') -PassThru |
Add-Member Noteproperty "Members" -value (#($members.SamAccountName) -join ',') -PassThru
$AllVMs+=$temp
}
}
$AllVMs
Change $owners.principal to $owner.principal after $members = Get-ADGroupMember? In case you have nested AD groups, Get-ADGroupMember has a -Recursive parameter.