зеркало из https://github.com/microsoft/MSLab.git
Коммит
b6c6ff73e8
|
@ -1,6 +1,6 @@
|
|||
$LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPassword='LS1setup!' ; DCEdition='4'; Internet=$true ; TelemetryLevel='Full' ; TelemetryNickname='' ; AdditionalNetworksConfig=@(); VMs=@()}
|
||||
|
||||
#Azure Stack HCI 21H2
|
||||
#Azure Stack HCI 22H2
|
||||
1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "AzSHCI$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI22H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 1GB; VMProcessorCount=4 ; vTPM=$true}}
|
||||
#Or with nested virtualization enabled
|
||||
#1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "AzSHCI$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI22H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 4GB; VMProcessorCount=4 ; vTPM=$true ; NestedVirt=$true}}
|
||||
|
|
|
@ -171,9 +171,9 @@
|
|||
}
|
||||
}
|
||||
#define and install other features
|
||||
$features="Failover-Clustering","RSAT-Clustering-PowerShell","Hyper-V-PowerShell","NetworkATC","NetworkHUD","Data-Center-Bridging","RSAT-DataCenterBridging-LLDP-Tools","FS-SMBBW"
|
||||
#optional - affects perf even if not enabled on volumes as filter driver is attached (Bitlocker,SR,Dedup)
|
||||
#$features+="RSAT-Feature-Tools-BitLocker","Storage-Replica","RSAT-Storage-Replica","FS-Data-Deduplication","System-Insights","RSAT-System-Insights"
|
||||
$features="Failover-Clustering","RSAT-Clustering-PowerShell","Hyper-V-PowerShell","NetworkATC","NetworkHUD","Data-Center-Bridging","RSAT-DataCenterBridging-LLDP-Tools","FS-SMBBW","System-Insights","RSAT-System-Insights"
|
||||
#optional - affects perf even if not enabled on volumes as filter driver is attached (SR,Dedup) and also Bitlocker, that affects a little bit
|
||||
#$features+="Storage-Replica","RSAT-Storage-Replica","FS-Data-Deduplication","BitLocker","RSAT-Feature-Tools-BitLocker"
|
||||
Invoke-Command -ComputerName $servers -ScriptBlock {Install-WindowsFeature -Name $using:features}
|
||||
#endregion
|
||||
|
||||
|
@ -823,6 +823,10 @@ if ($NetATC){
|
|||
|
||||
#check number of live migrations
|
||||
get-vmhost -CimSession $Servers | Select-Object Name,MaximumVirtualMachineMigrations
|
||||
|
||||
#check it in cluster (is only 1 - expected)
|
||||
get-cluster -Name $ClusterName | Select-Object Name,MaximumParallelMigrations
|
||||
|
||||
#endregion
|
||||
|
||||
#remove net intent global overrides if necessary
|
||||
|
|
|
@ -392,7 +392,7 @@
|
|||
#Copy agent and bootloader to VMs
|
||||
#create sessions
|
||||
$sessions=New-PSSession -ComputerName $VMs.VMName
|
||||
#copy ARC agent
|
||||
#copy AVD agent
|
||||
foreach ($session in $sessions){
|
||||
Copy-Item -Path "$env:USERPROFILE\Downloads\AVDAgent.msi" -Destination "$env:USERPROFILE\Downloads\" -tosession $session -force
|
||||
Copy-Item -Path "$env:USERPROFILE\Downloads\AVDAgentBootloader.msi" -Destination "$env:USERPROFILE\Downloads\" -tosession $session -force
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
$LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!' ; Prefix = 'MSLab-' ; DCEdition='4'; Internet=$true ; TelemetryLevel='Full' ; TelemetryNickname='' ; AdditionalNetworksConfig=@(); VMs=@()}
|
||||
|
||||
#2 nodes for AzSHCI Cluster
|
||||
1..2 | ForEach-Object {$VMNames="ArcVMs" ; $LABConfig.VMs += @{ VMName = "$VMNames$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI21H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 4TB ; MemoryStartupBytes= 14GB; VMProcessorCount="Max" ; NestedVirt=$true ; VirtualTPM=$true}}
|
||||
|
||||
#or 2 nodes for Windows Server 2022
|
||||
#1..2 | ForEach-Object {$VMNames="ArcVMs" ; $LABConfig.VMs += @{ VMName = "$VMNames$_" ; Configuration = 'S2D' ; ParentVHD = 'Win2022Core_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 4TB ; MemoryStartupBytes= 14GB; VMProcessorCount="Max" ; NestedVirt=$true ; VirtualTPM=$true}}
|
||||
|
||||
#optional Windows Admin Center gateway
|
||||
#$LabConfig.VMs += @{ VMName = 'WACGW' ; ParentVHD = 'Win2022Core_G2.vhdx' ; MGMTNICs=1 }
|
||||
|
||||
#optional Windows Management machine
|
||||
#$LabConfig.VMs += @{ VMName = 'Management' ; ParentVHD = 'Win2022_G2.vhdx' ; MGMTNICs=1 }
|
||||
|
|
@ -1,592 +0,0 @@
|
|||
#https://aka.ms/ArcEnabledHCI
|
||||
#https://aka.ms/AzureArcVM
|
||||
|
||||
###################
|
||||
### Run from DC ###
|
||||
###################
|
||||
|
||||
#region Variables
|
||||
$ClusterNodeNames="ArcVMs1","ArcVMs2"
|
||||
$ClusterName="ArcVMs-Cluster"
|
||||
$vswitchName="vSwitch"
|
||||
$controlPlaneIP="10.0.0.111"
|
||||
$VolumeName="MOC"
|
||||
$VolumePath="c:\ClusterStorage\$VolumeName"
|
||||
$CredSSPUserName="CORP\LabAdmin"
|
||||
$CredSSPPassword="LS1setup!"
|
||||
$CustomLocationName="$ClusterName-cl"
|
||||
$LibraryVolumeName="Library" #volume for Gallery images for VMs
|
||||
$AzureImages=@()
|
||||
$AzureImages+=@{PublisherName = "microsoftwindowsserver";Offer="windowsserver";SKU="2022-datacenter-azure-edition-smalldisk";OSType="Windows"} #OS TYpe can be "Windows" or "Linux" - first letter has to be capital!
|
||||
$AzureImages+=@{PublisherName = "microsoftwindowsserver";Offer="windowsserver";SKU="2022-datacenter-azure-edition-core-smalldisk";OSType="Windows"} #OS TYpe can be "Windows" or "Linux" - first letter has to be capital!
|
||||
|
||||
#Install or update Azure packages
|
||||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
|
||||
$ModuleNames="Az.Accounts","Az.Compute","Az.Resources","Az.StackHCI"
|
||||
foreach ($ModuleName in $ModuleNames){
|
||||
$Module=Get-InstalledModule -Name $ModuleName -ErrorAction Ignore
|
||||
if ($Module){$LatestVersion=(Find-Module -Name $ModuleName).Version}
|
||||
if (-not($Module) -or ($Module.Version -lt $LatestVersion)){
|
||||
Install-Module -Name $ModuleName -Force
|
||||
}
|
||||
}
|
||||
|
||||
#login to Azure
|
||||
if (-not (Get-AzContext)){
|
||||
Login-AzAccount -UseDeviceAuthentication
|
||||
}
|
||||
|
||||
$ResourceGroupName="$ClusterName-rg"
|
||||
$SubscriptionID=(Get-AzContext).Subscription.ID
|
||||
|
||||
#select context
|
||||
$context=Get-AzContext -ListAvailable
|
||||
if (($context).count -gt 1){
|
||||
$context=$context | Out-GridView -OutputMode Single
|
||||
$context | Set-AzContext
|
||||
}
|
||||
|
||||
#I did only test same for all (EastUS)
|
||||
$VMImageLocation="eastus"
|
||||
$ArcResourceBridgeLocation="eastus"
|
||||
$AzureStackLocation="eastus"
|
||||
|
||||
<#or populate by choosing your own
|
||||
#grab region where to grab VMs from
|
||||
$VMImageLocation = (Get-AzLocation | Where-Object Providers -Contains "Microsoft.Compute" | Out-GridView -OutputMode Single -Title "Choose location where to grab VMs from").Location
|
||||
|
||||
#grab location for Arc Resource Bridge and Custom location
|
||||
$ArcResourceBridgeLocation=(Get-AzLocation | Where-Object Providers -Contains "Microsoft.ResourceConnector" | Out-GridView -OutputMode Single -Title "Choose location for Arc Resource Bridge and Custom location").Location
|
||||
|
||||
#grab location for Azure Stack
|
||||
$AzureStackLocation=(Get-AzLocation | Where-Object Providers -Contains "Microsoft.AzureStackHCI" | Out-GridView -OutputMode Single -Title "Choose location for Azure Stack HCI - where it should be registered").Location
|
||||
#>
|
||||
|
||||
#virtual network name
|
||||
$vnetName=$vswitchName
|
||||
|
||||
#endregion
|
||||
|
||||
#region Create 2 node cluster (just simple. Not for prod - follow hyperconverged scenario for real clusters https://github.com/microsoft/MSLab/tree/master/Scenarios/AzSHCI%20Deployment)
|
||||
# Install features for management on server
|
||||
Install-WindowsFeature -Name RSAT-Clustering,RSAT-Clustering-Mgmt,RSAT-Clustering-PowerShell,RSAT-Hyper-V-Tools
|
||||
|
||||
# Update servers (optional)
|
||||
<#
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock {
|
||||
New-PSSessionConfigurationFile -RunAsVirtualAccount -Path $env:TEMP\VirtualAccount.pssc
|
||||
Register-PSSessionConfiguration -Name 'VirtualAccount' -Path $env:TEMP\VirtualAccount.pssc -Force
|
||||
} -ErrorAction Ignore
|
||||
# Run Windows Update via ComObject.
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ConfigurationName 'VirtualAccount' {
|
||||
$Searcher = New-Object -ComObject Microsoft.Update.Searcher
|
||||
$SearchCriteriaAllUpdates = "IsInstalled=0 and DeploymentAction='Installation' or
|
||||
IsPresent=1 and DeploymentAction='Uninstallation' or
|
||||
IsInstalled=1 and DeploymentAction='Installation' and RebootRequired=1 or
|
||||
IsInstalled=0 and DeploymentAction='Uninstallation' and RebootRequired=1"
|
||||
$SearchResult = $Searcher.Search($SearchCriteriaAllUpdates).Updates
|
||||
$Session = New-Object -ComObject Microsoft.Update.Session
|
||||
$Downloader = $Session.CreateUpdateDownloader()
|
||||
$Downloader.Updates = $SearchResult
|
||||
$Downloader.Download()
|
||||
$Installer = New-Object -ComObject Microsoft.Update.Installer
|
||||
$Installer.Updates = $SearchResult
|
||||
$Result = $Installer.Install()
|
||||
$Result
|
||||
}
|
||||
#remove temporary PSsession config
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock {
|
||||
Unregister-PSSessionConfiguration -Name 'VirtualAccount'
|
||||
Remove-Item -Path $env:TEMP\VirtualAccount.pssc
|
||||
}
|
||||
#>
|
||||
|
||||
# Install features on servers
|
||||
Invoke-Command -computername $ClusterNodeNames -ScriptBlock {
|
||||
Enable-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V -Online -NoRestart
|
||||
Install-WindowsFeature -Name "Failover-Clustering","RSAT-Clustering-Powershell","Hyper-V-PowerShell"
|
||||
}
|
||||
|
||||
# restart servers
|
||||
Restart-Computer -ComputerName $ClusterNodeNames -Protocol WSMan -Wait -For PowerShell
|
||||
#failsafe - sometimes it evaluates, that servers completed restart after first restart (hyper-v needs 2)
|
||||
Start-sleep 20
|
||||
|
||||
# create vSwitch (sometimes happens, that I need to restart servers again and then it will create vSwitch...)
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock {New-VMSwitch -Name $using:vswitchName -EnableEmbeddedTeaming $TRUE -NetAdapterName (Get-NetIPAddress -IPAddress 10.* ).InterfaceAlias}
|
||||
|
||||
#create cluster
|
||||
New-Cluster -Name $ClusterName -Node $ClusterNodeNames
|
||||
Start-Sleep 5
|
||||
Clear-DNSClientCache
|
||||
|
||||
#add file share witness
|
||||
#Create new directory
|
||||
$WitnessName=$ClusterName+"Witness"
|
||||
Invoke-Command -ComputerName DC -ScriptBlock {new-item -Path c:\Shares -Name $using:WitnessName -ItemType Directory}
|
||||
$accounts=@()
|
||||
$accounts+="corp\$($ClusterName)$"
|
||||
$accounts+="corp\Domain Admins"
|
||||
New-SmbShare -Name $WitnessName -Path "c:\Shares\$WitnessName" -FullAccess $accounts -CimSession DC
|
||||
#Set NTFS permissions
|
||||
Invoke-Command -ComputerName DC -ScriptBlock {(Get-SmbShare $using:WitnessName).PresetPathAcl | Set-Acl}
|
||||
#Set Quorum
|
||||
Set-ClusterQuorum -Cluster $ClusterName -FileShareWitness "\\DC\$WitnessName"
|
||||
|
||||
#Enable S2D
|
||||
Enable-ClusterS2D -CimSession $ClusterName -Verbose -Confirm:0
|
||||
|
||||
#configure thin volumes a default if available (because why not :)
|
||||
$OSInfo=Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
||||
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\'
|
||||
}
|
||||
if ($OSInfo.productname -eq "Azure Stack HCI" -and $OSInfo.CurrentBuild -ge 20348){
|
||||
Get-StoragePool -CimSession $ClusterName -FriendlyName S2D* | Set-StoragePool -ProvisioningTypeDefault Thin
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register Azure Stack HCI to Azure - if not registered, VMs are not added as cluster resources = AKS script will fail
|
||||
#download Azure module
|
||||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
|
||||
if (!(Get-InstalledModule -Name Az.StackHCI -ErrorAction Ignore)){
|
||||
Install-Module -Name Az.StackHCI -Force
|
||||
}
|
||||
|
||||
#login to azure
|
||||
#download Azure module
|
||||
if (!(Get-InstalledModule -Name az.accounts -ErrorAction Ignore)){
|
||||
Install-Module -Name Az.Accounts -Force
|
||||
}
|
||||
Connect-AzAccount -UseDeviceAuthentication
|
||||
|
||||
#select subscription if more available
|
||||
$subscription=Get-AzSubscription
|
||||
if (($subscription).count -gt 1){
|
||||
$subscription | Out-GridView -OutputMode Single | Set-AzContext
|
||||
}
|
||||
|
||||
#grab subscription ID
|
||||
$subscriptionID=(Get-AzContext).Subscription.id
|
||||
|
||||
<# Register AZSHCi without prompting for creds,
|
||||
Notes: As Dec. 2021, in Azure Stack HCI 21H2, if you Register-AzStackHCI the cluster multiple times in same ResourceGroup (e.g. default
|
||||
resource group name is AzSHCI-Cluster-rg) without run UnRegister-AzStackHCI first, although you may succeed in cluster registration, but
|
||||
sever node Arc integration will fail, even if you have deleted the ResourceGroup in Azure Portal before running Register-AzStackHCI #>
|
||||
|
||||
$armTokenItemResource = "https://management.core.windows.net/"
|
||||
$graphTokenItemResource = "https://graph.windows.net/"
|
||||
$azContext = Get-AzContext
|
||||
$authFactory = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory
|
||||
$graphToken = $authFactory.Authenticate($azContext.Account, $azContext.Environment, $azContext.Tenant.Id, $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $graphTokenItemResource).AccessToken
|
||||
$armToken = $authFactory.Authenticate($azContext.Account, $azContext.Environment, $azContext.Tenant.Id, $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $armTokenItemResource).AccessToken
|
||||
$id = $azContext.Account.Id
|
||||
|
||||
#grab location
|
||||
if (!(Get-InstalledModule -Name Az.Resources -ErrorAction Ignore)){
|
||||
Install-Module -Name Az.Resources -Force
|
||||
}
|
||||
|
||||
#register
|
||||
Register-AzStackHCI -SubscriptionID $subscriptionID -Region $AzureStackLocation -ComputerName $ClusterName -GraphAccessToken $graphToken -ArmAccessToken $armToken -AccountId $id
|
||||
|
||||
#Install Azure Stack HCI RSAT Tools to all nodes
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock {
|
||||
Install-WindowsFeature -Name RSAT-Azure-Stack-HCI
|
||||
}
|
||||
#Validate registration (query on just one node is needed)
|
||||
Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
||||
Get-AzureStackHCI
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Install modules and create MOC agent Service
|
||||
#Install required modules
|
||||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
|
||||
Install-Module -Name PowershellGet -Force -Confirm:$false -SkipPublisherCheck
|
||||
Update-Module -Name PowerShellGet
|
||||
|
||||
#to be able to install ArcHci and MOC, powershellget 2.2.5 needs to be used - to this posh restart is needed
|
||||
Start-Process -Wait -FilePath PowerShell -ArgumentList {
|
||||
Install-Module -Name MOC -Repository PSGallery -Force -AcceptLicense
|
||||
Install-Module -Name ArcHci -Force -Confirm:$false -SkipPublisherCheck -AcceptLicense
|
||||
}
|
||||
|
||||
#Increase MaxEvenlope and create session to copy files to
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock {Set-Item -Path WSMan:\localhost\MaxEnvelopeSizekb -Value 4096}
|
||||
|
||||
#distribute modules to cluster nodes
|
||||
$ModuleNames="ArcHci","Moc","DownloadSDK"
|
||||
$PSSessions=New-PSSession -ComputerName $ClusterNodeNames
|
||||
Foreach ($PSSession in $PSSessions){
|
||||
Foreach ($ModuleName in $ModuleNames){
|
||||
Copy-Item -Path $env:ProgramFiles\windowspowershell\modules\$ModuleName -Destination $env:ProgramFiles\windowspowershell\modules -ToSession $PSSession -Recurse -Force
|
||||
}
|
||||
Foreach ($ModuleName in $RequiredModules.ModuleName){
|
||||
Copy-Item -Path $env:ProgramFiles\windowspowershell\modules\$ModuleName -Destination $env:ProgramFiles\windowspowershell\modules -ToSession $PSSession -Recurse -Force
|
||||
}
|
||||
}
|
||||
|
||||
#Enable CredSSP
|
||||
# Temporarily enable CredSSP delegation to avoid double-hop issue
|
||||
foreach ($ClusterNodeName in $ClusterNodeNames){
|
||||
Enable-WSManCredSSP -Role "Client" -DelegateComputer $ClusterNodeName -Force
|
||||
}
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Enable-WSManCredSSP Server -Force }
|
||||
|
||||
$SecureStringPassword = ConvertTo-SecureString $CredSSPPassword -AsPlainText -Force
|
||||
$Credentials = New-Object System.Management.Automation.PSCredential ($CredSSPUserName, $SecureStringPassword)
|
||||
|
||||
#initialize MOC
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
Initialize-MocNode
|
||||
}
|
||||
|
||||
#Create volume for MOC if does not exist
|
||||
if (-not (Get-Volume -FriendlyName $VolumeName -CimSession $ClusterName -ErrorAction SilentlyContinue)) {
|
||||
New-Volume -FriendlyName $VolumeName -CimSession $ClusterName -Size 1TB -StoragePoolFriendlyName S2D*
|
||||
}
|
||||
|
||||
#prepare arc resource bridge
|
||||
#Configure MOC
|
||||
Invoke-Command -ComputerName $ClusterNodeNames[0] -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
$Vnet=New-MocNetworkSetting -Name hcirb-vnet -vswitchName $using:vswitchName -vipPoolStart $using:controlPlaneIP -vipPoolEnd $using:controlPlaneIP
|
||||
Set-MocConfig -workingDir "\\$using:ClusterName\ClusterStorage$\$using:VolumeName\workingDir" -vnet $vnet -imageDir $using:VolumePath\imageStore -skipHostLimitChecks -cloudConfigLocation $using:VolumePath\cloudStore -catalog aks-hci-stable-catalogs-ext -ring stable
|
||||
}
|
||||
|
||||
#Install MOC Cloud Agent Service
|
||||
Invoke-Command -ComputerName $ClusterNodeNames[0] -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
Install-moc
|
||||
}
|
||||
|
||||
# Disable CredSSP
|
||||
Disable-WSManCredSSP -Role Client
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Disable-WSManCredSSP Server }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Create custom location and install Arc Resource Bridge
|
||||
#Login to azure
|
||||
if (!(Get-AzContext)){
|
||||
Connect-AzAccount -UseDeviceAuthentication
|
||||
}
|
||||
|
||||
#generate variables
|
||||
#Grab registration info
|
||||
$RegistrationInfo=Invoke-Command -ComputerName $CLusterName -ScriptBlock {Get-AzureStackHCI}
|
||||
$AzureResourceUri= $RegistrationInfo.AzureResourceUri
|
||||
$HCIResourceGroupName=$AzureResourceUri.split("/")[4]
|
||||
$HCISubscriptionID=$AzureResourceUri.split("/")[2]
|
||||
#create bridge resource name
|
||||
$BridgeResourceName=("$($RegistrationInfo.AzureResourceName)-arcbridge").ToLower()
|
||||
|
||||
#install Az CLI
|
||||
#download
|
||||
Start-BitsTransfer -Source https://aka.ms/installazurecliwindows -Destination $env:userprofile\Downloads\AzureCLI.msi
|
||||
#install
|
||||
Start-Process msiexec.exe -Wait -ArgumentList "/I $env:userprofile\Downloads\AzureCLI.msi /quiet"
|
||||
#add az to enviromental variables so no posh restart is needed
|
||||
[System.Environment]::SetEnvironmentVariable('PATH',$Env:PATH+';C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin')
|
||||
|
||||
#add Az extensions
|
||||
az extension add --name customlocation
|
||||
az extension add --name azurestackhci
|
||||
az extension add --name arcappliance
|
||||
az extension add --name k8s-extension
|
||||
az extension add --name connectedk8s
|
||||
|
||||
#register namespaces
|
||||
#register
|
||||
$Providers="Microsoft.ExtendedLocation","Microsoft.ResourceConnector"
|
||||
foreach ($Provider in $Providers){
|
||||
Register-AzResourceProvider -ProviderNamespace $Provider
|
||||
}
|
||||
#wait until resource providers are registered
|
||||
foreach ($Provider in $Providers){
|
||||
do {
|
||||
$Status=Get-AzResourceProvider -ProviderNamespace $Provider
|
||||
Write-Output "Registration Status - $Provider : $(($status.RegistrationState -match 'Registered').Count)/$($Status.Count)"
|
||||
Start-Sleep 1
|
||||
} while (($status.RegistrationState -match "Registered").Count -ne ($Status.Count))
|
||||
}
|
||||
|
||||
#login with device authentication
|
||||
az login --use-device-code
|
||||
$allSubscriptions = (az account list | ConvertFrom-Json).ForEach({$_ | Select-Object -Property Name, id, tenantId })
|
||||
if (($allSubscriptions).Count -gt 1){
|
||||
$subscription = ($allSubscriptions | Out-GridView -OutputMode Single)
|
||||
az account set --subscription $subscription.id
|
||||
}
|
||||
#create arc appliance
|
||||
#generate config files
|
||||
#Enable CredSSP
|
||||
# Temporarily enable CredSSP delegation to avoid double-hop issue
|
||||
foreach ($ClusterNodeName in $ClusterNodeNames){
|
||||
Enable-WSManCredSSP -Role "Client" -DelegateComputer $ClusterNodeName -Force
|
||||
}
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Enable-WSManCredSSP Server -Force }
|
||||
|
||||
$SecureStringPassword = ConvertTo-SecureString $CredSSPPassword -AsPlainText -Force
|
||||
$Credentials = New-Object System.Management.Automation.PSCredential ($CredSSPUserName, $SecureStringPassword)
|
||||
|
||||
Invoke-Command -ComputerName $ClusterNodeNames[0] -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
New-ArcHciConfigFiles -subscriptionID $using:HCISubscriptionID -location $using:ArcResourceBridgeLocation -resourceGroup $using:HCIResourceGroupName -resourceName $using:BridgeResourceName -workDirectory "\\$using:ClusterName\ClusterStorage$\$using:VolumeName\workingDir"
|
||||
}
|
||||
# Disable CredSSP
|
||||
Disable-WSManCredSSP -Role Client
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Disable-WSManCredSSP Server }
|
||||
|
||||
#prepare
|
||||
az arcappliance prepare hci --config-file \\$ClusterName\ClusterStorage$\$VolumeName\workingDir\hci-appliance.yaml
|
||||
|
||||
#Create folder for config
|
||||
New-Item -Path $env:USERPROFILE\.kube -ItemType Directory -ErrorAction Ignore
|
||||
|
||||
#deploy control plane and export kube config
|
||||
az arcappliance deploy hci --config-file \\$ClusterName\ClusterStorage$\$VolumeName\workingDir\hci-appliance.yaml --outfile $env:USERPROFILE\.kube\config
|
||||
#create connection to Azure (might throw error, dont worry! It's being deployed on background)
|
||||
az arcappliance create hci --config-file \\$ClusterName\ClusterStorage$\$VolumeName\workingDir\hci-appliance.yaml --kubeconfig $env:USERPROFILE\.kube\config
|
||||
|
||||
#wait until appliance is running
|
||||
do {
|
||||
$Status=az arcappliance show --only-show-errors --resource-group $HCIResourceGroupName --name $BridgeResourceName | ConvertFrom-Json
|
||||
Write-Host -NoNewline -Object "."
|
||||
Start-Sleep 2
|
||||
} until ($status.status -match "Running")
|
||||
|
||||
#verify if appliance is running
|
||||
az arcappliance show --resource-group $HCIResourceGroupName --name $BridgeResourceName | ConvertFrom-Json
|
||||
|
||||
#Add K8s extension
|
||||
#create
|
||||
az k8s-extension create --cluster-type appliances --cluster-name $BridgeResourceName --resource-group $HCIResourceGroupName --name hci-vmoperator --extension-type Microsoft.AZStackHCI.Operator --scope cluster --release-namespace helm-operator2 --configuration-settings Microsoft.CustomLocation.ServiceAccount=hci-vmoperator --configuration-protected-settings-file \\$ClusterName\ClusterStorage$\$VolumeName\workingDir\hci-config.json --configuration-settings HCIClusterID=$AzureResourceUri --auto-upgrade true
|
||||
#validate
|
||||
az k8s-extension show --cluster-type appliances --cluster-name $BridgeResourceName --resource-group $HCIResourceGroupName --name hci-vmoperator
|
||||
|
||||
#Create custom location (has to be created after arcappliance deployment)
|
||||
az customlocation create --resource-group $HCIResourceGroupName --name $CustomLocationName --cluster-extension-ids "/subscriptions/$HCISubscriptionID/resourceGroups/$HCIResourceGroupName/providers/Microsoft.ResourceConnector/appliances/$BridgeResourceName/providers/Microsoft.KubernetesConfiguration/extensions/hci-vmoperator" --namespace hci-vmoperator --host-resource-id "/subscriptions/$HCISubscriptionID/resourceGroups/$HCIResourceGroupName/providers/Microsoft.ResourceConnector/appliances/$BridgeResourceName" --location $ArcResourceBridgeLocation
|
||||
|
||||
<# Or with PowerShell
|
||||
#install Az.CustomLocation module
|
||||
if (!(Get-InstalledModule -Name az.CustomLocation -ErrorAction Ignore)){
|
||||
Install-Module -Name Az.CustomLocation -Force
|
||||
}
|
||||
New-AzCustomLocation -ResourceGroupName $ResourceGroupName -Name $CustomLocationName -ClusterExtensionID "/subscriptions/$SubscriptionID/resourceGroups/$ResourceGroupName/providers/Microsoft.ResourceConnector/appliances/$BridgeResourceName/providers/Microsoft.KubernetesConfiguration/extensions/hci-vmoperator" -NameSpace hci-vmoperator -HostResourceID "/subscriptions/$SubscriptionID/resourceGroups/$ResourceGroupName/providers/Microsoft.ResourceConnector/appliances/$BridgeResourceName" -Location $ArcResourceBridgeLocation
|
||||
#>
|
||||
|
||||
#endregion
|
||||
|
||||
#region Copy kube config to nodes to have it available there
|
||||
$Sessions=New-PSSession -ComputerName $ClusterNodeNames
|
||||
|
||||
#copy kube to cluster nodes
|
||||
Foreach ($Session in $Sessions){
|
||||
Copy-Item -Path "$env:userprofile\.kube" -Destination $env:userprofile -ToSession $Session -Recurse -Force
|
||||
}
|
||||
|
||||
$Sessions | Remove-PSSession
|
||||
#endregion
|
||||
|
||||
#region create virtual network
|
||||
#Grab registration info
|
||||
$RegistrationInfo=Invoke-Command -ComputerName $CLusterName -ScriptBlock {Get-AzureStackHCI}
|
||||
$AzureResourceUri= $RegistrationInfo.AzureResourceUri
|
||||
$HCIResourceGroupName=$AzureResourceUri.split("/")[4]
|
||||
$HCISubscriptionID=$AzureResourceUri.split("/")[2]
|
||||
#create network
|
||||
az azurestackhci virtualnetwork create --subscription $HCISubscriptionID --resource-group $HCIResourceGroupName --extended-location name="/subscriptions/$HCISubscriptionID/resourceGroups/$HCIResourceGroupName/providers/Microsoft.ExtendedLocation/customLocations/$CustomLocationName" type="CustomLocation" --location $ArcResourceBridgeLocation --network-type "Transparent" --name $vnetName
|
||||
#endregion
|
||||
|
||||
#region create images
|
||||
#Create library volume
|
||||
if (-not (Get-VirtualDisk -CimSession $ClusterName -FriendlyName $LibraryVolumeName -ErrorAction Ignore)){
|
||||
New-Volume -StoragePoolFriendlyName "S2D*" -FriendlyName $LibraryVolumeName -FileSystem CSVFS_ReFS -Size 500GB -ResiliencySettingName Mirror -CimSession $ClusterName
|
||||
}
|
||||
|
||||
#region download Azure images to library
|
||||
#list windows server Offers
|
||||
Get-AzVMImageSku -Location $VMImageLocation -PublisherName "microsoftwindowsserver" -Offer "WindowsServer"
|
||||
|
||||
#Create managed disks with azure images
|
||||
foreach ($AzureImage in $AzureImages){
|
||||
$image=Get-AzVMImage -Location $VMImageLocation -PublisherName $AzureImage.PublisherName -Offer $AzureImage.Offer -SKU $AzureImage.SKU | Sort-Object Version -Descending |Select-Object -First 1
|
||||
$ImageVersionID = $image.id
|
||||
# Export the OS disk
|
||||
$imageOSDisk = @{Id = $ImageVersionID}
|
||||
$OSDiskConfig = New-AzDiskConfig -Location $VMImageLocation -CreateOption "FromImage" -ImageReference $imageOSDisk
|
||||
New-AzDisk -ResourceGroupName $ResourceGroupName -DiskName $AzureImage.SKU -Disk $OSDiskConfig
|
||||
}
|
||||
|
||||
#Download AZCopy
|
||||
# Download the package
|
||||
Start-BitsTransfer -Source "https://aka.ms/downloadazcopy-v10-windows" -Destination "$env:UserProfile\Downloads\AzCopy.zip"
|
||||
Expand-Archive -Path "$env:UserProfile\Downloads\AzCopy.zip" -DestinationPath "$env:UserProfile\Downloads\AZCopy" -Force
|
||||
$item=Get-ChildItem -Name azcopy.exe -Recurse -Path "$env:UserProfile\Downloads\AZCopy"
|
||||
Move-Item -Path "$env:UserProfile\Downloads\AZCopy\$item" -Destination "$env:UserProfile\Downloads\" -Force
|
||||
Remove-Item -Path "$env:UserProfile\Downloads\AZCopy\" -Recurse
|
||||
Remove-Item -Path "$env:UserProfile\Downloads\AzCopy.zip"
|
||||
|
||||
#Download Images
|
||||
foreach ($AzureImage in $AzureImages){
|
||||
#Grant Access https://docs.microsoft.com/en-us/azure/storage/common/storage-sas-overview
|
||||
$output=Grant-AzDiskAccess -ResourceGroupName $ResourceGroupName -DiskName $AzureImage.SKU -Access 'Read' -DurationInSecond 3600
|
||||
#Grab shared access signature
|
||||
$SAS=$output.accesssas
|
||||
#Download
|
||||
& $env:UserProfile\Downloads\azcopy.exe copy $sas "\\$ClusterName\ClusterStorage$\$LibraryVolumeName\$($AzureImage.SKU).vhd" --check-md5 NoCheck --cap-mbps 500
|
||||
#once disk is downloaded, disk access can be revoked
|
||||
Revoke-AzDiskAccess -ResourceGroupName $ResourceGroupName -Name $AzureImage.SKU
|
||||
#and disk itself can be removed
|
||||
Remove-AzDisk -ResourceGroupName $ResourceGroupName -DiskName $AzureImage.SKU -Force
|
||||
#and disk itself can be converted to VHDx and compacted
|
||||
Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
||||
Convert-VHD -Path "c:\clusterstorage\$using:LibraryVolumeName\$($using:AzureImage.sku).vhd" -DestinationPath "c:\clusterstorage\$using:LibraryVolumeName\$($using:AzureImage.sku).vhdx" -VHDType Dynamic -DeleteSource
|
||||
Optimize-VHD -Path "c:\clusterstorage\$using:LibraryVolumeName\$($using:AzureImage.sku).vhdx" -Mode Full
|
||||
}
|
||||
if ($AzureImage.SKU -like "*-smalldisk*"){
|
||||
#and it can be also expanded from default 32GB (since image is small to safe some space)
|
||||
Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
||||
Resize-VHD -Path "c:\clusterstorage\$using:LibraryVolumeName\$($using:AzureImage.sku).vhdx" -SizeBytes 127GB
|
||||
#mount VHD
|
||||
$VHDMount=Mount-VHD "c:\clusterstorage\$using:LibraryVolumeName\$($using:AzureImage.sku).vhdx" -Passthru
|
||||
$partition = $vhdmount | Get-Disk | Get-Partition | Where-Object PartitionNumber -Eq 4
|
||||
$partition | Resize-Partition -Size ($Partition | Get-PartitionSupportedSize).SizeMax
|
||||
$VHDMount| Dismount-VHD
|
||||
}
|
||||
#and since it's no longer smalldisk, it can be renamed
|
||||
Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
||||
$NewName=($using:AzureImage.SKU).replace("-smalldisk","")
|
||||
Rename-Item -Path "c:\clusterstorage\$using:LibraryVolumeName\$($using:AzureImage.sku).vhdx" -NewName "$NewName.vhdx"
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region create gallery image offer
|
||||
$RegistrationInfo=Invoke-Command -ComputerName $CLusterName -ScriptBlock {Get-AzureStackHCI}
|
||||
$AzureResourceUri= $RegistrationInfo.AzureResourceUri
|
||||
$HCIResourceGroupName=$AzureResourceUri.split("/")[4]
|
||||
$HCISubscriptionID=$AzureResourceUri.split("/")[2]
|
||||
foreach ($AzureImage in $AzureImages){
|
||||
#Since disk was expandend, -smalldisk can be removed from name
|
||||
$NewName=($AzureImage.SKU).replace("-smalldisk","")
|
||||
$galleryImageName=$NewName
|
||||
$galleryImageSourcePath="c:\ClusterStorage\$LibraryVolumeName\$galleryImageName.vhdx"
|
||||
$osType=$AzureImage.OSType
|
||||
az azurestackhci galleryimage create --subscription $HCISubscriptionID --resource-group $HCIResourceGroupName --extended-location name="/subscriptions/$HCISubscriptionID/resourceGroups/$HCIResourceGroupName/providers/Microsoft.ExtendedLocation/customLocations/$CustomLocationName" type="CustomLocation" --location $ArcResourceBridgeLocation --image-path $galleryImageSourcePath --name $galleryImageName --os-type $osType
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#now you can navigate to https://aka.ms/AzureArcVM to create VMs!
|
||||
|
||||
#region collect logs
|
||||
#Enable CredSSP
|
||||
# Temporarily enable CredSSP delegation to avoid double-hop issue
|
||||
foreach ($ClusterNodeName in $ClusterNodeNames){
|
||||
Enable-WSManCredSSP -Role "Client" -DelegateComputer $ClusterNodeName -Force
|
||||
}
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Enable-WSManCredSSP Server -Force }
|
||||
|
||||
$SecureStringPassword = ConvertTo-SecureString $CredSSPPassword -AsPlainText -Force
|
||||
$Credentials = New-Object System.Management.Automation.PSCredential ($CredSSPUserName, $SecureStringPassword)
|
||||
|
||||
#generate logs
|
||||
Invoke-Command -ComputerName $ClusterNodeNames[0] -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
get-archcilogs
|
||||
}
|
||||
|
||||
# Disable CredSSP
|
||||
Disable-WSManCredSSP -Role Client
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Disable-WSManCredSSP Server }
|
||||
|
||||
#copy logs to downloads
|
||||
$Session=New-PSSession -ComputerName $ClusterNodeNames[0]
|
||||
Copy-Item -Path $env:userprofile\Documents\archcilogs.zip -Destination $env:userprofile\Downloads -FromSession $Sessions
|
||||
$Session | Remove-PSSession
|
||||
#endregion
|
||||
|
||||
#region cleanup
|
||||
|
||||
#unregister Azure Stack HCI
|
||||
#Grab registration info
|
||||
$RegistrationInfo=Invoke-Command -ComputerName $CLusterName -ScriptBlock {Get-AzureStackHCI}
|
||||
$AzureResourceUri= $RegistrationInfo.AzureResourceUri
|
||||
$HCIResourceGroupName=$AzureResourceUri.split("/")[4]
|
||||
$HCISubscriptionID=$AzureResourceUri.split("/")[2]
|
||||
#login to Azure
|
||||
if (-not (Get-AzContext)){
|
||||
Login-AzAccount -UseDeviceAuthentication
|
||||
}
|
||||
$subscriptionID=(Get-AzContext).Subscription.id
|
||||
$armTokenItemResource = "https://management.core.windows.net/"
|
||||
$graphTokenItemResource = "https://graph.windows.net/"
|
||||
$azContext = Get-AzContext
|
||||
$authFactory = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory
|
||||
$graphToken = $authFactory.Authenticate($azContext.Account, $azContext.Environment, $azContext.Tenant.Id, $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $graphTokenItemResource).AccessToken
|
||||
$armToken = $authFactory.Authenticate($azContext.Account, $azContext.Environment, $azContext.Tenant.Id, $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $armTokenItemResource).AccessToken
|
||||
$id = $azContext.Account.Id
|
||||
UnRegister-AzStackHCI -SubscriptionID $subscriptionID -ComputerName $ClusterName -GraphAccessToken $graphToken -ArmAccessToken $armToken -AccountId $id -Confirm:0
|
||||
|
||||
#login to Azure
|
||||
if (-not (Get-AzContext)){
|
||||
Login-AzAccount -UseDeviceAuthentication
|
||||
}
|
||||
|
||||
#remove virtual network
|
||||
az azurestackhci virtualnetwork delete --subscription $HCISubscriptionID --resource-group $HCIResourceGroupName --name $vnetName --yes
|
||||
|
||||
#remove gallery images
|
||||
foreach ($AzureImage in $AzureImages){
|
||||
az azurestackhci galleryimage delete --subscription $HCISubscriptionID --resource-group $HCIResourceGroupName --name $AzureImage.SKU
|
||||
}
|
||||
|
||||
#remove custom location
|
||||
az customlocation delete --resource-group $HCIResourceGroupName --name $customLocationName --yes
|
||||
|
||||
#remove Kubernetes Extension
|
||||
$BridgeResourceName=("$($RegistrationInfo.AzureResourceName)-arcbridge").ToLower()
|
||||
az k8s-extension delete --cluster-type appliances --cluster-name $BridgeResourceName --resource-group $HCIResourceGroupName --name hci-vmoperator --yes
|
||||
|
||||
#remove appliance
|
||||
az arcappliance delete hci --config-file \\$ClusterName\clusterstorage$\MOC\workingDir\hci-appliance.yaml --yes
|
||||
|
||||
#remove configfiles
|
||||
Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
||||
Remove-ArcHciConfigFiles
|
||||
}
|
||||
|
||||
#remove resource group
|
||||
#login to Azure
|
||||
if (-not (Get-AzContext)){
|
||||
Login-AzAccount -UseDeviceAuthentication
|
||||
}
|
||||
Remove-AzResourceGroup -Name $ResourceGroupName -Force
|
||||
|
||||
#uninstall MOC
|
||||
#Enable CredSSP
|
||||
# Temporarily enable CredSSP delegation to avoid double-hop issue
|
||||
foreach ($ClusterNodeName in $ClusterNodeNames){
|
||||
Enable-WSManCredSSP -Role "Client" -DelegateComputer $ClusterNodeName -Force
|
||||
}
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Enable-WSManCredSSP Server -Force }
|
||||
|
||||
$SecureStringPassword = ConvertTo-SecureString $CredSSPPassword -AsPlainText -Force
|
||||
$Credentials = New-Object System.Management.Automation.PSCredential ($CredSSPUserName, $SecureStringPassword)
|
||||
|
||||
#uninstall MOC
|
||||
Invoke-Command -ComputerName $ClusterNodeNames[0] -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
Uninstall-Moc
|
||||
}
|
||||
# Disable CredSSP
|
||||
Disable-WSManCredSSP -Role Client
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Disable-WSManCredSSP Server }
|
||||
|
||||
#remove volume for MOC
|
||||
Remove-VirtualDisk -FriendlyName $VolumeName -CimSession $ClusterName -Confirm:0
|
||||
|
||||
#remove volume for MOC
|
||||
Remove-VirtualDisk -FriendlyName $LibraryVolumeName -CimSession $ClusterName -Confirm:0
|
||||
|
||||
#endregion
|
|
@ -279,9 +279,17 @@ if ($offline){
|
|||
|
||||
#region apply updates on nodes
|
||||
foreach ($Node in $Nodes){
|
||||
#make sure there is no maintenance mode
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Disabling maintenance mode - if there is any"
|
||||
Get-StorageFaultDomain -Type StorageScaleUnit -CimSession $ClusterName | Disable-StorageMaintenanceMode -CimSession $ClusterName
|
||||
#make sure all nodes are up
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') Make sure all nodes are up"
|
||||
$NodesNotUp=Get-ClusterNode -Cluster $ClusterName | Where-Object state -ne up
|
||||
if ($NodesNotUp){
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') Some nodes were not up, resuming : $($NodesNotUp.Name)"
|
||||
$NodesNotUp | Resume-ClusterNode -Failback Immediate | Out-Null
|
||||
if (Get-ClusterNode -Cluster $ClusterName | Where-Object state -ne up){
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') Resuming nodes failed. Interrupting"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
#check for repair jobs, if found, wait until finished
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Waiting for Storage jobs to finish"
|
||||
|
@ -368,11 +376,13 @@ if ($offline){
|
|||
$MSUpdateInstallResult=$Null
|
||||
}
|
||||
}
|
||||
}else{
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Skipping Microsoft Updates as requested"
|
||||
}
|
||||
|
||||
#Suspend node
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Suspending Cluster Node"
|
||||
Suspend-ClusterNode -Name "$Node" -Cluster $ClusterName -Drain -Wait | Out-Null
|
||||
Suspend-ClusterNode -Name "$Node" -Cluster $ClusterName -Drain -Wait -ErrorAction Ignore | Out-Null
|
||||
|
||||
if (Get-ClusterResource -Cluster $ClusterName | Where-Object OwnerNode -eq $Node | Where-Object State -eq "Online"){
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Suspending Cluster Node Failed. Resuming and terminating patch run"
|
||||
|
@ -380,9 +390,9 @@ if ($offline){
|
|||
break
|
||||
}
|
||||
|
||||
#enable storage maintenance mode
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Enabling Storage Maintenance mode"
|
||||
Get-StorageFaultDomain -CimSession $ClusterName -FriendlyName $Node | Enable-StorageMaintenanceMode -CimSession $ClusterName
|
||||
#enable storage maintenance mode (not needed as node goes to maintenance mode when suspended)
|
||||
#Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Enabling Storage Maintenance mode"
|
||||
#Get-StorageFaultDomain -CimSession $ClusterName -FriendlyName $Node | Enable-StorageMaintenanceMode -CimSession $ClusterName
|
||||
|
||||
#Install Dell updates https://dl.dell.com/content/manual36290092-dell-emc-system-update-version-1-9-3-0-user-s-guide.pdf?language=en-us&ps=true
|
||||
#assuming dell updates might interrupt server, therefore it's done during maintenance mode
|
||||
|
@ -401,11 +411,13 @@ if ($offline){
|
|||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Skipping Dell Updates as requested"
|
||||
}
|
||||
|
||||
#Check if reboot is required and reboot
|
||||
if (($Compliance | Where-Object {$_.NodeName -eq $Node -and $_.rebootrequired -eq $True}) -or ($Scanresult | Where-Object ($_.ComputerName -eq $node -and $_.MicrosoftUpdateRequired -eq $True)) -or ($ForceReboot -eq $True)){
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Reboot is required"
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Reboot is requested"
|
||||
#restart node and wait for PowerShell to come up (with powershell 7 you need to wait for WINRM :)
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Restarting Cluster Node"
|
||||
Restart-Computer -ComputerName $Node -Protocol WSMan -Wait -For PowerShell -Force | Out-Null
|
||||
|
@ -417,9 +429,9 @@ if ($offline){
|
|||
Start-Sleep 5
|
||||
}while($state -ne "Paused")
|
||||
|
||||
#disable storage maintenance mode
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Disabling Storage Maintenance mode"
|
||||
Get-StorageFaultDomain -Type StorageScaleUnit -CimSession $Node | Where-Object FriendlyName -eq $Node | Disable-StorageMaintenanceMode -CimSession $Node
|
||||
#disable storage maintenance mode (not needed as node resumes from maintenance mode when resumed)
|
||||
#Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Disabling Storage Maintenance mode"
|
||||
#Get-StorageFaultDomain -Type StorageScaleUnit -CimSession $Node | Where-Object FriendlyName -eq $Node | Disable-StorageMaintenanceMode -CimSession $Node
|
||||
|
||||
#resume cluster node
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Resuming Cluster Node"
|
||||
|
@ -430,6 +442,12 @@ if ($offline){
|
|||
do {Start-Sleep 5}while(
|
||||
Get-CimInstance -CimSession $Nodes -Namespace root\virtualization\v2 -ClassName Msvm_MigrationJob | Where-Object StatusDescriptions -eq "Job is running"
|
||||
)
|
||||
|
||||
#wait for node to resume from maintenance mode
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') $($Node): Waiting for node to resume from Maintenance mode"
|
||||
do {Start-Sleep 5}while(
|
||||
Get-StorageFaultDomain -CimSession $Node | Where-Object OperationalStatus -eq "In Maintenance Mode"
|
||||
)
|
||||
}
|
||||
|
||||
#cleanup updates folder on nodes
|
||||
|
|
|
@ -2,9 +2,9 @@ $LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPass
|
|||
|
||||
#Azure Stack HCI 22H2
|
||||
#labconfig will not domain join VMs, will add "Tools disk" and will also execute powershell command to make this tools disk online.
|
||||
1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "ASNode$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI22H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 1GB; VMProcessorCount=4 ; vTPM=$true ; AddToolsVHD=$True ; Unattend="NoDjoin" }}
|
||||
1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "ASNode$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI23H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 1GB; VMProcessorCount=4 ; vTPM=$true ; Unattend="NoDjoin" }}
|
||||
#labconfig for nested virtualization
|
||||
#1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "ASNode$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI22H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 6GB; VMProcessorCount=4 ; vTPM=$true ; AddToolsVHD=$True ; Unattend="NoDjoin" ; NestedVirt=$true }}
|
||||
#1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "ASNode$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI23H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 6GB; VMProcessorCount=4 ; vTPM=$true ; Unattend="NoDjoin" ; NestedVirt=$true }}
|
||||
|
||||
#Windows Admin Center in GW mode
|
||||
$LabConfig.VMs += @{ VMName = 'WACGW' ; ParentVHD = 'Win2022Core_G2.vhdx'; MGMTNICs=1}
|
||||
|
|
|
@ -23,51 +23,19 @@
|
|||
Install-WindowsFeature -Name RSAT-AD-PowerShell,GPMC
|
||||
|
||||
#populate objects
|
||||
New-HciAdObjectsPreCreation -Deploy -AsHciDeploymentUserCredential $Credentials -AsHciOUName $AsHCIOUName -AsHciPhysicalNodeList $Servers -DomainFQDN $DomainFQDN -AsHciClusterName $ClusterName -AsHciDeploymentPrefix $Prefix
|
||||
New-HciAdObjectsPreCreation -Deploy -AzureStackLCMUserCredential $Credentials -AsHciOUName $AsHCIOUName -AsHciPhysicalNodeList $Servers -DomainFQDN $DomainFQDN -AsHciClusterName $ClusterName -AsHciDeploymentPrefix $Prefix
|
||||
|
||||
#install management features to explore cluster,settings...
|
||||
Install-WindowsFeature -Name "RSAT-ADDS","RSAT-Clustering"
|
||||
#endregion
|
||||
|
||||
#region Deploy - run from ASNode1!
|
||||
#make D drives online
|
||||
$Servers="ASNode1","ASNode2","ASNode3","ASNode4"
|
||||
#add $Servers into trustedhosts
|
||||
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $($Servers -join ',') -Force
|
||||
#invoke command
|
||||
Invoke-Command -ComputerName $Servers -ScriptBlock {
|
||||
get-disk -Number 1 | Set-Disk -IsReadOnly $false
|
||||
get-disk -Number 1 | Set-Disk -IsOffline $false
|
||||
}
|
||||
|
||||
#Download files
|
||||
$downloadfolder="D:"
|
||||
$files=@()
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2210545" ; FileName="BootstrapCloudDeploymentTool.ps1" ; Description="Bootstrap PowerShell"}
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2210546" ; FileName="CloudDeployment_10.2303.0.36.zip" ; Description="Cloud Deployment Package"}
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2210608" ; FileName="Verify-CloudDeployment.zip_Hash.ps1" ; Description="Verify Cloud Deployment PowerShell"}
|
||||
|
||||
foreach ($file in $files){
|
||||
if (-not (Test-Path "$downloadfolder\$($file.filename)")){
|
||||
Start-BitsTransfer -Source $file.uri -Destination "$downloadfolder\$($file.filename)" -DisplayName "Downloading: $($file.filename)"
|
||||
}
|
||||
}
|
||||
|
||||
#Start bootstrap (script is looking for file "CloudDeployment_*.zip"
|
||||
& D:\BootstrapCloudDeploymentTool.ps1
|
||||
|
||||
#create deployment credentials
|
||||
$UserName="ASClus01-DeployUser"
|
||||
$Password="LS1setup!"
|
||||
$SecuredPassword = ConvertTo-SecureString $password -AsPlainText -Force
|
||||
$AzureStackLCMUserCredential = New-Object System.Management.Automation.PSCredential ($UserName,$SecuredPassword)
|
||||
|
||||
$UserName="Administrator"
|
||||
$Password="LS1setup!"
|
||||
$SecuredPassword = ConvertTo-SecureString $password -AsPlainText -Force
|
||||
$LocalAdminCred = New-Object System.Management.Automation.PSCredential ($UserName,$SecuredPassword)
|
||||
$CloudName="AzureCloud"
|
||||
#region prepare azure prerequisites - run from ASNode1 or Management (you can run from Management, just copy the resulting variables and use it in next region)
|
||||
#variables
|
||||
$StorageAccountName="asclus01$(Get-Random -Minimum 100000 -Maximum 999999)"
|
||||
$ServicePrincipal=$True #or false if you want to use MFA (and skip SP creation)
|
||||
$ServicePrincipalName="Azure-Stack-Registration"
|
||||
$ResourceGroupName="ASClus01-RG"
|
||||
$Location="EastUS"
|
||||
|
||||
#login to azure
|
||||
#download Azure module
|
||||
|
@ -77,6 +45,7 @@
|
|||
if (-not (Get-AzContext)){
|
||||
Connect-AzAccount -UseDeviceAuthentication
|
||||
}
|
||||
|
||||
#select subscription if more available
|
||||
$subscriptions=Get-AzSubscription
|
||||
#list subscriptions
|
||||
|
@ -87,12 +56,41 @@
|
|||
$SubscriptionID=$subscriptions.id
|
||||
}
|
||||
|
||||
if (!(Get-InstalledModule -Name az.Resources -ErrorAction Ignore)){
|
||||
Install-Module -Name Az.Resources -Force
|
||||
#make sure resource providers are registered
|
||||
if (!(Get-InstalledModule -Name "az.resources" -ErrorAction Ignore)){
|
||||
Install-Module -Name "az.resources" -Force
|
||||
}
|
||||
$Providers="Microsoft.ResourceConnector","Microsoft.Authorization","Microsoft.AzureStackHCI","Microsoft.HybridCompute","Microsoft.GuestConfiguration"
|
||||
foreach ($Provider in $Providers){
|
||||
Register-AzResourceProvider -ProviderNamespace $Provider
|
||||
#wait for provider to finish registration
|
||||
do {
|
||||
$Status=Get-AzResourceProvider -ProviderNamespace $Provider
|
||||
Write-Output "Registration Status - $Provider : $(($status.RegistrationState -match 'Registered').Count)/$($Status.Count)"
|
||||
Start-Sleep 1
|
||||
} while (($status.RegistrationState -match "Registered").Count -ne ($Status.Count))
|
||||
}
|
||||
|
||||
#Create Storage Account
|
||||
if (!(Get-InstalledModule -Name "az.storage"-ErrorAction Ignore)){
|
||||
Install-Module -Name "az.storage" -Force
|
||||
}
|
||||
|
||||
#create resource group first
|
||||
if (-not(Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction Ignore)){
|
||||
New-AzResourceGroup -Name $ResourceGroupName -Location $location
|
||||
}
|
||||
#create Storage Account
|
||||
If (-not(Get-AzStorageAccountKey -Name $StorageAccountName -ResourceGroupName $ResourceGroupName -ErrorAction Ignore)){
|
||||
New-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -SkuName Standard_LRS -Location $location -Kind StorageV2 -AccessTier Cool
|
||||
}
|
||||
$StorageAccountAccessKey=(Get-AzStorageAccountKey -Name $StorageAccountName -ResourceGroupName $ResourceGroupName | Select-Object -First 1).Value
|
||||
|
||||
#create service principal if requested
|
||||
if ($ServicePrincipal){
|
||||
#Create Azure Stack HCI registration role https://learn.microsoft.com/en-us/azure-stack/hci/deploy/register-with-azure#assign-permissions-from-azure-portal
|
||||
if (-not (Get-AzRoleDefinition -Name "Azure Stack HCI registration role - Custom")){
|
||||
#https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#azure-connected-machine-onboarding
|
||||
if (-not (Get-AzRoleDefinition -Name "Azure Stack HCI registration role - Custom" -ErrorAction Ignore)){
|
||||
$Content=@"
|
||||
{
|
||||
"Name": "Azure Stack HCI registration role - Custom",
|
||||
|
@ -110,7 +108,13 @@
|
|||
"Microsoft.Authorization/roleAssignments/read",
|
||||
"Microsoft.HybridCompute/register/action",
|
||||
"Microsoft.GuestConfiguration/register/action",
|
||||
"Microsoft.HybridConnectivity/register/action"
|
||||
"Microsoft.HybridConnectivity/register/action",
|
||||
"Microsoft.HybridCompute/machines/extensions/write",
|
||||
"Microsoft.HybridCompute/machines/extensions/read",
|
||||
"Microsoft.HybridCompute/machines/read",
|
||||
"Microsoft.HybridCompute/machines/write",
|
||||
"Microsoft.HybridCompute/privateLinkScopes/read",
|
||||
"Microsoft.GuestConfiguration/guestConfigurationAssignments/read"
|
||||
],
|
||||
"NotActions": [
|
||||
],
|
||||
|
@ -122,7 +126,8 @@
|
|||
$Content | Out-File "$env:USERPROFILE\Downloads\customHCIRole.json"
|
||||
New-AzRoleDefinition -InputFile "$env:USERPROFILE\Downloads\customHCIRole.json"
|
||||
}
|
||||
#Create AzADServicePrincipal for Azure Stack HCI registration
|
||||
|
||||
#Create AzADServicePrincipal for Azure Stack HCI registration (if it does not exist)
|
||||
$SP=Get-AZADServicePrincipal -DisplayName $ServicePrincipalName
|
||||
if (-not $SP){
|
||||
$SP=New-AzADServicePrincipal -DisplayName $ServicePrincipalName -Role "Azure Stack HCI registration role - Custom"
|
||||
|
@ -133,36 +138,104 @@
|
|||
#Create new SPN password
|
||||
$credential = New-Object -TypeName "Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential" -Property @{
|
||||
"KeyID" = (new-guid).Guid ;
|
||||
"EndDateTime" = [DateTime]::UtcNow.AddYears(10)
|
||||
"EndDateTime" = [DateTime]::UtcNow.AddYears(1)
|
||||
}
|
||||
$Creds=New-AzADAppCredential -PasswordCredentials $credential -ApplicationID $SP.AppID
|
||||
$SPNSecret=$Creds.SecretText
|
||||
Write-Host "Your Password is: " -NoNewLine ; Write-Host $SPNSecret -ForegroundColor Cyan
|
||||
$SPAppID=$SP.AppID
|
||||
}
|
||||
|
||||
Disconnect-AzAccount
|
||||
#output variables
|
||||
Write-Host -ForegroundColor Cyan @"
|
||||
#Variables to copy
|
||||
`$SubscriptionID=`"$SubscriptionID`"
|
||||
`$SPAppID=`"$SPAppID`"
|
||||
`$SPNSecret=`"$SPNSecret`"
|
||||
`$ResourceGroupName=`"$ResourceGroupName`"
|
||||
`$StorageAccountName=`"$StorageAccountName`"
|
||||
`$StorageAccountAccessKey=`"$StorageAccountAccessKey`"
|
||||
`$Location=`"$Location`"
|
||||
"@
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Deploy - run from ASNode1!
|
||||
#variables
|
||||
#create deployment credentials
|
||||
$UserName="ASClus01-DeployUser"
|
||||
$Password="LS1setup!"
|
||||
$SecuredPassword = ConvertTo-SecureString $password -AsPlainText -Force
|
||||
$AzureStackLCMUserCredential = New-Object System.Management.Automation.PSCredential ($UserName,$SecuredPassword)
|
||||
$UserName="Administrator"
|
||||
$Password="LS1setup!"
|
||||
$SecuredPassword = ConvertTo-SecureString $password -AsPlainText -Force
|
||||
$LocalAdminCred = New-Object System.Management.Automation.PSCredential ($UserName,$SecuredPassword)
|
||||
|
||||
#the one you have to populate if you did not run above region from Seed node
|
||||
<#
|
||||
$SubscriptionID=""
|
||||
$SPAppID="" #not needed if you use MFA
|
||||
$SPNSecret="" #not needed if you use MFA
|
||||
$ResourceGroupName=""
|
||||
$StorageAccountName=""
|
||||
$StorageAccountAccessKey=""
|
||||
$Location=""
|
||||
#>
|
||||
|
||||
#download folder
|
||||
$downloadfolder="c:\temp"
|
||||
|
||||
$Servers="ASNode1","ASNode2","ASNode3","ASNode4"
|
||||
|
||||
#Download files
|
||||
#create folder
|
||||
if (-not (Test-Path $downloadfolder)){New-Item -Path $downloadfolder -ItemType Directory}
|
||||
$files=@()
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2210545" ; FileName="BootstrapCloudDeploymentTool.ps1" ; Description="Bootstrap PowerShell"}
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2210546" ; FileName="CloudDeployment_10.2306.0.47.zip" ; Description="Cloud Deployment Package"}
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2210608" ; FileName="Verify-CloudDeployment.zip_Hash.ps1" ; Description="Verify Cloud Deployment PowerShell"}
|
||||
|
||||
foreach ($file in $files){
|
||||
if (-not (Test-Path "$downloadfolder\$($file.filename)")){
|
||||
Start-BitsTransfer -Source $file.uri -Destination "$downloadfolder\$($file.filename)" -DisplayName "Downloading: $($file.filename)"
|
||||
}
|
||||
}
|
||||
|
||||
#Start bootstrap (script is looking for file "CloudDeployment_*.zip"
|
||||
& $downloadfolder\BootstrapCloudDeploymentTool.ps1
|
||||
|
||||
#create authentication token (Service Principal or MFA)
|
||||
if ($SPAppID){
|
||||
$SPNsecStringPassword = ConvertTo-SecureString $SPNSecret -AsPlainText -Force
|
||||
$SPNCred=New-Object System.Management.Automation.PSCredential ($SP.AppID, $SPNsecStringPassword)
|
||||
$SPNCred=New-Object System.Management.Automation.PSCredential ($SPAppID, $SPNsecStringPassword)
|
||||
}else{
|
||||
Set-AuthenticationToken -RegistrationCloudName AzureCloud -RegistrationSubscriptionID $SubscriptionID
|
||||
}
|
||||
|
||||
#create config.json
|
||||
#add servers to trusted hosts so you can query IP address dynamically (in the lab we dont exactly now which adapter is first and what IP was assigned
|
||||
$TrustedHosts=@()
|
||||
$TrustedHosts+=$Servers
|
||||
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $($TrustedHosts -join ',') -Force
|
||||
|
||||
$Content=@"
|
||||
{
|
||||
"Version": "3.0.0.0",
|
||||
"Version": "10.0.0.0",
|
||||
"ScaleUnits": [
|
||||
{
|
||||
"DeploymentData": {
|
||||
"SecuritySettings": {
|
||||
"SecurityModeSealed": true,
|
||||
"SecuredCoreEnforced": true,
|
||||
"VBSProtection": true,
|
||||
"HVCIProtection": true,
|
||||
"DRTMProtection": true,
|
||||
"KernelDMAProtection": true,
|
||||
"DriftControlEnforced": true,
|
||||
"CredentialGuardEnforced": false,
|
||||
"CredentialGuardEnforced": true,
|
||||
"SMBSigningEnforced": true,
|
||||
"SMBClusterEncryption": false,
|
||||
"SideChannelMitigationEnforced": true,
|
||||
"BitlockerBootVolume": true,
|
||||
"BitlockerDataVolumes": true,
|
||||
"SEDProtectionEnforced": true,
|
||||
"BitlockerDataVolumes": false,
|
||||
"WDACEnforced": true
|
||||
},
|
||||
"Observability": {
|
||||
|
@ -172,6 +245,10 @@
|
|||
},
|
||||
"Cluster": {
|
||||
"Name": "ASClus01",
|
||||
"WitnessType": "Cloud",
|
||||
"WitnessPath": "",
|
||||
"CloudAccountName": "$StorageAccountName",
|
||||
"AzureServiceEndpoint": "core.windows.net",
|
||||
"StaticAddress": [
|
||||
""
|
||||
]
|
||||
|
@ -179,15 +256,9 @@
|
|||
"Storage": {
|
||||
"ConfigurationMode": "Express"
|
||||
},
|
||||
"OptionalServices": {
|
||||
"VirtualSwitchName": "",
|
||||
"CSVPath": "",
|
||||
"ARBRegion": "westeurope"
|
||||
},
|
||||
"TimeZone": "Pacific Standard Time",
|
||||
"NamingPrefix": "ASClus01",
|
||||
"DomainFQDN": "corp.contoso.com",
|
||||
"ExternalDomainFQDN": "corp.contoso.com",
|
||||
"InfrastructureNetwork": [
|
||||
{
|
||||
"VlanId": "0",
|
||||
|
@ -196,7 +267,7 @@
|
|||
"IPPools": [
|
||||
{
|
||||
"StartingAddress": "10.0.0.100",
|
||||
"EndingAddress": "10.0.0.199"
|
||||
"EndingAddress": "10.0.0.110"
|
||||
}
|
||||
],
|
||||
"DNSServers": [
|
||||
|
@ -276,10 +347,14 @@
|
|||
]
|
||||
}
|
||||
"@
|
||||
$Content | Out-File -FilePath d:\config.json
|
||||
$Content | Out-File -FilePath c:\config.json
|
||||
|
||||
#set trusted hosts back
|
||||
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "" -Force
|
||||
|
||||
#start deployment
|
||||
#make sure some prereqs (that will be fixed in future) are set
|
||||
<#
|
||||
#Make sure Windows Update is disabled and ping enabled (https://learn.microsoft.com/en-us/azure-stack/hci/hci-known-issues-2303)
|
||||
Microsoft.PowerShell.Core\Invoke-Command -ComputerName $Servers -ScriptBlock {
|
||||
reg add HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU /v NoAutoUpdate /t REG_DWORD /d 1 /f
|
||||
|
@ -290,19 +365,25 @@ $Content | Out-File -FilePath d:\config.json
|
|||
}
|
||||
#add hostnames and IPs to trusted hosts (bug that in BareMetal.psm1 is invoke-command with IP that is not in trusted hosts)
|
||||
$TrustedHosts=@()
|
||||
$TrustedHosts+=(Get-NetIPAddress -CimSession $Servers -InterfaceAlias Ethernet* -AddressFamily IPv4).IPAddress
|
||||
$TrustedHosts+=$Servers
|
||||
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $($TrustedHosts -join ',') -Force
|
||||
#>
|
||||
|
||||
#create secured storage access key
|
||||
$StorageAccountAccessKeySecured = ConvertTo-SecureString $StorageAccountAccessKey -AsPlainText -Force
|
||||
|
||||
#deploy
|
||||
.\Invoke-CloudDeployment -JSONFilePath D:\config.json -AzureStackLCMUserCredential $AzureStackLCMUserCredential -LocalAdminCredential $LocalAdminCred -RegistrationSPCredential $SPNCred -RegistrationCloudName $CloudName -RegistrationSubscriptionID $SubscriptionID
|
||||
|
||||
if ($SPAppID){
|
||||
.\Invoke-CloudDeployment -JSONFilePath c:\config.json -AzureStackLCMUserCredential $AzureStackLCMUserCredential -LocalAdminCredential $LocalAdminCred -RegistrationSPCredential $SPNCred -RegistrationCloudName AzureCloud -RegistrationSubscriptionID $SubscriptionID -RegistrationResourceGroupName $ResourceGroupName -WitnessStorageKey $StorageAccountAccessKeySecured -RegistrationRegion $Location
|
||||
}else{
|
||||
.\Invoke-CloudDeployment -JSONFilePath c:\config.json -AzureStackLCMUserCredential $AzureStackLCMUserCredential -LocalAdminCredential $LocalAdminCred -RegistrationCloudName AzureCloud -RegistrationSubscriptionID $SubscriptionID -RegistrationResourceGroupName $ResourceGroupName -WitnessStorageKey $StorageAccountAccessKeySecured -RegistrationRegion $Location
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Validate deployment - run from management VM!
|
||||
$SeedNode="ASNode1"
|
||||
|
||||
Invoke-Command -ComputerName $SeedNode -ScriptBlock {
|
||||
([xml](Get-Content C:\ecestore\efb61d70-47ed-8f44-5d63-bed6adc0fb0f\086a22e3-ef1a-7b3a-dc9d-f407953b0f84)) | Select-Xml -XPath "//Action/Steps/Step" | ForEach-Object { $_.Node } | Select-Object FullStepIndex, Status, Name, StartTimeUtc, EndTimeUtc, @{Name="Duration";Expression={new-timespan -Start $_.StartTimeUtc -End $_.EndTimeUtc } } | ft -AutoSize
|
||||
([xml](Get-Content C:\ecestore\efb61d70-47ed-8f44-5d63-bed6adc0fb0f\086a22e3-ef1a-7b3a-dc9d-f407953b0f84)) | Select-Xml -XPath "//Action/Steps/Step" | ForEach-Object { $_.Node } | Select-Object FullStepIndex, Status, Name, StartTimeUtc, EndTimeUtc, @{Name="Duration";Expression={new-timespan -Start $_.StartTimeUtc -End $_.EndTimeUtc } } | Format-Table -AutoSize
|
||||
}
|
||||
#endregion
|
|
@ -276,16 +276,17 @@ Foreach ($PSSession in $PSSessions){
|
|||
$VolumeName="AKS"
|
||||
$Servers=(Get-ClusterNode -Cluster $ClusterName).Name
|
||||
$DHCPServer="DC"
|
||||
$DHCPScopeID="10.0.0.0"
|
||||
$VIPPoolStart="10.0.1.2"
|
||||
$VIPPoolEnd="10.0.1.100"
|
||||
$VLANID=11
|
||||
$resourcegroupname="$ClusterName-rg"
|
||||
|
||||
#if dhcp is disabled:
|
||||
$k8sNodeIpPoolStart="10.0.1.101"
|
||||
$k8sNodeIpPoolEnd="10.0.1.254"
|
||||
$IPAddressPrefix="10.0.1.0/24"
|
||||
$DNSServers="10.0.1.1"
|
||||
$Gateway="10.0.1.1"
|
||||
$VLANID=11
|
||||
$resourcegroupname="$ClusterName-rg"
|
||||
|
||||
#JaromirK note: it would be great if I could simply run "Initialize-AksHciNode -ComputerName $ClusterName". I could simply skip credssp. Same applies for AksHciConfig and AksHciRegistration
|
||||
|
||||
|
@ -323,11 +324,13 @@ Foreach ($PSSession in $PSSessions){
|
|||
#configure aks
|
||||
#note: I'm assigning larger control plane VM than default as I saw IP disapperaring IP address if it was smaller in virtual environment (I tested manually incresed size to 8cores and 8GB RAM)
|
||||
Invoke-Command -ComputerName $servers[0] -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
#install nuget first
|
||||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
|
||||
#DHCP
|
||||
#$vnet = New-AksHciNetworkSetting -Name $using:vNetName -vSwitchName $using:vSwitchName -vippoolstart $using:vippoolstart -vippoolend $using:vippoolend
|
||||
$vnet = New-AksHciNetworkSetting -Name $using:vNetName -vSwitchName $using:vSwitchName -vippoolstart $using:vippoolstart -vippoolend $using:vippoolend -vlanID $using:VLANID
|
||||
#Static
|
||||
$vnet = New-AksHciNetworkSetting -Name $using:vNetName -ipAddressPrefix $using:IPAddressPrefix -vSwitchName $using:vSwitchName -vippoolstart $using:vippoolstart -vippoolend $using:vippoolend -k8sNodeIpPoolStart $using:k8sNodeIpPoolStart -k8sNodeIpPoolEnd $using:k8sNodeIpPoolEnd -vlanID $using:VLANID -DNSServers $using:DNSServers -gateway $Using:Gateway
|
||||
Set-AksHciConfig -vnet $vnet -workingDir c:\clusterstorage\$using:VolumeName\ImagesStore -imageDir c:\clusterstorage\$using:VolumeName\Images -cloudConfigLocation c:\clusterstorage\$using:VolumeName\Config -ClusterRoleName "$($using:ClusterName)_AKS" -controlPlaneVmSize 'Standard_A4_v2' # Get-AksHciVmSize
|
||||
#$vnet = New-AksHciNetworkSetting -Name $using:vNetName -ipAddressPrefix $using:IPAddressPrefix -vSwitchName $using:vSwitchName -vippoolstart $using:vippoolstart -vippoolend $using:vippoolend -k8sNodeIpPoolStart $using:k8sNodeIpPoolStart -k8sNodeIpPoolEnd $using:k8sNodeIpPoolEnd -vlanID $using:VLANID -DNSServers $using:DNSServers -gateway $Using:Gateway
|
||||
Set-AksHciConfig -vnet $vnet -workingDir c:\clusterstorage\$using:VolumeName\WorkDir -imageDir c:\clusterstorage\$using:VolumeName\Images -cloudConfigLocation c:\clusterstorage\$using:VolumeName\Config -ClusterRoleName "$($using:ClusterName)_AKS" -controlPlaneVmSize 'Standard_A4_v2' # Get-AksHciVmSize
|
||||
}
|
||||
|
||||
#validate config
|
||||
|
@ -518,7 +521,7 @@ $password="" #if blank, password will be created
|
|||
#create credentials
|
||||
$ClientID=$sp.AppId
|
||||
$SecureSecret= ConvertTo-SecureString $password -AsPlainText -Force
|
||||
$Credentials = New-Object System.Management.Automation.PSCredential ($ClientID , $SecureSecret)
|
||||
$SPCredentials = New-Object System.Management.Automation.PSCredential ($ClientID , $SecureSecret)
|
||||
|
||||
#register namespace Microsoft.KubernetesConfiguration and Microsoft.Kubernetes
|
||||
Register-AzResourceProvider -ProviderNamespace Microsoft.Kubernetes
|
||||
|
@ -529,7 +532,7 @@ Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
|||
#Generate kubeconfig
|
||||
Get-AksHciCredential -Name $using:KubernetesClusterName -confirm:0
|
||||
#onboard
|
||||
Enable-AksHciArcConnection -Name $using:KubernetesClusterName -tenantId $using:tenantID -subscriptionId $using:subscriptionID -resourcegroup $using:resourcegroup -Location $using:location -credential $using:Credentials
|
||||
Enable-AksHciArcConnection -Name $using:KubernetesClusterName -tenantId $using:tenantID -subscriptionId $using:subscriptionID -resourcegroup $using:resourcegroup -Location $using:location -credential $using:SPCredentials
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -30,12 +30,12 @@
|
|||
$files=@()
|
||||
#$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2026036" ; FileName="adksetup.exe" ; Description="Windows 10 ADK 1809"}
|
||||
#$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2022233" ; FileName="adkwinpesetup.exe" ; Description="WindowsPE 1809"}
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2165884" ; FileName="adksetup.exe" ; Description="Windows 11 21H2 ADK"}
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2166133" ; FileName="adkwinpesetup.exe" ; Description="WindowsPE for Windows 11 21H2"}
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2196127" ; FileName="adksetup.exe" ; Description="Windows 11 22H2 ADK"}
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2196224" ; FileName="adkwinpesetup.exe" ; Description="WindowsPE for Windows 11 22H2"}
|
||||
$Files+=@{Uri="https://download.microsoft.com/download/3/3/9/339BE62D-B4B8-4956-B58D-73C4685FC492/MicrosoftDeploymentToolkit_x64.msi" ; FileName="MicrosoftDeploymentToolkit_x64.msi" ; Description="Microsoft Deployment Toolkit"}
|
||||
#$Files+=@{Uri="https://software-download.microsoft.com/download/pr/AzureStackHCI_17784.1408_EN-US.iso" ; FileName="AzureStackHCI_17784.1408_EN-US.iso" ; Description="Azure Stack HCI ISO"}
|
||||
#$Files+=@{Uri="https://software-static.download.prss.microsoft.com/sg/download/888969d5-f34g-4e03-ac9d-1f9786c66749/AzureStackHCI_20348.587_en-us.iso" ; FileName="AzureStackHCI_20348.587_en-us.iso" ; Description="Azure Stack HCI ISO"}
|
||||
$Files+=@{Uri="https://software-static.download.prss.microsoft.com/dbazure/988969d5-f34g-4e03-ac9d-1f9786c66751/20349.1129.221007-2120.fe_release_hciv3_svc_refresh_SERVERAZURESTACKHCICOR_OEMRET_x64FRE_en-us.iso" ; FileName="AzureStackHCI_20349.1129_en-us.iso" ; Description="Azure Stack HCI ISO"}
|
||||
$Files+=@{Uri="https://software-static.download.prss.microsoft.com/dbazure/888969d5-f34g-4e03-ac9d-1f9786c66750/AzureStackHCI_20349.1607_en-us.iso" ; FileName="AzureStackHCI_20349.1607_en-us.iso" ; Description="Azure Stack HCI ISO"}
|
||||
$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=866658" ; FileName="SQL2019-SSEI-Expr.exe" ; Description="SQL Express 2019"}
|
||||
#$Files+=@{Uri="https://aka.ms/ssmsfullsetup" ; FileName="SSMS-Setup-ENU.exe" ; Description="SQL Management Studio"}
|
||||
foreach ($file in $files){
|
||||
|
@ -177,7 +177,7 @@
|
|||
}
|
||||
|
||||
#Import Operating System
|
||||
$ISO = Mount-DiskImage -ImagePath "$downloadfolder\AzureStackHCI_20349.1129_en-us.iso" -PassThru
|
||||
$ISO = Mount-DiskImage -ImagePath "$downloadfolder\AzureStackHCI_20349.1607_en-us.iso" -PassThru
|
||||
$ISOMediaPath = (Get-Volume -DiskImage $ISO).DriveLetter+':\'
|
||||
Import-mdtoperatingsystem -path "DS001:\Operating Systems" -SourcePath $ISOMediaPath -DestinationFolder "Azure Stack HCI SERVERAZURESTACKHCICORE x64" -Verbose
|
||||
|
||||
|
@ -588,6 +588,7 @@ $text = [IO.File]::ReadAllText($CustomSettingsFile) -replace "`n", "`r`n"
|
|||
|
||||
#region configure SQL to be able to access it remotely using MDTUser account(NamedPipes) or create dedicated SQL user (TCPIP)
|
||||
#Add permissions for MDT account to sql database
|
||||
#note: added -TrustServerCertificate as I saw an error since 05/2023 when invoke-sqlcmd ran. "The certificate chain was issued by an authority that is not trusted..."
|
||||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
|
||||
Install-Module -Name sqlserver -AllowClobber -Force
|
||||
if ((Get-ExecutionPolicy) -eq "Restricted"){
|
||||
|
@ -610,7 +611,7 @@ ALTER ROLE [db_datareader] ADD MEMBER [$env:userdomain\mdtuser]
|
|||
GO
|
||||
|
||||
"@
|
||||
Invoke-Sqlcmd -ServerInstance $MDTServer\sqlexpress -Database MDTDB -Query $sqlscript
|
||||
Invoke-Sqlcmd -ServerInstance $MDTServer\sqlexpress -Database MDTDB -Query $sqlscript -TrustServerCertificate
|
||||
|
||||
}elseif($Connection -eq "TCPIP"){
|
||||
#TCP (add user and change authentication mode to be able to use both SQL and Windows Auth
|
||||
|
@ -632,7 +633,7 @@ GO
|
|||
|
||||
"@
|
||||
#TCP
|
||||
Invoke-Sqlcmd -ServerInstance "tcp:$MDTServer" -Database MDTDB -Query $sqlscript
|
||||
Invoke-Sqlcmd -ServerInstance "tcp:$MDTServer" -Database MDTDB -Query $sqlscript -TrustServerCertificate
|
||||
#restart service to apply mixed auth mode
|
||||
Invoke-Command -ComputerName $MDTServer -scriptblock {
|
||||
Restart-Service 'MSSQL$SQLEXPRESS'
|
||||
|
@ -927,23 +928,18 @@ foreach ($idrac_ip in $idrac_ips){
|
|||
}
|
||||
Return $HVHosts
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region create DHCP reservation for machines
|
||||
#Create DHCP reservations for Hyper-V hosts
|
||||
#Add DHCP Reservations
|
||||
foreach ($HVHost in $HVHosts){
|
||||
if (!(Get-DhcpServerv4Reservation -ErrorAction SilentlyContinue -ComputerName $DHCPServer -ScopeId $ScopeID -ClientId ($HVHost.MACAddress).Replace(":","") | Where-Object IPAddress -eq $HVHost.IPAddress)){
|
||||
Add-DhcpServerv4Reservation -ComputerName $DHCPServer -ScopeId $ScopeID -IPAddress $HVHost.IPAddress -ClientId ($HVHost.MACAddress).Replace(":","")
|
||||
}
|
||||
Add-DhcpServerv4Reservation -ComputerName $DHCPServer -ScopeId $ScopeID -IPAddress $HVHost.IPAddress -ClientId ($HVHost.MACAddress).Replace(":","") -ErrorAction Ignore
|
||||
}
|
||||
|
||||
#configure NTP server in DHCP (might be useful if Servers have issues with time)
|
||||
if (!(get-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -ErrorAction SilentlyContinue)){
|
||||
Set-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -Value "10.0.0.1"
|
||||
}
|
||||
Set-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -Value "10.0.0.1" -ErrorAction Ignore
|
||||
|
||||
#endregion
|
||||
|
||||
#region add deploy info to AD Object and MDT Database
|
||||
|
@ -1718,4 +1714,3 @@ foreach ($idrac_ip in $idrac_ips){
|
|||
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
|
@ -163,6 +163,7 @@
|
|||
$DestinationClusterName="AzSHCI-Cluster"
|
||||
$SourceStoragePath="C:\ClusterStorage\CSV1"
|
||||
$DestinationStoragePath="C:\ClusterStorage\CSV1"
|
||||
$DestinationSwitchName=(Get-VMSwitch -CimSession ((Get-ClusterNode -Cluster $ClusterName).Name | Select-Object -First 1)).Name
|
||||
$VMNames=(Get-VM -cimsession (get-clusternode -cluster $SourceClusterName).Name | Where-Object Path -Like "$SourceStoragePath*").Name
|
||||
|
||||
# Temporarily enable CredSSP delegation to avoid double-hop issue
|
||||
|
@ -178,27 +179,42 @@
|
|||
#do the move
|
||||
foreach ($VMName in $VMNames){
|
||||
#remove VM from HA Resources
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Removing VM $($VM.VMName) From Cluster resources"
|
||||
Get-ClusterResource -Cluster $SourceClusterName -name "Virtual Machine $VMName" -ErrorAction Ignore | Remove-ClusterResource -force
|
||||
Get-ClusterGroup -Cluster $SourceClusterName -Name $VMName -ErrorAction Ignore | Remove-ClusterGroup -force
|
||||
#Grab random node in cluster $DestinationClusterName
|
||||
#Grab random node in cluster $DestinationClusterName (does not have to be random of course)
|
||||
$VM=Get-VM -Cimsession (get-clusternode -cluster $SourceClusterName).Name -Name $VMName
|
||||
$VM | Stop-VM -Save
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Stopping VM $($VM.VMName)"
|
||||
$VM | Stop-VM
|
||||
#or just save it, but we need to update version anyway
|
||||
#$VM | Stop-VM -Save
|
||||
#If there is different switch name in destination node, you should consider disconnecting vNICs before removing VM and saving config
|
||||
$VM | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter
|
||||
#Backup config
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Backing up VM config"
|
||||
Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Copy-Item -Path "$($using:VM.Path)\Virtual Machines" -Destination "$($using:VM.Path)\Virtual Machines Bak" -Recurse}
|
||||
#If there is different switch name in destination node, you should consider disconnecting vNICs first
|
||||
#$VM | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter
|
||||
#Remove VM
|
||||
$VM | Remove-VM -Force
|
||||
#Remove VM (Just making sure hyper-v command is used, because SCVMM Remove-VM also removes VHDs)
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Removing VM"
|
||||
$VM | Hyper-V\Remove-VM -Force
|
||||
#Restore Config
|
||||
#Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Copy-Item -Path "$($using:VM.Path)\Virtual Machines Bak\*" -Destination "$($using:VM.Path)\Virtual Machines" -Recurse}
|
||||
Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Move-Item -Path "$($using:VM.Path)\Virtual Machines Bak\*" -Destination "$($using:VM.Path)\Virtual Machines"}
|
||||
Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Remove-Item -Path "$($using:VM.Path)\Virtual Machines Bak\"}
|
||||
#zip config to have a backup (in case something goes wrong with updating VM version)
|
||||
Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Compress-Archive -Path "$($using:VM.Path)\Virtual Machines\" -DestinationPath "$($using:VM.Path)\Virtual Machines.zip"}
|
||||
#Copy machine to destination node using CredSSP
|
||||
$VolumeName=$DestinationStoragePath | Split-Path -Leaf
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Copying VM $($VM.VMName) to \\$DestinationClusterName\ClusterStorage$\$VolumeName\"
|
||||
Invoke-Command -ComputerName ($Servers | Get-Random) -Credential $Credentials -Authentication Credssp -ScriptBlock {Copy-Item -Path "$($using:VM.Path)" -Destination "\\$using:DestinationClusterName\ClusterStorage$\$using:VolumeName\" -Recurse}
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Copying VM $($VM.VMName) Finished"
|
||||
#Import VM and Start
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Importing VM $($VM.VMName) And Starting"
|
||||
$DestinationHost=(get-clusternode -cluster $DestinationClusterName | get-random).Name
|
||||
$NewVM=Import-VM -Path "$DestinationStoragePath\$($VM.Name)\Virtual Machines\$($VM.ID.GUID).vmcx" -CimSession $DestinationHost
|
||||
$NewVM | Update-VMVersion -Force
|
||||
$NewVM | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName $DestinationSwitchName
|
||||
$NewVM | Start-VM
|
||||
}
|
||||
|
||||
|
@ -213,6 +229,7 @@
|
|||
$DestinationClusterName="AzSHCI-Cluster"
|
||||
$SourceClusterVolumes=(Get-ClusterSharedVolume -Cluster $SourceClusterName).sharedvolumeinfo.Friendlyvolumename
|
||||
$DestinationClusterVolumes=(Get-ClusterSharedVolume -Cluster $DestinationClusterName).sharedvolumeinfo.Friendlyvolumename
|
||||
$DestinationSwitchName=(Get-VMSwitch -CimSession ((Get-ClusterNode -Cluster $ClusterName).Name | Select-Object -First 1)).Name
|
||||
|
||||
# Temporarily enable CredSSP delegation to avoid double-hop issue
|
||||
$Servers=(get-clusternode -cluster $SourceClusterName).Name
|
||||
|
@ -234,27 +251,45 @@
|
|||
}
|
||||
$VMNames=(Get-VM -cimsession (get-clusternode -cluster $SourceClusterName).Name | Where-Object Path -Like "$SourceClusterVolume*").Name
|
||||
foreach ($VMName in $VMNames){
|
||||
|
||||
|
||||
#remove VM from HA Resources
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Removing VM $($VM.VMName) From Cluster resources"
|
||||
Get-ClusterResource -Cluster $SourceClusterName -name "Virtual Machine $VMName" -ErrorAction Ignore | Remove-ClusterResource -force
|
||||
Get-ClusterGroup -Cluster $SourceClusterName -Name $VMName -ErrorAction Ignore | Remove-ClusterGroup -force
|
||||
#Grab random node in cluster $DestinationClusterName (does not have to be random of course)
|
||||
$VM=Get-VM -Cimsession (get-clusternode -cluster $SourceClusterName).Name -Name $VMName
|
||||
$VM | Stop-VM -Save
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Stopping VM $($VM.VMName)"
|
||||
$VM | Stop-VM
|
||||
#or just save it, but we need to update version anyway
|
||||
#$VM | Stop-VM -Save
|
||||
#If there is different switch name in destination node, you should consider disconnecting vNICs before removing VM and saving config
|
||||
$VM | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter
|
||||
#Backup config
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Backing up VM config"
|
||||
Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Copy-Item -Path "$($using:VM.Path)\Virtual Machines" -Destination "$($using:VM.Path)\Virtual Machines Bak" -Recurse}
|
||||
#If there is different switch name in destination node, you should consider disconnecting vNICs first
|
||||
#$VM | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter
|
||||
#Backup config
|
||||
Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Copy-Item -Path "$($using:VM.Path)\Virtual Machines" -Destination "$($using:VM.Path)\Virtual Machines Bak" -Recurse}
|
||||
#Remove VM
|
||||
$VM | Remove-VM -Force
|
||||
#Remove VM (Just making sure hyper-v command is used, because SCVMM Remove-VM also removes VHDs)
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Removing VM"
|
||||
$VM | Hyper-V\Remove-VM -Force
|
||||
#Restore Config
|
||||
#Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Copy-Item -Path "$($using:VM.Path)\Virtual Machines Bak\*" -Destination "$($using:VM.Path)\Virtual Machines" -Recurse}
|
||||
Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Move-Item -Path "$($using:VM.Path)\Virtual Machines Bak\*" -Destination "$($using:VM.Path)\Virtual Machines"}
|
||||
Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Remove-Item -Path "$($using:VM.Path)\Virtual Machines Bak\"}
|
||||
#zip config to have a backup (in case something goes wrong with updating VM version)
|
||||
Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Compress-Archive -Path "$($using:VM.Path)\Virtual Machines\" -DestinationPath "$($using:VM.Path)\Virtual Machines.zip"}
|
||||
#Copy machine to destination node using CredSSP
|
||||
$VolumeName=$DestinationClusterVolumes[$index] | Split-Path -Leaf
|
||||
$VolumeName=$DestinationStoragePath | Split-Path -Leaf
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Copying VM $($VM.VMName) to \\$DestinationClusterName\ClusterStorage$\$VolumeName\"
|
||||
Invoke-Command -ComputerName ($Servers | Get-Random) -Credential $Credentials -Authentication Credssp -ScriptBlock {Copy-Item -Path "$($using:VM.Path)" -Destination "\\$using:DestinationClusterName\ClusterStorage$\$using:VolumeName\" -Recurse}
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Copying VM $($VM.VMName) Finished"
|
||||
#Import VM and Start
|
||||
Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Importing VM $($VM.VMName) And Starting"
|
||||
$DestinationHost=(get-clusternode -cluster $DestinationClusterName | get-random).Name
|
||||
$NewVM=Import-VM -Path "$($DestinationClusterVolumes[$index])\$($VM.Name)\Virtual Machines\$($VM.ID.GUID).vmcx" -CimSession $DestinationHost
|
||||
$NewVM=Import-VM -Path "$DestinationStoragePath\$($VM.Name)\Virtual Machines\$($VM.ID.GUID).vmcx" -CimSession $DestinationHost
|
||||
$NewVM | Update-VMVersion -Force
|
||||
$NewVM | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName $DestinationSwitchName
|
||||
$NewVM | Start-VM
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
$LabConfig=@{ManagementSubnetIDs=0..1 ;DomainAdminName='LabAdmin'; AdminPassword='LS1setup!' ; <#Prefix = 'MSLab-'#> ; DCEdition='4'; Internet=$true ; TelemetryLevel='Full' ; TelemetryNickname='JaromiK' ; CustomDnsForwarders="10.8.8.8","10.7.7.7" ; AdditionalNetworksConfig=@(); VMs=@()}
|
||||
|
||||
#2 nodes for AzSHCI Cluster
|
||||
1..2 | ForEach-Object {$VMNames="ASHCIRB" ; $LABConfig.VMs += @{ VMName = "$VMNames$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI22H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 4TB ; MemoryStartupBytes= 14GB; VMProcessorCount="Max" ; NestedVirt=$true ; VirtualTPM=$true}}
|
||||
|
||||
#or small just when host is limited
|
||||
#1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName = "AzSHCI$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI21H2_G2.vhdx' ; HDDNumber = 10 ; HDDSize= 10TB ; MemoryStartupBytes= 1GB; VMProcessorCount="Max" ; vTPM=$true}}
|
||||
|
||||
#Optional Windows Admin Center in GW mode
|
||||
$LabConfig.VMs += @{ VMName = 'WACGW' ; ParentVHD = 'Win2022Core_G2.vhdx'; MGMTNICs=1}
|
||||
|
||||
#Management machine
|
||||
$LabConfig.VMs += @{ VMName = 'Management' ; ParentVHD = 'Win2022_G2.vhdx' ; MGMTNICs=1 }
|
|
@ -0,0 +1,487 @@
|
|||
#https://learn.microsoft.com/en-us/azure/aks/hybrid/deploy-aks-service-hci?tabs=powershell#step-2-install-the-aks-hybrid-extension-on-the-azure-arc-resource-bridge
|
||||
#https://aka.ms/ArcEnabledHCI
|
||||
|
||||
#region Variables
|
||||
$ClusterNodeNames="ASHCIRB1","ASHCIRB2"
|
||||
$ClusterName="ASHCIRB-Cluster"
|
||||
$vswitchName="vSwitch"
|
||||
$controlPlaneIP="10.0.0.101"
|
||||
$VolumeName="MOC"
|
||||
$VolumePath="c:\ClusterStorage\$VolumeName"
|
||||
$CredSSPUserName="CORP\LabAdmin"
|
||||
$CredSSPPassword="LS1setup!"
|
||||
$CustomLocationName="$ClusterName"
|
||||
$CustomLocationNameSpace="customlocation-ns"
|
||||
|
||||
#ARC VMs virtual network name (the one that is visible in portal when you create a VM)
|
||||
$vnetName="management"
|
||||
|
||||
#AKS config
|
||||
$AKSvnetName="AKSvnet"
|
||||
$VIPPoolStart="10.0.1.2"
|
||||
$VIPPoolEnd="10.0.1.50"
|
||||
$DHCPServer="DC"
|
||||
$DHCPScopeID="10.0.1.0"
|
||||
$VLANID=11
|
||||
|
||||
#for static aks deployment
|
||||
$IPAddressPrefix="10.0.1.0/24"
|
||||
$Gateway="10.0.1.1"
|
||||
$dnsservers="10.0.1.1"
|
||||
$k8snodeippoolstart="10.0.1.51"
|
||||
$k8snodeippoolend="10.0.1.254"
|
||||
|
||||
#if you want custom images to add
|
||||
$LibraryVolumeName="Library" #volume for Gallery images for VMs
|
||||
$AzureImages=@()
|
||||
$AzureImages+=@{PublisherName = "microsoftwindowsserver";Offer="windowsserver";SKU="2022-datacenter-azure-edition-smalldisk";OSType="Windows"} #OS TYpe can be "Windows" or "Linux" - first letter has to be capital!
|
||||
$AzureImages+=@{PublisherName = "microsoftwindowsserver";Offer="windowsserver";SKU="2022-datacenter-azure-edition-core-smalldisk";OSType="Windows"} #OS TYpe can be "Windows" or "Linux" - first letter has to be capital!
|
||||
|
||||
|
||||
#Install or update Azure packages
|
||||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
|
||||
$ModuleNames="Az.Accounts","Az.Compute","Az.Resources","Az.StackHCI"
|
||||
foreach ($ModuleName in $ModuleNames){
|
||||
$Module=Get-InstalledModule -Name $ModuleName -ErrorAction Ignore
|
||||
if ($Module){$LatestVersion=(Find-Module -Name $ModuleName).Version}
|
||||
if (-not($Module) -or ($Module.Version -lt $LatestVersion)){
|
||||
Install-Module -Name $ModuleName -Force
|
||||
}
|
||||
}
|
||||
|
||||
#login to Azure
|
||||
if (-not (Get-AzContext)){
|
||||
Login-AzAccount -UseDeviceAuthentication
|
||||
}
|
||||
|
||||
$ResourceGroupName="$ClusterName-rg"
|
||||
$SubscriptionID=(Get-AzContext).Subscription.ID
|
||||
|
||||
#select context
|
||||
$context=Get-AzContext -ListAvailable
|
||||
if (($context).count -gt 1){
|
||||
$context=$context | Out-GridView -OutputMode Single
|
||||
$context | Set-AzContext
|
||||
}
|
||||
|
||||
#I did only test same for all (EastUS)
|
||||
$VMImageLocation="eastus"
|
||||
$ArcResourceBridgeLocation="eastus"
|
||||
$AzureStackLocation="eastus"
|
||||
|
||||
<#or populate by choosing your own
|
||||
#grab region where to grab VMs from
|
||||
$VMImageLocation = (Get-AzLocation | Where-Object Providers -Contains "Microsoft.Compute" | Out-GridView -OutputMode Single -Title "Choose location where to grab VMs from").Location
|
||||
|
||||
#grab location for Arc Resource Bridge and Custom location
|
||||
$ArcResourceBridgeLocation=(Get-AzLocation | Where-Object Providers -Contains "Microsoft.ResourceConnector" | Out-GridView -OutputMode Single -Title "Choose location for Arc Resource Bridge and Custom location").Location
|
||||
|
||||
#grab location for Azure Stack
|
||||
$AzureStackLocation=(Get-AzResourceProvider -ProviderNamespace Microsoft.AzureStackHCI).Where{($_.ResourceTypes.ResourceTypeName -eq 'clusters' -and $_.RegistrationState -eq 'Registered')}.Locations | Out-GridView -OutputMode Single -Title "Please select Location for AzureStackHCI metadata"
|
||||
$AzureStackLocation = $region -replace '\s',''
|
||||
$AzureStackLocation = $region.ToLower()
|
||||
#>
|
||||
|
||||
#endregion
|
||||
|
||||
#region Create 2 node cluster (just simple. Not for prod - follow hyperconverged scenario for real clusters https://github.com/microsoft/MSLab/tree/master/Scenarios/AzSHCI%20Deployment%2022H2%20Edition)
|
||||
# Install features for management on server
|
||||
Install-WindowsFeature -Name RSAT-Clustering,RSAT-Clustering-Mgmt,RSAT-Clustering-PowerShell,RSAT-Hyper-V-Tools
|
||||
|
||||
# Update servers (optional)
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock {
|
||||
New-PSSessionConfigurationFile -RunAsVirtualAccount -Path $env:TEMP\VirtualAccount.pssc
|
||||
Register-PSSessionConfiguration -Name 'VirtualAccount' -Path $env:TEMP\VirtualAccount.pssc -Force
|
||||
} -ErrorAction Ignore
|
||||
# Run Windows Update via ComObject.
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ConfigurationName 'VirtualAccount' {
|
||||
$Searcher = New-Object -ComObject Microsoft.Update.Searcher
|
||||
$SearchCriteriaAllUpdates = "IsInstalled=0 and DeploymentAction='Installation' or
|
||||
IsPresent=1 and DeploymentAction='Uninstallation' or
|
||||
IsInstalled=1 and DeploymentAction='Installation' and RebootRequired=1 or
|
||||
IsInstalled=0 and DeploymentAction='Uninstallation' and RebootRequired=1"
|
||||
$SearchResult = $Searcher.Search($SearchCriteriaAllUpdates).Updates
|
||||
$Session = New-Object -ComObject Microsoft.Update.Session
|
||||
$Downloader = $Session.CreateUpdateDownloader()
|
||||
$Downloader.Updates = $SearchResult
|
||||
$Downloader.Download()
|
||||
$Installer = New-Object -ComObject Microsoft.Update.Installer
|
||||
$Installer.Updates = $SearchResult
|
||||
$Result = $Installer.Install()
|
||||
$Result
|
||||
}
|
||||
#remove temporary PSsession config
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock {
|
||||
Unregister-PSSessionConfiguration -Name 'VirtualAccount'
|
||||
Remove-Item -Path $env:TEMP\VirtualAccount.pssc
|
||||
}
|
||||
|
||||
# Install features on servers
|
||||
Invoke-Command -computername $ClusterNodeNames -ScriptBlock {
|
||||
Enable-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V -Online -NoRestart
|
||||
Install-WindowsFeature -Name "Failover-Clustering","RSAT-Clustering-Powershell","Hyper-V-PowerShell"
|
||||
}
|
||||
|
||||
# restart servers
|
||||
Restart-Computer -ComputerName $ClusterNodeNames -Protocol WSMan -Wait -For PowerShell
|
||||
#failsafe - sometimes it evaluates, that servers completed restart after first restart (hyper-v needs 2)
|
||||
Start-sleep 20
|
||||
|
||||
# create vSwitch (sometimes happens, that I need to restart servers again and then it will create vSwitch...)
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock {New-VMSwitch -Name $using:vswitchName -EnableEmbeddedTeaming $TRUE -NetAdapterName (Get-NetIPAddress -IPAddress 10.* ).InterfaceAlias}
|
||||
|
||||
#create cluster
|
||||
New-Cluster -Name $ClusterName -Node $ClusterNodeNames
|
||||
Start-Sleep 5
|
||||
Clear-DNSClientCache
|
||||
|
||||
#add file share witness
|
||||
#Create new directory
|
||||
$WitnessName=$ClusterName+"Witness"
|
||||
Invoke-Command -ComputerName DC -ScriptBlock {new-item -Path c:\Shares -Name $using:WitnessName -ItemType Directory}
|
||||
$accounts=@()
|
||||
$accounts+="corp\$($ClusterName)$"
|
||||
$accounts+="corp\Domain Admins"
|
||||
New-SmbShare -Name $WitnessName -Path "c:\Shares\$WitnessName" -FullAccess $accounts -CimSession DC
|
||||
#Set NTFS permissions
|
||||
Invoke-Command -ComputerName DC -ScriptBlock {(Get-SmbShare $using:WitnessName).PresetPathAcl | Set-Acl}
|
||||
#Set Quorum
|
||||
Set-ClusterQuorum -Cluster $ClusterName -FileShareWitness "\\DC\$WitnessName"
|
||||
|
||||
#Enable S2D
|
||||
Enable-ClusterS2D -CimSession $ClusterName -Verbose -Confirm:0
|
||||
|
||||
#configure thin volumes a default if available (because why not :)
|
||||
$OSInfo=Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
||||
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\'
|
||||
}
|
||||
if ($OSInfo.productname -eq "Azure Stack HCI" -and $OSInfo.CurrentBuild -ge 20348){
|
||||
Get-StoragePool -CimSession $ClusterName -FriendlyName S2D* | Set-StoragePool -ProvisioningTypeDefault Thin
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register Azure Stack HCI to Azure - if not registered, VMs are not added as cluster resources = AKS script will fail
|
||||
#download Azure module
|
||||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
|
||||
if (!(Get-InstalledModule -Name Az.StackHCI -ErrorAction Ignore)){
|
||||
Install-Module -Name Az.StackHCI -Force
|
||||
}
|
||||
|
||||
#login to azure
|
||||
#download Azure module
|
||||
if (!(Get-InstalledModule -Name az.accounts -ErrorAction Ignore)){
|
||||
Install-Module -Name Az.Accounts -Force
|
||||
}
|
||||
Connect-AzAccount -UseDeviceAuthentication
|
||||
|
||||
#select subscription if more available
|
||||
$subscription=Get-AzSubscription
|
||||
if (($subscription).count -gt 1){
|
||||
$subscription | Out-GridView -OutputMode Single | Set-AzContext
|
||||
}
|
||||
|
||||
#grab subscription ID
|
||||
$subscriptionID=(Get-AzContext).Subscription.id
|
||||
|
||||
<# Register AZSHCi without prompting for creds,
|
||||
Notes: As Dec. 2021, in Azure Stack HCI 21H2, if you Register-AzStackHCI the cluster multiple times in same ResourceGroup (e.g. default
|
||||
resource group name is AzSHCI-Cluster-rg) without run UnRegister-AzStackHCI first, although you may succeed in cluster registration, but
|
||||
sever node Arc integration will fail, even if you have deleted the ResourceGroup in Azure Portal before running Register-AzStackHCI #>
|
||||
|
||||
$armTokenItemResource = "https://management.core.windows.net/"
|
||||
$graphTokenItemResource = "https://graph.windows.net/"
|
||||
$azContext = Get-AzContext
|
||||
$authFactory = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory
|
||||
$armToken = $authFactory.Authenticate($azContext.Account, $azContext.Environment, $azContext.Tenant.Id, $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $armTokenItemResource).AccessToken
|
||||
$id = $azContext.Account.Id
|
||||
|
||||
#register
|
||||
Register-AzStackHCI -SubscriptionID $subscriptionID -Region $AzureStackLocation -ComputerName $ClusterName -ArmAccessToken $armToken -AccountId $id
|
||||
|
||||
#Install Azure Stack HCI RSAT Tools to all nodes
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock {
|
||||
Install-WindowsFeature -Name RSAT-Azure-Stack-HCI
|
||||
}
|
||||
#Validate registration (query on just one node is needed)
|
||||
Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
||||
Get-AzureStackHCI
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Install modules and create MOC agent Service
|
||||
#Install required modules
|
||||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
|
||||
Install-Module -Name PowershellGet -Force -Confirm:$false -SkipPublisherCheck
|
||||
Update-Module -Name PowerShellGet
|
||||
|
||||
#to be able to install ArcHci and MOC, powershellget 2.2.5 needs to be used - to this posh restart is needed
|
||||
Start-Process -Wait -FilePath PowerShell -ArgumentList {
|
||||
Install-Module -Name MOC -Repository PSGallery -Force -AcceptLicense
|
||||
Install-Module -Name ArcHci -Force -Confirm:$false -SkipPublisherCheck -AcceptLicense
|
||||
}
|
||||
|
||||
#Increase MaxEvenlope and create session to copy files to
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock {Set-Item -Path WSMan:\localhost\MaxEnvelopeSizekb -Value 4096}
|
||||
|
||||
#distribute modules to cluster nodes
|
||||
$ModuleNames="ArcHci","Moc","DownloadSDK"
|
||||
$PSSessions=New-PSSession -ComputerName $ClusterNodeNames
|
||||
Foreach ($PSSession in $PSSessions){
|
||||
Foreach ($ModuleName in $ModuleNames){
|
||||
Copy-Item -Path $env:ProgramFiles\windowspowershell\modules\$ModuleName -Destination $env:ProgramFiles\windowspowershell\modules -ToSession $PSSession -Recurse -Force
|
||||
}
|
||||
Foreach ($ModuleName in $RequiredModules.ModuleName){
|
||||
Copy-Item -Path $env:ProgramFiles\windowspowershell\modules\$ModuleName -Destination $env:ProgramFiles\windowspowershell\modules -ToSession $PSSession -Recurse -Force
|
||||
}
|
||||
}
|
||||
|
||||
#Enable CredSSP
|
||||
# Temporarily enable CredSSP delegation to avoid double-hop issue
|
||||
foreach ($ClusterNodeName in $ClusterNodeNames){
|
||||
Enable-WSManCredSSP -Role "Client" -DelegateComputer $ClusterNodeName -Force
|
||||
}
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Enable-WSManCredSSP Server -Force }
|
||||
|
||||
$SecureStringPassword = ConvertTo-SecureString $CredSSPPassword -AsPlainText -Force
|
||||
$Credentials = New-Object System.Management.Automation.PSCredential ($CredSSPUserName, $SecureStringPassword)
|
||||
|
||||
#initialize MOC
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
Initialize-MocNode
|
||||
}
|
||||
|
||||
#Create volume for MOC if does not exist
|
||||
if (-not (Get-Volume -FriendlyName $VolumeName -CimSession $ClusterName -ErrorAction SilentlyContinue)) {
|
||||
New-Volume -FriendlyName $VolumeName -CimSession $ClusterName -Size 1TB -StoragePoolFriendlyName S2D*
|
||||
}
|
||||
|
||||
#prepare arc resource bridge
|
||||
#Configure MOC
|
||||
Invoke-Command -ComputerName $ClusterNodeNames[0] -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
Set-MocConfig -workingDir $using:VolumePath\workingDir -imageDir $using:VolumePath\imageStore -skipHostLimitChecks -cloudConfigLocation $using:VolumePath\cloudStore -catalog aks-hci-stable-catalogs-ext -ring stable -createAutoConfigContainers $false
|
||||
}
|
||||
|
||||
#Install MOC Cloud Agent Service
|
||||
Invoke-Command -ComputerName $ClusterNodeNames[0] -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
Install-moc
|
||||
}
|
||||
|
||||
# Disable CredSSP
|
||||
Disable-WSManCredSSP -Role Client
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Disable-WSManCredSSP Server }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Create custom location and install Arc Resource Bridge
|
||||
#Login to azure
|
||||
if (!(Get-AzContext)){
|
||||
Connect-AzAccount -UseDeviceAuthentication
|
||||
}
|
||||
|
||||
#generate variables
|
||||
#Grab registration info
|
||||
$RegistrationInfo=Invoke-Command -ComputerName $CLusterName -ScriptBlock {Get-AzureStackHCI}
|
||||
$AzureResourceUri= $RegistrationInfo.AzureResourceUri
|
||||
$HCIResourceGroupName=$AzureResourceUri.split("/")[4]
|
||||
$HCISubscriptionID=$AzureResourceUri.split("/")[2]
|
||||
#create bridge resource name
|
||||
$BridgeResourceName=("$($RegistrationInfo.AzureResourceName)-arcbridge").ToLower()
|
||||
|
||||
#install Az CLI
|
||||
#download
|
||||
Start-BitsTransfer -Source https://aka.ms/installazurecliwindows -Destination $env:userprofile\Downloads\AzureCLI.msi
|
||||
#install
|
||||
Start-Process msiexec.exe -Wait -ArgumentList "/I $env:userprofile\Downloads\AzureCLI.msi /quiet"
|
||||
#add az to enviromental variables so no posh restart is needed
|
||||
[System.Environment]::SetEnvironmentVariable('PATH',$Env:PATH+';C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin')
|
||||
|
||||
#add Az extensions
|
||||
az extension add --name customlocation
|
||||
az extension add --name azurestackhci
|
||||
az extension add --name arcappliance
|
||||
az extension add --name k8s-extension
|
||||
az extension add --name connectedk8s
|
||||
|
||||
#register namespaces
|
||||
#register
|
||||
$Providers="Microsoft.ExtendedLocation","Microsoft.ResourceConnector"
|
||||
foreach ($Provider in $Providers){
|
||||
Register-AzResourceProvider -ProviderNamespace $Provider
|
||||
}
|
||||
#wait until resource providers are registered
|
||||
foreach ($Provider in $Providers){
|
||||
do {
|
||||
$Status=Get-AzResourceProvider -ProviderNamespace $Provider
|
||||
Write-Output "Registration Status - $Provider : $(($status.RegistrationState -match 'Registered').Count)/$($Status.Count)"
|
||||
Start-Sleep 1
|
||||
} while (($status.RegistrationState -match "Registered").Count -ne ($Status.Count))
|
||||
}
|
||||
|
||||
#login with device authentication
|
||||
az login --use-device-code
|
||||
$allSubscriptions = (az account list | ConvertFrom-Json).ForEach({$_ | Select-Object -Property Name, id, tenantId })
|
||||
if (($allSubscriptions).Count -gt 1){
|
||||
$subscription = ($allSubscriptions | Out-GridView -OutputMode Single)
|
||||
az account set --subscription $subscription.id
|
||||
}
|
||||
#create arc appliance
|
||||
#generate config files
|
||||
#Enable CredSSP
|
||||
# Temporarily enable CredSSP delegation to avoid double-hop issue
|
||||
foreach ($ClusterNodeName in $ClusterNodeNames){
|
||||
Enable-WSManCredSSP -Role "Client" -DelegateComputer $ClusterNodeName -Force
|
||||
}
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Enable-WSManCredSSP Server -Force }
|
||||
|
||||
$SecureStringPassword = ConvertTo-SecureString $CredSSPPassword -AsPlainText -Force
|
||||
$Credentials = New-Object System.Management.Automation.PSCredential ($CredSSPUserName, $SecureStringPassword)
|
||||
|
||||
Invoke-Command -ComputerName $ClusterNodeNames[0] -Credential $Credentials -Authentication Credssp -ScriptBlock {
|
||||
New-ArcHciConfigFiles -subscriptionID $using:HCISubscriptionID -location $using:ArcResourceBridgeLocation -resourceGroup $using:HCIResourceGroupName -resourceName $using:BridgeResourceName -workDirectory "\\$using:ClusterName\ClusterStorage$\$using:VolumeName\workingDir" -controlPlaneIP $using:controlPlaneIP -vipPoolStart $using:controlPlaneIP -vipPoolEnd $using:controlPlaneIP -vswitchName $using:vswitchName #-vLanID $vlanID
|
||||
}
|
||||
# Disable CredSSP
|
||||
Disable-WSManCredSSP -Role Client
|
||||
Invoke-Command -ComputerName $ClusterNodeNames -ScriptBlock { Disable-WSManCredSSP Server }
|
||||
|
||||
#prepare
|
||||
az arcappliance prepare hci --config-file \\$ClusterName\ClusterStorage$\$VolumeName\workingDir\hci-appliance.yaml
|
||||
|
||||
#Create folder for config
|
||||
New-Item -Path $env:USERPROFILE\.kube -ItemType Directory -ErrorAction Ignore
|
||||
|
||||
#deploy control plane and export kube config
|
||||
az arcappliance deploy hci --config-file \\$ClusterName\ClusterStorage$\$VolumeName\workingDir\hci-appliance.yaml --outfile $env:USERPROFILE\.kube\config
|
||||
#create connection to Azure (might throw error, dont worry! It's being deployed on background)
|
||||
az arcappliance create hci --config-file \\$ClusterName\ClusterStorage$\$VolumeName\workingDir\hci-appliance.yaml --kubeconfig $env:USERPROFILE\.kube\config
|
||||
|
||||
#wait until appliance is running
|
||||
do {
|
||||
$Status=az arcappliance show --only-show-errors --resource-group $HCIResourceGroupName --name $BridgeResourceName | ConvertFrom-Json
|
||||
Write-Host -NoNewline -Object "."
|
||||
Start-Sleep 2
|
||||
} until ($status.status -match "Running")
|
||||
|
||||
#verify if appliance is running
|
||||
az arcappliance show --resource-group $HCIResourceGroupName --name $BridgeResourceName | ConvertFrom-Json
|
||||
|
||||
#Add hci-vmoperator extension
|
||||
#create
|
||||
az k8s-extension create --cluster-type appliances --cluster-name $BridgeResourceName --resource-group $HCIResourceGroupName --name hci-vmoperator --extension-type Microsoft.AZStackHCI.Operator --scope cluster --release-namespace helm-operator2 --configuration-settings Microsoft.CustomLocation.ServiceAccount=hci-vmoperator --config-protected-file \\$ClusterName\ClusterStorage$\$VolumeName\workingDir\hci-config.json --configuration-settings HCIClusterID=$AzureResourceUri --auto-upgrade true
|
||||
#validate
|
||||
az k8s-extension show --cluster-type appliances --cluster-name $BridgeResourceName --resource-group $HCIResourceGroupName --name hci-vmoperator
|
||||
|
||||
#Create custom location (has to be created after arcappliance deployment)
|
||||
az customlocation create --resource-group $HCIResourceGroupName --name $CustomLocationName --cluster-extension-ids "/subscriptions/$HCISubscriptionID/resourceGroups/$HCIResourceGroupName/providers/Microsoft.ResourceConnector/appliances/$BridgeResourceName/providers/Microsoft.KubernetesConfiguration/extensions/hci-vmoperator" --namespace $CustomLocationNameSpace --host-resource-id "/subscriptions/$HCISubscriptionID/resourceGroups/$HCIResourceGroupName/providers/Microsoft.ResourceConnector/appliances/$BridgeResourceName" --location $ArcResourceBridgeLocation
|
||||
|
||||
<# Or with PowerShell
|
||||
#install Az.CustomLocation module
|
||||
if (!(Get-InstalledModule -Name az.CustomLocation -ErrorAction Ignore)){
|
||||
Install-Module -Name Az.CustomLocation -Force
|
||||
}
|
||||
New-AzCustomLocation -ResourceGroupName $ResourceGroupName -Name $CustomLocationName -ClusterExtensionID "/subscriptions/$SubscriptionID/resourceGroups/$ResourceGroupName/providers/Microsoft.ResourceConnector/appliances/$BridgeResourceName/providers/Microsoft.KubernetesConfiguration/extensions/hci-vmoperator" -NameSpace hci-vmoperator -HostResourceID "/subscriptions/$SubscriptionID/resourceGroups/$ResourceGroupName/providers/Microsoft.ResourceConnector/appliances/$BridgeResourceName" -Location $ArcResourceBridgeLocation
|
||||
#>
|
||||
|
||||
#endregion
|
||||
|
||||
#region create virtual network for arcVMs
|
||||
#Grab registration info
|
||||
$RegistrationInfo=Invoke-Command -ComputerName $CLusterName -ScriptBlock {Get-AzureStackHCI}
|
||||
$AzureResourceUri= $RegistrationInfo.AzureResourceUri
|
||||
$HCIResourceGroupName=$AzureResourceUri.split("/")[4]
|
||||
$HCISubscriptionID=$AzureResourceUri.split("/")[2]
|
||||
#create network
|
||||
az azurestackhci virtualnetwork create --subscription $HCISubscriptionID --resource-group $HCIResourceGroupName --extended-location name="/subscriptions/$HCISubscriptionID/resourceGroups/$HCIResourceGroupName/providers/Microsoft.ExtendedLocation/customLocations/$CustomLocationName" type="CustomLocation" --location $ArcResourceBridgeLocation --network-type "Transparent" --name $vnetName
|
||||
#endregion
|
||||
|
||||
#region Copy kube config to nodes to have it available there
|
||||
$Sessions=New-PSSession -ComputerName $ClusterNodeNames
|
||||
|
||||
#copy kube to cluster nodes
|
||||
Foreach ($Session in $Sessions){
|
||||
Copy-Item -Path "$env:userprofile\.kube" -Destination $env:userprofile -ToSession $Session -Recurse -Force
|
||||
}
|
||||
|
||||
$Sessions | Remove-PSSession
|
||||
#endregion
|
||||
|
||||
#region add aks hybrid extension to the custom location
|
||||
#https://learn.microsoft.com/en-us/azure/aks/hybrid/deploy-aks-service-hci?tabs=powershell#step-2-install-the-aks-hybrid-extension-on-the-azure-arc-resource-bridge
|
||||
|
||||
#add extension
|
||||
$aksHybridExtnName = "aks-hybrid-extn"
|
||||
az k8s-extension create --resource-group $HCIResourceGroupName --cluster-name $BridgeResourceName --cluster-type appliances --name $aksHybridExtnName --extension-type Microsoft.HybridAKSOperator --config Microsoft.CustomLocation.ServiceAccount=$CustomLocationNameSpace
|
||||
|
||||
#Patch your existing custom location to support AKS hybrid alongside Arc VMs
|
||||
$ArcResourceBridgeId=az arcappliance show -g $HCIResourceGroupName --name $BridgeResourceName --query id -o tsv
|
||||
$VMClusterExtensionResourceId=az k8s-extension list -g $HCIResourceGroupName --cluster-name $BridgeResourceName --cluster-type appliances --query "[?extensionType == ``microsoft.azstackhci.operator``].id" -o tsv
|
||||
$AKSClusterExtensionResourceId=az k8s-extension show -g $HCIResourceGroupName --cluster-name $BridgeResourceName --cluster-type appliances --name $aksHybridExtnName --query id -o tsv
|
||||
az customlocation patch --name $customLocationName --namespace $CustomLocationNameSpace --host-resource-id $ArcResourceBridgeId --cluster-extension-ids $VMClusterExtensionResourceId $AKSClusterExtensionResourceId --resource-group $HCIResourceGroupName
|
||||
|
||||
#check
|
||||
az customlocation show --name $customLocationName --resource-group $HCIResourceGroupName --query "clusterExtensionIds" -o tsv
|
||||
#endregion
|
||||
|
||||
#region create virtual network for AKS
|
||||
#https://learn.microsoft.com/en-us/azure/aks/hybrid/create-aks-hybrid-preview-networks?tabs=dhcp%2Clinux-vhd
|
||||
|
||||
#make sure latest module is installed (note required version)
|
||||
Start-Process -Wait -FilePath PowerShell -ArgumentList {
|
||||
Install-Module -Name MOC -Repository PSGallery -Force -AcceptLicense
|
||||
Install-Module -Name ArcHci -Force -Confirm:$false -SkipPublisherCheck -AcceptLicense -RequiredVersion 0.2.24
|
||||
}
|
||||
|
||||
#distribute new module to cluster nodes
|
||||
$ModuleNames="ArcHci"
|
||||
$PSSessions=New-PSSession -ComputerName $ClusterNodeNames
|
||||
Foreach ($PSSession in $PSSessions){
|
||||
Foreach ($ModuleName in $ModuleNames){
|
||||
Copy-Item -Path $env:ProgramFiles\windowspowershell\modules\$ModuleName -Destination $env:ProgramFiles\windowspowershell\modules -ToSession $PSSession -Recurse -Force
|
||||
}
|
||||
Foreach ($ModuleName in $RequiredModules.ModuleName){
|
||||
Copy-Item -Path $env:ProgramFiles\windowspowershell\modules\$ModuleName -Destination $env:ProgramFiles\windowspowershell\modules -ToSession $PSSession -Recurse -Force
|
||||
}
|
||||
}
|
||||
|
||||
#since there was a subnet configured for AKS (note the labconfig), let's exclude VIP pool from dhcp
|
||||
#make sure dhcp tools are installed
|
||||
install-windowsfeature -name RSAT-DHCP
|
||||
#exclude
|
||||
Add-DhcpServerv4ExclusionRange -StartRange $VIPPoolStart -EndRange $VIPPoolEnd -ScopeId $DHCPScopeID -ComputerName $DHCPServer
|
||||
|
||||
#
|
||||
Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
||||
#dhcp does not work as it keeps asking for gw, dns servers...
|
||||
#New-ArcHciVirtualNetwork -name AKSvnet -vswitchname $using:vswitchname -vippoolstart $using:vipPoolStart -vippoolend $using:vipPoolEnd -vlanid $using:vlanid
|
||||
#without dhcp
|
||||
New-ArcHciVirtualNetwork -name $using:AKSVnetName -vswitchname $using:vswitchname -vippoolstart $using:vipPoolStart -vippoolend $using:vipPoolEnd -vlanid $using:vlanid -ipAddressPrefix $Using:ipaddressprefix -gateway $using:gateway -dnsservers $using:DNSServers -k8sNodeIpPoolStart $using:k8sNodeIpPoolStart -k8sNodeIpPoolEnd $using:k8sNodeIpPoolend
|
||||
}
|
||||
|
||||
#Connect your on-premises AKS hybrid network to Azure
|
||||
az extension add --name hybridaks
|
||||
#register namespace provider
|
||||
$Providers="Microsoft.HybridContainerService"
|
||||
foreach ($Provider in $Providers){
|
||||
Register-AzResourceProvider -ProviderNamespace $Provider
|
||||
}
|
||||
#wait until resource providers are registered
|
||||
foreach ($Provider in $Providers){
|
||||
do {
|
||||
$Status=Get-AzResourceProvider -ProviderNamespace $Provider
|
||||
Write-Output "Registration Status - $Provider : $(($status.RegistrationState -match 'Registered').Count)/$($Status.Count)"
|
||||
Start-Sleep 1
|
||||
} while (($status.RegistrationState -match "Registered").Count -ne ($Status.Count))
|
||||
}
|
||||
|
||||
#add network
|
||||
az hybridaks vnet create -n $AKSVnetName -g $HCIResourceGroupName --custom-location $customLocationName --moc-vnet-name $AKSVnetName
|
||||
|
||||
#endregion
|
||||
|
||||
#region add image for aks
|
||||
Invoke-Command -ComputerName $ClusterName -ScriptBlock {
|
||||
Add-ArcHciK8sGalleryImage -k8sVersion 1.22.11 -version 1.0.16.10113
|
||||
}
|
||||
#endregion
|
||||
|
|
@ -178,7 +178,7 @@ function Get-WindowsBuildNumber {
|
|||
$downloadurl = $webcontent.BaseResponse.ResponseUri.AbsoluteUri.Substring(0,$webcontent.BaseResponse.ResponseUri.AbsoluteUri.LastIndexOf('/'))+($webcontent.Links | where-object { $_.'data-url' -match '/Diskspd.*zip$' }|Select-Object -ExpandProperty "data-url")
|
||||
}
|
||||
#>
|
||||
$downloadurl="https://github.com/microsoft/diskspd/releases/download/v2.0.21a/DiskSpd.zip"
|
||||
$downloadurl="https://github.com/microsoft/diskspd/releases/download/v2.1/DiskSpd.ZIP"
|
||||
Invoke-WebRequest -Uri $downloadurl -OutFile "$PSScriptRoot\Temp\ToolsVHD\DiskSpd\diskspd.zip"
|
||||
}catch{
|
||||
WriteError "`t Failed to download Diskspd!"
|
||||
|
|
|
@ -354,13 +354,13 @@ If (-not $isAdmin) {
|
|||
Kind = "Full"
|
||||
Edition="4"
|
||||
VHDName="Win2016_G2.vhdx"
|
||||
Size=60GB
|
||||
Size=127GB
|
||||
}
|
||||
$ServerVHDs += @{
|
||||
Kind = "Core"
|
||||
Edition="3"
|
||||
VHDName="Win2016Core_G2.vhdx"
|
||||
Size=30GB
|
||||
Size=127GB
|
||||
}
|
||||
<# Removed since it does not work with newer than 14393.2724
|
||||
$ServerVHDs += @{
|
||||
|
@ -376,13 +376,13 @@ If (-not $isAdmin) {
|
|||
Kind = "Full"
|
||||
Edition="4"
|
||||
VHDName="Win2019_G2.vhdx"
|
||||
Size=60GB
|
||||
Size=127GB
|
||||
}
|
||||
$ServerVHDs += @{
|
||||
Kind = "Core"
|
||||
Edition="3"
|
||||
VHDName="Win2019Core_G2.vhdx"
|
||||
Size=30GB
|
||||
Size=127GB
|
||||
}
|
||||
}elseif ($BuildNumber -eq 20348){
|
||||
#Windows Server 2022
|
||||
|
@ -390,20 +390,20 @@ If (-not $isAdmin) {
|
|||
Kind = "Full"
|
||||
Edition="4"
|
||||
VHDName="Win2022_G2.vhdx"
|
||||
Size=60GB
|
||||
Size=127GB
|
||||
}
|
||||
$ServerVHDs += @{
|
||||
Kind = "Core"
|
||||
Edition="3"
|
||||
VHDName="Win2022Core_G2.vhdx"
|
||||
Size=30GB
|
||||
Size=127GB
|
||||
}
|
||||
}elseif ($BuildNumber -gt 20348 -and $SAC){
|
||||
$ServerVHDs += @{
|
||||
Kind = "Core"
|
||||
Edition="2"
|
||||
VHDName="WinSrvInsiderCore_$BuildNumber.vhdx"
|
||||
Size=30GB
|
||||
Size=127GB
|
||||
}
|
||||
#DCEdition fix
|
||||
if ($LabConfig.DCEdition -gt 2){
|
||||
|
@ -415,13 +415,13 @@ If (-not $isAdmin) {
|
|||
Kind = "Full"
|
||||
Edition="4"
|
||||
VHDName="WinSrvInsider_$BuildNumber.vhdx"
|
||||
Size=60GB
|
||||
Size=127GB
|
||||
}
|
||||
$ServerVHDs += @{
|
||||
Kind = "Core"
|
||||
Edition="3"
|
||||
VHDName="WinSrvInsiderCore_$BuildNumber.vhdx"
|
||||
Size=30GB
|
||||
Size=127GB
|
||||
}
|
||||
}else{
|
||||
$ISOServer | Dismount-DiskImage
|
||||
|
|
|
@ -168,11 +168,14 @@ If (-not $isAdmin) {
|
|||
20348 {
|
||||
"AzSHCI21H2_G2.vhdx"
|
||||
}
|
||||
22621 {
|
||||
20349 {
|
||||
"AzSHCI22H2_G2.vhdx"
|
||||
}
|
||||
25398 {
|
||||
"AzSHCI23H2_G2.vhdx"
|
||||
}
|
||||
if ($BuildNumber -GT 20348){
|
||||
}
|
||||
if ($BuildNumber -GT 25398){
|
||||
$tempvhdname="AzSHCIInsider_$BuildNumber.vhdx"
|
||||
}
|
||||
}elseif (($Edition -like "*Server*Core*") -or ($Edition -like "Windows Server * Datacenter") -or ($Edition -like "Windows Server * Standard")){
|
||||
|
@ -321,9 +324,9 @@ If (-not $isAdmin) {
|
|||
if(!$vhdname){$vhdname=$tempvhdname}
|
||||
|
||||
#ask for size
|
||||
[int64]$size=(Read-Host -Prompt "Please type size of the Image in GB. If nothing specified, 60 is used")
|
||||
[int64]$size=(Read-Host -Prompt "Please type size of the Image in GB. If nothing specified, 127 is used")
|
||||
$size=$size*1GB
|
||||
if (!$size){$size=60GB}
|
||||
if (!$size){$size=127GB}
|
||||
|
||||
#Create VHD
|
||||
if ($nanoserver -eq "y"){
|
||||
|
|
Загрузка…
Ссылка в новой задаче