· 8 years ago · Jun 20, 2018, 09:50 PM
1# -------------------------------------------------------------------------- #
2# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs #
3# #
4# Licensed under the Apache License, Version 2.0 (the "License"); you may #
5# not use this file except in compliance with the License. You may obtain #
6# a copy of the License at #
7# #
8# http://www.apache.org/licenses/LICENSE-2.0 #
9# #
10# Unless required by applicable law or agreed to in writing, software #
11# distributed under the License is distributed on an "AS IS" BASIS, #
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
13# See the License for the specific language governing permissions and #
14# limitations under the License. #
15#--------------------------------------------------------------------------- #
16
17# Original work by:
18
19#################################################################
20##### Windows Powershell Script to configure OpenNebula VMs #####
21##### Created by andremonteiro@ua.pt and tsbatista@ua.pt #####
22##### DETI/IEETA Universidade de Aveiro 2011 #####
23#################################################################
24
25Start-Transcript -Append -Path "$env:SystemDrive\.opennebula-context.out" | Out-Null
26
27Write-Output "Running Script: $($MyInvocation.InvocationName)"
28Get-Date
29Write-Output ""
30
31Set-ExecutionPolicy unrestricted -force # not needed if already done once on the VM
32[string]$computerName = "$env:computername"
33[string]$ConnectionString = "WinNT://$computerName"
34
35function getContext($file) {
36 Write-Host "Loading Context File"
37 $context = @{}
38 switch -regex -file $file {
39 "^([^=]+)='(.+?)'$" {
40 $name, $value = $matches[1..2]
41 $context[$name] = $value
42 }
43 }
44 return $context
45}
46
47function envContext($context) {
48 ForEach ($h in $context.GetEnumerator()) {
49 $name = "Env:"+$h.Name
50 Set-Item $name $h.Value
51 }
52}
53
54function addLocalUser($context) {
55 # Create new user
56 $username = $context["USERNAME"]
57 $password = $context["PASSWORD"]
58
59 if ($username -Or $password) {
60
61 if ($username -eq $null) {
62 # ATTENTION - Language/Regional settings have influence on the naming
63 # of this user. Use the User SID instead (S-1-5-21domain-500)
64 $username = (Get-WmiObject -Class "Win32_UserAccount" |
65 where { $_.SID -like "S-1-5-21[0-9-]*-500" } |
66 select -ExpandProperty Name)
67 }
68
69 Write-Output "Creating Account for $username"
70
71 $ADSI = [adsi]$ConnectionString
72
73 if(!([ADSI]::Exists("WinNT://$computerName/$username"))) {
74 # User does not exist, Create the User
75 Write-Output "- Creating account"
76 $user = $ADSI.Create("user",$username)
77 $user.setPassword($password)
78 $user.SetInfo()
79 } else {
80 # User exists, Set Password
81 Write-Output "- Setting Password"
82 $admin = [ADSI]"WinNT://$env:computername/$username"
83 $admin.psbase.invoke("SetPassword", $password)
84 }
85
86 # Set Password to Never Expire
87 Write-Output "- Setting password to never expire"
88 $admin = [ADSI]"WinNT://$env:computername/$username"
89 $admin.UserFlags.value = $admin.UserFlags.value -bor 0x10000
90 $admin.CommitChanges()
91
92 # Add user to local Administrators
93 # ATTENTION - Language/Regional settings have influence on the naming
94 # of this group. Use the Group SID instead (S-1-5-32-544)
95 $groups = (Get-WmiObject -Class "Win32_Group" |
96 where { $_.SID -like "S-1-5-32-544" } |
97 select -ExpandProperty Name)
98
99 ForEach ($grp in $groups) {
100
101 # Make sure the Group exists
102 If([ADSI]::Exists("WinNT://$computerName/$grp,group")) {
103
104 # Check if the user is a Member of the Group
105 $group = [ADSI] "WinNT://$computerName/$grp,group"
106 $members = @($group.psbase.Invoke("Members"))
107
108 $memberNames = @()
109 $members | ForEach-Object {
110 $memberNames += $_.GetType().InvokeMember(
111 "Name", 'GetProperty', $null, $_, $null);
112 }
113
114 If (-Not $memberNames -Contains $username) {
115
116 # Make sure the user exists, again
117 if([ADSI]::Exists("WinNT://$computerName/$username")) {
118
119 # Add the user
120 Write-Output "- Adding to $grp"
121 $group.Add("WinNT://$computerName/$username")
122 }
123 }
124 }
125 }
126 }
127 Write-Output ""
128}
129
130function configureNetwork($context) {
131
132 # Get the NIC in the Context
133 $nicIds = ($context.Keys | Where {$_ -match '^ETH\d+_IP6?$'} | ForEach-Object {$_ -replace '(^ETH|_IP$|_IP6$)',''} | Get-Unique | Sort-Object)
134
135 $nicId = 0;
136
137 foreach ($nicId in $nicIds) {
138 # Retrieve data from Context
139 $nicIpKey = "ETH" + $nicId + "_IP"
140 $nicIp6Key = "ETH" + $nicId + "_IP6"
141 $nicPrefix = "ETH" + $nicId + "_"
142
143 $ipKey = $nicPrefix + "IP"
144 $netmaskKey = $nicPrefix + "MASK"
145 $macKey = $nicPrefix + "MAC"
146 $dnsKey = $nicPrefix + "DNS"
147 $dnsSuffixKey = $nicPrefix + "SEARCH_DOMAIN"
148 $gatewayKey = $nicPrefix + "GATEWAY"
149 $networkKey = $nicPrefix + "NETWORK"
150
151 $ip6Key = $nicPrefix + "IP6"
152 $ip6ULAKey = $nicPrefix + "IP6_ULA"
153 $ip6PrefixKey = $nicPrefix + "IP6_PREFIX_LENGTH"
154 $gw6Key = $nicPrefix + "GATEWAY6"
155
156 $ip = $context[$ipKey]
157 $netmask = $context[$netmaskKey]
158 $mac = $context[$macKey]
159 $dns = (($context[$dnsKey] -split " " | Where {$_ -match '^(([0-9]*).?){4}$'}) -join ' ')
160 $dns6 = (($context[$dnsKey] -split " " | Where {$_ -match '^(([0-9A-F]*):?)*$'}) -join ' ')
161 $dnsSuffix = $context[$dnsSuffixKey]
162 $gateway = $context[$gatewayKey]
163 $network = $context[$networkKey]
164
165 $ip6 = $context[$ip6Key]
166 $ip6ULA = $context[$ip6ULAKey]
167 $ip6Prefix = $context[$ip6PrefixKey]
168 $gw6 = $context[$gw6Key]
169
170 $mac = $mac.ToUpper()
171 if (!$netmask) {
172 $netmask = "255.255.255.0"
173 }
174 if (!$ip6Prefix) {
175 $ip6Prefix = "64"
176 }
177 if (!$network) {
178 $network = $ip -replace "\.[^.]+$", ".0"
179 }
180 if ($nicId -eq 0 -and !$gateway) {
181 $gateway = $ip -replace "\.[^.]+$", ".1"
182 }
183
184 # Load the NIC Configuration Object
185 $nic = $false
186 $retry = 30
187 do {
188 $retry--
189 Start-Sleep -s 1
190 $nic = Get-WMIObject Win32_NetworkAdapterConfiguration | `
191 where {$_.IPEnabled -eq "TRUE" -and $_.MACAddress -eq $mac}
192 } while (!$nic -and $retry)
193
194 If (!$nic) {
195 Write-Output ("Configuring Network Settings: " + $mac)
196 Write-Output (" ... Failed: Interface with MAC not found")
197 Continue
198 }
199
200 Write-Output ("Configuring Network Settings: " + $nic.Description.ToString())
201
202 # Release the DHCP lease, will fail if adapter not DHCP Configured
203 Write-Output "- Release DHCP Lease"
204 $ret = $nic.ReleaseDHCPLease()
205 If ($ret.ReturnValue) {
206 Write-Output (" ... Failed: " + $ret.ReturnValue.ToString())
207 } Else {
208 Write-Output " ... Success"
209 }
210
211 if ($ip) {
212 # set static IP address and retry for few times if there was a problem
213 # with acquiring write lock (2147786788) for network configuration
214 # https://msdn.microsoft.com/en-us/library/aa390383(v=vs.85).aspx
215 Write-Output "- Enable Static IP"
216 $retry = 10
217 do {
218 $retry--
219 Start-Sleep -s 1
220 $ret = $nic.EnableStatic($ip , $netmask)
221 } while ($ret.ReturnValue -eq 2147786788 -and $retry);
222 If ($ret.ReturnValue) {
223 Write-Output (" ... Failed: " + $ret.ReturnValue.ToString())
224 } Else {
225 Write-Output " ... Success"
226 }
227
228
229 if ($gateway) {
230
231 # Set the Gateway
232 Write-Output "- Set Gateway"
233 $ret = $nic.SetGateways($gateway)
234 If ($ret.ReturnValue) {
235 Write-Output (" ... Failed: " + $ret.ReturnValue.ToString())
236 } Else {
237 Write-Output " ... Success"
238 }
239
240 If ($dns) {
241
242 # DNS Servers
243 $dnsServers = $dns -split " "
244
245 # DNS Server Search Order
246 Write-Output "- Set DNS Server Search Order"
247 $ret = $nic.SetDNSServerSearchOrder($dnsServers)
248 If ($ret.ReturnValue) {
249 Write-Output (" ... Failed: " + $ret.ReturnValue.ToString())
250 } Else {
251 Write-Output " ... Success"
252 }
253
254 # Set Dynamic DNS Registration
255 Write-Output "- Set Dynamic DNS Registration"
256 $ret = $nic.SetDynamicDNSRegistration("TRUE")
257 If ($ret.ReturnValue) {
258 Write-Output (" ... Failed: " + $ret.ReturnValue.ToString())
259 } Else {
260 Write-Output " ... Success"
261 }
262
263 # WINS Addresses
264 # $nic.SetWINSServer($DNSServers[0], $DNSServers[1])
265 }
266
267 if ($dnsSuffix) {
268
269 # DNS Suffixes
270 $dnsSuffixes = $dnsSuffix -split " "
271
272 # Set DNS Suffix Search Order
273 Write-Output "- Set DNS Suffix Search Order"
274 $ret = ([WMIClass]"Win32_NetworkAdapterConfiguration").SetDNSSuffixSearchOrder(($dnsSuffixes))
275 If ($ret.ReturnValue) {
276 Write-Output (" ... Failed: " + $ret.ReturnValue.ToString())
277 } Else {
278 Write-Output " ... Success"
279 }
280
281 # Set Primary DNS Domain
282 Write-Output "- Set Primary DNS Domain"
283 $ret = $nic.SetDNSDomain($dnsSuffixes[0])
284 If ($ret.ReturnValue) {
285 Write-Output (" ... Failed: " + $ret.ReturnValue.ToString())
286 } Else {
287 Write-Output " ... Success"
288 }
289 }
290 }
291 }
292
293 if ($ip6) {
294 # We need the connection ID (i.e. "Local Area Connection",
295 # which can be discovered from the NetworkAdapter object
296 $na = Get-WMIObject Win32_NetworkAdapter | `
297 where {$_.deviceId -eq $nic.index}
298
299
300 # Disable router discovery
301 Write-Output "- Disable IPv6 router discovery"
302 netsh interface ipv6 set interface $na.NetConnectionId `
303 advertise=disabled routerdiscover=disabled | Out-Null
304
305 If ($?) {
306 Write-Output " ... Success"
307 } Else {
308 Write-Output " ... Failed"
309 }
310
311 # Remove old IPv6 addresses
312 Write-Output "- Removing old IPv6 addresses"
313 if (Get-Command Remove-NetIPAddress -errorAction SilentlyContinue) {
314 # Windows 8.1 and Server 2012 R2 and up
315 # we want to remove everything except the link-local address
316 Remove-NetIPAddress -InterfaceAlias $na.NetConnectionId `
317 -AddressFamily IPv6 -Confirm:$false `
318 -PrefixOrigin Other,Manual,Dhcp,RouterAdvertisement `
319 -errorAction SilentlyContinue
320
321 If ($?) {
322 Write-Output " ... Success"
323 } Else {
324 Write-Output " ... Nothing to do"
325 }
326 } Else {
327 Write-Output " ... Not implemented"
328 }
329
330 # Set IPv6 Address
331 Write-Output "- Set IPv6 Address"
332 netsh interface ipv6 add address $na.NetConnectionId $ip6/$ip6Prefix
333 if ($ip6ULA) {
334 netsh interface ipv6 add address $na.NetConnectionId $ip6ULA/64
335 }
336
337 # Set IPv6 Gateway
338 if ($gw6) {
339 netsh interface ipv6 add route ::/0 $na.NetConnectionId $gw6
340 }
341
342 # Remove old IPv6 DNS Servers
343 Write-Output "- Removing old IPv6 DNS Servers"
344 netsh interface ipv6 set dnsservers $na.NetConnectionId source=static address=
345
346 If ($dns6) {
347 # Set IPv6 DNS Servers
348 Write-Output "- Set IPv6 DNS Servers"
349 $dns6Servers = $dns6 -split " "
350 foreach ($dns6Server in $dns6Servers) {
351 netsh interface ipv6 add dnsserver $na.NetConnectionId address=$dns6Server
352 }
353 }
354
355 doPing($ip6)
356 }
357
358 If ($ip) {
359 doPing($ip)
360 }
361 }
362
363
364 Write-Output ""
365}
366
367function renameComputer($context) {
368
369 # Initialize Variables
370 $current_hostname = hostname
371 $context_hostname = $context["SET_HOSTNAME"]
372 $logged_hostname = "Unknown"
373
374 if (! $context_hostname) {
375 return
376 }
377
378 # Check for the .opennebula-renamed file
379 If (Test-Path "$env:SystemDrive\.opennebula-renamed") {
380
381 # Grab the JSON content
382 $json = Get-Content -Path "$env:SystemDrive\.opennebula-renamed" `
383 | Out-String
384
385 # Convert to a Hash Table and set the Logged Hostname
386 try {
387 $status = $json | ConvertFrom-Json
388 $logged_hostname = $status.ComputerName
389 }
390 # Invalid JSON
391 catch [System.ArgumentException] {
392 Write-Output "Invalid JSON:"
393 Write-Output $json.ToString()
394 }
395 }
396
397 If ((!(Test-Path "$env:SystemDrive\.opennebula-renamed")) -or `
398 ($context_hostname.ToLower() -ne $logged_hostname.ToLower())) {
399
400 # .opennebula-renamed not found or the logged_name does not match the
401 # context_name, rename the computer
402
403 Write-Output "Changing Hostname to $context_hostname"
404 # Load the ComputerSystem Object
405 $ComputerInfo = Get-WmiObject -Class Win32_ComputerSystem
406
407 # Rename the computer
408 $ret = $ComputerInfo.rename($context_hostname)
409
410 $contents = @{}
411 $contents["ComputerName"] = $context_hostname
412 ConvertTo-Json $contents | Out-File "$env:SystemDrive\.opennebula-renamed"
413
414 # Check success
415 If ($ret.ReturnValue) {
416
417 # Returned Non Zero, Failed, No restart
418 Write-Output (" ... Failed: " + $ret.ReturnValue.ToString())
419 Write-Output " Check the computername."
420 Write-Output "Possible Issues: The name cannot include control" `
421 "characters, leading or trailing spaces, or any of" `
422 "the following characters: `" / \ [ ] : | < > + = ; , ?"
423
424 } Else {
425
426 # Returned Zero, Success
427 Write-Output "... Success"
428
429 # Restart the Computer
430 Write-Output "... Rebooting"
431 Restart-Computer -Force
432
433 # Exit here so the script doesn't continue to run
434 Exit 0
435 }
436 } else {
437 If ($current_hostname -eq $context_hostname) {
438 Write-Output "Computer Name already set: $context_hostname"
439 }
440 ElseIf (($current_hostname -ne $context_hostname) -and `
441 ($context_hostname -eq $logged_hostname)) {
442 Write-Output "Computer Rename Attempted but failed:"
443 Write-Output "- Current: $current_hostname"
444 Write-Output "- Context: $context_hostname"
445 }
446 }
447 Write-Output ""
448}
449
450function enableRemoteDesktop()
451{
452 Write-Output "Enabling Remote Desktop"
453 # Windows 7 only - add firewall exception for RDP
454 Write-Output "- Enable Remote Desktop Rule Group"
455 netsh advfirewall Firewall set rule group="Remote Desktop" new enable=yes
456
457 # Enable RDP
458 Write-Output "- Enable Allow Terminal Services Connections"
459 $ret = (Get-WmiObject -Class "Win32_TerminalServiceSetting" -Namespace root\cimv2\terminalservices).SetAllowTsConnections(1)
460 If ($ret.ReturnValue) {
461 Write-Output (" ... Failed: " + $ret.ReturnValue.ToString())
462 } Else {
463 Write-Output " ... Success"
464 }
465 Write-Output ""
466}
467
468function enablePing()
469{
470 Write-Output "Enabling Ping"
471 #Create firewall manager object
472 $fwm=new-object -com hnetcfg.fwmgr
473
474 # Get current profile
475 $pro=$fwm.LocalPolicy.CurrentProfile
476
477 Write-Output "- Enable Allow Inbound Echo Requests"
478 $ret = $pro.IcmpSettings.AllowInboundEchoRequest=$true
479 If ($ret) {
480 Write-Output " ... Success"
481 } Else {
482 Write-Output " ... Failed"
483 }
484
485 Write-Output ""
486}
487
488function doPing($ip, $retries=30)
489{
490 Write-Output "- Ping Interface IP $ip"
491
492 $ping = $false
493 $retry = 0
494 do {
495 $retry++
496 Start-Sleep -s 1
497 $ping = Test-Connection -ComputerName $ip -Count 1 -Quiet -ErrorAction SilentlyContinue
498 } while (!$ping -and ($retry -lt $retries))
499
500 If ($ping) {
501 Write-Output " ... Success ($retry tries)"
502 } Else {
503 Write-Output " ... Failed ($retry tries)"
504 }
505}
506
507function runScripts($context, $contextLetter)
508{
509 Write-Output "Running Scripts"
510
511 # Get list of scripts to run, " " delimited
512 $initscripts = $context["INIT_SCRIPTS"]
513
514 if ($initscripts) {
515
516 # Parse each script and run it
517 ForEach ($script in $initscripts.split(" ")) {
518
519 $script = $contextLetter + $script
520 If (Test-Path $script) {
521 Write-Output "- $script"
522 envContext($context)
523 & $script
524 }
525
526 }
527 }
528
529 # Execute START_SCRIPT or START_SCRIPT_64
530 $startScript = $context["START_SCRIPT"]
531 $startScript64 = $context["START_SCRIPT_BASE64"]
532
533 If ($startScript64) {
534 $startScript = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($startScript64))
535 }
536
537 If ($startScript) {
538
539 # Save the script as .opennebula-startscript.ps1
540 $startScriptPS = "$env:SystemDrive\.opennebula-startscript.ps1"
541 $startScript | Out-File $startScriptPS "UTF8"
542
543 # Launch the Script
544 Write-Output "- $startScriptPS"
545 envContext($context)
546 & $startScriptPS
547
548 }
549 Write-Output ""
550}
551
552function extendPartition($disk, $part)
553{
554 "select disk $disk","select partition $part","extend" | diskpart | Out-Null
555}
556
557function extendPartitions()
558{
559 Write-Output "- Extend partitions"
560
561 #$diskIds = ((wmic diskdrive get Index | Select-String "[0-9]+") -replace '\D','')
562 $diskId = 0
563
564 $partIds = ((wmic partition where DiskIndex=$diskId get Index | Select-String "[0-9]+") -replace '\D','' | %{[int]$_ + 1})
565
566 ForEach ($partId in $partIds) {
567 extendPartition $diskId $partId
568 }
569}
570
571################################################################################
572# Main
573################################################################################
574
575# Check the working WMI
576if (-Not (Get-WMIObject -ErrorAction SilentlyContinue Win32_Volume)) {
577 Write-Output "WMI not ready, exiting"
578 Stop-Transcript | Out-Null
579 exit 1
580}
581
582Write-Output "Detecting contextualization data"
583Write-Output "- Looking for CONTEXT ISO"
584
585# Get all drives and select only the one that has "CONTEXT" as a label
586$contextDrive = Get-WMIObject Win32_Volume | ? { $_.Label -eq "CONTEXT" }
587
588if ($contextDrive) {
589 Write-Output " ... Found"
590
591 # At this point we can obtain the letter of the contextDrive
592 $contextLetter = $contextDrive.Name
593 $contextScriptPath = $contextLetter + "context.sh"
594} else {
595 Write-Output " ... Not found"
596 Write-Output "- Looking for VMware tools"
597
598 # Try the VMware API
599 foreach ($pf in ${env:ProgramFiles}, ${env:ProgramFiles(x86)}, ${env:ProgramW6432}) {
600 $vmtoolsd = "${pf}\VMware\VMware Tools\vmtoolsd.exe"
601 if (Test-Path $vmtoolsd) {
602 Write-Output " ... Found in ${vmtoolsd}"
603 break
604 } else {
605 Write-Output " ... Not found in ${vmtoolsd}"
606 }
607 }
608
609 $vmwareContext = ""
610 if (Test-Path $vmtoolsd) {
611 $vmwareContext = & $vmtoolsd --cmd "info-get guestinfo.opennebula.context" | Out-String
612 }
613
614 if ("$vmwareContext" -eq "") {
615 Write-Host "No contextualization data found"
616 Stop-Transcript | Out-Null
617 exit 1
618 }
619
620 [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($vmwareContext)) | Out-File "$env:SystemDrive\context.sh" "UTF8"
621 $contextScriptPath = "$env:SystemDrive\context.sh"
622}
623
624# Execute script
625if(Test-Path $contextScriptPath) {
626 $context = getContext $contextScriptPath
627 extendPartitions
628 renameComputer $context
629 addLocalUser $context
630 enableRemoteDesktop
631 enablePing
632 configureNetwork $context
633 runScripts $context $contextLetter
634}
635
636Stop-Transcript | Out-Null